summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.bazelproject2
-rw-r--r--.bazelversion2
-rw-r--r--.gitignore28
-rw-r--r--.gitmodules20
-rw-r--r--.gitreview2
-rw-r--r--.zuul.yaml4
-rw-r--r--BUILD9
-rw-r--r--Documentation/BUILD15
-rw-r--r--Documentation/access-control.txt82
-rwxr-xr-xDocumentation/check_licenses_test.sh15
-rw-r--r--Documentation/cmd-create-project.txt7
-rw-r--r--Documentation/cmd-flush-caches.txt2
-rw-r--r--Documentation/cmd-gsql.txt64
-rw-r--r--Documentation/cmd-index.txt5
-rw-r--r--Documentation/cmd-ls-user-refs.txt4
-rw-r--r--Documentation/cmd-query.txt8
-rw-r--r--Documentation/cmd-review.txt4
-rw-r--r--Documentation/cmd-set-project.txt2
-rw-r--r--Documentation/cmd-stream-events.txt2
-rw-r--r--Documentation/config-auto-site-initialization.txt79
-rw-r--r--Documentation/config-cla.txt14
-rw-r--r--Documentation/config-gerrit.txt710
-rw-r--r--Documentation/config-login-register.txt137
-rw-r--r--Documentation/config-plugins.txt50
-rw-r--r--Documentation/config-project-config.txt215
-rw-r--r--Documentation/config-robot-comments.txt1
-rw-r--r--Documentation/config-themes.txt24
-rw-r--r--Documentation/database-setup.txt280
-rw-r--r--Documentation/dev-bazel.txt46
-rw-r--r--Documentation/dev-contributing.txt95
-rw-r--r--Documentation/dev-design.txt75
-rw-r--r--Documentation/dev-e2e-tests.txt80
-rw-r--r--Documentation/dev-eclipse.txt61
-rw-r--r--Documentation/dev-inspector.txt62
-rw-r--r--Documentation/dev-intellij.txt181
-rw-r--r--Documentation/dev-plugins.txt656
-rw-r--r--Documentation/dev-readme.txt22
-rw-r--r--Documentation/dev-release-deploy-config.txt2
-rw-r--r--Documentation/dev-release-subproject.txt2
-rw-r--r--Documentation/dev-release.txt134
-rw-r--r--Documentation/error-changeid-above-footer.txt31
-rw-r--r--Documentation/error-messages.txt1
-rw-r--r--Documentation/error-missing-changeid.txt19
-rw-r--r--Documentation/images/inline-edit-create-change-project-screen-dialog.pngbin12520 -> 0 bytes
-rw-r--r--Documentation/images/inline-edit-create-change-project-screen.pngbin67161 -> 0 bytes
-rw-r--r--Documentation/images/inline-edit-create-follow-up-change.pngbin114660 -> 0 bytes
-rw-r--r--Documentation/images/inline-edit-edit-in-diff-screen-patch-list.pngbin12974 -> 0 bytes
-rw-r--r--Documentation/images/inline-edit-edit-in-patch-list.pngbin24488 -> 0 bytes
-rw-r--r--Documentation/index.txt1
-rw-r--r--Documentation/install-j2ee.txt26
-rw-r--r--Documentation/install.txt23
-rw-r--r--Documentation/intro-project-owner.txt25
-rw-r--r--Documentation/intro-user.txt72
-rw-r--r--Documentation/js-api.txt6
-rw-r--r--Documentation/js_licenses.txt509
-rw-r--r--Documentation/licenses.txt3458
-rw-r--r--Documentation/metrics.txt17
-rw-r--r--Documentation/note-db.txt41
-rw-r--r--Documentation/pg-plugin-admin-api.txt2
-rw-r--r--Documentation/pg-plugin-dev.txt8
-rw-r--r--Documentation/pg-plugin-endpoints.txt49
-rw-r--r--Documentation/pgm-LocalUsernamesToLowerCase.txt2
-rw-r--r--Documentation/pgm-daemon.txt15
-rw-r--r--Documentation/pgm-gsql.txt55
-rw-r--r--Documentation/pgm-index.txt3
-rw-r--r--Documentation/pgm-init.txt5
-rw-r--r--Documentation/pgm-prolog-shell.txt4
-rw-r--r--Documentation/pgm-rulec.txt3
-rw-r--r--Documentation/project-configuration.txt196
-rw-r--r--Documentation/prolog-cookbook.txt33
-rw-r--r--Documentation/quota.txt50
-rwxr-xr-xDocumentation/replace_macros.py35
-rw-r--r--Documentation/rest-api-accounts.txt62
-rw-r--r--Documentation/rest-api-changes.txt49
-rw-r--r--Documentation/rest-api-config.txt41
-rw-r--r--Documentation/rest-api-plugins.txt14
-rw-r--r--Documentation/rest-api-projects.txt54
-rw-r--r--Documentation/rest-api.txt5
-rw-r--r--Documentation/user-search.txt42
-rw-r--r--Documentation/user-upload.txt2
-rw-r--r--WORKSPACE289
-rw-r--r--antlr3/BUILD3
-rw-r--r--antlr3/com/google/gerrit/index/query/Query.g20
-rw-r--r--contrib/mitm-ui/README.md61
-rw-r--r--contrib/mitm-ui/add-header.py5
-rwxr-xr-xcontrib/mitm-ui/dev-chrome.sh8
-rw-r--r--contrib/mitm-ui/force-version.py22
-rwxr-xr-xcontrib/mitm-ui/mitm-docker.sh43
-rwxr-xr-xcontrib/mitm-ui/mitm-plugins.sh39
-rwxr-xr-xcontrib/mitm-ui/mitm-serve-app-dev.sh15
-rwxr-xr-xcontrib/mitm-ui/mitm-single-plugin.sh38
-rwxr-xr-xcontrib/mitm-ui/mitm-theme.sh31
-rw-r--r--contrib/mitm-ui/serve-app-dev.py169
-rw-r--r--contrib/mitm-ui/serve-app-locally.py46
-rw-r--r--contrib/themes/diffy/etc/GerritSite.css29
-rw-r--r--contrib/themes/diffy/etc/GerritSiteHeader.html5
-rw-r--r--contrib/themes/diffy/static/diffy.svg326
-rw-r--r--contrib/themes/diffy/static/diffymute.svg198
-rw-r--r--contrib/themes/diffy/static/logo.pngbin3701 -> 0 bytes
-rw-r--r--contrib/themes/spotify/etc/GerritSite.css113
-rw-r--r--contrib/themes/spotify/etc/GerritSiteHeader.html1
-rw-r--r--contrib/themes/spotify/static/background-gradient.pngbin761 -> 0 bytes
-rw-r--r--contrib/themes/spotify/static/background-spotigreen.jpgbin18718 -> 0 bytes
-rw-r--r--contrib/themes/spotify/static/logo.pngbin19543 -> 0 bytes
-rw-r--r--e2e-tests/build.sbt5
-rw-r--r--e2e-tests/project/Dependencies.scala8
-rw-r--r--e2e-tests/project/plugins.sbt1
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json5
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json6
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject-body.json3
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json3
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json5
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json5
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json5
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json2
-rw-r--r--e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json2
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala6
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CacheFlushSimulation.scala2
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckMasterBranchReplica1.scala73
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala58
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.scala6
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala22
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala18
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala10
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala6
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala8
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala20
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.scala47
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala49
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetMasterBranchRevision.scala41
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetProjectsCacheEntries.scala6
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/ListProjects.scala33
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala4
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala18
-rw-r--r--e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala29
-rw-r--r--gerrit-gwtdebug/BUILD18
-rw-r--r--gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritGwtDebugLauncher.java77
-rw-r--r--gerrit-gwtui-common/BUILD63
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/GerritGwtUICommon.gwt.xml43
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/AccountFormatter.java70
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.java46
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.properties13
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.java40
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.properties9
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java99
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/GerritUiExtensionPoint.java46
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java142
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java126
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java93
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ActionInfo.java32
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AgreementInfo.java29
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java121
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java575
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/DownloadInfo.java126
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java75
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GeneralPreferences.java274
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GerritInfo.java70
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GpgKeyInfo.java50
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupBaseInfo.java31
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupInfo.java67
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java34
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/PushCertificateInfo.java25
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java113
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenu.java27
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuItem.java40
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuList.java22
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/WebLinkInfo.java50
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java101
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeString.java63
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/Natives.java107
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java38
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/HighlightSuggestion.java55
-rw-r--r--gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/RemoteSuggestOracle.java129
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/arrow_undo.pngbin631 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cog.pngbin512 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/comment_draft.pngbin382 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cross.pngbin655 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/diffy26.pngbin1147 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/disk.pngbin620 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/exclamation.pngbin701 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/find.pngbin659 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goDown.pngbin653 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goNext.pngbin623 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goPrev.pngbin609 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goUp.pngbin637 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/help.pngbin786 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lightbulb.pngbin782 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/listAdd.pngbin323 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lock.pngbin749 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/merge.pngbin710 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/note_add.pngbin641 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_edit.pngbin807 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_white_put.pngbin523 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_down_gray.pngbin282 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_next_gray.pngbin398 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_up_gray.pngbin277 -> 0 bytes
-rwxr-xr-xgerrit-gwtui-common/src/main/resources/com/google/gerrit/client/sideBySideDiff.pngbin3097 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star-open.pngbin488 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star.pngbin670 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue.pngbin586 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue_add.pngbin671 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tick.pngbin537 -> 0 bytes
-rwxr-xr-xgerrit-gwtui-common/src/main/resources/com/google/gerrit/client/unifiedDiff.pngbin3099 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_add.pngbin746 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_edit.pngbin833 -> 0 bytes
-rw-r--r--gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java216
-rw-r--r--gerrit-gwtui-common/src/test/java/com/google/gerrit/client/ui/HighlightSuggestionTest.java51
-rw-r--r--gerrit-gwtui/BUILD55
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml39
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml41
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java202
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationCallback.java33
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java88
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffObject.java183
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffWebLinkInfo.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java879
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java168
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java132
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java1134
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java199
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties121
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java299
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties21
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java28
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java103
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.java91
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml71
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java34
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java99
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/RangeInfo.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java74
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java163
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java312
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/StringListPanel.java359
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/Themer.java83
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java65
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java85
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml70
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/VoidResult.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/access/AccessMap.java51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java287
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java311
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties274
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties4
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java484
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java226
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java161
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EmailInfo.java28
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdInfo.java89
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java97
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java32
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyEditPreferencesScreen.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java289
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.ui.xml94
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java43
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java224
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java198
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPasswordScreen.java161
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java515
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java127
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MySshKeysScreen.java32
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchedProjectsScreen.java228
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java193
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java258
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchInfo.java97
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java141
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java103
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java52
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshKeyInfo.java33
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java387
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java204
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java52
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java118
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java295
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml157
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java159
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java243
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java436
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java97
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java293
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties204
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java46
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties13
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java37
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java69
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateGroupScreen.java160
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java306
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java53
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java232
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java116
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java151
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PaginatedProjectScreen.java75
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java329
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml146
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java74
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java258
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml107
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java122
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java31
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java196
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml82
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java312
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml87
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java673
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectDashboardsScreen.java63
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java826
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java259
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectScreen.java61
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java571
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RangeBox.java85
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java95
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java72
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java209
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml64
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css53
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/arrow_undo.pngbin631 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.pngbin313 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.pngbin334 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java293
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java322
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java63
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java99
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java48
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionPanel.java205
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionScreen.java134
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionSettingsScreen.java139
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/HtmlTemplate.java153
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java99
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java196
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginName.java107
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PopupHelper.java72
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java65
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java50
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.java23
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.properties2
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/blame/BlameInfo.java33
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java50
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.java105
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java260
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml96
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java82
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java114
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml43
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.java239
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.ui.xml66
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AssigneeSuggestOracle.java69
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeActions.java91
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java79
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties36
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.properties9
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java1653
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml634
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java80
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java219
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml198
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java78
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java121
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml43
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java41
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java261
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java69
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java57
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml37
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java940
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java272
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml82
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java141
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInAction.java39
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.java106
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml77
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java353
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java100
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml45
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LocalComments.java276
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java209
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml136
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java67
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsAction.java42
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java233
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml76
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PathSuggestOracle.java85
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ProjectChangeId.java117
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java111
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java70
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java459
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java594
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java78
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java116
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java127
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java541
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml85
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java38
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java50
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java75
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReviewerSuggestOracle.java104
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java328
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml69
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RightSidePopdownAction.java81
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/StarIcon.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java57
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitFailureDialog.java31
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java148
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java77
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml65
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateCheckTimer.java94
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/VotableInfo.java37
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/common.css63
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css115
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/moreLess.pngbin235 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css98
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java212
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java401
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java187
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties103
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java142
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java100
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeListScreen.java17
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java57
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java558
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentApi.java77
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java154
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CustomDashboardScreen.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java119
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java135
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ProjectDashboardScreen.java62
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java96
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInfo.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java88
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/RevisionInfoCache.java49
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java201
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitInfo.java28
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java79
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/config/CapabilityInfo.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java60
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.java31
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.properties6
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardInfo.java47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java53
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java154
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/Util.java21
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java137
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.css147
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java158
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java202
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java451
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java56
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java165
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java116
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java46
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java190
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java931
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.css41
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java184
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Direction.java21
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java466
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml96
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java71
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java367
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml95
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java62
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java225
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/NoOpKeyCommand.java28
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java240
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml74
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesAction.java86
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java644
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml341
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java239
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml82
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java43
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java141
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java101
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java118
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java414
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java262
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentGroup.java159
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java136
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.java112
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml147
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java217
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml52
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java149
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java383
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.ui.xml30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java330
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentGroup.java88
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java203
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.java84
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.ui.xml152
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goNext.pngbin623 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goPrev.pngbin609 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goUp.pngbin637 -> 0 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties5
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java35
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java23
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties2
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java78
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java126
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java22
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java60
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java60
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java66
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java102
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlPanel.java62
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java28
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties7
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesAction.java71
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java333
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml282
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java699
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml185
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css1060
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java270
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupAuditEventInfo.java50
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupList.java34
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupMap.java78
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css88
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java91
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties44
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties4
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties4
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java22
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SkippedLine.java50
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java28
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java262
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java141
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java461
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java55
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java82
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/RefInfo.java30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ThemeInfo.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java260
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java111
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpCallback.java22
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpResponse.java56
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java519
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java26
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties2
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java80
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java67
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java22
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java88
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java70
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java85
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java107
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentLinkProcessor.java95
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentedActionDialog.java110
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java115
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java116
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java222
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingInlineHyperlink.java36
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingProjectsTable.java38
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HintTextBox.java218
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java68
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/InlineHyperlink.java52
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java91
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MenuScreen.java76
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MorphingTabPanel.java101
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java107
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java408
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java27
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java96
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java192
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PagingHyperlink.java34
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java100
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java233
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java35
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectSearchLink.java33
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java121
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java169
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RemoteSuggestBox.java165
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java199
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadEvent.java42
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadHandler.java21
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java29
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestAfterTypingNCharsOracle.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextAreaActionDialog.java40
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java41
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties13
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.properties3
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UserActivityMonitor.java113
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Util.java48
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml24
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java92
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java28
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.java23
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.properties2
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java448
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java31
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java46
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Extras.java221
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/KeyMap.java43
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java35
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java41
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/LintLine.java63
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java100
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java50
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java42
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Rect.java30
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java40
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java54
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java70
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/lib/style.css124
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java270
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInjector.java114
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java510
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js26
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/theme/ThemeLoader.java119
-rw-r--r--gerrit-gwtui/src/main/java/net/codemirror/theme/Themes.java165
-rw-r--r--gerrit-gwtui/src/test/java/com/google/gerrit/client/change/ProjectChangeIdTest.java86
-rw-r--r--gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/LineMapperTest.java117
-rw-r--r--gerrit-plugin-gwtui/BUILD83
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml26
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java76
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java155
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/PluginEntryPoint.java46
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/extension/Panel.java109
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java21
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java160
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/screen/Screen.java144
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/ui/GroupSuggestOracle.java75
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java31
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java86
-rw-r--r--gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js3
-rw-r--r--java/com/google/gerrit/acceptance/AbstractDaemonTest.java404
-rw-r--r--java/com/google/gerrit/acceptance/AbstractNotificationTest.java75
-rw-r--r--java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java201
-rw-r--r--java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java48
-rw-r--r--java/com/google/gerrit/acceptance/AccountCreator.java11
-rw-r--r--java/com/google/gerrit/acceptance/BUILD17
-rw-r--r--java/com/google/gerrit/acceptance/ConfigAnnotationParser.java37
-rw-r--r--java/com/google/gerrit/acceptance/DisabledChangeIndex.java12
-rw-r--r--java/com/google/gerrit/acceptance/EventRecorder.java2
-rw-r--r--java/com/google/gerrit/acceptance/FakeGroupAuditService.java95
-rw-r--r--java/com/google/gerrit/acceptance/GcAssert.java9
-rw-r--r--java/com/google/gerrit/acceptance/GerritServer.java161
-rw-r--r--java/com/google/gerrit/acceptance/HttpSession.java7
-rw-r--r--java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java57
-rw-r--r--java/com/google/gerrit/acceptance/InProcessProtocol.java86
-rw-r--r--java/com/google/gerrit/acceptance/ProjectResetter.java8
-rw-r--r--java/com/google/gerrit/acceptance/PushOneCommit.java100
-rw-r--r--java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java11
-rw-r--r--java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java10
-rw-r--r--java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java12
-rw-r--r--java/com/google/gerrit/acceptance/RestResponse.java7
-rw-r--r--java/com/google/gerrit/acceptance/RestSession.java2
-rw-r--r--java/com/google/gerrit/acceptance/SkipProjectClone.java26
-rw-r--r--java/com/google/gerrit/acceptance/SshSession.java31
-rw-r--r--java/com/google/gerrit/acceptance/StandaloneSiteTest.java21
-rw-r--r--java/com/google/gerrit/acceptance/TestAccount.java79
-rw-r--r--java/com/google/gerrit/acceptance/ssh/GracefulCommand.java31
-rw-r--r--java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java31
-rw-r--r--java/com/google/gerrit/acceptance/ssh/TestCommand.java49
-rw-r--r--java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java25
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/ThrowingConsumer.java8
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/ThrowingFunction.java8
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java10
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java34
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java4
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/TestAccountUpdate.java4
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java8
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java10
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java38
-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/ProjectOperations.java44
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java101
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java71
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java78
-rw-r--r--java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java114
-rw-r--r--java/com/google/gerrit/asciidoctor/AsciiDoctor.java12
-rw-r--r--java/com/google/gerrit/common/BUILD28
-rw-r--r--java/com/google/gerrit/common/Common.gwt.xml24
-rw-r--r--java/com/google/gerrit/common/FileUtil.java2
-rw-r--r--java/com/google/gerrit/common/FooterConstants.java2
-rw-r--r--java/com/google/gerrit/common/FormatUtil.java25
-rw-r--r--java/com/google/gerrit/common/IoUtil.java2
-rw-r--r--java/com/google/gerrit/common/PluginData.java2
-rw-r--r--java/com/google/gerrit/common/ProjectAccessUtil.java66
-rw-r--r--java/com/google/gerrit/common/ProjectUtil.java43
-rw-r--r--java/com/google/gerrit/common/RawInputUtil.java2
-rw-r--r--java/com/google/gerrit/common/SiteLibraryLoaderUtil.java11
-rw-r--r--java/com/google/gerrit/common/UsedAt.java43
-rw-r--r--java/com/google/gerrit/common/Version.java2
-rw-r--r--java/com/google/gerrit/common/audit/Audit.java35
-rw-r--r--java/com/google/gerrit/common/auth/SignInRequired.java30
-rw-r--r--java/com/google/gerrit/common/data/AccessSection.java45
-rw-r--r--java/com/google/gerrit/common/data/CommentDetail.java2
-rw-r--r--java/com/google/gerrit/common/data/ContributorAgreement.java24
-rw-r--r--java/com/google/gerrit/common/data/GlobalCapability.java2
-rw-r--r--java/com/google/gerrit/common/data/GroupDetail.java37
-rw-r--r--java/com/google/gerrit/common/data/GroupInfo.java71
-rw-r--r--java/com/google/gerrit/common/data/HostPageData.java60
-rw-r--r--java/com/google/gerrit/common/data/LabelType.java13
-rw-r--r--java/com/google/gerrit/common/data/ParameterizedString.java4
-rw-r--r--java/com/google/gerrit/common/data/Permission.java11
-rw-r--r--java/com/google/gerrit/common/data/ProjectAccess.java142
-rw-r--r--java/com/google/gerrit/common/data/ProjectAdminService.java49
-rw-r--r--java/com/google/gerrit/common/data/RefConfigSection.java60
-rw-r--r--java/com/google/gerrit/common/data/SshHostKey.java45
-rw-r--r--java/com/google/gerrit/common/data/SubmitRecord.java6
-rw-r--r--java/com/google/gerrit/common/data/SubmitRequirement.java2
-rw-r--r--java/com/google/gerrit/common/data/SubscribeSection.java2
-rw-r--r--java/com/google/gerrit/common/data/SystemInfoService.java31
-rw-r--r--java/com/google/gerrit/common/data/WebLinkInfoCommon.java24
-rw-r--r--java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java11
-rw-r--r--java/com/google/gerrit/common/errors/EmailException.java29
-rw-r--r--java/com/google/gerrit/common/errors/InvalidNameException.java30
-rw-r--r--java/com/google/gerrit/common/errors/InvalidSshKeyException.java30
-rw-r--r--java/com/google/gerrit/common/errors/NameAlreadyUsedException.java26
-rw-r--r--java/com/google/gerrit/common/errors/NoSuchAccountException.java26
-rw-r--r--java/com/google/gerrit/common/errors/NoSuchEntityException.java30
-rw-r--r--java/com/google/gerrit/common/errors/NoSuchGroupException.java52
-rw-r--r--java/com/google/gerrit/common/errors/NotSignedInException.java26
-rw-r--r--java/com/google/gerrit/common/errors/UpdateParentFailedException.java26
-rw-r--r--java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java109
-rw-r--r--java/com/google/gerrit/elasticsearch/BUILD3
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java6
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java30
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java6
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java4
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java6
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java10
-rw-r--r--java/com/google/gerrit/elasticsearch/ElasticVersion.java5
-rw-r--r--java/com/google/gerrit/exceptions/BUILD8
-rw-r--r--java/com/google/gerrit/exceptions/DuplicateKeyException.java28
-rw-r--r--java/com/google/gerrit/exceptions/EmailException.java29
-rw-r--r--java/com/google/gerrit/exceptions/InvalidNameException.java30
-rw-r--r--java/com/google/gerrit/exceptions/InvalidSshKeyException.java30
-rw-r--r--java/com/google/gerrit/exceptions/NameAlreadyUsedException.java26
-rw-r--r--java/com/google/gerrit/exceptions/NoSuchAccountException.java26
-rw-r--r--java/com/google/gerrit/exceptions/NoSuchEntityException.java30
-rw-r--r--java/com/google/gerrit/exceptions/NoSuchGroupException.java52
-rw-r--r--java/com/google/gerrit/exceptions/NotSignedInException.java26
-rw-r--r--java/com/google/gerrit/exceptions/StorageException.java44
-rw-r--r--java/com/google/gerrit/extensions/BUILD13
-rw-r--r--java/com/google/gerrit/extensions/Extensions.gwt.xml18
-rw-r--r--java/com/google/gerrit/extensions/annotations/ExportImpl.java52
-rw-r--r--java/com/google/gerrit/extensions/annotations/Exports.java7
-rw-r--r--java/com/google/gerrit/extensions/api/access/CoreOrPluginProjectPermission.java18
-rw-r--r--java/com/google/gerrit/extensions/api/access/PluginProjectPermission.java87
-rw-r--r--java/com/google/gerrit/extensions/api/changes/ChangeApi.java151
-rw-r--r--java/com/google/gerrit/extensions/api/changes/Changes.java35
-rw-r--r--java/com/google/gerrit/extensions/api/changes/FileApi.java28
-rw-r--r--java/com/google/gerrit/extensions/api/changes/RevisionApi.java41
-rw-r--r--java/com/google/gerrit/extensions/api/config/Server.java9
-rw-r--r--java/com/google/gerrit/extensions/api/groups/GroupApi.java59
-rw-r--r--java/com/google/gerrit/extensions/api/projects/ConfigInfo.java1
-rw-r--r--java/com/google/gerrit/extensions/api/projects/Projects.java1
-rw-r--r--java/com/google/gerrit/extensions/api/projects/ThemeInfo.java29
-rw-r--r--java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java2
-rw-r--r--java/com/google/gerrit/extensions/client/EditPreferencesInfo.java4
-rw-r--r--java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java5
-rw-r--r--java/com/google/gerrit/extensions/client/KeyMapType.java22
-rw-r--r--java/com/google/gerrit/extensions/client/ListAccountsOption.java31
-rw-r--r--java/com/google/gerrit/extensions/client/ListChangesOption.java31
-rw-r--r--java/com/google/gerrit/extensions/client/ListGroupsOption.java31
-rw-r--r--java/com/google/gerrit/extensions/client/ListOption.java49
-rw-r--r--java/com/google/gerrit/extensions/client/MenuItem.java5
-rw-r--r--java/com/google/gerrit/extensions/client/Theme.java123
-rw-r--r--java/com/google/gerrit/extensions/client/UiType.java32
-rw-r--r--java/com/google/gerrit/extensions/common/ChangeInfo.java1
-rw-r--r--java/com/google/gerrit/extensions/common/GerritInfo.java5
-rw-r--r--java/com/google/gerrit/extensions/common/MessageOfTheDayInfo.java27
-rw-r--r--java/com/google/gerrit/extensions/common/PluginInfo.java10
-rw-r--r--java/com/google/gerrit/extensions/common/ServerInfo.java5
-rw-r--r--java/com/google/gerrit/extensions/common/testing/BUILD1
-rw-r--r--java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java20
-rw-r--r--java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java38
-rw-r--r--java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java14
-rw-r--r--java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java14
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java7
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java9
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java17
-rw-r--r--java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java17
-rw-r--r--java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java22
-rw-r--r--java/com/google/gerrit/extensions/common/testing/RangeSubject.java21
-rw-r--r--java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java15
-rw-r--r--java/com/google/gerrit/extensions/config/CapabilityDefinition.java5
-rw-r--r--java/com/google/gerrit/extensions/config/PluginPermissionDefinition.java25
-rw-r--r--java/com/google/gerrit/extensions/config/PluginProjectPermissionDefinition.java21
-rw-r--r--java/com/google/gerrit/extensions/persistence/DataSourceInterceptor.java21
-rw-r--r--java/com/google/gerrit/extensions/registration/DynamicItem.java7
-rw-r--r--java/com/google/gerrit/extensions/registration/DynamicSet.java19
-rw-r--r--java/com/google/gerrit/extensions/registration/Extension.java2
-rw-r--r--java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java7
-rw-r--r--java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java13
-rw-r--r--java/com/google/gerrit/extensions/systemstatus/MessageOfTheDay.java63
-rw-r--r--java/com/google/gerrit/extensions/webui/GwtPlugin.java33
-rw-r--r--java/com/google/gerrit/extensions/webui/WebUiPlugin.java5
-rw-r--r--java/com/google/gerrit/git/BUILD11
-rw-r--r--java/com/google/gerrit/git/LockFailureException.java49
-rw-r--r--java/com/google/gerrit/git/RefUpdateUtil.java183
-rw-r--r--java/com/google/gerrit/git/testing/CommitSubject.java97
-rw-r--r--java/com/google/gerrit/git/testing/ObjectIdSubject.java41
-rw-r--r--java/com/google/gerrit/git/testing/PushResultSubject.java20
-rw-r--r--java/com/google/gerrit/gpg/BUILD3
-rw-r--r--java/com/google/gerrit/gpg/CheckResult.java4
-rw-r--r--java/com/google/gerrit/gpg/GerritPublicKeyChecker.java5
-rw-r--r--java/com/google/gerrit/gpg/PublicKeyChecker.java2
-rw-r--r--java/com/google/gerrit/gpg/PublicKeyStore.java124
-rw-r--r--java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java7
-rw-r--r--java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java3
-rw-r--r--java/com/google/gerrit/gpg/server/DeleteGpgKey.java11
-rw-r--r--java/com/google/gerrit/gpg/server/GpgKeys.java5
-rw-r--r--java/com/google/gerrit/gpg/server/PostGpgKeys.java15
-rw-r--r--java/com/google/gerrit/gpg/testing/TestKeys.java77
-rw-r--r--java/com/google/gerrit/httpd/AllRequestFilter.java8
-rw-r--r--java/com/google/gerrit/httpd/AllowRenderInFrameFilter.java59
-rw-r--r--java/com/google/gerrit/httpd/BUILD8
-rw-r--r--java/com/google/gerrit/httpd/CacheBasedWebSession.java3
-rw-r--r--java/com/google/gerrit/httpd/GitOverHttpServlet.java219
-rw-r--r--java/com/google/gerrit/httpd/GwtCacheControlFilter.java104
-rw-r--r--java/com/google/gerrit/httpd/HttpRequestContext.java12
-rw-r--r--java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java70
-rw-r--r--java/com/google/gerrit/httpd/RunAsFilter.java17
-rw-r--r--java/com/google/gerrit/httpd/UniversalWebLoginFilter.java23
-rw-r--r--java/com/google/gerrit/httpd/UrlModule.java89
-rw-r--r--java/com/google/gerrit/httpd/WebModule.java6
-rw-r--r--java/com/google/gerrit/httpd/XsrfConstants.java30
-rw-r--r--java/com/google/gerrit/httpd/XsrfCookieFilter.java3
-rw-r--r--java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java92
-rw-r--r--java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java13
-rw-r--r--java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java5
-rw-r--r--java/com/google/gerrit/httpd/auth/oauth/BUILD4
-rw-r--r--java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java5
-rw-r--r--java/com/google/gerrit/httpd/auth/openid/BUILD5
-rw-r--r--java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java5
-rw-r--r--java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java14
-rw-r--r--java/com/google/gerrit/httpd/gitweb/GitwebCssServlet.java4
-rw-r--r--java/com/google/gerrit/httpd/init/BUILD3
-rw-r--r--java/com/google/gerrit/httpd/init/ReviewDbDataSourceProvider.java77
-rw-r--r--java/com/google/gerrit/httpd/init/SiteInitializer.java11
-rw-r--r--java/com/google/gerrit/httpd/init/WebAppInitializer.java91
-rw-r--r--java/com/google/gerrit/httpd/plugins/HttpPluginModule.java4
-rw-r--r--java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java18
-rw-r--r--java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java18
-rw-r--r--java/com/google/gerrit/httpd/raw/BazelBuild.java5
-rw-r--r--java/com/google/gerrit/httpd/raw/CatServlet.java16
-rw-r--r--java/com/google/gerrit/httpd/raw/DirectoryGwtUiServlet.java54
-rw-r--r--java/com/google/gerrit/httpd/raw/HostPageServlet.java411
-rw-r--r--java/com/google/gerrit/httpd/raw/IndexServlet.java22
-rw-r--r--java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java4
-rw-r--r--java/com/google/gerrit/httpd/raw/RecompileGwtUiFilter.java126
-rw-r--r--java/com/google/gerrit/httpd/raw/ResourceServlet.java8
-rw-r--r--java/com/google/gerrit/httpd/raw/StaticModule.java114
-rw-r--r--java/com/google/gerrit/httpd/raw/ThemeFactory.java64
-rw-r--r--java/com/google/gerrit/httpd/raw/ToolServlet.java17
-rw-r--r--java/com/google/gerrit/httpd/raw/UserAgentRule.java93
-rw-r--r--java/com/google/gerrit/httpd/raw/WarGwtUiServlet.java46
-rw-r--r--java/com/google/gerrit/httpd/restapi/LogRedactUtil.java2
-rw-r--r--java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java83
-rw-r--r--java/com/google/gerrit/httpd/restapi/RestApiServlet.java35
-rw-r--r--java/com/google/gerrit/httpd/rpc/AuditedHttpServletResponse.java63
-rw-r--r--java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java289
-rw-r--r--java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java47
-rw-r--r--java/com/google/gerrit/httpd/rpc/Handler.java92
-rw-r--r--java/com/google/gerrit/httpd/rpc/RpcServletModule.java48
-rw-r--r--java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java68
-rw-r--r--java/com/google/gerrit/httpd/rpc/UiRpcModule.java31
-rw-r--r--java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java116
-rw-r--r--java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java295
-rw-r--r--java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java251
-rw-r--r--java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java80
-rw-r--r--java/com/google/gerrit/httpd/rpc/project/ProjectModule.java39
-rw-r--r--java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java245
-rw-r--r--java/com/google/gerrit/index/BUILD4
-rw-r--r--java/com/google/gerrit/index/FieldDef.java12
-rw-r--r--java/com/google/gerrit/index/Index.java44
-rw-r--r--java/com/google/gerrit/index/IndexedQuery.java133
-rw-r--r--java/com/google/gerrit/index/Schema.java16
-rw-r--r--java/com/google/gerrit/index/project/IndexedProjectQuery.java2
-rw-r--r--java/com/google/gerrit/index/project/ProjectField.java2
-rw-r--r--java/com/google/gerrit/index/project/ProjectIndexer.java3
-rw-r--r--java/com/google/gerrit/index/query/AndPredicate.java3
-rw-r--r--java/com/google/gerrit/index/query/AndSource.java37
-rw-r--r--java/com/google/gerrit/index/query/DataSource.java7
-rw-r--r--java/com/google/gerrit/index/query/IndexedQuery.java128
-rw-r--r--java/com/google/gerrit/index/query/IntegerRangePredicate.java5
-rw-r--r--java/com/google/gerrit/index/query/InternalQuery.java92
-rw-r--r--java/com/google/gerrit/index/query/ListResultSet.java47
-rw-r--r--java/com/google/gerrit/index/query/Matchable.java10
-rw-r--r--java/com/google/gerrit/index/query/NotPredicate.java3
-rw-r--r--java/com/google/gerrit/index/query/OrPredicate.java3
-rw-r--r--java/com/google/gerrit/index/query/Paginated.java4
-rw-r--r--java/com/google/gerrit/index/query/Predicate.java3
-rw-r--r--java/com/google/gerrit/index/query/QueryBuilder.java155
-rw-r--r--java/com/google/gerrit/index/query/QueryProcessor.java32
-rw-r--r--java/com/google/gerrit/index/query/QueryResult.java11
-rw-r--r--java/com/google/gerrit/index/query/ResultSet.java52
-rw-r--r--java/com/google/gerrit/index/query/TimestampRangePredicate.java2
-rw-r--r--java/com/google/gerrit/index/query/testing/BUILD17
-rw-r--r--java/com/google/gerrit/index/query/testing/TreeSubject.java73
-rw-r--r--java/com/google/gerrit/jgit/BUILD13
-rw-r--r--java/com/google/gerrit/jgit/diff/ReplaceEdit.java36
-rw-r--r--java/com/google/gerrit/json/BUILD10
-rw-r--r--java/com/google/gerrit/json/JavaSqlTimestampHelper.java108
-rw-r--r--java/com/google/gerrit/json/OutputFormat.java69
-rw-r--r--java/com/google/gerrit/json/SqlTimestampDeserializer.java64
-rw-r--r--java/com/google/gerrit/launcher/GerritLauncher.java3
-rw-r--r--java/com/google/gerrit/lifecycle/LifecycleModule.java14
-rw-r--r--java/com/google/gerrit/lucene/AbstractLuceneIndex.java50
-rw-r--r--java/com/google/gerrit/lucene/BUILD7
-rw-r--r--java/com/google/gerrit/lucene/ChangeSubIndex.java4
-rw-r--r--java/com/google/gerrit/lucene/LuceneAccountIndex.java9
-rw-r--r--java/com/google/gerrit/lucene/LuceneChangeIndex.java115
-rw-r--r--java/com/google/gerrit/lucene/LuceneGroupIndex.java9
-rw-r--r--java/com/google/gerrit/lucene/LuceneProjectIndex.java9
-rw-r--r--java/com/google/gerrit/lucene/LuceneVersionManager.java6
-rw-r--r--java/com/google/gerrit/mail/MailHeaderParser.java2
-rw-r--r--java/com/google/gerrit/metrics/DisabledMetricMaker.java5
-rw-r--r--java/com/google/gerrit/metrics/MetricMaker.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/BucketedCallback.java2
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl0.java13
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java9
-rw-r--r--java/com/google/gerrit/metrics/dropwizard/MetricJson.java2
-rw-r--r--java/com/google/gerrit/metrics/proc/ProcMetricModule.java11
-rw-r--r--java/com/google/gerrit/pgm/BUILD5
-rw-r--r--java/com/google/gerrit/pgm/Daemon.java67
-rw-r--r--java/com/google/gerrit/pgm/DeleteZombieDrafts.java6
-rw-r--r--java/com/google/gerrit/pgm/Gsql.java77
-rw-r--r--java/com/google/gerrit/pgm/Init.java40
-rw-r--r--java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java11
-rw-r--r--java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java3
-rw-r--r--java/com/google/gerrit/pgm/MigrateToNoteDb.java252
-rw-r--r--java/com/google/gerrit/pgm/Passwd.java2
-rw-r--r--java/com/google/gerrit/pgm/PrologShell.java11
-rw-r--r--java/com/google/gerrit/pgm/ProtobufImport.java146
-rw-r--r--java/com/google/gerrit/pgm/Reindex.java13
-rw-r--r--java/com/google/gerrit/pgm/Rulec.java4
-rw-r--r--java/com/google/gerrit/pgm/SwitchSecureStore.java4
-rw-r--r--java/com/google/gerrit/pgm/http/jetty/BUILD2
-rw-r--r--java/com/google/gerrit/pgm/http/jetty/HttpLog.java2
-rw-r--r--java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java3
-rw-r--r--java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java3
-rw-r--r--java/com/google/gerrit/pgm/http/jetty/JettyServer.java13
-rw-r--r--java/com/google/gerrit/pgm/init/BUILD3
-rw-r--r--java/com/google/gerrit/pgm/init/BaseInit.java132
-rw-r--r--java/com/google/gerrit/pgm/init/DB2Initializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java27
-rw-r--r--java/com/google/gerrit/pgm/init/DatabaseConfigModule.java61
-rw-r--r--java/com/google/gerrit/pgm/init/DerbyInitializer.java50
-rw-r--r--java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java3
-rw-r--r--java/com/google/gerrit/pgm/init/GroupsOnInit.java26
-rw-r--r--java/com/google/gerrit/pgm/init/H2Initializer.java50
-rw-r--r--java/com/google/gerrit/pgm/init/HANAInitializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/InitAdminUser.java112
-rw-r--r--java/com/google/gerrit/pgm/init/InitAuth.java2
-rw-r--r--java/com/google/gerrit/pgm/init/InitDatabase.java140
-rw-r--r--java/com/google/gerrit/pgm/init/InitHttpd.java2
-rw-r--r--java/com/google/gerrit/pgm/init/InitModule.java9
-rw-r--r--java/com/google/gerrit/pgm/init/JDBCInitializer.java49
-rw-r--r--java/com/google/gerrit/pgm/init/Libraries.java141
-rw-r--r--java/com/google/gerrit/pgm/init/LibraryDownloader.java316
-rw-r--r--java/com/google/gerrit/pgm/init/MariaDbInitializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/MaxDbInitializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/MySqlInitializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/OracleInitializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java32
-rw-r--r--java/com/google/gerrit/pgm/init/StaleLibraryRemover.java7
-rw-r--r--java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java12
-rw-r--r--java/com/google/gerrit/pgm/init/api/BUILD2
-rw-r--r--java/com/google/gerrit/pgm/init/api/ConsoleUI.java2
-rw-r--r--java/com/google/gerrit/pgm/init/api/InitUtil.java2
-rw-r--r--java/com/google/gerrit/pgm/init/api/SequencesOnInit.java10
-rw-r--r--java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java11
-rw-r--r--java/com/google/gerrit/pgm/util/AbstractProgram.java4
-rw-r--r--java/com/google/gerrit/pgm/util/BUILD2
-rw-r--r--java/com/google/gerrit/pgm/util/BatchProgramModule.java66
-rw-r--r--java/com/google/gerrit/pgm/util/ErrorLogFile.java25
-rw-r--r--java/com/google/gerrit/pgm/util/PerThreadReviewDbModule.java81
-rw-r--r--java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java56
-rw-r--r--java/com/google/gerrit/pgm/util/SiteProgram.java146
-rw-r--r--java/com/google/gerrit/pgm/util/ThreadLimiter.java50
-rw-r--r--java/com/google/gerrit/prettify/BUILD20
-rw-r--r--java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml27
-rw-r--r--java/com/google/gerrit/prettify/common/EditList.java8
-rw-r--r--java/com/google/gerrit/proto/BUILD18
-rw-r--r--java/com/google/gerrit/proto/ProtoGen.java84
-rw-r--r--java/com/google/gerrit/proto/Protos.java119
-rw-r--r--java/com/google/gerrit/proto/testing/BUILD15
-rw-r--r--java/com/google/gerrit/proto/testing/SerializedClassSubject.java110
-rw-r--r--java/com/google/gerrit/reviewdb/BUILD19
-rw-r--r--java/com/google/gerrit/reviewdb/ReviewDB.gwt.xml18
-rw-r--r--java/com/google/gerrit/reviewdb/client/Account.java29
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroup.java35
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupById.java16
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java29
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupMember.java16
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java29
-rw-r--r--java/com/google/gerrit/reviewdb/client/AccountGroupName.java53
-rw-r--r--java/com/google/gerrit/reviewdb/client/Branch.java19
-rw-r--r--java/com/google/gerrit/reviewdb/client/Change.java71
-rw-r--r--java/com/google/gerrit/reviewdb/client/ChangeMessage.java33
-rw-r--r--java/com/google/gerrit/reviewdb/client/Comment.java42
-rw-r--r--java/com/google/gerrit/reviewdb/client/CommentRange.java6
-rw-r--r--java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java58
-rw-r--r--java/com/google/gerrit/reviewdb/client/LabelId.java6
-rw-r--r--java/com/google/gerrit/reviewdb/client/Patch.java15
-rw-r--r--java/com/google/gerrit/reviewdb/client/PatchLineComment.java37
-rw-r--r--java/com/google/gerrit/reviewdb/client/PatchSet.java33
-rw-r--r--java/com/google/gerrit/reviewdb/client/PatchSetApproval.java31
-rw-r--r--java/com/google/gerrit/reviewdb/client/Project.java24
-rw-r--r--java/com/google/gerrit/reviewdb/client/RefNames.java36
-rw-r--r--java/com/google/gerrit/reviewdb/client/RevId.java3
-rw-r--r--java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java5
-rw-r--r--java/com/google/gerrit/reviewdb/client/TrackingId.java159
-rw-r--r--java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java38
-rw-r--r--java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java47
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java38
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java38
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java46
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java97
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java126
-rw-r--r--java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java38
-rw-r--r--java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java56
-rw-r--r--java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java81
-rw-r--r--java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java45
-rw-r--r--java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java93
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverter.java39
-rw-r--r--java/com/google/gerrit/reviewdb/converter/ProtoConverter.java27
-rw-r--r--java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java38
-rw-r--r--java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java34
-rw-r--r--java/com/google/gerrit/reviewdb/server/AccountGroupByIdAccess.java38
-rw-r--r--java/com/google/gerrit/reviewdb/server/AccountGroupByIdAudAccess.java37
-rw-r--r--java/com/google/gerrit/reviewdb/server/AccountGroupMemberAccess.java37
-rw-r--r--java/com/google/gerrit/reviewdb/server/AccountGroupMemberAuditAccess.java38
-rw-r--r--java/com/google/gerrit/reviewdb/server/AccountGroupNameAccess.java32
-rw-r--r--java/com/google/gerrit/reviewdb/server/ChangeAccess.java31
-rw-r--r--java/com/google/gerrit/reviewdb/server/ChangeMessageAccess.java39
-rw-r--r--java/com/google/gerrit/reviewdb/server/DisallowReadFromChangesReviewDbWrapper.java281
-rw-r--r--java/com/google/gerrit/reviewdb/server/PatchLineCommentAccess.java69
-rw-r--r--java/com/google/gerrit/reviewdb/server/PatchSetAccess.java35
-rw-r--r--java/com/google/gerrit/reviewdb/server/PatchSetApprovalAccess.java44
-rw-r--r--java/com/google/gerrit/reviewdb/server/ReviewDb.java120
-rw-r--r--java/com/google/gerrit/reviewdb/server/ReviewDbCodecs.java38
-rw-r--r--java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java77
-rw-r--r--java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java1267
-rw-r--r--java/com/google/gerrit/reviewdb/server/SchemaVersionAccess.java28
-rw-r--r--java/com/google/gerrit/server/AccessPath.java3
-rw-r--r--java/com/google/gerrit/server/ApprovalCopier.java76
-rw-r--r--java/com/google/gerrit/server/ApprovalsUtil.java130
-rw-r--r--java/com/google/gerrit/server/BUILD11
-rw-r--r--java/com/google/gerrit/server/ChangeMessagesUtil.java97
-rw-r--r--java/com/google/gerrit/server/ChangeUtil.java35
-rw-r--r--java/com/google/gerrit/server/CommentsUtil.java206
-rw-r--r--java/com/google/gerrit/server/CreateGroupPermissionSyncer.java7
-rw-r--r--java/com/google/gerrit/server/DynamicOptions.java52
-rw-r--r--java/com/google/gerrit/server/IdentifiedUser.java51
-rw-r--r--java/com/google/gerrit/server/LibModuleLoader.java4
-rw-r--r--java/com/google/gerrit/server/LibModuleType.java40
-rw-r--r--java/com/google/gerrit/server/OutputFormat.java70
-rw-r--r--java/com/google/gerrit/server/PatchSetUtil.java80
-rw-r--r--java/com/google/gerrit/server/ProjectUtil.java24
-rw-r--r--java/com/google/gerrit/server/PublishCommentUtil.java17
-rw-r--r--java/com/google/gerrit/server/ReviewerSet.java3
-rw-r--r--java/com/google/gerrit/server/Sequences.java166
-rw-r--r--java/com/google/gerrit/server/StarredChangesUtil.java59
-rw-r--r--java/com/google/gerrit/server/StartupChecks.java2
-rw-r--r--java/com/google/gerrit/server/UsedAt.java46
-rw-r--r--java/com/google/gerrit/server/WebLinks.java30
-rw-r--r--java/com/google/gerrit/server/account/AccountConfig.java12
-rw-r--r--java/com/google/gerrit/server/account/AccountControl.java15
-rw-r--r--java/com/google/gerrit/server/account/AccountManager.java25
-rw-r--r--java/com/google/gerrit/server/account/AccountResolver.java716
-rw-r--r--java/com/google/gerrit/server/account/AccountsUpdate.java123
-rw-r--r--java/com/google/gerrit/server/account/CapabilityCollection.java4
-rw-r--r--java/com/google/gerrit/server/account/DefaultRealm.java4
-rw-r--r--java/com/google/gerrit/server/account/Emails.java14
-rw-r--r--java/com/google/gerrit/server/account/GroupControl.java2
-rw-r--r--java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java5
-rw-r--r--java/com/google/gerrit/server/account/GroupMembers.java4
-rw-r--r--java/com/google/gerrit/server/account/GroupMembership.java2
-rw-r--r--java/com/google/gerrit/server/account/Preferences.java43
-rw-r--r--java/com/google/gerrit/server/account/SetInactiveFlag.java5
-rw-r--r--java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java2
-rw-r--r--java/com/google/gerrit/server/account/externalids/AllExternalIds.java9
-rw-r--r--java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java4
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java5
-rw-r--r--java/com/google/gerrit/server/api/BUILD6
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangeApiImpl.java151
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java3
-rw-r--r--java/com/google/gerrit/server/api/changes/ChangesImpl.java6
-rw-r--r--java/com/google/gerrit/server/api/changes/FileApiImpl.java21
-rw-r--r--java/com/google/gerrit/server/api/changes/RevisionApiImpl.java37
-rw-r--r--java/com/google/gerrit/server/api/config/ServerImpl.java13
-rw-r--r--java/com/google/gerrit/server/api/groups/GroupApiImpl.java17
-rw-r--r--java/com/google/gerrit/server/api/groups/GroupsImpl.java2
-rw-r--r--java/com/google/gerrit/server/api/projects/ProjectsImpl.java7
-rw-r--r--java/com/google/gerrit/server/args4j/AccountIdHandler.java16
-rw-r--r--java/com/google/gerrit/server/args4j/ChangeIdHandler.java4
-rw-r--r--java/com/google/gerrit/server/args4j/ProjectHandler.java2
-rw-r--r--java/com/google/gerrit/server/audit/BUILD10
-rw-r--r--java/com/google/gerrit/server/audit/RpcAuditEvent.java47
-rw-r--r--java/com/google/gerrit/server/auth/UniversalAuthBackend.java25
-rw-r--r--java/com/google/gerrit/server/auth/ldap/Helper.java10
-rw-r--r--java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java6
-rw-r--r--java/com/google/gerrit/server/cache/CacheMetrics.java2
-rw-r--r--java/com/google/gerrit/server/cache/ForwardingRemovalListener.java10
-rw-r--r--java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java12
-rw-r--r--java/com/google/gerrit/server/cache/serialize/BUILD3
-rw-r--r--java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java56
-rw-r--r--java/com/google/gerrit/server/cache/serialize/ProtoCacheSerializers.java127
-rw-r--r--java/com/google/gerrit/server/cache/serialize/ProtobufSerializer.java38
-rw-r--r--java/com/google/gerrit/server/cache/testing/BUILD3
-rw-r--r--java/com/google/gerrit/server/cache/testing/SerializedClassSubject.java109
-rw-r--r--java/com/google/gerrit/server/change/AbandonOp.java34
-rw-r--r--java/com/google/gerrit/server/change/AbandonUtil.java6
-rw-r--r--java/com/google/gerrit/server/change/AccountPatchReviewStore.java26
-rw-r--r--java/com/google/gerrit/server/change/ActionJson.java9
-rw-r--r--java/com/google/gerrit/server/change/AddReviewersEmail.java14
-rw-r--r--java/com/google/gerrit/server/change/AddReviewersOp.java87
-rw-r--r--java/com/google/gerrit/server/change/BatchAbandon.java52
-rw-r--r--java/com/google/gerrit/server/change/ChangeAttributeFactory.java48
-rw-r--r--java/com/google/gerrit/server/change/ChangeCleanupRunner.java3
-rw-r--r--java/com/google/gerrit/server/change/ChangeFinder.java26
-rw-r--r--java/com/google/gerrit/server/change/ChangeInserter.java42
-rw-r--r--java/com/google/gerrit/server/change/ChangeJson.java97
-rw-r--r--java/com/google/gerrit/server/change/ChangeKindCache.java3
-rw-r--r--java/com/google/gerrit/server/change/ChangeKindCacheImpl.java26
-rw-r--r--java/com/google/gerrit/server/change/ChangeMessages.java2
-rw-r--r--java/com/google/gerrit/server/change/ChangeResource.java19
-rw-r--r--java/com/google/gerrit/server/change/ConsistencyChecker.java91
-rw-r--r--java/com/google/gerrit/server/change/DeleteChangeOp.java58
-rw-r--r--java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java33
-rw-r--r--java/com/google/gerrit/server/change/DeleteReviewerOp.java90
-rw-r--r--java/com/google/gerrit/server/change/EmailReviewComments.java47
-rw-r--r--java/com/google/gerrit/server/change/LabelsJson.java44
-rw-r--r--java/com/google/gerrit/server/change/MergeabilityCacheImpl.java11
-rw-r--r--java/com/google/gerrit/server/change/NotifyResolver.java116
-rw-r--r--java/com/google/gerrit/server/change/NotifyUtil.java117
-rw-r--r--java/com/google/gerrit/server/change/PatchSetInserter.java76
-rw-r--r--java/com/google/gerrit/server/change/PluginDefinedAttributesFactories.java64
-rw-r--r--java/com/google/gerrit/server/change/PluginDefinedAttributesFactory.java23
-rw-r--r--java/com/google/gerrit/server/change/PureRevert.java110
-rw-r--r--java/com/google/gerrit/server/change/RebaseChangeOp.java34
-rw-r--r--java/com/google/gerrit/server/change/RebaseUtil.java29
-rw-r--r--java/com/google/gerrit/server/change/ReviewerAdder.java166
-rw-r--r--java/com/google/gerrit/server/change/ReviewerJson.java24
-rw-r--r--java/com/google/gerrit/server/change/RevisionJson.java20
-rw-r--r--java/com/google/gerrit/server/change/RevisionResource.java6
-rw-r--r--java/com/google/gerrit/server/change/SetAssigneeOp.java9
-rw-r--r--java/com/google/gerrit/server/change/SetHashtagsOp.java17
-rw-r--r--java/com/google/gerrit/server/change/SetPrivateOp.java31
-rw-r--r--java/com/google/gerrit/server/change/TestSubmitInput.java14
-rw-r--r--java/com/google/gerrit/server/change/WalkSorter.java10
-rw-r--r--java/com/google/gerrit/server/change/WorkInProgressOp.java60
-rw-r--r--java/com/google/gerrit/server/config/AuthConfig.java4
-rw-r--r--java/com/google/gerrit/server/config/CacheResource.java10
-rw-r--r--java/com/google/gerrit/server/config/ChangeCleanupConfig.java8
-rw-r--r--java/com/google/gerrit/server/config/ChangeUpdateExecutor.java29
-rw-r--r--java/com/google/gerrit/server/config/ConfigResource.java11
-rw-r--r--java/com/google/gerrit/server/config/DisableReverseDnsLookup.java24
-rw-r--r--java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java33
-rw-r--r--java/com/google/gerrit/server/config/EnableReverseDnsLookup.java24
-rw-r--r--java/com/google/gerrit/server/config/EnableReverseDnsLookupProvider.java33
-rw-r--r--java/com/google/gerrit/server/config/GerritGlobalModule.java27
-rw-r--r--java/com/google/gerrit/server/config/GerritOptions.java12
-rw-r--r--java/com/google/gerrit/server/config/GerritRequestModule.java1
-rw-r--r--java/com/google/gerrit/server/config/GerritServerConfigProvider.java3
-rw-r--r--java/com/google/gerrit/server/config/GerritServerConfigReloader.java11
-rw-r--r--java/com/google/gerrit/server/config/GerritServerIdProvider.java4
-rw-r--r--java/com/google/gerrit/server/config/ProjectConfigEntry.java8
-rw-r--r--java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java66
-rw-r--r--java/com/google/gerrit/server/config/SitePaths.java5
-rw-r--r--java/com/google/gerrit/server/config/SysExecutorModule.java30
-rw-r--r--java/com/google/gerrit/server/config/TrackingFootersProvider.java7
-rw-r--r--java/com/google/gerrit/server/documentation/MarkdownFormatterHeader.java40
-rw-r--r--java/com/google/gerrit/server/edit/ChangeEditModifier.java76
-rw-r--r--java/com/google/gerrit/server/edit/ChangeEditUtil.java36
-rw-r--r--java/com/google/gerrit/server/events/EventBroker.java42
-rw-r--r--java/com/google/gerrit/server/events/EventDispatcher.java7
-rw-r--r--java/com/google/gerrit/server/events/EventFactory.java76
-rw-r--r--java/com/google/gerrit/server/events/ReviewerAddedEvent.java1
-rw-r--r--java/com/google/gerrit/server/events/StreamEventsApiListener.java127
-rw-r--r--java/com/google/gerrit/server/extensions/events/AssigneeChanged.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/ChangeDeleted.java4
-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.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/CommentAdded.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/EventUtil.java18
-rw-r--r--java/com/google/gerrit/server/extensions/events/HashtagsEdited.java4
-rw-r--r--java/com/google/gerrit/server/extensions/events/PluginEvent.java10
-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.java11
-rw-r--r--java/com/google/gerrit/server/extensions/events/TopicEdited.java4
-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/git/BanCommit.java1
-rw-r--r--java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java16
-rw-r--r--java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java3
-rw-r--r--java/com/google/gerrit/server/git/GarbageCollection.java14
-rw-r--r--java/com/google/gerrit/server/git/GroupCollector.java51
-rw-r--r--java/com/google/gerrit/server/git/HookUtil.java8
-rw-r--r--java/com/google/gerrit/server/git/LockFailureException.java49
-rw-r--r--java/com/google/gerrit/server/git/MergeUtil.java52
-rw-r--r--java/com/google/gerrit/server/git/MergedByPushOp.java14
-rw-r--r--java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java4
-rw-r--r--java/com/google/gerrit/server/git/NotesBranchUtil.java3
-rw-r--r--java/com/google/gerrit/server/git/PerThreadRequestScope.java7
-rw-r--r--java/com/google/gerrit/server/git/PureRevertCache.java200
-rw-r--r--java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java20
-rw-r--r--java/com/google/gerrit/server/git/TagMatcher.java9
-rw-r--r--java/com/google/gerrit/server/git/TagSet.java2
-rw-r--r--java/com/google/gerrit/server/git/TagSetHolder.java7
-rw-r--r--java/com/google/gerrit/server/git/UserConfigSections.java3
-rw-r--r--java/com/google/gerrit/server/git/WorkQueue.java54
-rw-r--r--java/com/google/gerrit/server/git/meta/MetaDataUpdate.java19
-rw-r--r--java/com/google/gerrit/server/git/meta/VersionedMetaData.java9
-rw-r--r--java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java103
-rw-r--r--java/com/google/gerrit/server/git/receive/BUILD2
-rw-r--r--java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java8
-rw-r--r--java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java72
-rw-r--r--java/com/google/gerrit/server/git/receive/MessageSender.java2
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveCommits.java413
-rw-r--r--java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java5
-rw-r--r--java/com/google/gerrit/server/git/receive/ReplaceOp.java58
-rw-r--r--java/com/google/gerrit/server/git/receive/ResultChangeIds.java27
-rw-r--r--java/com/google/gerrit/server/git/validators/CommitValidators.java72
-rw-r--r--java/com/google/gerrit/server/git/validators/MergeValidators.java16
-rw-r--r--java/com/google/gerrit/server/git/validators/RefOperationValidators.java7
-rw-r--r--java/com/google/gerrit/server/group/GroupAuditService.java3
-rw-r--r--java/com/google/gerrit/server/group/db/AuditLogReader.java8
-rw-r--r--java/com/google/gerrit/server/group/db/GroupConfig.java23
-rw-r--r--java/com/google/gerrit/server/group/db/GroupConfigEntry.java2
-rw-r--r--java/com/google/gerrit/server/group/db/GroupNameNotes.java14
-rw-r--r--java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java29
-rw-r--r--java/com/google/gerrit/server/group/db/GroupsUpdate.java168
-rw-r--r--java/com/google/gerrit/server/group/db/RenameGroupOp.java5
-rw-r--r--java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java4
-rw-r--r--java/com/google/gerrit/server/group/testing/InternalGroupSubject.java31
-rw-r--r--java/com/google/gerrit/server/index/GerritIndexStatus.java4
-rw-r--r--java/com/google/gerrit/server/index/IndexModule.java21
-rw-r--r--java/com/google/gerrit/server/index/IndexUtils.java30
-rw-r--r--java/com/google/gerrit/server/index/OnlineReindexer.java37
-rw-r--r--java/com/google/gerrit/server/index/VersionManager.java6
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexer.java5
-rw-r--r--java/com/google/gerrit/server/index/account/AccountIndexerImpl.java17
-rw-r--r--java/com/google/gerrit/server/index/account/IndexedAccountQuery.java5
-rw-r--r--java/com/google/gerrit/server/index/change/AllChangesIndexer.java16
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeField.java201
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeIndexer.java153
-rw-r--r--java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java15
-rw-r--r--java/com/google/gerrit/server/index/change/DummyChangeIndex.java9
-rw-r--r--java/com/google/gerrit/server/index/change/IndexedChangeQuery.java17
-rw-r--r--java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java21
-rw-r--r--java/com/google/gerrit/server/index/change/StalenessChecker.java56
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndexer.java5
-rw-r--r--java/com/google/gerrit/server/index/group/GroupIndexerImpl.java15
-rw-r--r--java/com/google/gerrit/server/index/group/IndexedGroupQuery.java2
-rw-r--r--java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java3
-rw-r--r--java/com/google/gerrit/server/index/project/StalenessChecker.java3
-rw-r--r--java/com/google/gerrit/server/ioutil/BUILD2
-rw-r--r--java/com/google/gerrit/server/ioutil/HostPlatform.java7
-rw-r--r--java/com/google/gerrit/server/mail/CheckTokenException.java28
-rw-r--r--java/com/google/gerrit/server/mail/MailUtil.java19
-rw-r--r--java/com/google/gerrit/server/mail/SignedToken.java228
-rw-r--r--java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java4
-rw-r--r--java/com/google/gerrit/server/mail/ValidToken.java36
-rw-r--r--java/com/google/gerrit/server/mail/XsrfException.java24
-rw-r--r--java/com/google/gerrit/server/mail/receive/MailProcessor.java40
-rw-r--r--java/com/google/gerrit/server/mail/send/AbandonedSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/AddKeySender.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/AddReviewerSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/ChangeEmail.java74
-rw-r--r--java/com/google/gerrit/server/mail/send/CommentSender.java19
-rw-r--r--java/com/google/gerrit/server/mail/send/CreateChangeSender.java9
-rw-r--r--java/com/google/gerrit/server/mail/send/DeleteKeySender.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/DeleteVoteSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/EmailArguments.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/EmailSender.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/MergedSender.java13
-rw-r--r--java/com/google/gerrit/server/mail/send/NewChangeSender.java7
-rw-r--r--java/com/google/gerrit/server/mail/send/NotificationEmail.java9
-rw-r--r--java/com/google/gerrit/server/mail/send/OutgoingEmail.java25
-rw-r--r--java/com/google/gerrit/server/mail/send/ProjectWatch.java12
-rw-r--r--java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java2
-rw-r--r--java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java9
-rw-r--r--java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java5
-rw-r--r--java/com/google/gerrit/server/mail/send/RestoredSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/RevertedSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/SetAssigneeSender.java6
-rw-r--r--java/com/google/gerrit/server/mail/send/SmtpEmailSender.java13
-rw-r--r--java/com/google/gerrit/server/notedb/AbstractChangeNotes.java114
-rw-r--r--java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java37
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeBundle.java973
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeBundleReader.java25
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java63
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNoteUtil.java1
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotes.java419
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotesCache.java27
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotesParser.java40
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeNotesState.java95
-rw-r--r--java/com/google/gerrit/server/notedb/ChangeUpdate.java145
-rw-r--r--java/com/google/gerrit/server/notedb/CommentJsonMigrator.java251
-rw-r--r--java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java20
-rw-r--r--java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java5
-rw-r--r--java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java2
-rw-r--r--java/com/google/gerrit/server/notedb/DraftCommentNotes.java119
-rw-r--r--java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java52
-rw-r--r--java/com/google/gerrit/server/notedb/IntBlob.java136
-rw-r--r--java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java3
-rw-r--r--java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java2
-rw-r--r--java/com/google/gerrit/server/notedb/MutableNotesMigration.java104
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbChangeState.java477
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbMetrics.java25
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbModule.java71
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbRewriter.java3
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java361
-rw-r--r--java/com/google/gerrit/server/notedb/NoteDbUtil.java2
-rw-r--r--java/com/google/gerrit/server/notedb/NotesMigration.java250
-rw-r--r--java/com/google/gerrit/server/notedb/NotesMigrationState.java92
-rw-r--r--java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java577
-rw-r--r--java/com/google/gerrit/server/notedb/RepoSequence.java176
-rw-r--r--java/com/google/gerrit/server/notedb/RevisionNote.java24
-rw-r--r--java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java2
-rw-r--r--java/com/google/gerrit/server/notedb/RevisionNoteMap.java2
-rw-r--r--java/com/google/gerrit/server/notedb/RobotCommentNotes.java5
-rw-r--r--java/com/google/gerrit/server/notedb/RobotCommentUpdate.java63
-rw-r--r--java/com/google/gerrit/server/notedb/Sequences.java140
-rw-r--r--java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java125
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java25
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java64
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java185
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java81
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java685
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java82
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java32
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java29
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java59
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java81
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/Event.java146
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/EventList.java170
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/EventSorter.java112
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java101
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java121
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java62
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/MigrationException.java30
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java1183
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java35
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java119
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java86
-rw-r--r--java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java63
-rw-r--r--java/com/google/gerrit/server/patch/AutoMerger.java2
-rw-r--r--java/com/google/gerrit/server/patch/IntraLineDiff.java2
-rw-r--r--java/com/google/gerrit/server/patch/IntraLineLoader.java2
-rw-r--r--java/com/google/gerrit/server/patch/PatchFile.java2
-rw-r--r--java/com/google/gerrit/server/patch/PatchScriptBuilder.java20
-rw-r--r--java/com/google/gerrit/server/patch/PatchScriptFactory.java35
-rw-r--r--java/com/google/gerrit/server/patch/PatchSetInfoFactory.java16
-rw-r--r--java/com/google/gerrit/server/permissions/ChangeControl.java62
-rw-r--r--java/com/google/gerrit/server/permissions/ChangePermission.java3
-rw-r--r--java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java17
-rw-r--r--java/com/google/gerrit/server/permissions/DefaultPermissionMappings.java12
-rw-r--r--java/com/google/gerrit/server/permissions/DefaultRefFilter.java194
-rw-r--r--java/com/google/gerrit/server/permissions/FailedPermissionBackend.java24
-rw-r--r--java/com/google/gerrit/server/permissions/PermissionBackend.java89
-rw-r--r--java/com/google/gerrit/server/permissions/PermissionBackendCondition.java8
-rw-r--r--java/com/google/gerrit/server/permissions/PluginPermissionsUtil.java117
-rw-r--r--java/com/google/gerrit/server/permissions/ProjectControl.java39
-rw-r--r--java/com/google/gerrit/server/permissions/ProjectPermission.java3
-rw-r--r--java/com/google/gerrit/server/permissions/RefControl.java15
-rw-r--r--java/com/google/gerrit/server/plugins/CopyConfigModule.java9
-rw-r--r--java/com/google/gerrit/server/plugins/JarPluginProvider.java3
-rw-r--r--java/com/google/gerrit/server/plugins/JarScanner.java17
-rw-r--r--java/com/google/gerrit/server/plugins/ListPlugins.java4
-rw-r--r--java/com/google/gerrit/server/plugins/Plugin.java5
-rw-r--r--java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java4
-rw-r--r--java/com/google/gerrit/server/plugins/PluginLoader.java23
-rw-r--r--java/com/google/gerrit/server/plugins/PluginMetricMaker.java11
-rw-r--r--java/com/google/gerrit/server/plugins/PluginUtil.java20
-rw-r--r--java/com/google/gerrit/server/plugins/ServerPlugin.java7
-rw-r--r--java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java25
-rw-r--r--java/com/google/gerrit/server/project/ContributorAgreementsChecker.java40
-rw-r--r--java/com/google/gerrit/server/project/CreateProjectArgs.java1
-rw-r--r--java/com/google/gerrit/server/project/CreateRefControl.java21
-rw-r--r--java/com/google/gerrit/server/project/NoSuchChangeException.java4
-rw-r--r--java/com/google/gerrit/server/project/ProjectCache.java10
-rw-r--r--java/com/google/gerrit/server/project/ProjectCacheImpl.java51
-rw-r--r--java/com/google/gerrit/server/project/ProjectConfig.java178
-rw-r--r--java/com/google/gerrit/server/project/ProjectCreator.java256
-rw-r--r--java/com/google/gerrit/server/project/ProjectState.java67
-rw-r--r--java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java16
-rw-r--r--java/com/google/gerrit/server/project/Reachable.java42
-rw-r--r--java/com/google/gerrit/server/project/RefPattern.java7
-rw-r--r--java/com/google/gerrit/server/project/RemoveReviewerControl.java23
-rw-r--r--java/com/google/gerrit/server/project/SectionMatcher.java5
-rw-r--r--java/com/google/gerrit/server/project/SubmitRuleEvaluator.java8
-rw-r--r--java/com/google/gerrit/server/project/testing/Util.java18
-rw-r--r--java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/account/AccountPredicates.java5
-rw-r--r--java/com/google/gerrit/server/query/account/AccountQueryBuilder.java23
-rw-r--r--java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java14
-rw-r--r--java/com/google/gerrit/server/query/account/InternalAccountQuery.java48
-rw-r--r--java/com/google/gerrit/server/query/change/AddedPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/AfterPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/AgePredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/AndChangeSource.java10
-rw-r--r--java/com/google/gerrit/server/query/change/AssigneePredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/AuthorPredicate.java10
-rw-r--r--java/com/google/gerrit/server/query/change/BeforePredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/BooleanPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeData.java346
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeIdPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java13
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java25
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java254
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java100
-rw-r--r--java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java5
-rw-r--r--java/com/google/gerrit/server/query/change/CommentByPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/CommentPredicate.java6
-rw-r--r--java/com/google/gerrit/server/query/change/CommitPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/CommitterPredicate.java10
-rw-r--r--java/com/google/gerrit/server/query/change/ConflictKey.java8
-rw-r--r--java/com/google/gerrit/server/query/change/ConflictsPredicate.java131
-rw-r--r--java/com/google/gerrit/server/query/change/DeletedPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/DeltaPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/DestinationPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/DirectoryPredicate.java39
-rw-r--r--java/com/google/gerrit/server/query/change/EditByPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/EqualsFilePredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java10
-rw-r--r--java/com/google/gerrit/server/query/change/EqualsPathPredicate.java13
-rw-r--r--java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java10
-rw-r--r--java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java10
-rw-r--r--java/com/google/gerrit/server/query/change/ExactTopicPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java44
-rw-r--r--java/com/google/gerrit/server/query/change/FileExtensionPredicate.java41
-rw-r--r--java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java37
-rw-r--r--java/com/google/gerrit/server/query/change/FooterPredicate.java45
-rw-r--r--java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java6
-rw-r--r--java/com/google/gerrit/server/query/change/GroupPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/HasDraftByPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/HasStarsPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/HashtagPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/InternalChangeQuery.java126
-rw-r--r--java/com/google/gerrit/server/query/change/IsReviewedPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java9
-rw-r--r--java/com/google/gerrit/server/query/change/LabelPredicate.java8
-rw-r--r--java/com/google/gerrit/server/query/change/MessagePredicate.java6
-rw-r--r--java/com/google/gerrit/server/query/change/OrSource.java12
-rw-r--r--java/com/google/gerrit/server/query/change/OutputStreamQuery.java30
-rw-r--r--java/com/google/gerrit/server/query/change/OwnerPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/OwnerinPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java22
-rw-r--r--java/com/google/gerrit/server/query/change/ProjectPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/RefPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java47
-rw-r--r--java/com/google/gerrit/server/query/change/RegexPathPredicate.java16
-rw-r--r--java/com/google/gerrit/server/query/change/RegexProjectPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/RegexRefPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/RegexTopicPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/RevertOfPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/ReviewerPredicate.java27
-rw-r--r--java/com/google/gerrit/server/query/change/ReviewerinPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/StarPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/SubmittablePredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/change/TrackingIdPredicate.java14
-rw-r--r--java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java5
-rw-r--r--java/com/google/gerrit/server/query/group/GroupQueryBuilder.java22
-rw-r--r--java/com/google/gerrit/server/query/group/InternalGroupQuery.java13
-rw-r--r--java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java3
-rw-r--r--java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java4
-rw-r--r--java/com/google/gerrit/server/quota/DefaultQuotaBackend.java182
-rw-r--r--java/com/google/gerrit/server/quota/QuotaBackend.java103
-rw-r--r--java/com/google/gerrit/server/quota/QuotaEnforcer.java85
-rw-r--r--java/com/google/gerrit/server/quota/QuotaException.java29
-rw-r--r--java/com/google/gerrit/server/quota/QuotaGroupDefinitions.java25
-rw-r--r--java/com/google/gerrit/server/quota/QuotaRequestContext.java54
-rw-r--r--java/com/google/gerrit/server/quota/QuotaResponse.java133
-rw-r--r--java/com/google/gerrit/server/restapi/BUILD7
-rw-r--r--java/com/google/gerrit/server/restapi/access/ListAccess.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/AccountsCollection.java22
-rw-r--r--java/com/google/gerrit/server/restapi/account/AddSshKey.java9
-rw-r--r--java/com/google/gerrit/server/restapi/account/CreateAccount.java23
-rw-r--r--java/com/google/gerrit/server/restapi/account/CreateEmail.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteActive.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java26
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteEmail.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteSshKey.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetAccount.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetCapabilities.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetDetail.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetExternalIds.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetGroups.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetSshKeys.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/Module.java2
-rw-r--r--java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutActive.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutAgreement.java5
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutHttpPassword.java13
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutName.java8
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutPreferred.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutStatus.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/PutUsername.java7
-rw-r--r--java/com/google/gerrit/server/restapi/account/QueryAccounts.java6
-rw-r--r--java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/SetEditPreferences.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/SetPreferences.java4
-rw-r--r--java/com/google/gerrit/server/restapi/account/SshKeys.java3
-rw-r--r--java/com/google/gerrit/server/restapi/account/StarredChanges.java29
-rw-r--r--java/com/google/gerrit/server/restapi/account/Stars.java9
-rw-r--r--java/com/google/gerrit/server/restapi/change/Abandon.java47
-rw-r--r--java/com/google/gerrit/server/restapi/change/ApplyFix.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangeEdits.java27
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangeMessages.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/ChangesCollection.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/Check.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/CherryPick.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/CherryPickChange.java79
-rw-r--r--java/com/google/gerrit/server/restapi/change/CherryPickCommit.java5
-rw-r--r--java/com/google/gerrit/server/restapi/change/Comments.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/CreateChange.java385
-rw-r--r--java/com/google/gerrit/server/restapi/change/CreateDraftComment.java17
-rw-r--r--java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java27
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteAssignee.java20
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteChange.java31
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java29
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteComment.java21
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java18
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeletePrivate.java14
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteReviewer.java20
-rw-r--r--java/com/google/gerrit/server/restapi/change/DeleteVote.java63
-rw-r--r--java/com/google/gerrit/server/restapi/change/DownloadContent.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/DraftComments.java9
-rw-r--r--java/com/google/gerrit/server/restapi/change/Files.java24
-rw-r--r--java/com/google/gerrit/server/restapi/change/Fixes.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetAssignee.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetBlame.java8
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetChange.java54
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetComment.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetContent.java16
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetDetail.java17
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetDiff.java86
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetDraftComment.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetHashtags.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetMergeList.java2
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetPastAssignees.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetPureRevert.java8
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetRelated.java24
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetReview.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetReviewer.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetRevisionActions.java16
-rw-r--r--java/com/google/gerrit/server/restapi/change/GetRobotComment.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/Ignore.java6
-rw-r--r--java/com/google/gerrit/server/restapi/change/Index.java16
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeComments.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeMessages.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java9
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListReviewers.java16
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRevisionComments.java11
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java15
-rw-r--r--java/com/google/gerrit/server/restapi/change/ListRobotComments.java13
-rw-r--r--java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java17
-rw-r--r--java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java18
-rw-r--r--java/com/google/gerrit/server/restapi/change/Mergeable.java42
-rw-r--r--java/com/google/gerrit/server/restapi/change/Module.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/Move.java55
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostHashtags.java12
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostPrivate.java21
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostReview.java219
-rw-r--r--java/com/google/gerrit/server/restapi/change/PostReviewers.java35
-rw-r--r--java/com/google/gerrit/server/restapi/change/PreviewSubmit.java17
-rw-r--r--java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java17
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutAssignee.java25
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutDescription.java25
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutDraftComment.java24
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutMessage.java42
-rw-r--r--java/com/google/gerrit/server/restapi/change/PutTopic.java18
-rw-r--r--java/com/google/gerrit/server/restapi/change/QueryChanges.java17
-rw-r--r--java/com/google/gerrit/server/restapi/change/Rebase.java51
-rw-r--r--java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java4
-rw-r--r--java/com/google/gerrit/server/restapi/change/Rebuild.java109
-rw-r--r--java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java13
-rw-r--r--java/com/google/gerrit/server/restapi/change/Restore.java31
-rw-r--r--java/com/google/gerrit/server/restapi/change/Revert.java59
-rw-r--r--java/com/google/gerrit/server/restapi/change/Reviewed.java11
-rw-r--r--java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java16
-rw-r--r--java/com/google/gerrit/server/restapi/change/Reviewers.java13
-rw-r--r--java/com/google/gerrit/server/restapi/change/ReviewersUtil.java24
-rw-r--r--java/com/google/gerrit/server/restapi/change/RevisionReviewers.java13
-rw-r--r--java/com/google/gerrit/server/restapi/change/Revisions.java26
-rw-r--r--java/com/google/gerrit/server/restapi/change/RobotComments.java3
-rw-r--r--java/com/google/gerrit/server/restapi/change/SetReadyForReview.java45
-rw-r--r--java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java45
-rw-r--r--java/com/google/gerrit/server/restapi/change/Submit.java135
-rw-r--r--java/com/google/gerrit/server/restapi/change/SubmittedTogether.java25
-rw-r--r--java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java26
-rw-r--r--java/com/google/gerrit/server/restapi/change/SuggestReviewers.java9
-rw-r--r--java/com/google/gerrit/server/restapi/change/TestSubmitRule.java10
-rw-r--r--java/com/google/gerrit/server/restapi/change/TestSubmitType.java13
-rw-r--r--java/com/google/gerrit/server/restapi/change/Unignore.java7
-rw-r--r--java/com/google/gerrit/server/restapi/change/Votes.java13
-rw-r--r--java/com/google/gerrit/server/restapi/config/AgreementJson.java6
-rw-r--r--java/com/google/gerrit/server/restapi/config/CheckConsistency.java4
-rw-r--r--java/com/google/gerrit/server/restapi/config/ConfirmEmail.java4
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetServerInfo.java64
-rw-r--r--java/com/google/gerrit/server/restapi/config/GetVersion.java7
-rw-r--r--java/com/google/gerrit/server/restapi/config/IndexChanges.java27
-rw-r--r--java/com/google/gerrit/server/restapi/config/ListCapabilities.java41
-rw-r--r--java/com/google/gerrit/server/restapi/config/ListTopMenus.java18
-rw-r--r--java/com/google/gerrit/server/restapi/group/AddMembers.java32
-rw-r--r--java/com/google/gerrit/server/restapi/group/AddSubgroups.java13
-rw-r--r--java/com/google/gerrit/server/restapi/group/CreateGroup.java15
-rw-r--r--java/com/google/gerrit/server/restapi/group/DeleteMembers.java16
-rw-r--r--java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java9
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetAuditLog.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetDetail.java3
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetGroup.java3
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetMember.java3
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetOwner.java6
-rw-r--r--java/com/google/gerrit/server/restapi/group/GetSubgroup.java3
-rw-r--r--java/com/google/gerrit/server/restapi/group/GroupJson.java10
-rw-r--r--java/com/google/gerrit/server/restapi/group/ListGroups.java24
-rw-r--r--java/com/google/gerrit/server/restapi/group/ListMembers.java3
-rw-r--r--java/com/google/gerrit/server/restapi/group/ListSubgroups.java7
-rw-r--r--java/com/google/gerrit/server/restapi/group/MembersCollection.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/Module.java2
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutDescription.java9
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutName.java6
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutOptions.java5
-rw-r--r--java/com/google/gerrit/server/restapi/group/PutOwner.java7
-rw-r--r--java/com/google/gerrit/server/restapi/group/QueryGroups.java7
-rw-r--r--java/com/google/gerrit/server/restapi/project/CheckAccess.java19
-rw-r--r--java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/CommitsCollection.java40
-rw-r--r--java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java1
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateAccessChange.java20
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateProject.java230
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteBranch.java3
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteBranches.java3
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteRef.java15
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteTag.java3
-rw-r--r--java/com/google/gerrit/server/restapi/project/DeleteTags.java3
-rw-r--r--java/com/google/gerrit/server/restapi/project/GetAccess.java20
-rw-r--r--java/com/google/gerrit/server/restapi/project/Index.java18
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListChildProjects.java6
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListProjects.java186
-rw-r--r--java/com/google/gerrit/server/restapi/project/ListTags.java16
-rw-r--r--java/com/google/gerrit/server/restapi/project/Module.java1
-rw-r--r--java/com/google/gerrit/server/restapi/project/ProjectsCollection.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/PutConfig.java17
-rw-r--r--java/com/google/gerrit/server/restapi/project/PutDescription.java9
-rw-r--r--java/com/google/gerrit/server/restapi/project/QueryProjects.java6
-rw-r--r--java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetAccess.java13
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetAccessUtil.java4
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java7
-rw-r--r--java/com/google/gerrit/server/restapi/project/SetParent.java5
-rw-r--r--java/com/google/gerrit/server/rules/DefaultSubmitRule.java4
-rw-r--r--java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java16
-rw-r--r--java/com/google/gerrit/server/rules/PrologEnvironment.java8
-rw-r--r--java/com/google/gerrit/server/rules/PrologRuleEvaluator.java9
-rw-r--r--java/com/google/gerrit/server/rules/StoredValues.java30
-rw-r--r--java/com/google/gerrit/server/schema/AbstractDisabledAccess.java141
-rw-r--r--java/com/google/gerrit/server/schema/AllProjectsCreator.java253
-rw-r--r--java/com/google/gerrit/server/schema/AllProjectsInput.java136
-rw-r--r--java/com/google/gerrit/server/schema/AllUsersCreator.java12
-rw-r--r--java/com/google/gerrit/server/schema/BUILD4
-rw-r--r--java/com/google/gerrit/server/schema/BaseDataSourceType.java63
-rw-r--r--java/com/google/gerrit/server/schema/DB2.java50
-rw-r--r--java/com/google/gerrit/server/schema/DataSourceModule.java41
-rw-r--r--java/com/google/gerrit/server/schema/DataSourceProvider.java206
-rw-r--r--java/com/google/gerrit/server/schema/DataSourceType.java37
-rw-r--r--java/com/google/gerrit/server/schema/DatabaseModule.java41
-rw-r--r--java/com/google/gerrit/server/schema/Derby.java47
-rw-r--r--java/com/google/gerrit/server/schema/GroupBundle.java768
-rw-r--r--java/com/google/gerrit/server/schema/GroupRebuilder.java299
-rw-r--r--java/com/google/gerrit/server/schema/H2.java67
-rw-r--r--java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java10
-rw-r--r--java/com/google/gerrit/server/schema/HANA.java56
-rw-r--r--java/com/google/gerrit/server/schema/InMemoryAccountPatchReviewStore.java58
-rw-r--r--java/com/google/gerrit/server/schema/JDBC.java36
-rw-r--r--java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java67
-rw-r--r--java/com/google/gerrit/server/schema/JdbcUtil.java38
-rw-r--r--java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java10
-rw-r--r--java/com/google/gerrit/server/schema/MariaDb.java55
-rw-r--r--java/com/google/gerrit/server/schema/MaxDb.java50
-rw-r--r--java/com/google/gerrit/server/schema/MySql.java58
-rw-r--r--java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java10
-rw-r--r--java/com/google/gerrit/server/schema/NoChangesReviewDbWrapper.java214
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java191
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaVersion.java46
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java77
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java96
-rw-r--r--java/com/google/gerrit/server/schema/NoteDbSchemaVersions.java60
-rw-r--r--java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java78
-rw-r--r--java/com/google/gerrit/server/schema/Oracle.java50
-rw-r--r--java/com/google/gerrit/server/schema/PostgreSQL.java52
-rw-r--r--java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java10
-rw-r--r--java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java53
-rw-r--r--java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java43
-rw-r--r--java/com/google/gerrit/server/schema/ReviewDbFactory.java31
-rw-r--r--java/com/google/gerrit/server/schema/SchemaCreator.java279
-rw-r--r--java/com/google/gerrit/server/schema/SchemaCreatorImpl.java226
-rw-r--r--java/com/google/gerrit/server/schema/SchemaModule.java9
-rw-r--r--java/com/google/gerrit/server/schema/SchemaUpdater.java133
-rw-r--r--java/com/google/gerrit/server/schema/SchemaVersion.java231
-rw-r--r--java/com/google/gerrit/server/schema/SchemaVersionCheck.java95
-rw-r--r--java/com/google/gerrit/server/schema/Schema_100.java27
-rw-r--r--java/com/google/gerrit/server/schema/Schema_101.java144
-rw-r--r--java/com/google/gerrit/server/schema/Schema_102.java70
-rw-r--r--java/com/google/gerrit/server/schema/Schema_103.java27
-rw-r--r--java/com/google/gerrit/server/schema/Schema_104.java27
-rw-r--r--java/com/google/gerrit/server/schema/Schema_105.java87
-rw-r--r--java/com/google/gerrit/server/schema/Schema_106.java160
-rw-r--r--java/com/google/gerrit/server/schema/Schema_107.java37
-rw-r--r--java/com/google/gerrit/server/schema/Schema_108.java191
-rw-r--r--java/com/google/gerrit/server/schema/Schema_109.java35
-rw-r--r--java/com/google/gerrit/server/schema/Schema_110.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_111.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_112.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_113.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_114.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_115.java209
-rw-r--r--java/com/google/gerrit/server/schema/Schema_116.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_117.java50
-rw-r--r--java/com/google/gerrit/server/schema/Schema_118.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_119.java233
-rw-r--r--java/com/google/gerrit/server/schema/Schema_120.java119
-rw-r--r--java/com/google/gerrit/server/schema/Schema_121.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_122.java27
-rw-r--r--java/com/google/gerrit/server/schema/Schema_123.java86
-rw-r--r--java/com/google/gerrit/server/schema/Schema_124.java138
-rw-r--r--java/com/google/gerrit/server/schema/Schema_125.java128
-rw-r--r--java/com/google/gerrit/server/schema/Schema_126.java86
-rw-r--r--java/com/google/gerrit/server/schema/Schema_127.java87
-rw-r--r--java/com/google/gerrit/server/schema/Schema_128.java80
-rw-r--r--java/com/google/gerrit/server/schema/Schema_129.java40
-rw-r--r--java/com/google/gerrit/server/schema/Schema_130.java74
-rw-r--r--java/com/google/gerrit/server/schema/Schema_131.java76
-rw-r--r--java/com/google/gerrit/server/schema/Schema_132.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_133.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_134.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_135.java98
-rw-r--r--java/com/google/gerrit/server/schema/Schema_136.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_137.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_138.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_139.java212
-rw-r--r--java/com/google/gerrit/server/schema/Schema_140.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_141.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_142.java77
-rw-r--r--java/com/google/gerrit/server/schema/Schema_143.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_144.java97
-rw-r--r--java/com/google/gerrit/server/schema/Schema_145.java50
-rw-r--r--java/com/google/gerrit/server/schema/Schema_146.java327
-rw-r--r--java/com/google/gerrit/server/schema/Schema_147.java98
-rw-r--r--java/com/google/gerrit/server/schema/Schema_148.java82
-rw-r--r--java/com/google/gerrit/server/schema/Schema_149.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_150.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_151.java106
-rw-r--r--java/com/google/gerrit/server/schema/Schema_152.java43
-rw-r--r--java/com/google/gerrit/server/schema/Schema_153.java41
-rw-r--r--java/com/google/gerrit/server/schema/Schema_154.java194
-rw-r--r--java/com/google/gerrit/server/schema/Schema_155.java57
-rw-r--r--java/com/google/gerrit/server/schema/Schema_156.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_157.java43
-rw-r--r--java/com/google/gerrit/server/schema/Schema_158.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_159.java67
-rw-r--r--java/com/google/gerrit/server/schema/Schema_160.java152
-rw-r--r--java/com/google/gerrit/server/schema/Schema_161.java112
-rw-r--r--java/com/google/gerrit/server/schema/Schema_162.java71
-rw-r--r--java/com/google/gerrit/server/schema/Schema_163.java57
-rw-r--r--java/com/google/gerrit/server/schema/Schema_164.java85
-rw-r--r--java/com/google/gerrit/server/schema/Schema_165.java142
-rw-r--r--java/com/google/gerrit/server/schema/Schema_166.java52
-rw-r--r--java/com/google/gerrit/server/schema/Schema_167.java288
-rw-r--r--java/com/google/gerrit/server/schema/Schema_168.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_169.java92
-rw-r--r--java/com/google/gerrit/server/schema/Schema_170.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_180.java22
-rw-r--r--java/com/google/gerrit/server/schema/Schema_181.java29
-rw-r--r--java/com/google/gerrit/server/schema/Schema_83.java33
-rw-r--r--java/com/google/gerrit/server/schema/Schema_84.java26
-rw-r--r--java/com/google/gerrit/server/schema/Schema_85.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_86.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_87.java80
-rw-r--r--java/com/google/gerrit/server/schema/Schema_88.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_89.java40
-rw-r--r--java/com/google/gerrit/server/schema/Schema_90.java35
-rw-r--r--java/com/google/gerrit/server/schema/Schema_91.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_92.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_93.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_94.java35
-rw-r--r--java/com/google/gerrit/server/schema/Schema_95.java42
-rw-r--r--java/com/google/gerrit/server/schema/Schema_96.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_97.java25
-rw-r--r--java/com/google/gerrit/server/schema/Schema_98.java39
-rw-r--r--java/com/google/gerrit/server/schema/Schema_99.java25
-rw-r--r--java/com/google/gerrit/server/schema/ScriptRunner.java125
-rw-r--r--java/com/google/gerrit/server/schema/UpdateUI.java5
-rw-r--r--java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java177
-rw-r--r--java/com/google/gerrit/server/schema/testing/BUILD16
-rw-r--r--java/com/google/gerrit/server/securestore/SecureStoreClassName.java14
-rw-r--r--java/com/google/gerrit/server/securestore/testing/BUILD13
-rw-r--r--java/com/google/gerrit/server/securestore/testing/InMemorySecureStore.java59
-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.java9
-rw-r--r--java/com/google/gerrit/server/submit/CherryPick.java15
-rw-r--r--java/com/google/gerrit/server/submit/CommitMergeStatus.java13
-rw-r--r--java/com/google/gerrit/server/submit/EmailMerge.java50
-rw-r--r--java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java81
-rw-r--r--java/com/google/gerrit/server/submit/MergeOp.java85
-rw-r--r--java/com/google/gerrit/server/submit/MergeOpRepoManager.java20
-rw-r--r--java/com/google/gerrit/server/submit/MergeSorter.java11
-rw-r--r--java/com/google/gerrit/server/submit/MergeSuperSet.java44
-rw-r--r--java/com/google/gerrit/server/submit/MergeSuperSetComputation.java8
-rw-r--r--java/com/google/gerrit/server/submit/RebaseSorter.java17
-rw-r--r--java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java22
-rw-r--r--java/com/google/gerrit/server/submit/SubmitDryRun.java10
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategy.java32
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategyFactory.java8
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategyListener.java2
-rw-r--r--java/com/google/gerrit/server/submit/SubmitStrategyOp.java83
-rw-r--r--java/com/google/gerrit/server/submit/SubmoduleOp.java20
-rw-r--r--java/com/google/gerrit/server/update/BatchUpdate.java558
-rw-r--r--java/com/google/gerrit/server/update/BatchUpdateListener.java2
-rw-r--r--java/com/google/gerrit/server/update/BatchUpdateReviewDb.java104
-rw-r--r--java/com/google/gerrit/server/update/ChangeContext.java10
-rw-r--r--java/com/google/gerrit/server/update/Context.java24
-rw-r--r--java/com/google/gerrit/server/update/NoteDbBatchUpdate.java456
-rw-r--r--java/com/google/gerrit/server/update/Order.java34
-rw-r--r--java/com/google/gerrit/server/update/RefUpdateUtil.java153
-rw-r--r--java/com/google/gerrit/server/update/RepoView.java3
-rw-r--r--java/com/google/gerrit/server/update/RetryHelper.java34
-rw-r--r--java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java844
-rw-r--r--java/com/google/gerrit/server/util/CommitMessageUtil.java17
-rw-r--r--java/com/google/gerrit/server/util/FallbackRequestContext.java13
-rw-r--r--java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java6
-rw-r--r--java/com/google/gerrit/server/util/MagicBranch.java16
-rw-r--r--java/com/google/gerrit/server/util/ManualRequestContext.java24
-rw-r--r--java/com/google/gerrit/server/util/MostSpecificComparator.java6
-rw-r--r--java/com/google/gerrit/server/util/OneOffRequestContext.java18
-rw-r--r--java/com/google/gerrit/server/util/PluginRequestContext.java13
-rw-r--r--java/com/google/gerrit/server/util/RequestContext.java4
-rw-r--r--java/com/google/gerrit/server/util/RequestScopePropagator.java36
-rw-r--r--java/com/google/gerrit/server/util/ServerRequestContext.java13
-rw-r--r--java/com/google/gerrit/server/util/ThreadLocalRequestContext.java8
-rw-r--r--java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java9
-rw-r--r--java/com/google/gerrit/sshd/AbstractGitCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/BUILD5
-rw-r--r--java/com/google/gerrit/sshd/BaseCommand.java4
-rw-r--r--java/com/google/gerrit/sshd/ChangeArgumentParser.java17
-rw-r--r--java/com/google/gerrit/sshd/CommandFactoryProvider.java22
-rw-r--r--java/com/google/gerrit/sshd/DispatchCommandProvider.java14
-rw-r--r--java/com/google/gerrit/sshd/NoShell.java9
-rw-r--r--java/com/google/gerrit/sshd/SshCommand.java21
-rw-r--r--java/com/google/gerrit/sshd/SshDaemon.java77
-rw-r--r--java/com/google/gerrit/sshd/SshKeyCreatorImpl.java2
-rw-r--r--java/com/google/gerrit/sshd/SshModule.java5
-rw-r--r--java/com/google/gerrit/sshd/SshScope.java30
-rw-r--r--java/com/google/gerrit/sshd/SshSession.java10
-rw-r--r--java/com/google/gerrit/sshd/SshUtil.java21
-rw-r--r--java/com/google/gerrit/sshd/commands/AdminQueryShell.java61
-rw-r--r--java/com/google/gerrit/sshd/commands/ApproveOption.java170
-rw-r--r--java/com/google/gerrit/sshd/commands/AproposCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/BanCommitCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java3
-rw-r--r--java/com/google/gerrit/sshd/commands/CloseConnection.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateAccountCommand.java5
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateBranchCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateGroupCommand.java14
-rw-r--r--java/com/google/gerrit/sshd/commands/CreateProjectCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/DefaultCommandModule.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/FlushCaches.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexActivateCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexChangesCommand.java5
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/IndexStartCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/KillCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListGroupsCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListMembersCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ListProjectsCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/LsUserRefs.java24
-rw-r--r--java/com/google/gerrit/sshd/commands/PatchSetParser.java15
-rw-r--r--java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/PluginLsCommand.java9
-rw-r--r--java/com/google/gerrit/sshd/commands/Query.java6
-rw-r--r--java/com/google/gerrit/sshd/commands/QueryShell.java781
-rw-r--r--java/com/google/gerrit/sshd/commands/ReloadConfig.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/RenameGroupCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ReviewCommand.java142
-rw-r--r--java/com/google/gerrit/sshd/commands/ScpCommand.java18
-rw-r--r--java/com/google/gerrit/sshd/commands/SetAccountCommand.java26
-rw-r--r--java/com/google/gerrit/sshd/commands/SetHeadCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetMembersCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetParentCommand.java7
-rw-r--r--java/com/google/gerrit/sshd/commands/SetProjectCommand.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/SetReviewersCommand.java5
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowCaches.java4
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowConnections.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/ShowQueue.java1
-rw-r--r--java/com/google/gerrit/sshd/commands/UploadArchive.java2
-rw-r--r--java/com/google/gerrit/sshd/commands/VersionCommand.java1
-rw-r--r--java/com/google/gerrit/testing/BUILD2
-rw-r--r--java/com/google/gerrit/testing/DisabledReviewDb.java111
-rw-r--r--java/com/google/gerrit/testing/FakeEmailSender.java4
-rw-r--r--java/com/google/gerrit/testing/FakeGroupAuditService.java112
-rw-r--r--java/com/google/gerrit/testing/GerritBaseTests.java6
-rw-r--r--java/com/google/gerrit/testing/GerritServerTests.java27
-rw-r--r--java/com/google/gerrit/testing/InMemoryDatabase.java194
-rw-r--r--java/com/google/gerrit/testing/InMemoryH2Type.java30
-rw-r--r--java/com/google/gerrit/testing/InMemoryModule.java106
-rw-r--r--java/com/google/gerrit/testing/InMemoryTestEnvironment.java39
-rw-r--r--java/com/google/gerrit/testing/NoteDbChecker.java225
-rw-r--r--java/com/google/gerrit/testing/NoteDbMode.java87
-rw-r--r--java/com/google/gerrit/testing/TempFileUtil.java66
-rw-r--r--java/com/google/gerrit/testing/TestChanges.java18
-rw-r--r--java/com/google/gerrit/testing/TestTimeUtil.java9
-rw-r--r--java/com/google/gerrit/testing/TestUpdateUI.java10
-rw-r--r--java/com/google/gerrit/truth/BUILD1
-rw-r--r--java/com/google/gerrit/truth/CacheStatsSubject.java62
-rw-r--r--java/com/google/gerrit/truth/ListSubject.java51
-rw-r--r--java/com/google/gerrit/truth/OptionalSubject.java61
-rw-r--r--java/com/google/gerrit/util/cli/BUILD1
-rw-r--r--java/com/google/gerrit/util/cli/CmdLineParser.java381
-rw-r--r--java/com/google/gerrit/util/cli/OptionHandlers.java4
-rw-r--r--java/com/google/gerrit/util/cli/OptionUtil.java39
-rw-r--r--java/com/google/gerrit/util/cli/RequiresOptions.java45
-rw-r--r--java/com/google/gerrit/util/http/BUILD2
-rw-r--r--java/com/google/gerrit/util/http/RequestUtil.java38
-rw-r--r--java/com/google/gwtexpui/clippy/BUILD23
-rw-r--r--java/com/google/gwtexpui/clippy/Clippy.gwt.xml20
-rw-r--r--java/com/google/gwtexpui/clippy/client/ClippyCss.java25
-rw-r--r--java/com/google/gwtexpui/clippy/client/ClippyResources.java35
-rw-r--r--java/com/google/gwtexpui/clippy/client/CopyableLabel.java315
-rw-r--r--java/com/google/gwtexpui/clippy/client/CopyableLabelText.java28
-rw-r--r--java/com/google/gwtexpui/clippy/client/CopyableLabelText.properties3
-rw-r--r--java/com/google/gwtexpui/clippy/client/clippy.css39
-rw-r--r--java/com/google/gwtexpui/clippy/client/clippy.swfbin5380 -> 0 bytes
-rw-r--r--java/com/google/gwtexpui/clippy/client/page_white_copy.pngbin309 -> 0 bytes
-rw-r--r--java/com/google/gwtexpui/css/BUILD9
-rw-r--r--java/com/google/gwtexpui/css/CSS.gwt.xml19
-rw-r--r--java/com/google/gwtexpui/css/rebind/CssLinker.java121
-rw-r--r--java/com/google/gwtexpui/globalkey/BUILD16
-rw-r--r--java/com/google/gwtexpui/globalkey/GlobalKey.gwt.xml20
-rw-r--r--java/com/google/gwtexpui/globalkey/client/CompoundKeyCommand.java40
-rw-r--r--java/com/google/gwtexpui/globalkey/client/DocWidget.java59
-rw-r--r--java/com/google/gwtexpui/globalkey/client/GlobalKey.java191
-rw-r--r--java/com/google/gwtexpui/globalkey/client/HidePopupPanelCommand.java33
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyCommand.java104
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyCommandFilter.java19
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyCommandSet.java145
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyConstants.java52
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyConstants.properties17
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyCss.java37
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java233
-rw-r--r--java/com/google/gwtexpui/globalkey/client/KeyResources.java25
-rw-r--r--java/com/google/gwtexpui/globalkey/client/NpTextArea.java27
-rw-r--r--java/com/google/gwtexpui/globalkey/client/NpTextBox.java29
-rw-r--r--java/com/google/gwtexpui/globalkey/client/ShowHelpCommand.java72
-rw-r--r--java/com/google/gwtexpui/globalkey/client/key.css99
-rw-r--r--java/com/google/gwtexpui/progress/BUILD10
-rw-r--r--java/com/google/gwtexpui/progress/Progress.gwt.xml19
-rw-r--r--java/com/google/gwtexpui/progress/client/ProgressBar.java77
-rw-r--r--java/com/google/gwtexpui/progress/client/ProgressCss.java25
-rw-r--r--java/com/google/gwtexpui/progress/client/ProgressResources.java25
-rw-r--r--java/com/google/gwtexpui/progress/client/progress.css43
-rw-r--r--java/com/google/gwtexpui/safehtml/BUILD10
-rw-r--r--java/com/google/gwtexpui/safehtml/SafeHtml.gwt.xml19
-rw-r--r--java/com/google/gwtexpui/safehtml/client/AttMap.java141
-rw-r--r--java/com/google/gwtexpui/safehtml/client/Buffer.java34
-rw-r--r--java/com/google/gwtexpui/safehtml/client/BufferDirect.java63
-rw-r--r--java/com/google/gwtexpui/safehtml/client/BufferSealElement.java63
-rw-r--r--java/com/google/gwtexpui/safehtml/client/FindReplace.java36
-rw-r--r--java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java151
-rw-r--r--java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java82
-rw-r--r--java/com/google/gwtexpui/safehtml/client/RawFindReplace.java54
-rw-r--r--java/com/google/gwtexpui/safehtml/client/SafeHtml.java335
-rw-r--r--java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java425
-rw-r--r--java/com/google/gwtexpui/safehtml/client/SafeHtmlCss.java25
-rw-r--r--java/com/google/gwtexpui/safehtml/client/SafeHtmlResources.java22
-rw-r--r--java/com/google/gwtexpui/safehtml/client/SafeHtmlString.java30
-rw-r--r--java/com/google/gwtexpui/safehtml/client/safehtml.css29
-rw-r--r--java/com/google/gwtexpui/user/BUILD10
-rw-r--r--java/com/google/gwtexpui/user/User.gwt.xml18
-rw-r--r--java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java79
-rw-r--r--java/com/google/gwtexpui/user/client/Tooltip.java79
-rw-r--r--java/com/google/gwtexpui/user/client/UserAgent.java168
-rw-r--r--java/com/google/gwtexpui/user/client/View.java55
-rw-r--r--java/com/google/gwtexpui/user/client/ViewSite.java84
-rw-r--r--java/com/google/gwtexpui/user/client/tooltip.css54
-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/StandardKeyEncoder.java111
-rw-r--r--java/com/google/gwtorm/client/StringKey.java86
-rw-r--r--java/gerrit/BUILD2
-rw-r--r--java/gerrit/PRED__load_commit_labels_1.java31
-rw-r--r--java/gerrit/PRED_commit_edits_2.java6
-rw-r--r--java/gerrit/PRED_get_legacy_label_types_1.java9
-rw-r--r--java/gerrit/PRED_pure_revert_1.java9
-rw-r--r--java/gerrit/PRED_unresolved_comments_count_1.java12
-rw-r--r--java/org/eclipse/jgit/BUILD49
-rw-r--r--java/org/eclipse/jgit/JGit.gwt.xml22
-rw-r--r--java/org/eclipse/jgit/diff/EditDeserializer.java95
-rw-r--r--java/org/eclipse/jgit/diff/Edit_JsonSerializer.java74
-rw-r--r--java/org/eclipse/jgit/diff/ReplaceEdit.java35
-rw-r--r--javatests/com/google/gerrit/acceptance/BUILD1
-rw-r--r--javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java3
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java734
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java135
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/BUILD2
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java20
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java35
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java1152
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java5
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java57
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java18
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/PluginFieldsIT.java86
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java261
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java38
-rw-r--r--javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java3
-rw-r--r--javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java60
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/BUILD1
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java391
-rw-r--r--javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java63
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java82
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/CommitIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java195
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java30
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/GetBlameIT.java128
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java295
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java187
-rw-r--r--javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java130
-rw-r--r--javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java72
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractGitOverHttpServlet.java65
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java599
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java384
-rw-r--r--javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java132
-rw-r--r--javatests/com/google/gerrit/acceptance/git/BUILD1
-rw-r--r--javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java70
-rw-r--r--javatests/com/google/gerrit/acceptance/git/HttpSubmitOnPushIT.java30
-rw-r--r--javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java3
-rw-r--r--javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java544
-rw-r--r--javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/git/SshSubmitOnPushIT.java27
-rw-r--r--javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java382
-rw-r--r--javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java401
-rw-r--r--javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java456
-rw-r--r--javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java11
-rw-r--r--javatests/com/google/gerrit/acceptance/pgm/InitIT.java59
-rw-r--r--javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java383
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java165
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java191
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/BUILD17
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java509
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java111
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/DeleteOnCollectionIT.java9
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/GroupsRestApiBindingsIT.java100
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java68
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java248
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java56
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/TraceIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/BUILD1
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java49
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java142
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java134
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java36
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java195
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/BUILD11
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java498
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java113
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java102
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java160
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java205
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/PluginsRemoteAdminRestApiBindingsIT.java70
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java256
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/binding/RootCollectionsRestApiBindingsIT.java58
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java201
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java69
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java95
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java31
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java84
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java38
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java74
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java29
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java44
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java303
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java27
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java38
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java190
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java20
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java15
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java31
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java9
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java36
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java151
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java17
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java96
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java60
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java160
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java130
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java266
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java22
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/AbstractHttpPushTag.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java22
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java68
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/BUILD2
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java16
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java21
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java17
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java38
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java173
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/PluginAccessIT.java80
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java27
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java24
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/util/BUILD12
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java104
-rw-r--r--javatests/com/google/gerrit/acceptance/rest/util/RestCall.java88
-rw-r--r--javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java357
-rw-r--r--javatests/com/google/gerrit/acceptance/server/account/BUILD7
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java108
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java201
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java118
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java13
-rw-r--r--javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java11
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java1040
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java11
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java21
-rw-r--r--javatests/com/google/gerrit/acceptance/server/mail/SignedTokenEmailTokenVerifierIT.java23
-rw-r--r--javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java1594
-rw-r--r--javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java15
-rw-r--r--javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java521
-rw-r--r--javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java706
-rw-r--r--javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java13
-rw-r--r--javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java150
-rw-r--r--javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java12
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/BUILD7
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java239
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java157
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java140
-rw-r--r--javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java144
-rw-r--r--javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java10
-rw-r--r--javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java13
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java4
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/IndexIT.java8
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java88
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/QueryIT.java2
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java11
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java6
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java100
-rw-r--r--javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java14
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java65
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java56
-rw-r--r--javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java147
-rw-r--r--javatests/com/google/gerrit/common/AutoValueTest.java3
-rw-r--r--javatests/com/google/gerrit/common/BUILD6
-rw-r--r--javatests/com/google/gerrit/common/data/AccessSectionTest.java22
-rw-r--r--javatests/com/google/gerrit/common/data/BUILD13
-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.java7
-rw-r--r--javatests/com/google/gerrit/common/data/LabelFunctionTest.java7
-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.java7
-rw-r--r--javatests/com/google/gerrit/common/data/PermissionTest.java19
-rw-r--r--javatests/com/google/gerrit/common/data/SubmitRecordTest.java3
-rw-r--r--javatests/com/google/gerrit/elasticsearch/BUILD2
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java10
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticContainer.java10
-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.java2
-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.java4
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java4
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java4
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java4
-rw-r--r--javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java41
-rw-r--r--javatests/com/google/gerrit/extensions/BUILD2
-rw-r--r--javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java3
-rw-r--r--javatests/com/google/gerrit/extensions/client/ListOptionTest.java72
-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/BUILD36
-rw-r--r--javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java121
-rw-r--r--javatests/com/google/gerrit/git/RefUpdateUtilTest.java119
-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/BUILD1
-rw-r--r--javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java35
-rw-r--r--javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java7
-rw-r--r--javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java46
-rw-r--r--javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java169
-rw-r--r--javatests/com/google/gerrit/httpd/BUILD5
-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/IndexServletTest.java32
-rw-r--r--javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java3
-rw-r--r--javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java3
-rw-r--r--javatests/com/google/gerrit/index/BUILD2
-rw-r--r--javatests/com/google/gerrit/index/SchemaUtilTest.java7
-rw-r--r--javatests/com/google/gerrit/index/query/FieldPredicateTest.java2
-rw-r--r--javatests/com/google/gerrit/index/query/NotPredicateTest.java2
-rw-r--r--javatests/com/google/gerrit/index/query/PredicateTest.java7
-rw-r--r--javatests/com/google/gerrit/index/query/QueryBuilderTest.java123
-rw-r--r--javatests/com/google/gerrit/index/query/QueryParserTest.java205
-rw-r--r--javatests/com/google/gerrit/integration/git/UploadArchiveIT.java10
-rw-r--r--javatests/com/google/gerrit/json/BUILD11
-rw-r--r--javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java87
-rw-r--r--javatests/com/google/gerrit/mail/AbstractParserTest.java5
-rw-r--r--javatests/com/google/gerrit/mail/BUILD1
-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/metrics/dropwizard/BUILD1
-rw-r--r--javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java3
-rw-r--r--javatests/com/google/gerrit/metrics/proc/BUILD1
-rw-r--r--javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java7
-rw-r--r--javatests/com/google/gerrit/pgm/BUILD2
-rw-r--r--javatests/com/google/gerrit/pgm/init/InitTestCase.java27
-rw-r--r--javatests/com/google/gerrit/pgm/init/LibrariesTest.java53
-rw-r--r--javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java114
-rw-r--r--javatests/com/google/gerrit/proto/BUILD13
-rw-r--r--javatests/com/google/gerrit/proto/ProtosTest.java156
-rw-r--r--javatests/com/google/gerrit/proto/ReviewDbProtoTest.java31
-rw-r--r--javatests/com/google/gerrit/reviewdb/BUILD13
-rw-r--r--javatests/com/google/gerrit/reviewdb/client/BUILD13
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java67
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/BUILD15
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverterTest.java83
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java67
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java67
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java83
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java214
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java363
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java67
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java98
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java203
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java83
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java137
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java71
-rw-r--r--javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java66
-rw-r--r--javatests/com/google/gerrit/server/BUILD8
-rw-r--r--javatests/com/google/gerrit/server/ChangeUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/server/IdentifiedUserTest.java9
-rw-r--r--javatests/com/google/gerrit/server/account/AccountResolverTest.java394
-rw-r--r--javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java5
-rw-r--r--javatests/com/google/gerrit/server/account/DestinationListTest.java17
-rw-r--r--javatests/com/google/gerrit/server/account/GroupUUIDTest.java3
-rw-r--r--javatests/com/google/gerrit/server/account/HashedPasswordTest.java3
-rw-r--r--javatests/com/google/gerrit/server/account/QueryListTest.java17
-rw-r--r--javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java18
-rw-r--r--javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java5
-rw-r--r--javatests/com/google/gerrit/server/auth/oauth/OAuthTokenCacheTest.java22
-rw-r--r--javatests/com/google/gerrit/server/cache/BUILD1
-rw-r--r--javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java7
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/BUILD4
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java72
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ProtoCacheSerializersTest.java116
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java43
-rw-r--r--javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java3
-rw-r--r--javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java11
-rw-r--r--javatests/com/google/gerrit/server/change/HashtagsTest.java3
-rw-r--r--javatests/com/google/gerrit/server/change/IncludedInResolverTest.java98
-rw-r--r--javatests/com/google/gerrit/server/change/LabelNormalizerTest.java63
-rw-r--r--javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java11
-rw-r--r--javatests/com/google/gerrit/server/config/ConfigUtilTest.java11
-rw-r--r--javatests/com/google/gerrit/server/config/GitwebConfigTest.java3
-rw-r--r--javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java30
-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/edit/ChangeEditTest.java3
-rw-r--r--javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java7
-rw-r--r--javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java9
-rw-r--r--javatests/com/google/gerrit/server/events/EventDeserializerTest.java11
-rw-r--r--javatests/com/google/gerrit/server/events/EventJsonTest.java8
-rw-r--r--javatests/com/google/gerrit/server/events/EventTypesTest.java3
-rw-r--r--javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java23
-rw-r--r--javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java18
-rw-r--r--javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java16
-rw-r--r--javatests/com/google/gerrit/server/fixes/StringModifierTest.java18
-rw-r--r--javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java2
-rw-r--r--javatests/com/google/gerrit/server/git/GroupCollectorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java16
-rw-r--r--javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java17
-rw-r--r--javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java49
-rw-r--r--javatests/com/google/gerrit/server/git/TagSetHolderTest.java5
-rw-r--r--javatests/com/google/gerrit/server/git/TagSetTest.java5
-rw-r--r--javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java5
-rw-r--r--javatests/com/google/gerrit/server/group/db/BUILD3
-rw-r--r--javatests/com/google/gerrit/server/group/db/GroupConfigTest.java73
-rw-r--r--javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java112
-rw-r--r--javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java14
-rw-r--r--javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java11
-rw-r--r--javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java4
-rw-r--r--javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java29
-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.java3
-rw-r--r--javatests/com/google/gerrit/server/ioutil/StringUtilTest.java3
-rw-r--r--javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java3
-rw-r--r--javatests/com/google/gerrit/server/logging/MutableTagsTest.java3
-rw-r--r--javatests/com/google/gerrit/server/logging/TraceContextTest.java3
-rw-r--r--javatests/com/google/gerrit/server/mail/SignedTokenTest.java162
-rw-r--r--javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java3
-rw-r--r--javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java6
-rw-r--r--javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java3
-rw-r--r--javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java48
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java1976
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java2
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java47
-rw-r--r--javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java98
-rw-r--r--javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java527
-rw-r--r--javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java3
-rw-r--r--javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java10
-rw-r--r--javatests/com/google/gerrit/server/notedb/IntBlobTest.java202
-rw-r--r--javatests/com/google/gerrit/server/notedb/NoteDbChangeStateTest.java241
-rw-r--r--javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java118
-rw-r--r--javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java231
-rw-r--r--javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java5
-rw-r--r--javatests/com/google/gerrit/server/patch/PatchListEntryTest.java3
-rw-r--r--javatests/com/google/gerrit/server/patch/PatchListTest.java3
-rw-r--r--javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java3
-rw-r--r--javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.java56
-rw-r--r--javatests/com/google/gerrit/server/permissions/RefControlTest.java49
-rw-r--r--javatests/com/google/gerrit/server/project/CommitsCollectionTest.java6
-rw-r--r--javatests/com/google/gerrit/server/project/GroupListTest.java3
-rw-r--r--javatests/com/google/gerrit/server/project/ProjectConfigTest.java79
-rw-r--r--javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java39
-rw-r--r--javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java2
-rw-r--r--javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java794
-rw-r--r--javatests/com/google/gerrit/server/query/change/BUILD5
-rw-r--r--javatests/com/google/gerrit/server/query/change/ChangeDataTest.java5
-rw-r--r--javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java11
-rw-r--r--javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java2
-rw-r--r--javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java18
-rw-r--r--javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java39
-rw-r--r--javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java2
-rw-r--r--javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java39
-rw-r--r--javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java2
-rw-r--r--javatests/com/google/gerrit/server/rules/GerritCommonTest.java2
-rw-r--r--javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java3
-rw-r--r--javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java176
-rw-r--r--javatests/com/google/gerrit/server/schema/GroupBundleTest.java146
-rw-r--r--javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java747
-rw-r--r--javatests/com/google/gerrit/server/schema/HANATest.java44
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java297
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java93
-rw-r--r--javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java74
-rw-r--r--javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java110
-rw-r--r--javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java97
-rw-r--r--javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java128
-rw-r--r--javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java149
-rw-r--r--javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java265
-rw-r--r--javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java222
-rw-r--r--javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java96
-rw-r--r--javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java227
-rw-r--r--javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java1125
-rw-r--r--javatests/com/google/gerrit/server/update/BUILD24
-rw-r--r--javatests/com/google/gerrit/server/update/BatchUpdateTest.java14
-rw-r--r--javatests/com/google/gerrit/server/update/RefUpdateUtilRepoTest.java117
-rw-r--r--javatests/com/google/gerrit/server/update/RefUpdateUtilTest.java119
-rw-r--r--javatests/com/google/gerrit/server/update/RepoViewTest.java3
-rw-r--r--javatests/com/google/gerrit/server/util/IdGeneratorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/util/LabelVoteTest.java3
-rw-r--r--javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java3
-rw-r--r--javatests/com/google/gerrit/server/util/git/BUILD3
-rw-r--r--javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java3
-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.java (renamed from java/com/google/gerrit/testing/GerritJUnitTest.java)0
-rw-r--r--javatests/com/google/gerrit/util/http/BUILD3
-rw-r--r--javatests/com/google/gerrit/util/http/RequestUtilTest.java52
-rw-r--r--javatests/com/google/gerrit/util/http/testutil/BUILD2
-rw-r--r--javatests/com/google/gwtexpui/safehtml/BUILD13
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java80
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java31
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java290
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java124
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java127
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java125
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java81
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyQuoteTest.java56
-rw-r--r--javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java120
-rw-r--r--javatests/org/eclipse/jgit/BUILD13
-rw-r--r--javatests/org/eclipse/jgit/diff/EditDeserializerTest.java26
-rw-r--r--lib/BUILD61
-rw-r--r--lib/LICENSE-clippy20
-rw-r--r--lib/LICENSE-codemirror-original19
-rw-r--r--lib/LICENSE-commonmark23
-rw-r--r--lib/LICENSE-diffy1
-rw-r--r--lib/LICENSE-mockito21
-rw-r--r--lib/LICENSE-postgresql26
-rw-r--r--lib/LICENSE-silk_icons338
-rw-r--r--lib/codemirror/BUILD12
-rw-r--r--lib/codemirror/cm.bzl376
-rw-r--r--lib/commons/BUILD7
-rw-r--r--lib/gitiles/BUILD58
-rw-r--r--lib/greenmail/BUILD2
-rw-r--r--lib/guava.bzl4
-rw-r--r--lib/gwt/BUILD47
-rw-r--r--lib/jetty/BUILD7
-rw-r--r--lib/jgit/jgit.bzl15
-rw-r--r--lib/jgit/org.eclipse.jgit/BUILD6
-rw-r--r--lib/js/bower_archives.bzl4
-rw-r--r--lib/mockito/BUILD37
-rwxr-xr-xlib/nongoogle_test.sh3
-rw-r--r--package.json2
-rw-r--r--plugins/BUILD9
m---------plugins/delete-project0
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/BUILD3
-rw-r--r--polygerrit-ui/README.md4
-rw-r--r--polygerrit-ui/app/.eslintrc.json1
-rw-r--r--polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html15
-rw-r--r--polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html20
-rw-r--r--polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html4
-rw-r--r--polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html7
-rw-r--r--polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html57
-rw-r--r--polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html3
-rw-r--r--polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html38
-rw-r--r--polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html2
-rw-r--r--polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html1
-rw-r--r--polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.html2
-rw-r--r--polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js1
-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-pointer-dialog/gr-create-pointer-dialog.html5
-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-repo-dialog/gr-create-repo-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.html5
-rw-r--r--polygerrit-ui/app/elements/admin/gr-group/gr-group.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html100
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js90
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html142
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.html24
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list_test.html35
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js1
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js1
-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-plugin-config/gr-repo-plugin-config.html109
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js130
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html175
-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.js43
-rw-r--r--polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html40
-rw-r--r--polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js1
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html9
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js11
-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/gr-change-list.html11
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js17
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.html1
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js1
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.html4
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js3
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html2
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html41
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js30
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js1
-rw-r--r--polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js8
-rw-r--r--polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js5
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html6
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html3
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html100
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js13
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html24
-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-view/gr-change-view.html43
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js85
-rw-r--r--polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html34
-rw-r--r--polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html7
-rw-r--r--polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js2
-rw-r--r--polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html9
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js11
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_test.html12
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html61
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js69
-rw-r--r--polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html96
-rw-r--r--polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html2
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-message/gr-message.html29
-rw-r--r--polygerrit-ui/app/elements/change/gr-message/gr-message.js41
-rw-r--r--polygerrit-ui/app/elements/change/gr-message/gr-message_test.html26
-rw-r--r--polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html2
-rw-r--r--polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js10
-rw-r--r--polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html1
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html28
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js37
-rw-r--r--polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html19
-rw-r--r--polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.html14
-rw-r--r--polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js1
-rw-r--r--polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.html10
-rw-r--r--polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js1
-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-manager/gr-error-manager.html2
-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.html3
-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-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html7
-rw-r--r--polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js14
-rw-r--r--polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html11
-rw-r--r--polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.html41
-rw-r--r--polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.js52
-rw-r--r--polygerrit-ui/app/elements/core/gr-message-header/gr-message-header_test.html60
-rw-r--r--polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html13
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js19
-rw-r--r--polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html4
-rw-r--r--polygerrit-ui/app/elements/core/gr-router/gr-router.js26
-rw-r--r--polygerrit-ui/app/elements/core/gr-router/gr-router_test.html28
-rw-r--r--polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js8
-rw-r--r--polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js1
-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.js1
-rw-r--r--polygerrit-ui/app/elements/diff/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js53
-rw-r--r--polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html24
-rw-r--r--polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js137
-rw-r--r--polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html138
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-binary.js8
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.js18
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.js28
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html271
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js173
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html488
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.html50
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.js133
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html232
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html126
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js466
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html718
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html389
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js659
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html856
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js3
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js208
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html213
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html8
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js400
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html530
-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-preferences-dialog/gr-diff-preferences-dialog.js1
-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.js215
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html12
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js3
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html8
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html6
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js20
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html49
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js14
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html25
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js526
-rw-r--r--polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html377
-rw-r--r--polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js1
-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.js156
-rw-r--r--polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html200
-rw-r--r--polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js28
-rw-r--r--polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html10
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.html1
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js113
-rw-r--r--polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html12
-rw-r--r--polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js1
-rw-r--r--polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js1
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js1
-rw-r--r--polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js1
-rw-r--r--polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js1
-rw-r--r--polygerrit-ui/app/elements/gr-app-it_test.html1
-rw-r--r--polygerrit-ui/app/elements/gr-app.html43
-rw-r--r--polygerrit-ui/app/elements/gr-app.js25
-rw-r--r--polygerrit-ui/app/elements/gr-app_test.html30
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api.js4
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html10
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js8
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js5
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html1
-rw-r--r--polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js6
-rw-r--r--polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js1
-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.html2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html12
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html4
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js1
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html3
-rw-r--r--polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js1
-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.html2
-rw-r--r--polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html3
-rw-r--r--polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-button/gr-button.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-button/gr-button.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html126
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js495
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html751
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html388
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js649
-rw-r--r--polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html847
-rw-r--r--polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html (renamed from polygerrit-ui/app/elements/diff/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html)0
-rw-r--r--polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js54
-rw-r--r--polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js5
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.js47
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html17
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.js49
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html3
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js45
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html44
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-action-context_test.html2
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js22
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api.js37
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js57
-rw-r--r--polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js1
-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.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.html4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js4
-rw-r--r--polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js1
-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-rest-api-interface/gr-rest-api-interface.js40
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html20
-rw-r--r--polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-select/gr-select.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js1
-rw-r--r--polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js1
-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/gr-tooltip.js1
-rw-r--r--polygerrit-ui/app/embed/embed.html3
-rw-r--r--polygerrit-ui/app/embed/gr-diff.html25
-rw-r--r--polygerrit-ui/app/samples/bind-parameters.html1
-rw-r--r--polygerrit-ui/app/samples/coverage-plugin.html1
-rw-r--r--polygerrit-ui/app/samples/repo-command.html1
-rw-r--r--polygerrit-ui/app/samples/some-screen.html1
-rw-r--r--polygerrit-ui/app/scripts/util.js34
-rw-r--r--polygerrit-ui/app/styles/gr-change-list-styles.html8
-rw-r--r--polygerrit-ui/app/styles/gr-form-styles.html4
-rw-r--r--polygerrit-ui/app/styles/themes/app-theme.html9
-rw-r--r--polygerrit-ui/app/styles/themes/dark-theme.html6
-rw-r--r--polygerrit-ui/app/test/common-test-setup.html1
-rw-r--r--polygerrit-ui/app/test/index.html7
-rw-r--r--polygerrit-ui/server.go111
-rw-r--r--proto/BUILD20
-rw-r--r--proto/cache.proto26
-rw-r--r--proto/entities.proto158
-rw-r--r--proto/testing/BUILD15
-rw-r--r--proto/testing/test.proto26
-rw-r--r--resources/com/google/gerrit/httpd/raw/HostPage.html26
-rw-r--r--resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy19
-rw-r--r--resources/com/google/gerrit/pgm/init/libraries.config50
-rw-r--r--resources/com/google/gerrit/reviewdb/BUILD8
-rw-r--r--resources/com/google/gerrit/reviewdb/server/index_generic.sql40
-rw-r--r--resources/com/google/gerrit/reviewdb/server/index_maxdb.sql43
-rw-r--r--resources/com/google/gerrit/reviewdb/server/index_postgres.sql87
-rw-r--r--resources/com/google/gerrit/server/change/ChangeMessages.properties4
-rw-r--r--resources/com/google/gerrit/server/mail/Abandoned.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/AbandonedHtml.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/AddKey.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/AddKeyHtml.soy4
-rw-r--r--resources/com/google/gerrit/server/mail/ChangeFooter.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/ChangeSubject.soy10
-rw-r--r--resources/com/google/gerrit/server/mail/Comment.soy10
-rw-r--r--resources/com/google/gerrit/server/mail/CommentHtml.soy14
-rw-r--r--resources/com/google/gerrit/server/mail/DeleteKey.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy4
-rw-r--r--resources/com/google/gerrit/server/mail/DeleteReviewer.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy6
-rw-r--r--resources/com/google/gerrit/server/mail/DeleteVote.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/Footer.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/FooterHtml.soy4
-rw-r--r--resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy4
-rw-r--r--resources/com/google/gerrit/server/mail/Merged.soy6
-rw-r--r--resources/com/google/gerrit/server/mail/MergedHtml.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/NewChange.soy10
-rw-r--r--resources/com/google/gerrit/server/mail/NewChangeHtml.soy14
-rw-r--r--resources/com/google/gerrit/server/mail/Private.soy11
-rw-r--r--resources/com/google/gerrit/server/mail/RegisterNewEmail.soy2
-rw-r--r--resources/com/google/gerrit/server/mail/ReplacePatchSet.soy12
-rw-r--r--resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy14
-rw-r--r--resources/com/google/gerrit/server/mail/Restored.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/RestoredHtml.soy6
-rw-r--r--resources/com/google/gerrit/server/mail/Reverted.soy8
-rw-r--r--resources/com/google/gerrit/server/mail/RevertedHtml.soy6
-rw-r--r--resources/com/google/gerrit/server/mail/SetAssignee.soy10
-rw-r--r--resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy12
-rw-r--r--tools/BUILD4
-rw-r--r--tools/bzl/gwt.bzl311
-rw-r--r--tools/bzl/js.bzl6
-rw-r--r--tools/bzl/junit.bzl2
-rw-r--r--tools/bzl/license-map.py2
-rw-r--r--tools/bzl/license.bzl2
-rw-r--r--tools/bzl/maven_jar.bzl3
-rw-r--r--tools/bzl/pkg_war.bzl7
-rw-r--r--tools/bzl/plugin.bzl44
-rw-r--r--tools/bzl/plugins.bzl4
-rwxr-xr-xtools/coverage.sh18
-rwxr-xr-xtools/download_file.py13
-rw-r--r--tools/eclipse/BUILD28
-rw-r--r--tools/eclipse/gerrit_gwt_debug.launch22
-rwxr-xr-xtools/eclipse/project.py59
-rwxr-xr-xtools/js/bower2bazel.py29
-rw-r--r--tools/maven/BUILD3
-rw-r--r--tools/maven/gerrit-acceptance-framework_pom.xml14
-rw-r--r--tools/maven/gerrit-extension-api_pom.xml14
-rw-r--r--tools/maven/gerrit-plugin-api_pom.xml14
-rw-r--r--tools/maven/gerrit-plugin-gwtui_pom.xml83
-rw-r--r--tools/maven/gerrit-war_pom.xml14
-rw-r--r--tools/nongoogle.bzl37
-rwxr-xr-xtools/version.py3
-rw-r--r--version.bzl2
-rw-r--r--webapp/WEB-INF/extra/jetty7/gerrit.xml36
-rw-r--r--webapp/WEB-INF/web.xml6
2921 files changed, 50494 insertions, 142708 deletions
diff --git a/.bazelproject b/.bazelproject
index 8a726ebbe8..e14c108008 100644
--- a/.bazelproject
+++ b/.bazelproject
@@ -1,6 +1,6 @@
# The project view file (.bazelproject) is used to import Gerrit Bazel packages into the IDE.
#
-# See: https://ij.bazel.io/docs/project-views.html
+# See: https://ij.bazel.build/docs/project-views.html
directories:
.
diff --git a/.bazelversion b/.bazelversion
index fd2a01863f..7c69a55dbb 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.1.0
+3.7.0
diff --git a/.gitignore b/.gitignore
index ac244a6470..fb53fc6e6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,28 +6,46 @@
*.swp
*~
.DS_Store
-.gwt_work_dir
/.apt_generated
/.apt_generated_tests
/.bazel_path
/.classpath
/.factorypath
/.idea
+/.ijwb
/.metadata
/.project
/.settings/org.eclipse.ltk.core.refactoring.prefs
/.settings/org.eclipse.m2e.core.prefs
/.settings/org.maven.ide.eclipse.prefs
+/.ts-out
+/.vscode
/bazel-*
/bin/
+/bower_components/
/eclipse-out
/extras
/gerrit-package-plugins
-/gwt-unitCache
/infer-out
/local.properties
-/plugins/cookbook-plugin/
+/node_modules/
+/package-lock.json
+/plugins/*
+!/plugins/BUILD
+!/plugins/codemirror-editor
+!/plugins/commit-message-length-validator
+!/plugins/delete-project
+!/plugins/download-commands
+!/plugins/external_plugin_deps.bzl
+!/plugins/gitiles
+!/plugins/hooks
+!/plugins/plugin-manager
+!/plugins/replication
+!/plugins/reviewnotes
+!/plugins/singleusergroup
+!/plugins/webhooks
/test_site
/tools/format
-/.vscode
-/.ijwb
+/tools/maven/gerrit-*_pom.xml.asc
+/tools/node_tools
+/tools/polygerrit-updater
diff --git a/.gitmodules b/.gitmodules
index 8d75bccc32..6844f6a54a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -8,16 +8,31 @@
url = ../plugins/commit-message-length-validator
branch = .
+[submodule "plugins/delete-project"]
+ path = plugins/delete-project
+ url = ../plugins/delete-project
+ branch = .
+
[submodule "plugins/download-commands"]
path = plugins/download-commands
url = ../plugins/download-commands
branch = .
+[submodule "plugins/gitiles"]
+ path = plugins/gitiles
+ url = ../plugins/gitiles
+ branch = .
+
[submodule "plugins/hooks"]
path = plugins/hooks
url = ../plugins/hooks
branch = .
+[submodule "plugins/plugin-manager"]
+ path = plugins/plugin-manager
+ url = ../plugins/plugin-manager
+ branch = .
+
[submodule "plugins/replication"]
path = plugins/replication
url = ../plugins/replication
@@ -32,3 +47,8 @@
path = plugins/singleusergroup
url = ../plugins/singleusergroup
branch = .
+
+[submodule "plugins/webhooks"]
+ path = plugins/webhooks
+ url = ../plugins/webhooks
+ branch = .
diff --git a/.gitreview b/.gitreview
index 663037eb1e..b3c37ad125 100644
--- a/.gitreview
+++ b/.gitreview
@@ -2,4 +2,4 @@
host=gerrit-review.googlesource.com
scheme=https
project=gerrit.git
-defaultbranch=stable-2.16
+defaultbranch=stable-3.0
diff --git a/.zuul.yaml b/.zuul.yaml
index a98975b1d8..463bc51026 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -20,11 +20,15 @@
# not need to be repeated here.
- 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
- project:
check:
diff --git a/BUILD b/BUILD
index 10162beddd..3989a75bdd 100644
--- a/BUILD
+++ b/BUILD
@@ -43,15 +43,9 @@ pkg_war(
)
pkg_war(
- name = "polygerrit",
- ui = "polygerrit",
-)
-
-pkg_war(
name = "release",
context = ["//plugins:core"],
doc = True,
- ui = "ui_optdbg_r",
)
pkg_war(
@@ -69,9 +63,6 @@ API_DEPS = [
"//plugins:plugin-api_deploy.jar",
"//plugins:plugin-api-sources_deploy.jar",
"//plugins:plugin-api-javadoc",
- "//gerrit-plugin-gwtui:gwtui-api_deploy.jar",
- "//gerrit-plugin-gwtui:gwtui-api-source_deploy.jar",
- "//gerrit-plugin-gwtui:gwtui-api-javadoc",
]
genrule2(
diff --git a/Documentation/BUILD b/Documentation/BUILD
index 79b9e518b6..52ab7a88a8 100644
--- a/Documentation/BUILD
+++ b/Documentation/BUILD
@@ -44,7 +44,6 @@ license_map(
name = "licenses",
opts = ["--asciidoctor"],
targets = [
- "//gerrit-gwtui:ui_module",
"//polygerrit-ui/app:polygerrit_ui",
"//java/com/google/gerrit/pgm",
],
@@ -53,14 +52,24 @@ license_map(
license_map(
name = "js_licenses",
targets = [
- "//gerrit-gwtui:ui_module",
"//polygerrit-ui/app:polygerrit_ui",
],
)
+sh_test(
+ name = "check_licenses",
+ srcs = ["check_licenses_test.sh"],
+ data = [
+ "js_licenses.gen.txt",
+ "js_licenses.txt",
+ "licenses.gen.txt",
+ "licenses.txt",
+ ],
+)
+
DOC_DIR = "Documentation"
-SRCS = glob(["*.txt"]) + [":licenses.txt"]
+SRCS = glob(["*.txt"])
genrule(
name = "index",
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 370a8913ec..9f7c45782f 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -5,6 +5,12 @@ member of one or more groups, and access and privileges are granted
to those groups. Access rights cannot be granted to individual
users.
+To view/edit the access controls for a specific project, first
+navigate to the projects page: for example,
+https://gerrit-review.googlesource.com/admin/repos/ . Then click on
+the individual project, and then click Access. This will bring you
+to a url that looks like
+https://gerrit-review.googlesource.com/admin/repos/gerrit,access
[[system_groups]]
== System Groups
@@ -828,12 +834,20 @@ file.
[[category_view_private_changes]]
=== View Private Changes
-This category permits users to view all private changes.
+This category permits users to view all private changes and all change edit refs.
The change owner and any explicitly added reviewers can always see
private changes (even without having the `View Private Changes` access
right assigned).
+[[category_toggle_work_in_progress_state]]
+=== Toggle Work In Progress state
+
+This category controls who is able to flip the Work In Progress bit.
+
+Change owner, server administrators and project owners can always flip
+the Work In Progress bit of the change (even without having the
+`Toggle Work In Progress state` access right assigned).
[[category_delete_own_changes]]
=== Delete Own Changes
@@ -1083,21 +1097,43 @@ non-blocked rules as they wish. This gives best of both worlds:
[[block]]
=== 'BLOCK' access rule
-The 'BLOCK' rule blocks a permission globally. An inherited 'BLOCK'
-rule cannot be overridden in the inheriting project. Any 'ALLOW' rule
-from an inheriting project, which conflicts with an inherited 'BLOCK'
-rule will not be honored. Searching for 'BLOCK' rules, in the chain
-of parent projects, ignores the Exclusive flag, unless the rule with
-the Exclusive flag is defined on the same project as the 'BLOCK'
-rule. This means within the same project a 'BLOCK' rule can be
-overruled by 'ALLOW' rules on the same access section and 'ALLOW'
-rules with Exclusive flag on access section for more specific refs.
+The 'BLOCK' rule can be used to take away rights from users. The BLOCK rule
+works across project inheritance, from the top down, so an administrator can
+use 'BLOCK' rules to enforce site-wide restrictions.
+
+For example, if a user in the 'Foo Users' group tries to push to
+'refs/heads/mater' with the permissions below, that user will be blocked
+
+[options="header"]
+|=========================================================================
+|Project | Inherits From |Reference Name |Permissions |
+|All-Projects | - |refs/* |push = block Foo Users |
+|Foo | All-Projects |refs/heads/* |push = Foo Users |
+|=========================================================================
+
+'BLOCK' rules are evaluated starting from the parent project, and after a 'BLOCK'
+rule is found to apply, further rules are ignored. Hence, in this example, the
+permissions on child-project is ignored.
+
+----
+All-Projects: project.config
+ [access "refs/heads/*"]
+ push = block group X
+
+child-project: project.config
+ [access "refs/heads/*"]
+ exclusiveGroupPermissions = push
+ push = group X
+----
+
+In this case push for group 'X' will be blocked, even though the Exclusive
+flag was set for the child-project.
A 'BLOCK' rule that blocks the 'push' permission blocks any type of push,
force or not. A blocking force push rule blocks only force pushes, but
allows non-forced pushes if an 'ALLOW' rule would have permitted it.
-It is also possible to block label ranges. To block a group 'X' from voting
+It is also possible to block label ranges. To block a group 'X' from voting
'-2' and '+2', but keep their existing voting permissions for the '-1..+1'
range intact we would define:
@@ -1124,6 +1160,24 @@ the same permission then this 'ALLOW' rule overrides the 'BLOCK' rule:
In this case a user which is a member of the group 'Y' will still be allowed to
push to 'refs/heads/*' even if it is a member of the group 'X'.
+=== 'BLOCK' and 'ALLOW' rules in the same project with the Exclusive flag
+
+When a project contains a 'BLOCK' and 'ALLOW' that uses the Exclusive flag in a
+more specific reference, the 'ALLOW' rule with the Exclusive flag will override
+the 'BLOCK' rule:
+
+----
+ [access "refs/*"]
+ read = block group X
+
+ [access "refs/heads/*"]
+ exclusiveGroupPermissions = read
+ read = group X
+----
+
+In this case a user which is a member of the group 'X' will still be allowed to
+read 'refs/heads/*'.
+
[NOTE]
An 'ALLOW' rule overrides a 'BLOCK' rule only when both of them are
inside the same access section of the same project. An 'ALLOW' rule in a
@@ -1198,8 +1252,7 @@ Below you find a list of capabilities available:
[[capability_accessDatabase]]
=== Access Database
-Allow users to access the database using the `gsql` command, and view code
-review metadata refs in repositories.
+Allow users to view code review metadata refs in repositories.
[[capability_administrateServer]]
@@ -1353,8 +1406,7 @@ When applying a query limit to a user the largest value granted by
any of their groups is used.
This limit applies not only to the link:cmd-query.html[`gerrit query`]
-command, but also to the web UI results pagination size in the new
-PolyGerrit UI and, limited to the full project list, in the old GWT UI.
+command, but also to the web UI results pagination size.
[[capability_readAs]]
diff --git a/Documentation/check_licenses_test.sh b/Documentation/check_licenses_test.sh
new file mode 100755
index 0000000000..52e27f22a3
--- /dev/null
+++ b/Documentation/check_licenses_test.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+hook=$(pwd)/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
+
+for f in js_licenses licenses ; do
+ if ! diff -u Documentation/${f}.txt Documentation/${f}.gen.txt ; then
+ echo ""
+ echo "FAIL: ${f}.txt out of date"
+ echo "to fix: "
+ echo ""
+ echo " cp bazel-bin/Documentation/${f}.gen.txt Documentation/${f}.txt"
+ echo ""
+ exit 1
+ fi
+done
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 026d7b1f5a..9e3d70b752 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -28,9 +28,8 @@ _ssh_ -p <port> <host> _gerrit create-project_
== DESCRIPTION
Creates a new bare Git repository under `gerrit.basePath`, using
the project name supplied. The newly created repository is empty
-(has no commits), but is registered in the Gerrit database so that
-the initial commit may be uploaded for review, or initial content
-can be pushed directly into a branch.
+(has no commits), and the initial content may either be uploaded for
+review, or pushed directly to a branch.
If replication is enabled, this command also connects to each of
the configured remote systems over SSH and uses command line git
@@ -119,7 +118,7 @@ Description values containing spaces should be quoted in single quotes
Defaults to MERGE_IF_NECESSARY unless
link:config-gerrit.html#repository.name.defaultSubmitType[
repository.<name>.defaultSubmitType] is set to a different value.
-For more details see link:project-configuration.html#submit_type[
+For more details see link:config-project-config.html#submit-type[
Submit Types].
--use-content-merge::
diff --git a/Documentation/cmd-flush-caches.txt b/Documentation/cmd-flush-caches.txt
index 55d9083178..5a84b9d7d9 100644
--- a/Documentation/cmd-flush-caches.txt
+++ b/Documentation/cmd-flush-caches.txt
@@ -16,7 +16,7 @@ Clear an in-memory cache, forcing Gerrit to reconsult the ground
truth when it needs the information again.
Flushing a cache may be necessary if an administrator modifies
-database records directly in the database, rather than going through
+NoteDb metadata directly in a repository, rather than going through
the Gerrit web interface.
If no options are supplied, defaults to `--all`.
diff --git a/Documentation/cmd-gsql.txt b/Documentation/cmd-gsql.txt
deleted file mode 100644
index 7f2aaf7b78..0000000000
--- a/Documentation/cmd-gsql.txt
+++ /dev/null
@@ -1,64 +0,0 @@
-= gerrit gsql
-
-== NAME
-gerrit gsql - Administrative interface to active database.
-
-== SYNOPSIS
-[verse]
---
-_ssh_ -p <port> <host> _gerrit gsql_
- [--format {PRETTY | JSON | JSON_SINGLE}]
- [-c QUERY]
---
-
-== DESCRIPTION
-Provides interactive query support directly against the underlying
-SQL database used by the host Gerrit server. All SQL statements
-are supported, including SELECT, UPDATE, INSERT, DELETE and ALTER.
-
-== OPTIONS
---format::
- Set the format records are output in. In PRETTY (the
- default) records are displayed in a tabular output suitable
- for reading by a human on a sufficiently wide terminal.
- In JSON mode records are output as JSON objects using the
- column names as the property names, one object per line.
- In JSON_SINGLE mode the whole result set is output as a
- single JSON object.
-
--c::
- Execute the single query statement supplied, and then exit.
-
-== ACCESS
-Caller must have been granted the
-link:access-control.html#capability_accessDatabase[Access Database]
-global capability.
-
-== SCRIPTING
-Intended for interactive use only, unless format is JSON, or
-JSON_SINGLE.
-
-== EXAMPLES
-To manually correct a user's SSH user name:
-
-----
-$ ssh -p 29418 review.example.com gerrit gsql
-Welcome to Gerrit Code Review v2.0.25
-(PostgreSQL 8.3.8)
-
-Type '\h' for help. Type '\r' to clear the buffer.
-
-gerrit> update accounts set ssh_user_name = 'alice' where account_id=1;
-UPDATE 1; 1 ms
-gerrit> \q
-Bye
-
-$ ssh -p 29418 review.example.com gerrit flush-caches --cache sshkeys --cache accounts
-----
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 25099fae2d..edb54b5ccb 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -71,7 +71,7 @@ link:cmd-ls-projects.html[gerrit ls-projects]::
List projects visible to the caller.
link:cmd-query.html[gerrit query]::
- Query the change database.
+ Query the change search index.
'gerrit receive-pack'::
'Deprecated alias for `git receive-pack`.'
@@ -130,9 +130,6 @@ link:cmd-flush-caches.html[gerrit flush-caches]::
link:cmd-gc.html[gerrit gc]::
Run the Git garbage collection.
-link:cmd-gsql.html[gerrit gsql]::
- Administrative interface to active database.
-
link:cmd-index-activate.html[gerrit index activate]::
Activate the latest index version available.
diff --git a/Documentation/cmd-ls-user-refs.txt b/Documentation/cmd-ls-user-refs.txt
index cba7d1bdef..0363f60cf5 100644
--- a/Documentation/cmd-ls-user-refs.txt
+++ b/Documentation/cmd-ls-user-refs.txt
@@ -32,8 +32,8 @@ Administrators
--user::
-u::
Required; User for which the visible refs should be listed. Gerrit
- will query the database to find matching users, so the
- full identity/name does not need to be specified.
+ will query the index to find matching users, so the full
+ identity/name does not need to be specified.
--only-refs-heads::
Only list the refs found under refs/heads/*
diff --git a/Documentation/cmd-query.txt b/Documentation/cmd-query.txt
index 79723c52d0..d0419d7bff 100644
--- a/Documentation/cmd-query.txt
+++ b/Documentation/cmd-query.txt
@@ -1,7 +1,7 @@
= gerrit query
== NAME
-gerrit query - Query the change database
+gerrit query - Query the change search index
== SYNOPSIS
[verse]
@@ -17,6 +17,7 @@ _ssh_ -p <port> <host> _gerrit query_
[--submit-records]
[--all-reviewers]
[--start <n> | -S <n>]
+ [--no-limit]
[--]
<query>
[limit:<n>]
@@ -24,7 +25,7 @@ _ssh_ -p <port> <host> _gerrit query_
== DESCRIPTION
-Queries the change database and returns results describing changes
+Queries the change search index and returns results describing changes
that match the input query. More recently updated changes appear
before older changes, which is the same order presented in the
web interface. For each matching change, the result contains data
@@ -101,6 +102,9 @@ command line parser in the server).
-S::
Number of changes to skip.
+--no-limit::
+ Return all results, overriding the default limit.
+
limit:<n>::
Maximum number of results to return. This is actually a
query operator, and not a command line option. If more
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index 27a39f4d92..eef47fceaa 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -25,7 +25,7 @@ _ssh_ -p <port> <host> _gerrit review_
== DESCRIPTION
Updates the current user's approval status of the specified patch
sets and/or submits them for merging, sending out email
-notifications and updating the database.
+notifications and updating code review metadata.
Patch sets may be specified in 'CHANGEID,PATCHSET' format, such as
'8242,2', or 'COMMIT' format.
@@ -142,7 +142,7 @@ This command is intended to be used in scripts.
Approve the change with commit c0ff33 as "Verified +1"
----
-$ ssh -p 29418 review.example.com gerrit review --verified +1 c0ff33
+$ ssh -p 29418 review.example.com gerrit review --verified +1 8242,2
----
Approve the change with change number 8242 and patch set 2 as "Code-Review +2"
diff --git a/Documentation/cmd-set-project.txt b/Documentation/cmd-set-project.txt
index 45b31ff24e..9686230f21 100644
--- a/Documentation/cmd-set-project.txt
+++ b/Documentation/cmd-set-project.txt
@@ -59,7 +59,7 @@ Description values containing spaces should be quoted in single quotes
+
For more details see
-link:project-configuration.html#submit_type[Submit Types].
+link:config-project-config.html#submit-type[Submit Types].
--content-merge::
If enabled, Gerrit will try to perform a 3-way merge of text
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index e8c5213e8f..f0ad460380 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -234,6 +234,8 @@ patchSet:: link:json.html#patchSet[patchSet attribute]
reviewer:: link:json.html#account[account attribute]
+adder:: user that added the reviewer as link:json.html#account[account attribute]
+
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
created.
diff --git a/Documentation/config-auto-site-initialization.txt b/Documentation/config-auto-site-initialization.txt
index 1be0af961a..2253ed0968 100644
--- a/Documentation/config-auto-site-initialization.txt
+++ b/Documentation/config-auto-site-initialization.txt
@@ -2,74 +2,41 @@
== Description
-Gerrit supports automatic site initialization on server startup
-when Gerrit runs in a servlet container. Both creation of a new site
-and upgrade of an existing site are supported. By default, all packaged
-plugins will be installed when Gerrit is deployed in a servlet container
-and the location of the Gerrit distribution can be determined at
-runtime. It is also possible to install only a subset of packaged
-plugins or not install any plugins.
-
-This feature may be useful for such setups where Gerrit administrators
-don't have direct access to the database and the file system of the
-server where Gerrit should be deployed and, therefore, cannot perform
-the init from their local machine prior to deploying Gerrit on such a
-server. It may also make deployment and testing in a local servlet
-container faster to set up as the init step could be skipped.
+Gerrit supports automatic site initialization on server startup when Gerrit runs
+in a servlet container. Both creation of a new site and upgrade of an existing
+site are supported. By default, all packaged plugins will be installed when
+Gerrit is deployed in a servlet container and the location of the Gerrit
+distribution can be determined at runtime. It is also possible to install only a
+subset of packaged plugins or not install any plugins.
+
+This feature may be useful for such setups where Gerrit administrators don't
+have direct access to the file system of the server where Gerrit should be
+deployed and, therefore, cannot perform the init from their local machine prior
+to deploying Gerrit on such a server. It may also make deployment and testing in
+a local servlet container faster to set up as the init step could be skipped.
== Gerrit Configuration
-The site initialization will be performed only if the `gerrit.init`
-system property exists. The value of the property is not used; only the
-existence of the property matters.
-
-If the `gerrit.site_path` system property is defined then the init is
-run for that site. The database connectivity, in that case, is defined
-in the `etc/gerrit.config`.
-
-`gerrit.site_path` system property must be defined to run the init for
-that site.
+In order to perform site initialization, define `gerrit.site_path` with the path
+to your site. If the site already exists, this is the only required property.
+If your site does not yet exist, set the `gerrit.init` system property to
+automatically initialize the site.
-[WARNING]
-Defining the `jdbc/ReviewDb` JNDI property for an H2 database under the
-path defined by `gerrit.site_path` will cause an incomplete auto
-initialization and Gerrit will fail to start.
+During initialization, if the `gerrit.install_plugins` property is not defined,
+then all packaged plugins will be installed. If it is defined, then it is parsed
+as a comma-separated list of plugin names to install. If the value is an empty
+string then no plugins will be installed.
-Opening a connection to such a database will create a subfolder under the
-site path folder (in order to create the H2 database) and Gerrit will
-no longer consider that site path to be new and, because of that,
-skip some required initialization steps (for example, Lucene index
-creation). In order to auto initialize Gerrit with an embedded H2
-database use the `gerrit.site_path` to define the location of the review
-site and don't define a JNDI resource with a URL under that path.
+=== Example
-If the `gerrit.install_plugins` property is not defined then all packaged
-plugins will be installed. If it is defined then it is parsed as a
-comma-separated list of plugin names to install. If the value is an
-empty string then no plugin will be installed.
-
-=== Example 1
-
-Prepare Tomcat so that a site is initialized at a given path using
-the H2 database (if the site doesn't exist yet) or using whatever
-database is defined in `etc/gerrit.config` of that site:
+Prepare Tomcat so that a site is initialized at a given path (if the site
+doesn't exist yet), installing all packaged plugins.
----
$ export CATALINA_OPTS='-Dgerrit.init -Dgerrit.site_path=/path/to/site'
$ catalina.sh start
----
-=== Example 2
-
-Assuming the database schema doesn't exist in the database defined
-via the `jdbc/ReviewDb` JNDI property, initialize a new site using that
-database and a given path:
-
-----
- $ export CATALINA_OPTS='-Dgerrit.init -Dgerrit.init_path=/path/to/site'
- $ catalina.sh start
-----
-
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-cla.txt b/Documentation/config-cla.txt
index 2234808388..2c7b194426 100644
--- a/Documentation/config-cla.txt
+++ b/Documentation/config-cla.txt
@@ -25,13 +25,15 @@ Download the existing configuration from Gerrit:
----
Contributor agreements are defined as contributor-agreement sections in
-`project.config`:
+`project.config` of `All-Projects`:
----
[contributor-agreement "Individual"]
description = If you are going to be contributing code on your own, this is the one you want. You can sign this one online.
agreementUrl = static/cla_individual.html
autoVerify = group CLA Accepted - Individual
accepted = group CLA Accepted - Individual
+ matchProjects = ^/.*$
+ excludeProjects = ^/not/my/project/
----
Each `contributor-agreement` section within the `project.config` file must
@@ -75,6 +77,16 @@ List of groups that will be considered when verifying that a
contributor agreement has been accepted. The groups' UUID must also
appear in the `groups` file.
+[[contributor-agreement.name.matchProjects]]contributor-agreement.<name>.matchProjects::
++
+List of project regular expressions identifying projects where the
+agreement is required. Defaults to every project when omitted.
+
+[[contributor-agreement.name.excludeProjects]]contributor-agreement.<name>.excludeProjects::
++
+List of project regular expressions identifying projects where the
+agreement does not apply. Defaults to empty. i.e. no projects excluded.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 223d905393..3594626906 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -32,7 +32,7 @@ this it is specified in the documentation for the property below.
=== Section accountPatchReviewDb
The AccountPatchReviewDb is a database used to store the user file reviewed
-flags. It co-exists with <<database,ReviewDb>> and link:note-db.html[NoteDb].
+flags.
[[accountPatchReviewDb.url]]accountPatchReviewDb.url::
+
@@ -46,8 +46,8 @@ Changing this parameter requires to migrate database using the
link:pgm-MigrateAccountPatchReviewDb.html[MigrateAccountPatchReviewDb] program.
Migration cannot be done while the server is running.
+
-Also note that the db_name has to be a new db and not reusing gerrit's own review database,
-otherwise gerrit's init will remove the table.
+Also note that the db_name has to be a new db and not reusing an old ReviewDb
+database from a former 2.x site, otherwise gerrit's init will remove the table.
----
[accountPatchReviewDb]
@@ -273,10 +273,7 @@ When this is the configured authentication method a hyperlink titled
`Become` appears in the top right corner of the page, taking the
user to a form where they can enter the username of any existing
user account, and immediately login as that account, without any
-authentication taking place. This form of authentication is only
-useful for the GWT hosted mode shell, where OpenID authentication
-redirects might be risky to the developer's host computer, and HTTP
-authentication is not possible.
+authentication taking place.
+
By default, OpenID.
@@ -485,13 +482,12 @@ the "Switch Account" link is displayed next to "Sign Out".
+
When `auth.type` does not normally enable this URL administrators may
set this to `login/`, allowing users to begin a new web session. This value
-is used as an href in PolyGerrit and the GWT UI, so absolute URLs like
+is used as an href in PolyGerrit, so absolute URLs like
`https://someotherhost/login` work as well.
+
If a ${path} parameter is included, then PolyGerrit will substitute the
currently viewed path in the link. Be aware that this path will include
a leading slash, so a value like this might be appropriate: `/login${path}`.
-Note: in the GWT UI this substitution for ${path} is *always* `/`.
[[auth.cookiePath]]auth.cookiePath::
+
@@ -704,7 +700,9 @@ H2 uses memory to cache its database content. The parameter `h2CacheSize`
allows to limit the memory used by H2 and thus prevent out-of-memory
caused by the H2 database using too much memory.
+
-See <<database.h2.cacheSize,database.h2.cacheSize>> for a detailed discussion.
+Technically the H2 cache size is configured using the CACHE_SIZE parameter in
+the H2 JDBC connection URL, as described
+link:http://www.h2database.com/html/features.html#cache_settings[here]
+
Default is unset, using up to half of the available memory.
+
@@ -814,11 +812,10 @@ cache `"accounts"`::
+
Cache entries contain important details of an active user, including
their display name, preferences, and known email addresses. Entry
-information is obtained from the `accounts` database table.
+information is obtained from NoteDb data in the `All-Users` repo.
+
-If direct updates are made to any of these database tables, this
-cache should be flushed.
+If direct updates are made to `All-Users`, this cache should be flushed.
cache `"adv_bases"`::
+
@@ -989,6 +986,11 @@ cache `"prolog_rules"`::
Caches parsed `rules.pl` contents for each project. This cache uses the same
size as the `projects` cache, and cannot be configured independently.
+cache `"pure_revert"`::
++
+Result of checking if one change or commit is a pure/clean revert of
+another.
+
cache `"sshkeys"`::
+
Caches unpacked versions of user SSH keys, so the internal SSH daemon
@@ -1150,40 +1152,9 @@ Default is true.
[[change]]
=== Section change
-[[change.largeChange]]change.largeChange::
-+
-Number of changed lines from which on a change is considered as a large
-change. The number of changed lines of a change is the sum of the lines
-that were inserted and deleted in the change.
-+
-The specified value is used to visualize the change sizes in the Web UI
-in change tables and user dashboards.
-+
-By default 500.
-
-[[change.updateDelay]]change.updateDelay::
-+
-How often in seconds the web interface should poll for updates to the
-currently open change. The poller relies on the client's browser
-cache to use If-Modified-Since and respect `304 Not Modified` HTTP
-responses. This allows for fast polls, often under 8 milliseconds.
-+
-With a configured 30 second delay a server with 4900 active users will
-typically need to dedicate 1 CPU to the update check. 4900 users
-divided by an average delay of 30 seconds is 163 requests arriving per
-second. If requests are served at \~6 ms response time, 1 CPU is
-necessary to keep up with the update request traffic. On a smaller
-user base of 500 active users, the default 30 second delay is only 17
-requests per second and requires ~10% CPU.
-+
-If 0 the update polling is disabled.
-+
-Default is 5 minutes.
-
[[change.allowBlame]]change.allowBlame::
+
-Allow blame on side by side diff in the GWT UI. If set to false, blame cannot be
-used.
+Allow blame on side by side diff. If set to false, blame cannot be used.
+
Default is true.
@@ -1198,6 +1169,15 @@ 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
+link:rest-api-changes.html#change-info[ChangeInfo] will never be set. It can
+be requested separately through the
+link:rest-api-changes.html#get-mergeable[get-mergeable] endpoint.
++
+Default is false.
+
[[change.cacheAutomerge]]change.cacheAutomerge::
+
When reviewing merge commits, the left-hand side shows the output of the
@@ -1213,6 +1193,65 @@ performance improvements by reducing the number of refs in the repo.
+
Default is true.
+[[change.disablePrivateChanges]]change.disablePrivateChanges::
++
+If set to true, users are not allowed to create private changes.
++
+The default is false.
+
+[[change.largeChange]]change.largeChange::
++
+Number of changed lines from which on a change is considered as a large
+change. The number of changed lines of a change is the sum of the lines
+that were inserted and deleted in the change.
++
+The specified value is used to visualize the change sizes in the Web UI
+in change tables and user dashboards.
++
+By default 500.
+
+[[change.move]]change.move::
++
+Whether the link:rest-api-changes.html#move-change[Move Change] REST
+endpoint is enabled.
++
+The move change functionality has some corner cases with undesired side
+effects. Hence administrators may decide to disable this functionality.
+In particular, if a change that has dependencies on other changes is
+moved to a new branch, and the moved change gets submitted to the new
+branch, the changes on which the change depends are silently merged
+into the new branch, although these changes have not been moved to that
+branch (see details in
+link:https://bugs.chromium.org/p/gerrit/issues/detail?id=9877[issue
+9877]).
++
+By default true.
+
+[[change.replyLabel]]change.replyLabel::
++
+Label name for the reply button. In the user interface an ellipsis (…)
+is appended.
++
+Default is "Reply". In the user interface it becomes "Reply…".
+
+[[change.replyTooltip]]change.replyTooltip::
++
+Tooltip for the reply button. In the user interface a note about the
+keyboard shortcut is appended.
++
+Default is "Reply and score". In the user interface it becomes "Reply
+and score (Shortcut: a)".
+
+[[change.robotCommentSizeLimit]]change.robotCommentSizeLimit::
++
+Maximum allowed size of a robot comment that will be accepted. Robot comments
+which exceed the indicated size will be rejected on addition. The specified
+value is interpreted as the maximum size in bytes of the JSON representation of
+the robot comment. Common unit suffixes of 'k', 'm', or 'g' are supported.
+Zero or negative values allow robot comments of unlimited size.
++
+The default limit is 1024kB.
+
[[change.showAssigneeInChangesTable]]change.showAssigneeInChangesTable::
+
Show assignee field in changes table. If set to false, assignees will
@@ -1220,6 +1259,14 @@ not be visible in changes table.
+
Default is false.
+[[change.strictLabels]]change.strictLabels::
++
+Reject invalid label votes: invalid labels or invalid values. This
+configuration option is provided for backwards compatibility and may
+be removed in future gerrit versions.
++
+Default is false.
+
[[change.submitLabel]]change.submitLabel::
+
Label name for the submit button.
@@ -1253,13 +1300,6 @@ submitted.
Default is "Submit all ${topicSize} changes of the same topic (${submitSize}
changes including ancestors and other changes related by topic)".
-[[change.submitWholeTopic]]change.submitWholeTopic::
-+
-Determines if the submit button submits the whole topic instead of
-just the current change.
-+
-Default is false.
-
[[change.submitTopicLabel]]change.submitTopicLabel::
+
If `change.submitWholeTopic` is set and a change has a topic,
@@ -1280,44 +1320,31 @@ Defaults to "Submit all ${topicSize} changes of the same topic
(${submitSize} changes including ancestors and other
changes related by topic)".
-[[change.replyLabel]]change.replyLabel::
-+
-Label name for the reply button. In the user interface an ellipsis (…)
-is appended.
-+
-Default is "Reply". In the user interface it becomes "Reply…".
-
-[[change.replyTooltip]]change.replyTooltip::
-+
-Tooltip for the reply button. In the user interface a note about the
-keyboard shortcut is appended.
-+
-Default is "Reply and score". In the user interface it becomes "Reply
-and score (Shortcut: a)".
-
-[[change.robotCommentSizeLimit]]change.robotCommentSizeLimit::
+[[change.submitWholeTopic]]change.submitWholeTopic::
+
-Maximum allowed size of a robot comment that will be accepted. Robot comments
-which exceed the indicated size will be rejected on addition. The specified
-value is interpreted as the maximum size in bytes of the JSON representation of
-the robot comment. Common unit suffixes of 'k', 'm', or 'g' are supported.
-Zero or negative values allow robot comments of unlimited size.
+Determines if the submit button submits the whole topic instead of
+just the current change.
+
-The default limit is 1024kB.
+Default is false.
-[[change.strictLabels]]change.strictLabels::
+[[change.updateDelay]]change.updateDelay::
+
-Reject invalid label votes: invalid labels or invalid values. This
-configuration option is provided for backwards compatibility and may
-be removed in future gerrit versions.
+How often in seconds the web interface should poll for updates to the
+currently open change. The poller relies on the client's browser
+cache to use If-Modified-Since and respect `304 Not Modified` HTTP
+responses. This allows for fast polls, often under 8 milliseconds.
+
-Default is false.
-
-[[change.disablePrivateChanges]]change.disablePrivateChanges::
+With a configured 30 second delay a server with 4900 active users will
+typically need to dedicate 1 CPU to the update check. 4900 users
+divided by an average delay of 30 seconds is 163 requests arriving per
+second. If requests are served at \~6 ms response time, 1 CPU is
+necessary to keep up with the update request traffic. On a smaller
+user base of 500 active users, the default 30 second delay is only 17
+requests per second and requires ~10% CPU.
+
-If set to true, users are not allowed to create private changes.
+If 0 the update polling is disabled.
+
-The default is false.
+Default is 5 minutes.
[[changeCleanup]]
=== Section changeCleanup
@@ -1349,6 +1376,13 @@ Whether changes which are mergeable should be auto-abandoned.
+
By default `true`.
+[[changeCleanup.cleanupAccountPatchReview]]changeCleanup.cleanupAccountPatchReview::
++
+Whether accountPatchReview data should be also removed when change
+gets auto-abandoned.
++
+By default `false`.
+
[[changeCleanup.abandonMessage]]changeCleanup.abandonMessage::
+
Change message that should be posted when a change is abandoned.
@@ -1428,13 +1462,10 @@ upper and lower case character for each position must be used. For
example, to match the string `bug` in a case insensitive way the match
pattern `[bB][uU][gG]` needs to be used.
+
-Between the GWT UI and PolyGerrit, the commentlink.name.match regular
-expressions are applied differently. Whereas in the GWT UI the
-expressions are applied to the formatted and escaped HTML result, the
-PolyGerrit UI applies them only to the raw, unformatted and unescaped
-text form. PolyGerrit does not support regex matching against HTML.
-Comment link patterns that are written in this style should be updated
-to match text formats.
+The commentlink.name.match regular expressions are applied to the raw,
+unformatted and unescaped text form. Regex matching against HTML is not
+supported. Comment link patterns that are written in this style should
+be updated to match text formats.
+
A common pattern to match is `bug\\s+(\\d+)`.
@@ -1696,222 +1727,6 @@ Values can be specified using standard time unit abbreviations (`ms`, `sec`,
+
Default is 1 hour.
-[[database]]
-=== Section database
-
-The database section configures ReviewDb, where Gerrit stores its metadata
-records about account groups and change reviews. Starting from 2.15, accounts
-are always stored in NoteDb and, optionally, changes too. See the
-link:note-db.html[NoteDb documentation] for more information.
-
-Note that user file reviewed flags are stored in a separate database. See the
-<<accountPatchReviewDb,accountPatchReviewDb>> section for more information.
-
-----
-[database]
- type = POSTGRESQL
- hostname = localhost
- database = reviewdb
- username = gerrit
- password = s3kr3t
-----
-
-[[database.type]]database.type::
-+
-Type of database server to connect to. If set this value will be
-used to automatically create correct database.driver and database.url
-values to open the connection.
-+
-* `DB2`
-+
-Connect to a DB2 database server.
-+
-* `DERBY`
-+
-Connect to an Apache Derby database server.
-+
-* `H2`
-+
-Connect to a local embedded H2 database.
-+
-* `JDBC`
-+
-Connect using a JDBC driver class name and URL.
-+
-* `MAXDB`
-+
-Connect to an SAP MaxDB database server.
-+
-* `MYSQL`
-+
-Connect to a MySQL database server.
-+
-* `MARIADB`
-+
-Connect to a MariaDB database server.
-+
-* `ORACLE`
-+
-Connect to an Oracle database server.
-+
-* `POSTGRESQL`
-+
-Connect to a PostgreSQL database server.
-
-+
-If not specified, database.driver and database.url are used as-is,
-and if they are also not specified, defaults to H2.
-
-[[database.hostname]]database.hostname::
-+
-Hostname of the database server. Defaults to 'localhost'.
-
-[[database.port]]database.port::
-+
-Port number of the database server. Defaults to the default port
-of the server named by database.type.
-
-[[database.database]]database.database::
-+
-For POSTGRESQL or MYSQL, the name of the database on the server.
-+
-For H2, this is the path to the database, and if not absolute is
-relative to `'$site_path'`.
-
-[[database.username]]database.username::
-+
-Username to connect to the database server as.
-
-[[database.password]]database.password::
-+
-Password to authenticate to the database server with.
-
-[[database.driver]]database.driver::
-+
-Name of the JDBC driver class to connect to the database with.
-Setting this usually isn't necessary as it can be derived from
-database.type or database.url for any supported database.
-
-[[database.url]]database.url::
-+
-'jdbc:' URL for the database. Setting this variable usually
-isn't necessary as it can be constructed from the all of the
-above properties.
-
-[[database.connectionPool]]database.connectionPool::
-+
-If true, use connection pooling for database connections. Otherwise, a
-new database connection is opened for each request.
-+
-Default is false for MySQL, and true for other database backends.
-
-[[database.poolLimit]]database.poolLimit::
-+
-Maximum number of open database connections. If the server needs
-more than this number, request processing threads will wait up
-to <<database.poolMaxWait, poolMaxWait>> seconds for a
-connection to be released before they abort with an exception.
-This limit must be several units higher than the total number of
-httpd and sshd threads as some request processing code paths may
-need multiple connections.
-+
-Default is <<sshd.threads, sshd.threads>>
- + <<httpd.maxThreads, httpd.maxThreads>> + 2.
-+
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
-
-[[database.poolMinIdle]]database.poolMinIdle::
-+
-Minimum number of connections to keep idle in the pool.
-Default is 4.
-+
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
-
-[[database.poolMaxIdle]]database.poolMaxIdle::
-+
-Maximum number of connections to keep idle in the pool. If there
-are more idle connections, connections will be closed instead of
-being returned back to the pool.
-Default is min(<<database.poolLimit, database.poolLimit>>, 16).
-+
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
-
-[[database.poolMaxWait]]database.poolMaxWait::
-+
-Maximum amount of time a request processing thread will wait to
-acquire a database connection from the pool. If no connection is
-released within this time period, the processing thread will abort
-its current operations and return an error to the client.
-Values should use common unit suffixes to express their setting:
-+
-* ms, milliseconds
-* s, sec, second, seconds
-* m, min, minute, minutes
-* h, hr, hour, hours
-
-+
---
-If a unit suffix is not specified, `milliseconds` is assumed.
-
-Default is `30 seconds`.
-
-This setting only applies if
-<<database.connectionPool,database.connectionPool>> is true.
---
-
-[[database.dataSourceInterceptorClass]]database.dataSourceInterceptorClass::
-
-Class that implements DataSourceInterceptor interface to monitor SQL activity.
-This class must have default constructor and be available on Gerrit's bootstrap
-classpath, e. g. in `$gerrit_site/lib` directory. Example implementation of
-SQL monitoring can be found in javamelody-plugin.
-
-[[database.h2]]database.h2::
-+
-The settings in this section are used for the reviewdb if the
-<<database.type,database.type>> is H2.
-+
-Additionally gerrit uses H2 for storing reviewed flags on changes.
-
-[[database.h2.cacheSize]]database.h2.cacheSize::
-+
-The size of the H2 internal database cache, in bytes. The H2 internal cache for
-persistent H2-backed caches is controlled by
-<<cache.h2CacheSize,cache.h2CacheSize>>.
-+
-H2 uses memory to cache its database content. The parameter `cacheSize`
-allows to limit the memory used by H2 and thus prevent out-of-memory
-caused by the H2 database using too much memory.
-+
-Technically the H2 cache size is configured using the CACHE_SIZE parameter in
-the H2 JDBC connection URL, as described
-link:http://www.h2database.com/html/features.html#cache_settings[here]
-+
-Default is unset, using up to half of the available memory.
-+
-H2 will persist this value in the database, so to unset explicitly specify 0.
-+
-Common unit suffixes of 'k', 'm', or 'g' are supported.
-
-[[database.h2.autoServer]]database.h2.autoServer::
-+
-If `true` enable the automatic mixed mode
-(see link:http://www.h2database.com/html/features.html#auto_mixed_mode[Automatic Mixed Mode]).
-This enables concurrent access to the embedded H2 database from command line
-utils (e.g. MigrateToNoteDb).
-+
-Default is `false`.
-
-[[database.h2.maxQueryTimeout]]database.h2.maxQueryTimeout::
-+
-The maximum timeout of a query in milliseconds.
-(see http://www.h2database.com/javadoc/org/h2/engine/DbSettings.html?#MAX_QUERY_TIMEOUT[MAX_QUERY_TIMEOUT]).
-+
-The default is 0, meaning no limit.
-
[[download]]
=== Section download
@@ -2175,10 +1990,20 @@ By default unset, as the HTTP daemon must be configured externally
by the system administrator, and might not even be running on the
same host as Gerrit.
+[[gerrit.installDbModule]]gerrit.installDbModule::
++
+Repeatable list of class name of additional Guice modules to load at
+Gerrit startup as part of the dbInjector and during the init phases.
+Classes are resolved using the primary Gerrit class loader, hence the
+class needs to be either declared in Gerrit or an additional JAR
+located under the `/lib` directory.
++
+By default unset.
+
[[gerrit.installModule]]gerrit.installModule::
+
Repeatable list of class name of additional Guice modules to load at
-Gerrit startup and init phases.
+Gerrit startup as part of the sysInjector and during the init phases.
Classes are resolved using the primary Gerrit class loader, hence the
class needs to be either declared in Gerrit or an additional JAR
located under the `/lib` directory.
@@ -2190,6 +2015,7 @@ Example:
[gerrit]
installModule = com.googlesource.gerrit.libmodule.MyModule
installModule = com.example.abc.OurSpecialSauceModule
+ installDbModule = com.example.def.OurCustomProvider
----
[[gerrit.listProjectsFromIndex]]gerrit.listProjectsFromIndex::
@@ -2232,19 +2058,16 @@ URL to direct users to when they need to report a bug.
By default unset, meaning no bug report URL will be displayed. Administrators
should set this to the URL of their issue tracker, if necessary.
-[[gerrit.reportBugText]]gerrit.reportBugText::
-+
-Text to be displayed in the link to the bug report URL.
-+
-Only used when `gerrit.reportBugUrl` is set and only supported in GWT (Old UI).
+[[gerrit.enableReverseDnsLookup]]gerrit.enableReverseDnsLookup::
+
-Defaults to "Report Bug".
-
-[[gerrit.disableReverseDnsLookup]]gerrit.disableReverseDnsLookup::
+Enable reverse DNS lookup during computing ref log entry for identified user,
+to record the actual hostname of the user's host in the ref log.
+
-Disables reverse DNS lookup during computing ref log entry for identified user.
+Enabling reverse DNS lookup can cause performance issues on git push when
+the reverse DNS lookup is slow.
+
-Defaults to false.
+Defaults to false, reverse DNS lookup is disabled. The user's IP address
+will be recorded in the ref log rather than their hostname.
[[gerrit.secureStoreClass]]gerrit.secureStoreClass::
+
@@ -2263,6 +2086,25 @@ Setting this option to true will prevent this behavior.
+
By default false.
+[[gerrit.xframeOption]]gerrit.xframeOption::
++
+Add link:https://tools.ietf.org/html/rfc7034[`X-Frame-Options`] header to all HTTP
+responses. The `X-Frame-Options` HTTP response header can be used to indicate
+whether or not a browser should be allowed to render a page in a
+`<frame>`, `<iframe>`, `<embed>` or `<object>`.
++
+Available values:
++
+1. ALLOW - The page can be displayed in a frame.
+2. SAMEORIGIN - The page can only be displayed in a frame on the same origin as the page itself.
++
+If link:#gerrit.canLoadInIFrame is set to false this option is ignored and the
+`X-Frame-Options` header is always set to `DENY`.
+Setting this option to `ALLOW` will cause the `X-Frame-Options` header to be omitted
+the the page can be displayed in a frame.
++
+By default SAMEORIGIN.
+
[[gerrit.cdnPath]]gerrit.cdnPath::
+
Path prefix for PolyGerrit's static resources if using a CDN.
@@ -2294,13 +2136,6 @@ NoteDb, Gerrit will not be able to use that instance of NoteDb. The serverId
used to create the NoteDb will show in the resulting exception message in case
the value differs.
-[[gerrit.enableGwtUi]]gerrit.enableGwtUi::
-+
-Allow to switch to the GWT UI. When this option is enabled, the user can switch
-the UI from PolyGerrit (the default) to GWT.
-+
-Defaults to true.
-
[[gitweb]]
=== Section gitweb
@@ -2957,8 +2792,7 @@ Defaults to no limit.
+
Maximum number of leaf terms to allow in a query. Too-large queries may
perform poorly, so setting this option causes query parsing to fail fast
-before attempting to send them to the secondary index. Should this limit
-be reached, database is used instead of index as applicable.
+before attempting to send them to the secondary index.
+
When the index type is `LUCENE`, also sets the maximum number of clauses
permitted per BooleanQuery. This is so that all enforced query limits
@@ -3214,9 +3048,10 @@ Defaults to 10000.
When security is enabled in Elasticsearch, the username and password must be provided.
Note that the same username and password are used for all servers.
-For further information about Elasticsearch security, please refer to the documentation:
-
-* link:https://www.elastic.co/guide/en/elastic-stack-overview/6.6/security-getting-started.html[Elasticsearch 6.6]
+For further information about Elasticsearch security, please refer to
+link:https://www.elastic.co/guide/en/elasticsearch/reference/current/security-getting-started.html[the documentation].
+This is the current documentation link. Select another Elasticsearch version
+from the dropdown menu available on that page if need be.
[[elasticsearch.username]]elasticsearch.username::
+
@@ -3702,9 +3537,9 @@ Common examples:
[[note-db]]
=== Section noteDb
-NoteDb is the next generation of Gerrit storage backend, currently powering
-`googlesource.com`. For more information, including how to migrate your data,
-see the link:note-db.html[documentation].
+NoteDb is the Git-based database storage backend for Gerrit. For more
+information, including how to migrate data from an older Gerrit version, see the
+link:note-db.html[documentation].
[[notedb.accounts.sequenceBatchSize]]notedb.accounts.sequenceBatchSize::
+
@@ -3719,13 +3554,18 @@ each process retrieves at once.
+
By default, 1.
-[[notedb.onlineMigrationThreads]] notedb.onlineMigrationThreads::
+[[notedb.changes.sequenceBatchSize]]notedb.changes.sequenceBatchSize::
+
-The number of threads used to run the online migration from reviewDb to noteDb.
-This allows to speed up online migration from reviewDb to noteDb at the expense
-of imposing a higher load on the running server.
+The next available change sequence number is stored as UTF-8 text in a
+blob pointed to by the `refs/sequences/changes` ref in the `All-Projects`
+repository. Multiple processes share the same sequence by incrementing
+the counter using normal git ref updates. To amortize the cost of these
+ref updates, processes increment the counter by a larger number and
+hand out numbers from that range in memory until they run out. This
+configuration parameter controls the size of the change ID batch that
+each process retrieves at once.
+
-By default, 1.
+By default, 20.
[[oauth]]
=== Section oauth
@@ -4064,9 +3904,9 @@ The default submit type for newly created projects. Supported values
are `INHERIT`, `MERGE_IF_NECESSARY`, `FAST_FORWARD_ONLY`, `REBASE_IF_NECESSARY`,
`REBASE_ALWAYS`, `MERGE_ALWAYS` and `CHERRY_PICK`.
+
-For more details see link:project-configuration.html#submit_type[Submit Types].
+For more details see link:config-project-config.html#submit-type[Submit Types].
+
-Default is link:project-configuration.html#submit_type_inherit[`INHERIT`].
+Default is link:config-project-config.html#submit_type_inherit[`INHERIT`].
+
This submit type is only applied at project creation time if a submit type is
omitted from the link:rest-api-projects.html#project-input[ProjectInput]. If the
@@ -4702,6 +4542,21 @@ Values should use common unit suffixes to express their setting:
+
By default, 30s.
+[[sshd.gracefulStopTimeout]]sshd.gracefulStopTimeout::
++
+Set a graceful stop time. If set, Gerrit ensures that all open SSH
+sessions are preserved for a maximum period of time, before forcing the
+shutdown of the SSH daemon. During this period, no new requests
+will be accepted. This option is meant to be used in setups performing
+rolling restarts.
++
+Values should use common unit suffixes to express their setting:
++
+* s, sec, second, seconds
+* m, min, minute, minutes
++
+By default, 0 seconds (immediate shutdown).
+
[[sshd.maxConnectionsPerUser]]sshd.maxConnectionsPerUser::
+
Maximum number of concurrent SSH sessions that a user account
@@ -4873,103 +4728,6 @@ used for suggesting accounts when adding members to a group.
+
By default 0.
-[[theme]]
-=== Section theme
-
-[[theme.backgroundColor]]theme.backgroundColor::
-+
-_(GWT UI only)_ Background color for the page, and major data tables like the all
-open changes table or the account dashboard. The value must be a
-valid HTML hex color code, or standard color name.
-+
-By default white, `FFFFFF`.
-
-[[theme.topMenuColor]]theme.topMenuColor::
-+
-_(GWT UI only)_ This is the color of the main menu bar at the top of the page.
-The value must be a valid HTML hex color code, or standard color
-name.
-+
-By default white, `FFFFFF`.
-
-[[theme.textColor]]theme.textColor::
-+
-_(GWT UI only)_ Text color for the page, and major data tables like the all open
-changes table or the account dashboard. The value must be a valid HTML hex color
-code, or standard color name.
-+
-By default dark grey, `353535`.
-
-[[theme.trimColor]]theme.trimColor::
-+
-_(GWT UI only)_ Primary color used as a background color behind text. This is
-the color of the main menu bar at the top, of table headers, and of major UI
-areas that we want to offset from other portions of the page. The value must be
-a valid HTML hex color code, or standard color name.
-+
-By default a light grey, `EEEEEE`.
-
-[[theme.selectionColor]]theme.selectionColor::
-+
-_(GWT UI only)_ Background color used within a trimColor area to denote the
-currently selected tab, or the background color used in a table to denote the
-currently selected row. The value must be a valid HTML hex color code, or
-standard color name.
-+
-By default a pale blue, `D8EDF9`.
-
-[[theme.changeTableOutdatedColor]]theme.changeTableOutdatedColor::
-+
-_(GWT UI only)_ Background color used for patch outdated messages. The value
-must be a valid HTML hex color code, or standard color name.
-+
-By default a shade of red, `F08080`.
-
-[[theme.tableOddRowColor]]theme.tableOddRowColor::
-+
-_(GWT UI only)_ Background color for tables such as lists of open reviews for
-odd rows. This is so you can have a different color for odd and even rows of
-the table. The value must be a valid HTML hex color code, or standard color
-name.
-+
-By default transparent.
-
-[[theme.tableEvenRowColor]]theme.tableEvenRowColor::
-+
-_(GWT UI only)_ Background color for tables such as lists of open reviews for
-even rows. This is so you can have a different color for odd and even rows of
-the table. The value must be a valid HTML hex color code, or standard color
-name.
-+
-By default transparent.
-
-A different theme may be used for signed-in vs. signed-out user status
-by using the "signed-in" and "signed-out" theme sections. Variables
-not specified in a section are inherited from the default theme.
-
-----
-[theme]
- backgroundColor = FFFFFF
-[theme "signed-in"]
- backgroundColor = C0C0C0
-[theme "signed-out"]
- backgroundColor = 00FFFF
-----
-
-As example, here is the theme configuration to have the old green look:
-
-----
-[theme]
- backgroundColor = FCFEEF
- textColor = 000000
- trimColor = D4E9A9
- selectionColor = FFFFCC
- topMenuColor = D4E9A9
- changeTableOutdatedColor = F08080
-[theme "signed-in"]
- backgroundColor = FFFFFF
-----
-
[[trackingid]]
=== Section trackingid
@@ -5090,54 +4848,6 @@ is set to true.
link:#schedule-configuration-examples[Schedule examples] can be found
in the link:#schedule-configuration[Schedule Configuration] section.
-[[urlAlias]]
-=== Section urlAlias
-
-[NOTE]
-urlAlias settings are only supported in GWT (Old UI) and only effective
-in GWT pages. Plugins that serve their own pages (e.g. gitiles) may
-ignore these settings. An alternative is serving Gerrit behind a
-link:config-reverseproxy.html[reverse proxy] and configure URL rewriting
-in the proxy configuration.
-
-URL aliases define regular expressions for URL tokens that are mapped
-to target URL tokens.
-
-Each URL alias must be specified in its own subsection. The subsection
-name should be a descriptive name. It must be unique, but is not
-interpreted in any way.
-
-The URL aliases are applied in no particular order. The first matching
-URL alias is used and further matches are ignored.
-
-URL aliases can be used to map plugin screens into the Gerrit URL
-namespace, or to replace Gerrit screens by plugin screens.
-
-Example:
-
-----
-[urlAlias "MyPluginScreen"]
- match = /myscreen/(.*)
- token = /x/myplugin/myscreen/$1
-[urlAlias "MyChangeScreen"]
- match = /c/(.*)
- token = /x/myplugin/c/$1
-----
-
-[[urlAlias.match]]urlAlias.match::
-+
-A regular expression for a URL token.
-+
-The matched URL token is replaced by `urlAlias.token`.
-
-[[urlAlias.token]]urlAlias.token::
-+
-The target URL token.
-+
-It can contain placeholders for the groups matched by the
-`urlAlias.match` regular expression: `$1` for the first matched group,
-`$2` for the second matched group, etc.
-
[[submodule]]
=== Section submodule
@@ -5286,6 +4996,38 @@ executions are `Wed 10:30`, `Fri 10:30`. etc.
Assuming that the server is started on `Mon 07:00` then this yields the
first run on Tuesday at 06:00 and a repetition interval of 1 day.
+[[All-Projects-project.config]]
+== File `etc/All-Projects/project.config`
+
+The optional file `'$site_path'/etc/All-Projects/project.config` provides
+defaults for configuration read from
+link:config-project-config.html[`project.config`] in the
+`All-Projects` repo. Unlike `gerrit.config`, this file contains project-type
+configuration rather than server-type configuration.
+
+Most administrators will not need this file, and should instead make commits to
+`All-Projects` to modify global config. However, a separate file can be useful
+when managing multiple Gerrit servers, since pushing changes to defaults using
+Puppet or a similar tool can be easier than scripting git updates to
+`All-Projects`.
+
+The contents of the file are loaded each time the `All-Projects` project is
+reloaded. Updating the file requires either evicting the project cache or
+restarting the server.
+
+Caveats:
+
+* The path from which the file is read corresponds to the name of the repo,
+ which is link:#gerrit.allProjects[configurable].
+* Although the file lives in a directory that shares a name with a repository,
+ this directory is not a Git repository.
+* Only the file `project.config` is read from this directory to provide
+ defaults; any other files in this directory, such as `rules.pl`, are ignored.
+ (This behavior may change in the future.)
+* Group names listed in the access config in this file are resolved to UUIDs
+ using the `groups` file in the repository, not in the config directory. As a
+ result, setting ACLs in this file is not recommended.
+
[[secure.config]]
== File `etc/secure.config`
diff --git a/Documentation/config-login-register.txt b/Documentation/config-login-register.txt
deleted file mode 100644
index 3dcef0a603..0000000000
--- a/Documentation/config-login-register.txt
+++ /dev/null
@@ -1,137 +0,0 @@
-[[usersetup]]
-== Initial Login
-It's time to exit the gerrit account as you now have Gerrit running on your
-host and setup your first workspace.
-
-Start a shell with the credentials of the account you will perform
-development under.
-
-Check whether there are any ssh keys already. You're looking for two files,
-id_rsa and id_rsa.pub.
-
-----
- user@host:~$ ls .ssh
- authorized_keys config id_rsa id_rsa.pub known_hosts
- user@host:~$
-----
-
-If you have the files, you may skip the key generating step.
-
-If you don't see the files in your listing, your will have to generate rsa
-keys for your ssh sessions:
-
-=== SSH key generation
-
-*Please don't generate new keys if you already have a valid keypair!*
-*They will be overwritten!*
-
-----
- user@host:~$ ssh-keygen -t rsa
- Generating public/private rsa key pair.
- Enter file in which to save the key (/home/user/.ssh/id_rsa):
- Created directory '/home/user/.ssh'.
- Enter passphrase (empty for no passphrase):
- Enter same passphrase again:
- Your identification has been saved in /home/user/.ssh/id_rsa.
- Your public key has been saved in /home/user/.ssh/id_rsa.pub.
- The key fingerprint is:
- 00:11:22:00:11:22:00:11:44:00:11:22:00:11:22:99 user@host
- The key's randomart image is:
- +--[ RSA 2048]----+
- | ..+.*=+oo.*E|
- | u.OoB.. . +|
- | ..*. |
- | o |
- | . S .. |
- | |
- | |
- | .. |
- | |
- +-----------------+
- user@host:~$
-----
-
-=== Registering your key in Gerrit
-
-Open a browser and enter the canonical url of your Gerrit server. You can
-find the url in the settings file.
-
-----
- gerrit@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config gerrit.canonicalWebUrl
- http://localhost:8080/
- gerrit@host:~$
-----
-
-Register a new account in Gerrit through the web interface with the
-email address of your choice.
-
-The default authentication type is OpenID. If your Gerrit server is behind a
-proxy, and you are using an external OpenID provider, you will need to add the
-proxy settings in the configuration file.
-
-----
- gerrit@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config --add http.proxy http://proxy:8080
- gerrit@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config --add http.proxyUsername username
- gerrit@host:~$ git config -f ~/gerrit_testsite/etc/gerrit.config --add http.proxyPassword password
-----
-
-Refer to the Gerrit configuration guide for more detailed information about
-link:config-gerrit.html#auth[authentication] and
-link:config-gerrit.html#http.proxy[proxy] settings.
-
-The first user to sign-in and register an account will be
-automatically placed into the fully privileged Administrators group,
-permitting server management over the web and over SSH. Subsequent
-users will be automatically registered as unprivileged users.
-
-Once signed in as your user, you find a little wizard to get you started.
-The wizard helps you fill out:
-
-* Real name (visible name in Gerrit)
-* Register your email (it must be confirmed later)
-* Select a username with which to communicate with Gerrit over ssh+git. Note
-that once saved, the username cannot be changed.
-
-* The server will ask you for an RSA public key.
-That's the key we generated above, and it's time to make sure that Gerrit knows
-about our new key and can identify us by it.
-
-----
- user@host:~$ cat .ssh/id_rsa.pub
- ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA1bidOd8LAp7Vp95M1b9z+LGO96OEWzdAgBPfZPq05jUh
- jw0mIdUuvg5lhwswnNsvmnFhGbsUoXZui6jdXj7xPUWOD8feX2NNEjTAEeX7DXOhnozNAkk/Z98WUV2B
- xUBqhRi8vhVmaCM8E+JkHzAc+7/HVYBTuPUS7lYPby5w95gs3zVxrX8d1++IXg/u/F/47zUxhdaELMw2
- deD8XLhrNPx2FQ83FxrjnVvEKQJyD2OoqxbC2KcUGYJ/3fhiupn/YpnZsl5+6mfQuZRJEoZ/FH2n4DEH
- wzgBBBagBr0ZZCEkl74s4KFZp6JJw/ZSjMRXsXXXWvwcTpaUEDii708HGw== John Doe@MACHINE
- user@host:~$
-----
-
-[IMPORTANT]
-Please take note of the extra line-breaks introduced in the key above
-for formatting purposes. Please be sure to copy and paste your key without
-line-breaks.
-
-Copy the string starting with ssh-rsa to your clipboard and then paste it
-into the box for RSA keys. Make *absolutely sure* no extra spaces or line feeds
-are entered in the middle of the RSA string.
-
-Verify that the ssh connection works for you.
-
-----
- user@host:~$ ssh user@localhost -p 29418
- The authenticity of host '[localhost]:29418 ([127.0.0.1]:29418)' can't be established.
- RSA key fingerprint is db:07:3d:c2:94:25:b5:8d:ac:bc:b5:9e:2f:95:5f:4a.
- Are you sure you want to continue connecting (yes/no)? yes
- Warning: Permanently added '[localhost]:29418' (RSA) to the list of known hosts.
-
- **** Welcome to Gerrit Code Review ****
-
- Hi user, you have successfully connected over SSH.
-
- Unfortunately, interactive shells are disabled.
- To clone a hosted Git repository, use:
-
- git clone ssh://user@localhost:29418/REPOSITORY_NAME.git
-
- user@host:~$
-----
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index 4b8e6ef5a2..fb53937d13 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -9,6 +9,10 @@ Plugin installation is as easy as dropping the plugin jar into the
link:config-gerrit.html#plugins.checkFrequency[a few minutes] until
the server picks up new and updated plugins.
+Due to caching, you might need to flush your browser cache after
+installing a plugin. Users will usually see the result within
+several minutes.
+
Plugins can also be installed via
link:rest-api-plugins.html#install-plugin[REST] and
link:cmd-plugin-install.html[SSH].
@@ -65,6 +69,18 @@ Documentation] |
link:https://gerrit.googlesource.com/plugins/commit-message-length-validator/+doc/master/src/main/resources/Documentation/config.md[
Configuration]
+[[delete-project]]
+=== delete-project
+
+Provides the ability to delete a project.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/delete-project[
+Project] |
+link:https://gerrit.googlesource.com/plugins/delete-project/+doc/master/src/main/resources/Documentation/about.md[
+Documentation] |
+link:https://gerrit.googlesource.com/plugins/delete-project/+doc/master/src/main/resources/Documentation/config.md[
+Configuration]
+
[[download-commands]]
=== download-commands
@@ -78,6 +94,14 @@ Documentation] |
link:https://gerrit.googlesource.com/plugins/download-commands/+doc/master/src/main/resources/Documentation/config.md[
Configuration]
+[[gitiles]]
+=== gitiles
+
+Plugin running Gitiles alongside a Gerrit server.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/gitiles[
+Project]
+
[[hooks]]
=== hooks
@@ -90,6 +114,20 @@ Documentation] |
link:https://gerrit.googlesource.com/plugins/hooks/+doc/master/src/main/resources/Documentation/config.md[
Configuration]
+[[plugin-manager]]
+=== plugin-manager
+
+This plugins provides an initial wizard to discover and install Gerrit plugins.
+Per default GerritForge CI is used to download the plugin artifacts from, but
+this can be changed per plugin configuration.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/plugin-manager[
+Project]
+link:https://gerrit.googlesource.com/plugins/plugin-manager/+doc/master/src/main/resources/Documentation/about.md[
+Documentation]
+link:https://gerrit.googlesource.com/plugins/plugin-manager/+doc/master/src/main/resources/Documentation/config.md[
+Configuration]
+
[[replication]]
=== replication
@@ -123,6 +161,18 @@ This plugin provides a group per user. This is useful to assign access
rights directly to a single user, since in Gerrit access rights can
only be assigned to groups.
+[[webhooks]]
+=== webhooks
+
+This plugin allows to propagate Gerrit events to remote http endpoints.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/webhooks[
+Project] |
+link:https://gerrit.googlesource.com/plugins/webhooks/+doc/master/src/main/resources/Documentation/about.md[
+Documentation] |
+link:https://gerrit.googlesource.com/plugins/webhooks/+doc/master/src/main/resources/Documentation/config.md[
+Configuration]
+
[[other-plugins]]
== Other Plugins
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index f2c7965199..22d8a0dbf8 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -96,7 +96,41 @@ The project section includes configuration of project settings.
These are the keys:
-- Description
+[[description]]description::
++
+A description for the project.
+
+[[state]]state::
++
+This setting defines the state of the project. A project can have the
+following states:
+
+- `Active`:
++
+The project is active and users can see and modify the project according
+to their access rights on the project.
+
+- `Read Only`:
++
+The project is read only and all modifying operations on it are
+disabled. E.g. this means that pushing to this project fails for all
+users even if they have push permissions assigned on it.
++
+Setting a project to this state is an easy way to temporary close a
+project, as you can keep all write access rights in place and they will
+become active again as soon as the project state is set back to
+`Active`.
++
+This state also makes sense if a project was moved to another location.
+In this case all new development should happen in the new project and
+you want to prevent that somebody accidentally works on the old
+project, while keeping the old project around for old references.
+
+- `Hidden`:
++
+The project is hidden and only visible to project owners. Other users
+are not able to see the project even if they have read permissions
+granted on the project.
[[receive-section]]
@@ -125,11 +159,27 @@ property is inherited from the parent project.
[[receive.requireChangeId]]receive.requireChangeId::
+
-Controls whether or not the Change-Id must be included in the commit message
-in the last paragraph. Default is `INHERIT`, which means that this property
-is inherited from the parent project. The global default for new hosts
-is `true`
-+
+The `Require Change-Id in commit message` option defines whether a
+link:user-changeid.html[Change-Id] in the commit message is required
+for pushing a commit for review. If this option is set, trying to push
+a commit for review that doesn't contain a Change-Id in the commit
+message fails with link:error-missing-changeid.html[missing Change-Id
+in commit message footer].
+
+It is recommended to set this option and use a
+link:user-changeid.html#create[commit-msg hook] (or other client side
+tooling like EGit) to automatically generate Change-Id's for new
+commits. This way the Change-Id is automatically in place when changes
+are reworked or rebased and uploading new patch sets gets easy.
+
+If this option is not set, commits can be uploaded without a Change-Id,
+but then users have to remember to copy the assigned Change-Id from the
+change screen and insert it manually into the commit message when they
+want to upload a second patch set.
+
+Default is `INHERIT`, which means that this property is inherited from
+the parent project. The global default for new hosts is `true`
+
This option is deprecated and future releases will behave as if this
is always `true`.
@@ -210,6 +260,25 @@ the implicit merge check.
Default is `INHERIT`, which means that this property is inherited from
the parent project.
+[[receive.createNewChangeForAllNotInTarget]]receive.createNewChangeForAllNotInTarget::
++
+The `create-new-change-for-all-not-in-target` option provides a
+convenience for selecting link:user-upload.html#base[the merge base]
+by setting it automatically to the target branch's tip so you can
+create new changes for all commits not in the target branch.
+
+This option is disabled if the tip of the push is a merge commit.
+
+This option also only works if there are no merge commits in the
+commit chain, in such cases it fails warning the user that such
+pushes can only be performed by manually specifying
+link:user-upload.html#base[bases]
+
+This option is useful if you want to push a change to your personal
+branch first and for review to another branch for example. Or in cases
+where a commit is already merged into a branch and you want to create
+a new open change for that commit on another branch.
+
[[change-section]]
=== Change section
@@ -247,26 +316,32 @@ the parent project.
The submit section includes configuration of project-specific
submit settings:
-- 'mergeContent': Defines whether to automatically merge changes. Valid values
-are 'true', 'false', or 'INHERIT'. Default is 'INHERIT'.
-
-- 'action': defines the link:project-configuration.html#submit_type[submit type]. Valid
-values are 'fast forward only', 'merge if necessary', 'rebase if necessary',
-'rebase always', 'merge always' and 'cherry pick'. The default is 'merge if necessary'.
-
-- 'matchAuthorToCommitterDate': Defines whether to the author date will be changed to match the
-submitter date upon submit, so that git log shows when the change was submitted instead of when the
-author last committed. Valid values are 'true', 'false', or 'INHERIT'. The default is 'INHERIT'.
-This option only takes effect in submit strategies which already modify the commit, i.e.
-Cherry Pick, Rebase Always, and (perhaps) Rebase If Necessary.
+[[submit.mergeContent]]submit.mergeContent::
++
+Defines whether to automatically merge changes. Valid values are 'true', 'false', or 'INHERIT'.
+Default is 'INHERIT'.
-- 'rejectEmptyCommit': Defines whether empty commits should be rejected when a change is merged.
-Changes might not seem empty at first but when attempting to merge, rebasing can lead to an empty
-commit. If this option is set to 'true' the merge would fail. An empty commit is still allowed as
-the initial commit on a branch.
+[[submit.action]]submit.action::
++
+Defines the link:#submit-type[submit type]. Valid values are 'fast forward only',
+'merge if necessary', 'rebase if necessary', 'rebase always', 'merge always' and 'cherry pick'.
+The default is 'merge if necessary'.
-Merge strategy
+[[submit.matchAuthorToCommitterDate]]submit.matchAuthorToCommitterDate::
++
+Defines whether the author date will be changed to match the submitter date upon submit, so that
+git log shows when the change was submitted instead of when the author last committed. Valid
+values are 'true', 'false', or 'INHERIT'. The default is 'INHERIT'. This option only takes effect
+in submit strategies which already modify the commit, i.e. Cherry Pick, Rebase Always, and
+(when rebase is necessary) Rebase If Necessary.
+[[submit.rejectEmptyCommit]]submit.rejectEmptyCommit::
++
+Defines whether empty commits should be rejected when a change is merged. When using
+link:#submit.action[submit action] Cherry Pick, Rebase If Necessary or Rebase Always changes may
+become empty upon submit, since the rebase|cherry-pick can lead to an empty commit. If this option
+is set to 'true' the merge would fail in such a case. An empty commit is still allowed as the
+initial commit on a branch.
[[access-section]]
=== Access section
@@ -404,6 +479,100 @@ interpretable by the 'Prolog Cafe' interpreter.
You can read more about the +rules.pl+ file and the prolog rules on
link:prolog-cookbook.html[the Prolog cookbook page].
+[[submit-type]]
+=== Submit Type
+
+The method Gerrit uses to submit a change to a project can be
+modified by any project owner through the project console, `Projects` >
+`List` > my/project. In general, a submitted change is only merged if all
+its dependencies are also submitted, with exceptions documented below.
+The following submit types are supported:
+
+[[submit_type_inherit]]
+* Inherit
++
+This is the default for new projects, unless overridden by a global
+link:config-gerrit.html#repository.name.defaultSubmitType[`defaultSubmitType` option].
++
+Inherit the submit type from the parent project. In `All-Projects`, this
+is equivalent to link:#merge_if_necessary[Merge If Necessary].
+
+[[fast_forward_only]]
+* Fast Forward Only
++
+With this method Gerrit does not create merge commits on submitting a
+change. Merge commits may still be submitted, but they must be created
+on the client prior to uploading to Gerrit for review.
++
+To submit a change, the change must be a strict superset of the
+destination branch. That is, the change must already contain the
+tip of the destination branch at submit time.
+
+[[merge_if_necessary]]
+* Merge If Necessary
++
+If the change being submitted is a strict superset of the destination
+branch, then the branch is fast-forwarded to the change. If not,
+then a merge commit is automatically created. This is identical
+to the classical `git merge` behavior, or `git merge --ff`.
+
+[[always_merge]]
+* Always Merge
++
+Always produce a merge commit, even if the change is a strict
+superset of the destination branch. This is identical to the
+behavior of `git merge --no-ff`, and may be useful if the
+project needs to follow submits with `git log --first-parent`.
+
+[[cherry_pick]]
+* Cherry Pick
++
+Always cherry pick the patch set, ignoring the parent lineage
+and instead creating a brand new commit on top of the current
+branch head.
++
+When cherry picking a change, Gerrit automatically appends onto the
+end of the commit message a short summary of the change's approvals,
+and a URL link back to the change on the web. The committer header
+is also set to the submitter, while the author header retains the
+original patch set author.
++
+Note that Gerrit ignores dependencies between changes when using this
+submit type unless
+link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
+is enabled and depending changes share the same topic. So generally
+submitters must remember to submit changes in the right order when using this
+submit type. If all you want is extra information in the commit message,
+consider using the Rebase Always submit strategy.
+
+[[rebase_if_necessary]]
+* Rebase If Necessary
++
+If the change being submitted is a strict superset of the destination
+branch, then the branch is fast-forwarded to the change. If not,
+then the change is automatically rebased and then the branch is
+fast-forwarded to the change.
++
+When Gerrit tries to do a merge, by default the merge will only
+succeed if there is no path conflict. A path conflict occurs when
+the same file has also been changed on the other side of the merge.
+
+[[rebase_always]]
+* Rebase Always
++
+Basically, the same as Rebase If Necessary, but it creates a new patchset even
+if fast forward is possible AND like Cherry Pick it ensures footers such as
+Change-Id, Reviewed-On, and others are present in resulting commit that is
+merged.
++
+Thus, Rebase Always can be considered similar to Cherry Pick, but with
+the important distinction that Rebase Always does not ignore dependencies.
+
+[[content_merge]]
+=== Allow content merges
+If `Allow content merges` is enabled, Gerrit will try
+to do a content merge when a path conflict occurs.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/config-robot-comments.txt b/Documentation/config-robot-comments.txt
index cf5de1012c..00776972b3 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 only supported with NoteDb, but not with ReviewDb.
* 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-themes.txt b/Documentation/config-themes.txt
index dcfd7114f5..a83c747a52 100644
--- a/Documentation/config-themes.txt
+++ b/Documentation/config-themes.txt
@@ -4,34 +4,28 @@ Gerrit supports some customization of the HTML it sends to
the browser, allowing organizations to alter the look and
feel of the application to fit with their general scheme.
-Configuration can either be sitewide or per-project. Projects without a
-specified theme inherit from their parents, or from the sitewide theme
-for `All-Projects`.
+== HTML Header/Footer and CSS
-Sitewide themes are stored in `'$site_path'/etc`, and per-project
-themes are stored in `'$site_path'/themes/{project-name}`. Files are
-only served from a single theme directory; if you want to modify or
-extend an inherited theme, you must copy it into the appropriate
-per-project directory.
-
-== HTML Header/Footer
+The HTML header, footer and CSS may be customized for login
+screens (LDAP, OAuth, OpenId) and the internally managed
+Gitweb servlet.
At startup Gerrit reads the following files (if they exist) and
uses them to customize the HTML page it sends to clients:
-* `<theme-dir>/GerritSiteHeader.html`
+* `etc/GerritSiteHeader.html`
+
HTML is inserted below the menu bar, but above any page content.
This is a good location for an organizational logo, or links to
other systems like bug tracking.
-* `<theme-dir>/GerritSiteFooter.html`
+* `etc/GerritSiteFooter.html`
+
HTML is inserted at the bottom of the page, below all other content,
but just above the footer rule and the "Powered by Gerrit Code
Review (v....)" message shown at the extreme bottom.
-* `<theme-dir>/GerritSite.css`
+* `etc/GerritSite.css`
+
The CSS rules are inlined into the top of the HTML page, inside
of a `<style>` tag. These rules can be used to support styling
@@ -129,9 +123,7 @@ instead of '#' because Analytics won't track anchors.
The `window.onload` callback is necessary to ensure that the
`Gerrit.on()` function has actually been defined by the
-page. Because GWT loads the module asynchronously any `<script>`
-block in the header or footer will execute before Gerrit has defined
-the function and is ready to register the hook callback.
+page.
GERRIT
------
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
deleted file mode 100644
index de4fb20254..0000000000
--- a/Documentation/database-setup.txt
+++ /dev/null
@@ -1,280 +0,0 @@
-[[createdb]]
-== Database Setup
-
-During the init phase of Gerrit you will need to specify which database to use.
-
-[[createdb_h2]]
-=== H2
-
-If you choose H2, Gerrit will automatically set up the embedded H2 database as
-backend so no set up or configuration is necessary.
-
-Using the embedded H2 database is the easiest way to get a Gerrit
-site up and running, making it ideal for proof of concepts or small team
-servers. On the flip side, H2 is not the recommended option for large
-corporate installations when using ReviewDb. This is because there is no easy way to interact
-with the database while Gerrit is offline, it's not easy to backup the data,
-and it's not possible to set up H2 in a load balanced/hotswap configuration.
-
-If this option interests you, you might want to consider
-link:linux-quickstart.html[the quick guide].
-
-[[createdb_derby]]
-=== Apache Derby
-
-If Derby is selected, Gerrit will automatically set up the embedded Derby
-database as backend so no set up or configuration is necessary.
-
-Currently only support for embedded mode is added. There are two other
-deployment options for Apache Derby that can be added later:
-
-* link:http://db.apache.org/derby/papers/DerbyTut/ns_intro.html#Network+Server+Options[
-Derby Network Server (standalone mode)]
-
-* link:http://db.apache.org/derby/papers/DerbyTut/ns_intro.html#Embedded+Server[
-Embedded Server (hybrid mode)]
-
-[[createdb_postgres]]
-=== PostgreSQL
-
-This option is more complicated than the H2 option but is recommended
-for larger installations. It's the database backend with the largest userbase
-in the Gerrit community.
-
-Create a user for the web application within PostgreSQL, assign it a
-password, create a database to store the metadata, and grant the user
-full rights on the newly created database:
-
-----
- $ createuser --username=postgres -RDIElPS gerrit
- $ createdb --username=postgres -E UTF-8 -O gerrit reviewdb
-----
-
-Visit PostgreSQL's link:http://www.postgresql.org/docs/9.1/interactive/index.html[documentation] for further information regarding
-using PostgreSQL.
-
-[[createdb_mysql]]
-=== MySQL
-
-Requirements: MySQL version 5.1 or later.
-
-This option is also more complicated than the H2 option. Just as with
-PostgreSQL it's also recommended for larger installations.
-
-Create a user for the web application within the database, assign it a
-password, create a database, and give the newly created user full
-rights on it:
-
-----
- mysql
-
- CREATE USER 'gerrit'@'localhost' IDENTIFIED BY 'secret';
- CREATE DATABASE reviewdb DEFAULT CHARACTER SET 'utf8';
- GRANT ALL ON reviewdb.* TO 'gerrit'@'localhost';
- FLUSH PRIVILEGES;
-----
-
-Visit MySQL's link:http://dev.mysql.com/doc/[documentation] for further
-information regarding using MySQL.
-
-[[createdb_mariadb]]
-=== MariaDB
-
-Requirements: MariaDB version 5.5 or later.
-
-Refer to MySQL section above how to create MariaDB database.
-
-Visit MariaDB's link:https://mariadb.com/kb/en/mariadb/[documentation] for further
-information regarding using MariaDB.
-
-[[createdb_oracle]]
-=== Oracle
-
-PostgreSQL or H2 is the recommended database for Gerrit Code Review.
-Oracle is supported for environments where running on an existing Oracle
-installation simplifies administrative overheads, such as database backups.
-
-Create a user for the web application within sqlplus, assign it a
-password, and grant the user full rights on the newly created database:
-
-----
- SQL> create user gerrit identified by secret_password default tablespace users;
- SQL> grant connect, resources to gerrit;
-----
-
-JDBC driver ojdbc6.jar must be obtained from your Oracle distribution. Gerrit
-initialization process tries to copy it from a known location:
-
-----
-/u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
-----
-
-If this file can not be located at this place, then the alternative location
-can be provided.
-
-Instance name is the Oracle SID. Sample database section in
-$site_path/etc/gerrit.config:
-
-----
-[database]
- type = oracle
- instance = xe
- hostname = localhost
- username = gerrit
- port = 1521
-----
-
-Sample database section in $site_path/etc/secure.config:
-
-----
-[database]
- password = secret_password
-----
-
-[[createdb_maxdb]]
-=== SAP MaxDB
-
-SAP MaxDB is a supported database for running Gerrit Code Review. However it is
-recommended only for environments where you intend to run Gerrit on an existing
-MaxDB installation to reduce administrative overhead.
-
-In the MaxDB studio or using the SQLCLI command line interface create a user
-'gerrit' with the user class 'RESOURCE' and a password <secret password>. This
-will also create an associated schema on the database.
-
-To run Gerrit on MaxDB, you need to obtain the MaxDB JDBC driver. It can be
-found in your MaxDB installation at the following location:
-
-- on Windows 64bit at "C:\Program Files\sdb\MaxDB\runtime\jar\sapdbc.jar"
-- on Linux at "/opt/sdb/MaxDB/runtime/jar/sapdbc.jar"
-
-It needs to be stored in the 'lib' folder of the review site.
-
-In the following sample database section it is assumed that the database name is
-'reviewdb' and the database is installed on localhost:
-
-In $site_path/etc/gerrit.config:
-
-----
-[database]
- type = maxdb
- database = reviewdb
- hostname = localhost
- username = gerrit
-
-----
-
-In $site_path/etc/secure.config:
-
-----
-[database]
- password = <secret password>
-----
-
-Visit SAP MaxDB's link:http://maxdb.sap.com/documentation/[documentation] for further
-information regarding using SAP MaxDB.
-
-[[createdb_db2]]
-=== DB2
-
-IBM DB2 is a supported database for running Gerrit Code Review. However it is
-recommended only for environments where you intend to run Gerrit on an existing
-DB2 installation to reduce administrative overhead.
-
-Create a system wide user for the Gerrit application, and grant the user
-full rights on the newly created database:
-
-----
- db2 => create database gerrit
- db2 => connect to gerrit
- db2 => grant connect,accessctrl,dataaccess,dbadm,secadm on database to gerrit;
-----
-
-JDBC driver db2jcc4.jar and db2jcc_license_cu.jar must be obtained
-from your DB2 distribution. Gerrit initialization process tries to copy
-it from a known location:
-
-----
-/opt/ibm/db2/V10.5/java/db2jcc4.jar
-/opt/ibm/db2/V10.5/java/db2jcc_license_cu.jar
-----
-
-If these files cannot be located at this place, then an alternative location
-can be provided during init step execution.
-
-Sample database section in $site_path/etc/gerrit.config:
-
-----
-[database]
- type = db2
- database = gerrit
- hostname = localhost
- username = gerrit
- port = 50001
-----
-
-Sample database section in $site_path/etc/secure.config:
-
-----
-[database]
- password = secret_password
-----
-
-[[createdb_hana]]
-=== SAP HANA
-
-SAP HANA is a supported database for running Gerrit Code Review. However it is
-recommended only for environments where you intend to run Gerrit on an existing
-HANA installation to reduce administrative overhead.
-
-In the HANA studio or the SAP HANA Web-based Development Workbench create a user
-'GERRIT2' with the role 'RESTRICTED_USER_JDBC_ACCESS' and a password
-<secret password>. This will also create an associated schema on the database.
-As this user would be required to change the password upon first login you might
-want to to disable the password lifetime check by executing
-'ALTER USER GERRIT2 DISABLE PASSWORD LIFETIME'.
-
-To run Gerrit on HANA, you need to obtain the HANA JDBC driver. It can be found
-as described
-link:http://help.sap.com/saphelp_hanaplatform/helpdata/en/ff/15928cf5594d78b841fbbe649f04b4/frameset.htm[here].
-It needs to be stored in the 'lib' folder of the review site.
-
-In the following sample database section it is assumed that HANA is running on
-the host 'hana.host' and listening on port '4242' where a schema/user GERRIT2
-was created:
-
-In $site_path/etc/gerrit.config:
-
-----
-[database]
- type = hana
- hostname = hana.host
- port = 4242
- username = GERRIT2
-
-----
-
-In order to configure a specific database in a multi-database environment (MDC)
-the database name has to be specified additionally:
-
-In $site_path/etc/gerrit.config:
-
-----
-[database]
- type = hana
- hostname = hana.host
- database = tdb1
- port = 4242
- username = GERRIT2
-
-----
-
-In $site_path/etc/secure.config:
-
-----
-[database]
- password = <secret password>
-----
-
-Visit SAP HANA's link:http://help.sap.com/hana_appliance/[documentation] for
-further information regarding using SAP HANA.
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index f21ffae1c5..3f9ff2e84f 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -9,7 +9,7 @@ To build Gerrit from source, you need:
* A JDK for Java 8|9|10|11|...
* Python 2 or 3
* Node.js
-* link:https://bazel.build/[Bazel] -launched with
+* link:https://docs.bazel.build/versions/master/install.html[Bazel] -launched with
link:https://github.com/bazelbuild/bazelisk[Bazelisk]
* Maven
* zip, unzip
@@ -32,7 +32,7 @@ seamlessly uses Bazelisk, which then runs the proper `bazel` binary version.
[[java-10]]
==== Java 10 support
-Java 10 is supported through vanilla java toolchain
+Java 10 (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
provide the path to JDK home:
@@ -111,7 +111,7 @@ in `$gerrit_site/etc/gerrit.config` to run Gerrit with Java 9:
=== Gerrit Development WAR File
-To build the Gerrit web application that includes the PolyGerrit UI:
+To build the Gerrit web application:
----
bazel build gerrit
@@ -130,8 +130,8 @@ The output executable WAR will be placed in:
[[release]]
=== Gerrit Release WAR File
-To build the Gerrit web application that includes the GWT UI, the
-PolyGerrit UI, core plugins and documentation:
+To build the Gerrit web application that includes the PolyGerrit UI,
+core plugins and documentation:
----
bazel build release
@@ -145,7 +145,7 @@ The output executable WAR will be placed in:
=== Headless Mode
-To build Gerrit in headless mode, i.e. without the PolyGerrit and GWT
+To build Gerrit in headless mode, i.e. without the PolyGerrit UI:
Web UI:
----
@@ -160,7 +160,7 @@ The output executable WAR will be placed in:
=== Extension and Plugin API JAR Files
-To build the extension, plugin and GWT API JAR files:
+To build the extension, plugin and acceptance-framework JAR files:
----
bazel build api
@@ -173,7 +173,8 @@ Java docs will be placed in:
bazel-bin/api.zip
----
-Install {extension,plugin,gwt}-api to the local maven repository:
+Install {extension,plugin,acceptance-framework}-api to the local
+maven repository:
----
tools/maven/api.sh install
@@ -230,7 +231,7 @@ To build with all Error Prone warnings activated, run:
=== IntelliJ
-The Gerrit build works with Bazel's link:https://ij.bazel.io[IntelliJ plugin].
+The Gerrit build works with Bazel's link:https://ij.bazel.build[IntelliJ plugin].
Please follow the instructions on <<dev-intellij#,IntelliJ Setup>>.
=== Eclipse
@@ -250,7 +251,7 @@ and then follow the link:dev-eclipse.html#setup[setup instructions].
If an updated classpath is needed, the Eclipse project can be
refreshed and missing dependency JARs can be downloaded by running
`project.py` again. For IntelliJ, you need to click the `Sync Project
-with BUILD Files` button of link:https://ij.bazel.io[IntelliJ plugin].
+with BUILD Files` button of link:https://ij.bazel.build[Bazel plugin].
[[documentation]]
=== Documentation
@@ -304,31 +305,6 @@ To run a specific test group, e.g. the rest-account test group:
bazel test //javatests/com/google/gerrit/acceptance/rest/account:rest_account
----
-To run the tests against NoteDb backend with write
-to NoteDb, but not read from it:
-
-----
- bazel test --test_env=GERRIT_NOTEDB=WRITE //...
-----
-
-Write and read from NoteDb:
-
-----
- bazel test --test_env=GERRIT_NOTEDB=READ_WRITE //...
-----
-
-Primary storage NoteDb:
-
-----
- bazel test --test_env=GERRIT_NOTEDB=PRIMARY //...
-----
-
-Primary storage NoteDb and ReviewDb disabled:
-
-----
- bazel test --test_env=GERRIT_NOTEDB=ON //...
-----
-
To run only tests that do not use SSH:
----
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index df8aa74d7f..0a7c1003eb 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -171,7 +171,7 @@ 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.0.0). Unused dependencies are found and removed using the
+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`.
@@ -245,7 +245,6 @@ back and consult this section when creating them.
Here are some design level objectives that you should keep in mind
when coding:
- * ORM entity objects should match exactly one row in the database.
* 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
@@ -264,11 +263,11 @@ when coding:
* 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 the DB. 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.
- * GWT EventBus is the new way forward.
+ 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).
@@ -317,31 +316,44 @@ especially if changing one without the other will break something!
[[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
-patchsets to these branches, even if no new release is planned.
-
-Fixes that are known to be needed for a particular release should be pushed
-for review on that release's stable branch. It will then be included in
-the master branch when the stable branch is merged back.
-
-=== Updating to new version of GWT
+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].
-When updating to a new version of GWT, there are several things that also need
-to be updated or at least checked.
-
-* Update common and plugin dependencies in `tools/gwt-constants.defs`.
-* Update to the same GWT version in the cookbook plugin and optionally in other
-plugins that have a dependency on GWT.
-* Update the GWT version in the archetype metadata in the
-`gerrit-plugin-gwt-archetype`.
-* Update the version of `gwt-maven-plugin` in the example pom.xml file in
-link:dev-plugins.html[dev-plugins].
-* Update to the same GWT version in the `gwtjsonrpc` project, and release a
-new version.
+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
@@ -374,20 +386,21 @@ is that we have a structured process for deprecation that users, administrators
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.
+
+ * 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
diff --git a/Documentation/dev-design.txt b/Documentation/dev-design.txt
index bdd2a68319..69af18d3a6 100644
--- a/Documentation/dev-design.txt
+++ b/Documentation/dev-design.txt
@@ -65,6 +65,9 @@ Gerrit 2.x is a complete rewrite of the Gerrit fork, completely
changing the implementation from Python on Google App Engine, to Java
on a J2EE servlet container and an SQL database.
+Since Gerrit 3.x link:note-db.html[NoteDb] replaced the SQL database
+and all metadata is now stored in Git.
+
* link:http://video.google.com/videoplay?docid=-8502904076440714866[Mondrian Code Review On The Web]
* link:https://github.com/rietveld-codereview/rietveld[Rietveld - Code Review for Subversion]
* link:http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst;hb=HEAD[Gitosis README]
@@ -83,9 +86,7 @@ and Git's own data integrity checks.
Each Git commit created on the client desktop system is converted
into a unique change record which can be reviewed independently.
-Change records are stored in a database: PostgreSQL, MySQL, or the
-built-in H2, where they can be queried to present customized user
-dashboards, enumerating any pending changes.
+Change records are stored in NoteDb.
A summary of each newly uploaded change is automatically emailed
to reviewers, so they receive a direct hyperlink to review the
@@ -121,9 +122,9 @@ to be the author of the change.
End-user web browsers make HTTP requests directly to Gerrit's
HTTP server. As nearly all of the user interface is implemented
-through Google Web Toolkit (GWT), the majority of these requests
-are transmitting compressed JSON payloads, with all HTML being
-generated within the browser. Most responses are under 1 KB.
+through PolyGerrit, the majority of these requests are transmitting
+compressed JSON payloads, with all HTML being generated within the
+browser. Most responses are under 1 KB.
Gerrit's HTTP server side component is implemented as a standard
Java servlet, and thus runs within any J2EE servlet container.
@@ -166,9 +167,7 @@ User authentication is handled by OpenID, and therefore Gerrit
requires that the OpenID provider selected by a user must be
online and operating in order to authenticate that user.
-* link:http://www.gwtproject.org/[Google Web Toolkit (GWT)]
* link:http://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html[Git Repository Format]
-* link:http://www.postgresql.org/about/[About PostgreSQL]
* link:http://openid.net/developers/specs/[OpenID Specifications]
*1 Although an effort is underway to eliminate the use of the
@@ -200,11 +199,6 @@ The majority of Gerrit's users will be writing change descriptions
and comments in English, and therefore an English user interface
is usable by the target user base.
-Gerrit uses GWT's i18n support to externalize all constant strings
-and messages shown to the user, so that in the future someone who
-really needed a translated version of the UI could contribute new
-string files for their locale(s).
-
Right-to-left (RTL) support is only barely considered within the
Gerrit code base. Some portions of the code have tried to take
RTL into consideration, while others probably need to be modified
@@ -235,20 +229,11 @@ provide hints to screen readers.
Supporting non-JavaScript enabled browsers is a non-goal for Gerrit.
-As Gerrit is a pure-GWT application with no server side rendering
-fallbacks, the browser must support modern JavaScript semantics in
-order to access the Gerrit web application. Dumb clients such as
-`lynx`, `wget`, `curl`, or even many search engine spiders are not
-able to access Gerrit content.
-
-As Google Web Toolkit (GWT) is used to generate the browser
-specific versions of the client-side JavaScript code, Gerrit works
-on any JavaScript enabled browser which GWT can produce code for.
-This covers the majority of the popular browsers.
-
-The Gerrit project does not have the development resources necessary
-to support two parallel UI implementations (GWT based JavaScript
-and server-side rendering). Consequently only one is implemented.
+As Gerrit is a pure JavaScript application on the client side, with
+no server side rendering fallbacks, the browser must support modern
+JavaScript semantics in order to access the Gerrit web application.
+Dumb clients such as `lynx`, `wget`, `curl`, or even many search engine
+spiders are not able to access Gerrit content.
There are number of web browsers available with full JavaScript
support, and nearly every operating system (including any PDA-like
@@ -317,34 +302,6 @@ they choose.
Gerrit does not integrate with any Google service, or any other
services other than those listed above.
-
-== Standards / Developer APIs
-
-Gerrit uses an XSRF protected variant of JSON-RPC 1.1 to communicate
-between the browser client and the server.
-
-As the protocol is not the GWT-RPC protocol, but is instead a
-self-describing standard JSON format it is easily implemented by
-any 3rd party client application, provided the client has a JSON
-parser and HTTP client library available.
-
-As the entire command set necessary for the standard web browser
-based UI is exposed through JSON-RPC over HTTP, there are no other
-data feeds or command interfaces to the server.
-
-Commands requiring user authentication may require the user agent to
-complete a sign-in cycle through the user's OpenID provider in order
-to establish the HTTP cookie Gerrit uses to track user identity.
-Automating this sign-in process for non-web browser agents is
-outside of the scope of Gerrit, as each OpenID provider uses its own
-sign-in sequence. Use of OpenID providers which have difficult to
-automate interfaces may make it impossible for non-browser agents
-to be used with the JSON-RPC interface.
-
-* link:http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html[JSON-RPC 1.1]
-* link:https://gerrit.googlesource.com/gwtjsonrpc/+/master/README[XSRF JSON-RPC]
-
-
== Privacy Considerations
Gerrit stores the following information per user account:
@@ -378,7 +335,7 @@ cases these same addresses would be more easily obtained from the
project's mailing list archives.
The user's name and email address is stored unencrypted in the
-Gerrit metadata store, typically a PostgreSQL database.
+link:config-accounts.html#all-users[All-Users] repository.
The snail-mail mailing address, country, and phone and fax numbers
are gathered to help project leads contact the user should there
@@ -648,12 +605,6 @@ lag largely allows for some downtime in a disaster scenario.
=== Backups
-PostgreSQL and MySQL can be configured to replicate their data to
-other systems, where they are applied to a warm-standby backup in
-real time. Gerrit instances which care about redundancy will setup
-this feature of PostgreSQL or MySQL to ensure the warm-standby is
-reasonably current should the master go offline.
-
Using the standard replication plugin, Gerrit can be configured
to replicate changes made to the local Git repositories over any
standard Git transports. After the plugin is installed, remote
diff --git a/Documentation/dev-e2e-tests.txt b/Documentation/dev-e2e-tests.txt
index bac516933a..20484e68a7 100644
--- a/Documentation/dev-e2e-tests.txt
+++ b/Documentation/dev-e2e-tests.txt
@@ -25,14 +25,33 @@ initially proposed, the link:https://github.com/GerritForge/gatling-git[Gatling
leveraged to run tests at the Git protocol level.
Gatling is written in Scala, but the abstraction provided by the Gatling DSL makes the scenarios
-implementation easy even without any Scala knowledge. The
-link:https://gitenterprise.me/2019/12/20/stress-your-gerrit-with-gatling/[Stress your Gerrit with Gatling]
-blog post has more introductory information.
+implementation easy even without any Scala knowledge. The online `End-to-end tests`
+link:https://www.gerritcodereview.com/presentations.html#list-of-presentations[presentation] links
+posted on the homepage have more introductory information.
+
+== IDE: IntelliJ
Examples of scenarios can be found in the `e2e-tests` directory. The files in that directory should
be formatted using the mainstream
link:https://plugins.jetbrains.com/plugin/1347-scala[Scala plugin for IntelliJ]. The latter is not
-mandatory but preferred for `sbt` and Scala IDE purposes in this project.
+mandatory but preferred for `sbt` and Scala IDE purposes in this project. So, Eclipse can also be
+used alongside as a development IDE; this is described below.
+
+=== Eclipse
+
+1. Install the link:http://scala-ide.org/docs/user/gettingstarted.html[Scala plugin for Eclipse].
+2. Run `sbt eclipse` from the `e2e-tests` root directory.
+3. Import the resulting `e2e-tests` eclipse file inside the Gerrit project, in Eclipse.
+4. You should see errors in Eclipse telling you there are missing packages.
+5. This is due to the sbt-eclipse plugin not properly linking the Gerrit Gatling e2e tests with
+ Gatling Git plugin.
+6. You then have to right-click on the root directory and choose the build path->link source option.
+7. Then you have to browse to `.sbt/1.0/staging`, find the folder where gatling-git is contained,
+ and choose that.
+8. That last step should link the gatling-git plugin to the project; e2e tests should not show
+ errors anymore.
+9. You may get errors in the gatling-git directory; these should not affect Gerrit Gatling
+ development and can be ignored.
== How to build the tests
@@ -105,7 +124,8 @@ The `CloneUsingBothProtocols` scenario is fed with the data coming from the
file contains the commands and repository used during the e2e test. That file currently looks like
below. This scenario serves as a simple example with no actual load in it. It can be used to test
or validate the local setup. More complex scenarios can be further developed, under the
-`com.google.gerrit.scenarios` package. The uppercase keywords are discussed further below.
+`com.google.gerrit.scenarios` package. The uppercase keywords are set through
+link:#_environment_properties[environment properties].
----
[
@@ -114,7 +134,7 @@ or validate the local setup. More complex scenarios can be further developed, un
"cmd": "clone"
},
{
- "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
"cmd": "clone"
}
]
@@ -151,21 +171,51 @@ with either one or many of these supported properties, from the core framework:
* `-Dcom.google.gerrit.scenarios.hostname=localhost`
* `-Dcom.google.gerrit.scenarios.ssh_port=29418`
* `-Dcom.google.gerrit.scenarios.http_port=8080`
+* `-Dcom.google.gerrit.scenarios.http_scheme=http`
Above, the properties can be set with values matching specific deployment topologies under test.
-The example values shown above are the currently coded default ones. The framework could support
-differing or more properties over time.
+The name of the property corresponds to the uppercase keyword found in the json file. For example,
+`hostname` above will set the value of `HOSTNAME` in the link:#_input_file[aforementioned example].
+
+The example values shown above are the currently coded default ones. For example, the `http` scheme
+above could be replaced with `https`. The framework may support differing or more properties over time.
+
+==== Replication delay
+
+The `replication_delay` property allows test scenario steps to wait for that many seconds, prior to
+expecting a done replication. Its default is `15` seconds and can be set using another value:
+
+* `-Dcom.google.gerrit.scenarios.replication_delay=15`
+
+There is a short time buffer added to this property. Now, the replication starts after replication
+plugin's own `replicationDelay`, in seconds, and typically takes some more seconds to complete.
+That whole replication time depends on the system under test. Therefore, this property here should
+be set to a value high enough, so that the test checks for a done replication at the right time.
-Plugin or otherwise non-core scenarios may do so just as well. The core java package
+==== Automatic properties
+
+The link:#_input_file[example keywords] also include `_PROJECT`, prefixed with an underscore, which
+means that its value gets automatically generated by the scenario. Any property setting for it is
+therefore not applicable. Its usage differs from the non-prefixed `PROJECT` keyword, in that sense.
+Using the latter instead in json files requires setting this `JAVA_OPTS` property:
+
+* `-Dcom.google.gerrit.scenarios.project=myOwnTestRepoProjectName`
+
+Other automatic keys may be used and implemented, always prefixed with an underscore that tells so.
+
+==== Plugin scenarios
+
+Plugin or otherwise non-core scenarios can also use such properties. The core java package
`com.google.gerrit.scenarios` from the example above has to be replaced with the one under which
those scenario classes are. Such extending scenarios can also add extension-specific properties.
-Early examples of this can be found in the Gerrit
-`link:https://gerrit.googlesource.com/plugins/high-availability[high-availability]` and
-`link:https://gerrit.googlesource.com/plugins/multi-site[multi-site]` plugins test code.
+Examples of this can be found in these Gerrit plugins test code:
+
+* `link:https://gerrit.googlesource.com/plugins/gc-conductor[gc-conductor]`
+* `link:https://gerrit.googlesource.com/plugins/high-availability[high-availability]`
+* `link:https://gerrit.googlesource.com/plugins/multi-site[multi-site]`
+* `link:https://gerrit.googlesource.com/plugins/rename-project[rename-project]`
-Further above, the `_PROJECT` keyword is prefixed with an underscore, which means that its value
-gets automatically generated by the scenario. Any property setting for it is therefore not
-applicable. Its usage differs from the non-prefixed `PROJECT` keyword, in that sense.
+==== Power factor
The following core property can be optionally set depending on the runtime environment. The test
environments used as reference for scenarios development assume its default value, `1.0`. For
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index e1b3e9e094..420151b140 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -1,11 +1,9 @@
= Gerrit Code Review - Eclipse Setup
This document is about configuring Gerrit Code Review into an
-Eclipse workspace for development and debugging with GWT.
-
-Java 8 or later SDK is also required to run GWT's compiler and
-runtime debugging environment.
+Eclipse workspace for development.
+Java 8 or later SDK is required.
[[setup]]
== Project Setup
@@ -135,61 +133,6 @@ Duplicate the existing launch configuration:
* Change Save as to be Local file.
* Close the Debug Configurations dialog and save the changes when prompted.
-
-=== Running GWT Debug Mode
-
-The `gerrit_gwt_debug` launch configuration uses GWT's
-link:http://www.gwtproject.org/articles/superdevmode.html[Super Dev Mode].
-
-* Make a local copy of the `gerrit_gwt_debug` configuration, using the
-process described for `gerrit_daemon` above.
-* Launch the local copy of `gerrit_gwt_debug` from the Eclipse debug menu.
-* If debugging GWT for the first time:
-
-** Open the link:http://localhost:9876/[codeserver URL] and add the `Dev Mode On`
-and `Dev Mode Off` bookmarklet to your bookmark bar.
-
-** Activate the source maps feature in your browser. Refer to the
-link:https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps[
-Chrome] and
-link:https://developer.mozilla.org/en-US/docs/Tools/Debugger#Use_a_source_map[
-Firefox] developer documentation.
-
-* Load the link:http://localhost:8080[Gerrit page].
-* Open the source tab in developer tools.
-* Click the `Dev Mode On` bookmark to incrementally recompile changed files.
-* Select the `gerrit_ui` module to compile (the `Compile` button can also be used
-as a bookmarklet).
-* In the developer tools source tab, open a file and set a breakpoint.
-* Navigate to the UI and confirm that the breakpoint is hit.
-* To end the debugging session, click the `Dev Mode Off` bookmark.
-
-.After changing the client side code:
-
-* Hitting `F5` in the browser only reloads the last compile output, without
-recompiling.
-* To reflect your changes in the debug session, click `Dev Mode On` then `Compile`.
-
-
-=== Running GWT Debug Mode for Gerrit plugins
-
-A Gerrit plugin can expose GWT module and its implementation can be inspected
-in the SDM debug session.
-
-`codeserver` needs two additional inputs to expose the plugin module in the SDM
-debug session: the module name and the source folder location. For example the
-module name and source folder of any GWT plugin should be added in the local
-copy of the `gerrit_gwt_debug` configuration:
-
-----
- com.googlesource.gerrit.plugins.myplugin.HelloForm \
- -src ${resource_loc:/gerrit}/plugins/myplugin/src/main/java \
- -- --console-log [...]
-----
-
-After doing that, both the Gerrit core and plugin GWT modules can be activated
-during SDM (debug session).
-
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-inspector.txt b/Documentation/dev-inspector.txt
index 2134f2f77a..b1559caf7d 100644
--- a/Documentation/dev-inspector.txt
+++ b/Documentation/dev-inspector.txt
@@ -54,8 +54,6 @@ the following on the console:
----
"Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
"m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
-"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
-"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
Welcome to the Gerrit Inspector
Enter help() to see the above again, EOF to quit and stop Gerrit
@@ -109,71 +107,11 @@ Java packages, classes and live instances.
'registerNatives', 'toString', 'wait']
----
-Startup script provides some convenient variables to access some global Gerrit components,
-for example a connection to the review database is kept open:
-
-----
->>> ds
-org.apache.commons.dbcp.BasicDataSource@61db2215
->>> ds.driverClassName
-u'org.postgresql.Driver'
->>> ds.dataSource
-org.apache.commons.dbcp.PoolingDataSource@23226fe1
->>> ds.dataSource.connection
-jdbc:postgresql://localhost/reviewdb, UserName=rv, PostgreSQL Native Driver
-----
-
-It is also possible to interact with the ORM layer:
-
-----
->>> db = schk.schema.open()
->>> db
-com.google.gerrit.reviewdb.server.ReviewDb_Schema_GwtOrm$$28@24cbbdf3
->>> db.getDialect()
-com.google.gwtorm.schema.sql.DialectPostgreSQL@4de07d3e
->>> for x in db.patchSets().iterateAllEntities():
-... print x
-...
-[PatchSet 1,1]
-[PatchSet 2,1]
-[PatchSet 3,1]
-[PatchSet 4,1]
-[PatchSet 5,1]
-[PatchSet 6,1]
-[PatchSet 7,1]
-[PatchSet 8,1]
-[PatchSet 6,2]
->>> for x in db.patchComments().iterateAllEntities():
-... print x
-com.google.gerrit.reviewdb.client.PatchLineComment@5381298a
-com.google.gerrit.reviewdb.client.PatchLineComment@44ce4dda
-com.google.gerrit.reviewdb.client.PatchLineComment@44594680
->>> dir(com.google.gerrit.reviewdb.client.PatchLineComment)
-['Key', 'STATUS_DRAFT', 'STATUS_PUBLISHED', 'Status', '__class__',
-'__copy__', '__deepcopy__', '__delattr__', '__doc__', '__eq__',
-'__getattribute__', '__hash__', '__init__', '__ne__', '__new__',
-'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
-'__unicode__', 'author', 'class', 'clone', 'equals', 'finalize',
-'getAuthor', 'getClass', 'getKey', 'getLine', 'getMessage',
-'getParentUuid', 'getSide', 'getStatus', 'getWrittenOn', 'hashCode',
-'key', 'line', 'lineNbr', 'message', 'notify', 'notifyAll',
-'parentUuid', 'registerNatives', 'setMessage', 'setSide', 'setStatus',
-'side', 'status', 'toString', 'updated', 'wait', 'writtenOn']
->>> for x in db.patchComments().iterateAllEntities():
-... print x.status, x.line, x.message
-...
-P 2 I like it!
-P 2 more
-P 1 better
-----
-
A built-in *help()* function provides values of global variables
defined in the interpreter:
----
>>> help()
-"schk" is "com.google.gerrit.server.schema.SchemaVersionCheck@5e8cb9bd"
-"ds" is "com.google.gerrit.server.schema.DataSourceProvider@6b3592c"
"m" is "com.google.gerrit.lifecycle.LifecycleManager@6f03b248"
"Shell" is "com.google.gerrit.pgm.shell.JythonShell@61644f2d"
"d" is "com.google.gerrit.pgm.Daemon@28a3f689"
diff --git a/Documentation/dev-intellij.txt b/Documentation/dev-intellij.txt
index 8bedd08ec7..50770798cc 100644
--- a/Documentation/dev-intellij.txt
+++ b/Documentation/dev-intellij.txt
@@ -1,10 +1,24 @@
-= Gerrit Code Review - IntelliJ Setup
+= Gerrit Code Review - IntelliJ IDEA Setup
== Prerequisites
-You need an installation of IntelliJ version 2016.2 or later. The latest version
-might not yet be in-sync with the Bazel plugin for IntelliJ. It usually becomes
-so quite quickly after new IDEA versions get released, though. It should then be
-possible to use the fairly latest IntelliJ release with an updated Bazel plugin.
+
+=== Bazel
+
+Bazel must be installed as described by
+<<dev-bazel#installation,Building with Bazel - Installation>>.
+
+It's strongly recommended to verify you can build your Gerrit tree with Bazel
+for Java 8 from the command line first. Ensure that at least
+`bazel build gerrit` runs successfully before you proceed.
+
+=== IntelliJ version and Bazel plugin
+
+Before downloading IntelliJ, look at the
+link:https://plugins.jetbrains.com/plugin/8609-bazel/versions[JetBrains plugin repository page of the Bazel plugin]
+to see what version of the IntelliJ IDEA it is actually compatible with.
+
+Also note that the version of the Bazel plugin used in turn may or may not be
+compatible with the Bazel version used.
In addition, Java 8 must be specified on your path or via `JAVA_HOME` so that
building with Bazel via the Bazel plugin is possible.
@@ -13,39 +27,64 @@ TIP: If the synchronization of the project with the BUILD files using the Bazel
plugin fails and IntelliJ reports the error **Could not get Bazel roots**, this
indicates that the Bazel plugin couldn't find Java 8.
-Bazel must be installed as described by
-<<dev-bazel#installation,Building with Bazel - Installation>>.
+=== Installation of IntelliJ IDEA
+
+Please refer to the
+link:https://www.jetbrains.com/help/idea/installation-guide.html[installation guide provided by Jetbrains]
+to install it on your platform. Make sure to install a version compatible with
+the Bazel plugin as mentioned above.
== Installation of the Bazel plugin
+The plugin is usually installed using the Jetbrains plugin repository as shown
+in the steps below, but it's also possible to
+link:https://github.com/bazelbuild/intellij[build it from source].
+
. Go to *File -> Settings -> Plugins*.
-. Click on *Browse Repositories*.
-. Search for the plugin `IntelliJ with Bazel`.
++
+(Or, from the welcome screen, *Configure -> Plugins*)
+. Activate the *Marketplace* tab.
+. Search for the plugin `Bazel` (by Google).
++
+TIP: In case the Bazel plugin is not listed, or if it shows an outdated version,
+verify the compatibility between the Bazel plugin and IntelliJ IDEA on link:https://plugins.jetbrains.com/plugin/8609-bazel/versions[the JetBrains plugin page].
. Install it.
-. Restart IntelliJ.
+. Restart IntelliJ IDEA.
+
+[TIP]
+====
+If your project's Bazel build fails with **Cannot run program "bazel": No such
+file or directory**, then you may have to set the binary location in the Bazel
+plugin settings:
+
+. Go to *Preferences -> Other Settings -> Bazel Settings*.
+. Set the *Bazel binary location*.
+====
-== Creation of IntelliJ project
+== Creation of the project
. Go to *File -> Import Bazel Project*.
++
+(Or, from the welcome screen, *Import Bazel Project* should already be shown in
+there.)
. For *Use existing bazel workspace -> Workspace*, select the directory
containing the Gerrit source code.
. Choose *Import from workspace* and select the `.bazelproject` file which is
located in the top directory of the Gerrit source code.
. Adjust the path of the project data directory and the name of the project if
desired.
+. Finish the creation of the project.
+. Verify that you can now build the project. Hit the button with the Bazel icon
+(located on the top-right by default) to synchronize the project. Note that
+warnings may be present in the build.
+
+At this point all the basic functionality should be working such as Java class
+inspection and running <<unit-tests,unit tests>>.
TIP: The project data directory can be separate from the source code. One
advantage of this is that project files don't need to be excluded from version
control.
-Unfortunately, the created project seems to have a broken output path. To fix
-it, please complete the following steps:
-
-. Go to *File -> Project Structure -> Project Settings -> Modules*.
-. Switch to the tab *Paths*.
-. Click on *Inherit project compile output path*.
-. Click on *Use module compile output path*.
-
== Recommended settings
=== Code style
@@ -54,17 +93,20 @@ it, please complete the following steps:
Install the `google-java-format` plugin by following these steps:
. Go to *File -> Settings -> Plugins*.
-. Click on *Browse Repositories*.
-. Search for the plugin `google-java-format`.
+. Activate the *Marketplace* tab.
+. Search for the plugin `google-java-format` by Google.
. Install it.
-. Restart IntelliJ.
+. Restart IntelliJ IDEA.
-Every time you start IntelliJ, make sure to use *Code -> Reformat with
+Every time you start IntelliJ IDEA, make sure to use *Code -> Reformat with
google-java-format* on an arbitrary line of code. This replaces the default
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>>
+for which version of `google-java-format` is used with Gerrit.
+
==== Code style settings
The `google-java-format` plugin is the preferred way to format the code. As it
only kicks in on demand, it's also recommended to have code style settings
@@ -77,11 +119,10 @@ to be as close as possible. So before submitting code, please make sure to run
https://raw.githubusercontent.com/google/styleguide/gh-pages/intellij-java-google-style.xml[
intellij-java-google-style.xml].
. Go to *File -> Settings -> Editor -> Code Style*.
-. Click on *Manage*.
-. Click on *Import*.
-. Choose `IntelliJ IDEA Code Style XML`.
+. Click on the wrench icon with the tooltip _Show Scheme Actions_.
+. Click on *Import Scheme*.
. Select the previously downloaded file `intellij-java-google-style.xml`.
-. Make sure that `Google Style` is chosen as *Scheme*.
+. Make sure that `GoogleStyle` is chosen as the current *Scheme*.
In addition, the EditorConfig settings (which ensure a consistent style between
Eclipse, IntelliJ, and other editors) should be applied on top of that. Those
@@ -90,7 +131,7 @@ will automatically pick up those settings if the EditorConfig plugin is enabled
and configured correctly as can be verified by:
. Go to *File -> Settings -> Plugins*.
-. Ensure that the EditorConfig plugin is enabled.
+. Ensure that the *EditorConfig* plugin (by JetBrains) is enabled.
. Go to *File -> Settings -> Editor -> Code Style*.
. Ensure that *Enable EditorConfig support* is checked.
@@ -98,33 +139,42 @@ NOTE: If IntelliJ notifies you later on that the EditorConfig settings override
the code style settings, simply confirm that.
=== Copyright
-Copy the folder `$(gerrit_source_code)/tools/intellij/copyright` (not just the
-contents) to `$(project_data_directory)/.idea`. If it already exists, replace
-it.
-
-=== File header
-By default, IntelliJ adds a file header containing the name of the author and
-the current date to new files. To disable that, follow these steps:
-. Go to *File -> Settings -> Editor -> File and Code Templates*.
-. Select the tab *Includes*.
-. Select *File Header*.
-. Remove the template code in the right editor.
+. Copy the folder `$(gerrit_source_code)/tools/intellij/copyright` (not just the
+contents) to `$(project_data_directory)/.idea`. If it already exists, replace
+it. If you didn't select a custom data directory the command could look like
+this, as run from the Gerrit source tree checkout as working directory:
++
+----
+cp -r tools/intellij/copyright .ijwb/.idea/
+----
+. Go to *File -> Settings -> Editor -> Copyright -> Copyright Profiles*.
+. Verify that the *Gerrit Copyright* is now present there.
++
+Only in case it hasn't picked up the copyright profile automatically, import
+the `Gerrit_Copyright.xml` from that folder manually.
+
+=== Git integration
+This section is only relevant in case you want to use the Git integration
+plugin in IntelliJ IDEA.
-=== Commit message
To simplify the creation of commit messages which are compliant with the
<<dev-contributing#commit-message,Commit Message>> format, do the following:
-. Go to *File -> Settings -> Version Control*.
-. Check *Commit message right margin (columns)*.
-. Make sure that 72 is specified as value.
-. Check *Wrap when typing reaches right margin*.
+. Go to *File -> Settings -> Version Control -> Commit Dialog*.
+. In the *Commit message inspections*, activate the three inspections:
+* *Blank line between subject and body*,
+* *Limit body line* and
+* *Limit subject line*.
+. For the limit line inspections, make sure that 72 is specified as value.
+. For *Limit body line*, tick *Show right margin* and *Wrap when typing reaches
+right margin*.
In addition, you should follow the instructions of
<<dev-contributing#git_commit_settings,this section>> (if you haven't
done so already):
-* Install the Git hook for the `Change-Id` line.
+* Install the Git commit message hook for the `Change-Id` line.
* Set up the HTTP access.
Setting up the HTTP access will allow you to commit changes via IntelliJ without
@@ -136,29 +186,19 @@ Run configurations can be accessed on the toolbar. To edit them or add new ones,
choose *Edit Configurations* on the drop-down list of the run configurations
or go to *Run -> Edit Configurations*.
-=== Pre-configured run configurations
+[[runconfigurations-daemon]]
+=== Gerrit Daemon
-In order to be able to use the pre-configured run configurations, the following
-steps are necessary:
-
-. Make sure that the folder `runConfigurations` exists within
-`$(project_data_directory)/.idea`. If it doesn't exist, create it.
-. Specify the IntelliJ path variable `GERRIT_TESTSITE`. (This configuration is
-shared among all IntelliJ projects.)
-.. Go to *Settings -> Appearance & Behavior -> Path Variables*.
-.. Click on the *+* to add a new path variable.
-.. Specify `GERRIT_TESTSITE` as name and the path to your local test site as
-value.
-
-The copied run configurations will be added automatically to the available run
-configurations of the IntelliJ project.
-
-==== Gerrit Daemon
-WARNING: At the moment running this configuration results in a
+[WARNING]
+====
+At the moment running this (local) configuration results in a
`java.io.FileNotFoundException`. To debug a local Gerrit server with IntelliJ,
use the instructions of <<dev-readme#run_daemon,Running the Daemon>> in
combination with <<remote-debug,Debugging a remote Gerrit server>>.
+(link:https://bugs.chromium.org/p/gerrit/issues/detail?id=11360[Issue 11360])
+====
+
Copy `$(gerrit_source_code)/tools/intellij/gerrit_daemon.xml` to
`$(project_data_directory)/.idea/runConfigurations/`.
@@ -168,19 +208,17 @@ This run configuration starts the Gerrit daemon similarly as
NOTE: The <<dev-readme#init,Site Initialization>> has to be completed
before this run configuration works properly.
+[[unit-tests]]
=== Unit tests
To create run configurations for unit tests, run or debug them via a right-click
on a method, class, file, or package. The created run configuration is a
-temporary one and can be saved to make it permanent.
+temporary one and can be saved to make it permanent by selecting *Create
+'Bazel test [...]'...* from the context menu.
Normally, this approach generates JUnit run configurations. When the Bazel
plugin manages a project, it intercepts the creation and creates a Bazel test
run configuration instead, which can be used just like the standard ones.
-TIP: If you would like to execute a test in NoteDb mode, add
-`--test_env=GERRIT_NOTEDB=READ_WRITE` to the *Bazel flags* of your run
-configuration.
-
[[remote-debug]]
=== Debugging a remote Gerrit server
If a remote Gerrit server is running and has opened a debug port, you can attach
@@ -188,10 +226,15 @@ IntelliJ via a `Remote debug configuration`.
. Go to *Run -> Edit Configurations*.
. Click on the *+* to add a new configuration.
-. Choose *Remote*.
+. Choose *Remote* from the *Templates*.
. Adjust *Configuration -> Settings -> Host* and *Port*.
. Start this configuration in `Debug` mode.
+TIP: This run configuration dialog also shows the line for the JVM as startup
+flag that you can copy to include in your
+`$(gerrit_test_site)/etc/gerrit.config` in the `[container]` section in order
+to work-around the <<runconfigurations-daemon,local run configuration issue>>.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index a80b4b55f3..8ad55358c7 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -322,10 +322,10 @@ from the Section.Factory rather than from an injected Config object.
Plugins' InitSteps are executed during the "Gerrit Plugin init" phase, after
the extraction of the plugins embedded in the distribution .war file into
-`$GERRIT_SITE/plugins` and before the DB Schema initialization or upgrade.
+`$GERRIT_SITE/plugins` and before the site initialization or upgrade.
-A plugin's InitStep cannot refer to Gerrit's DB Schema or any other Gerrit
-runtime objects injected at startup.
+A plugin's InitStep cannot refer to any Gerrit runtime objects injected at
+startup.
[source,java]
----
@@ -451,8 +451,8 @@ its own custom event class derived from
[source,java]
----
import com.google.gerrit.common.EventDispatcher;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
class MyPlugin {
@@ -466,7 +466,7 @@ class MyPlugin {
private void postEvent(MyPluginEvent event) {
try {
eventDispatcher.get().postEvent(event);
- } catch (OrmException e) {
+ } catch (StorageException e) {
// error handling
}
}
@@ -715,10 +715,9 @@ definition which creates a `MyPredicate`:
[source,java]
----
-@Singleton
public class SampleOperator
implements ChangeQueryBuilder.ChangeOperatorFactory {
- public static class MyPredicate extends OperatorChangePredicate<ChangeData> {
+ public static class MyPredicate extends PostFilterPredicate<ChangeData> {
...
}
@@ -748,7 +747,6 @@ A sample `ChangeHasOperandFactory` class implementing, and registering, a
new `has:sample_pluginName` operand is shown below:
====
- @Singleton
public class SampleHasOperand implements ChangeHasOperandFactory {
public static class Module extends AbstractModule {
@Override
@@ -798,56 +796,112 @@ public class SshModule extends AbstractModule {
}
----
+=== Calling Command Options ===
+
+Within an OptionHandler, during the processing of an option, plugins can
+provide and call extra parameters on the current command during parsing
+simulating as if they had been passed from the command line originally.
+
+To call additional parameters from within an option handler, instantiate
+the com.google.gerrit.util.cli.CmdLineParser.Parameters class with the
+existing parameters, and then call callParameters() with the additional
+parameters to be parsed. OptionHandlers may optionally pass this class to
+other methods which may then both parse/consume more parameters and call
+additional parameters.
+
+When calling command options not provided by your plugin, there is always
+a risk that the options may not exist, perhaps because the options being
+called are to be provided by another plugin, and said plugin is not
+currently installed. To protect againt this situation, it is possible to
+define an option as being dependent on other options using the
+@RequiresOptions() annotation. If the required options are not all not
+currently present, then the dependent option will not be available or
+visible in the help.
+
+The example below shows a plugin that adds a "--special" option (perhaps
+for use with the Query command) that calls (and requires) the
+"--format json" option.
+
+[source, java]
+----
+public class JsonOutputOptionHandler<T> extends OptionHandler<T> {
+ protected com.google.gerrit.util.cli.CmdLineParser.MyParser myParser;
+
+ public JsonOutputOptionHandler(CmdLineParser parser, OptionDef option, Setter<? super T> setter) {
+ super(parser, option, setter);
+ myParser = (com.google.gerrit.util.cli.CmdLineParser.MyParser) owner;
+ }
+
+ @Override
+ public int parseArguments(org.kohsuke.args4j.spi.Parameters params) throws CmdLineException {
+ new Parameters(params, myParser).callParameters("--format", "json");
+ setter.addValue(true);
+ return 0; // we didn't consume any additional args
+ }
+
+ @Override
+ public String getDefaultMetaVariable() {
+ ...
+ }
+}
+
+@RequiresOptions("--format")
+@Option(
+ name = "--special",
+ usage = "ouptut results using json",
+ handler = JsonOutputOptionHandler.class
+)
+boolean json;
+----
+
[[query_attributes]]
-=== Query Attributes ===
+=== Change Attributes ===
-Plugins can provide additional attributes to be returned in Gerrit queries by
-implementing the ChangeAttributeFactory interface and registering it to the
-ChangeQueryProcessor.ChangeAttributeFactory class in the plugin module's
-'configure()' method. The new attribute(s) will be output under a "plugin"
-attribute in the change query output. This can be further controlled with an
-option registered in the Http and Ssh modules' 'configure*()' methods.
+Plugins can provide additional attributes to be returned from the Get Change and
+Query Change APIs by implementing implementing the `ChangeAttributeFactory`
+interface and adding it to the `DynamicSet` in the plugin module's `configure()`
+method. The new attribute(s) will be output under a `plugin` attribute in the
+change output. This can be further controlled by registering a class containing
+@Option declarations as a `DynamicBean`, annotated with the with HTTP/SSH
+commands on which the options should be available.
-The example below shows a plugin that adds two attributes ('exampleName' and
-'changeValue'), to the change query output, when the query command is provided
-the --myplugin-name--all option.
+The example below shows a plugin that adds two attributes (`exampleName` and
+`changeValue`), to the change query output, when the query command is provided
+the `--myplugin-name--all` option.
[source, java]
----
public class Module extends AbstractModule {
@Override
protected void configure() {
- bind(ChangeAttributeFactory.class)
- .annotatedWith(Exports.named("example"))
+ // Register attribute factory.
+ DynamicSet.bind(binder(), ChangeAttributeFactory.class)
.to(AttributeFactory.class);
- }
-}
-public class MyQueryOptions implements DynamicBean {
- @Option(name = "--all", usage = "Include plugin output")
- public boolean all = false;
-}
+ // Register options for GET /changes/X/change and /changes/X/detail.
+ bind(DynamicBean.class)
+ .annotatedWith(Exports.named(GetChange.class))
+ .to(MyChangeOptions.class);
-public static class HttpModule extends HttpPluginModule {
- @Override
- protected void configureServlets() {
+ // Register options for GET /changes/?q=...
bind(DynamicBean.class)
.annotatedWith(Exports.named(QueryChanges.class))
- .to(MyQueryOptions.class);
- }
-}
+ .to(MyChangeOptions.class);
-public static class SshModule extends PluginCommandModule {
- @Override
- protected void configureCommands() {
+ // Register options for ssh gerrit query.
bind(DynamicBean.class)
.annotatedWith(Exports.named(Query.class))
- .to(MyQueryOptions.class);
+ .to(MyChangeOptions.class);
}
}
+public class MyChangeOptions implements DynamicBean {
+ @Option(name = "--all", usage = "Include plugin output")
+ public boolean all = false;
+}
+
public class AttributeFactory implements ChangeAttributeFactory {
- protected MyQueryOptions options;
+ protected MyChangeOptions options;
public class PluginAttribute extends PluginDefinedInfo {
public String exampleName;
@@ -860,9 +914,9 @@ public class AttributeFactory implements ChangeAttributeFactory {
}
@Override
- public PluginDefinedInfo create(ChangeData c, ChangeQueryProcessor qp, String plugin) {
+ public PluginDefinedInfo create(ChangeData c, BeanProvider bp, String plugin) {
if (options == null) {
- options = (MyQueryOptions) qp.getDynamicBean(plugin);
+ options = (MyChangeOptions) bp.getDynamicBean(plugin);
}
if (options.all) {
return new PluginAttribute(c);
@@ -890,6 +944,23 @@ Output:
],
...
}
+
+curl http://localhost:8080/changes/1?myplugin-name--all
+
+Output:
+
+{
+ "_number": 1,
+ ...
+ "plugins": [
+ {
+ "name": "myplugin-name",
+ "example_name": "Attribute Example",
+ "change_value": "1"
+ }
+ ],
+ ...
+}
----
[[simple-configuration]]
@@ -1299,7 +1370,7 @@ this can be specified by setting `scope = CapabilityScope.CORE`:
[[panels]]
=== Panels
-GWT plugins can contribute panels to Gerrit screens.
+UI plugins can contribute panels to Gerrit screens.
Gerrit screens define extension points where plugins can add GWT
panels with custom controls:
@@ -1512,9 +1583,8 @@ public Object apply(RevisionResource rcrs, Input in) {
// schedule a build
[...]
// update change
- ReviewDb db = dbProvider.get();
try (BatchUpdate bu = batchUpdateFactory.create(
- db, project.getNameKey(), user, TimeUtil.nowTs())) {
+ project.getNameKey(), user, TimeUtil.nowTs())) {
bu.addOp(change.getId(), new BatchUpdate.Op() {
@Override
public boolean updateChange(ChangeContext ctx) {
@@ -1875,317 +1945,6 @@ public class MyTopMenuExtension implements TopMenu {
----
-[[gwt_ui_extension]]
-== GWT UI Extension
-Plugins can extend the Gerrit UI with own GWT code.
-
-A GWT plugin must contain a GWT module file, e.g. `HelloPlugin.gwt.xml`,
-that bundles together all the configuration settings of the GWT plugin:
-
-[source,xml]
-----
-<?xml version="1.0" encoding="UTF-8"?>
-<module rename-to="hello_gwt_plugin">
- <!-- Inherit the core Web Toolkit stuff. -->
- <inherits name="com.google.gwt.user.User"/>
- <!-- Other module inherits -->
- <inherits name="com.google.gerrit.Plugin"/>
- <inherits name="com.google.gwt.http.HTTP"/>
- <!-- Using GWT built-in themes adds a number of static -->
- <!-- resources to the plugin. No theme inherits lines were -->
- <!-- added in order to make this plugin as simple as possible -->
- <!-- Specify the app entry point class. -->
- <entry-point class="${package}.client.HelloPlugin"/>
- <stylesheet src="hello.css"/>
-</module>
-----
-
-The GWT module must inherit `com.google.gerrit.Plugin` and
-`com.google.gwt.http.HTTP`.
-
-To register the GWT module a `GwtPlugin` needs to be bound.
-
-If no Guice modules are declared in the manifest, the GWT plugin may
-use auto-registration by using the `@Listen` annotation:
-
-[source,java]
-----
-@Listen
-public class MyExtension extends GwtPlugin {
- public MyExtension() {
- super("hello_gwt_plugin");
- }
-}
-----
-
-Otherwise the binding must be done in an `HttpModule`:
-
-[source,java]
-----
-public class HttpModule extends ServletModule {
-
- @Override
- protected void configureServlets() {
- DynamicSet.bind(binder(), WebUiPlugin.class)
- .toInstance(new GwtPlugin("hello_gwt_plugin"));
- }
-}
-----
-
-The HTTP module above must be declared in the `pom.xml` for Maven
-driven plugins:
-
-[source,xml]
-----
-<manifestEntries>
- <Gerrit-HttpModule>com.googlesource.gerrit.plugins.myplugin.HttpModule</Gerrit-HttpModule>
-</manifestEntries>
-----
-
-The name that is provided to the `GwtPlugin` must match the GWT
-module name compiled into the plugin. The name of the GWT module
-can be explicitly set in the GWT module XML file by specifying
-the `rename-to` attribute on the module. It is important that the
-module name be unique across all plugins installed on the server,
-as the module name determines the JavaScript namespace used by the
-compiled plugin code.
-
-[source,xml]
-----
-<module rename-to="hello_gwt_plugin">
-----
-
-The actual GWT code must be implemented in a class that extends
-`com.google.gerrit.plugin.client.PluginEntryPoint`:
-
-[source,java]
-----
-public class HelloPlugin extends PluginEntryPoint {
-
- @Override
- public void onPluginLoad() {
- // Create the dialog box
- final DialogBox dialogBox = new DialogBox();
-
- // The content of the dialog comes from a User specified Preference
- dialogBox.setText("Hello from GWT Gerrit UI plugin");
- dialogBox.setAnimationEnabled(true);
- Button closeButton = new Button("Close");
- VerticalPanel dialogVPanel = new VerticalPanel();
- dialogVPanel.setWidth("100%");
- dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
- dialogVPanel.add(closeButton);
-
- closeButton.addClickHandler(new ClickHandler() {
- public void onClick(ClickEvent event) {
- dialogBox.hide();
- }
- });
-
- // Set the contents of the Widget
- dialogBox.setWidget(dialogVPanel);
-
- RootPanel rootPanel = RootPanel.get(HelloMenu.MENU_ID);
- rootPanel.getElement().removeAttribute("href");
- rootPanel.addDomHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- dialogBox.center();
- dialogBox.show();
- }
- }, ClickEvent.getType());
- }
-}
-----
-
-This class must be set as entry point in the GWT module:
-
-[source,xml]
-----
-<entry-point class="${package}.client.HelloPlugin"/>
-----
-
-In addition this class must be defined as module in the `pom.xml` for the
-`gwt-maven-plugin` and the `webappDirectory` option of `gwt-maven-plugin`
-must be set to `${project.build.directory}/classes/static`:
-
-[source,xml]
-----
-<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>gwt-maven-plugin</artifactId>
- <version>2.7.0</version>
- <configuration>
- <module>com.googlesource.gerrit.plugins.myplugin.HelloPlugin</module>
- <disableClassMetadata>true</disableClassMetadata>
- <disableCastChecking>true</disableCastChecking>
- <webappDirectory>${project.build.directory}/classes/static</webappDirectory>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- </goals>
- </execution>
- </executions>
-</plugin>
-----
-
-To attach a GWT widget defined by the plugin to the Gerrit core UI
-`com.google.gwt.user.client.ui.RootPanel` can be used to manipulate the
-Gerrit core widgets:
-
-[source,java]
-----
-RootPanel rootPanel = RootPanel.get(HelloMenu.MENU_ID);
-rootPanel.getElement().removeAttribute("href");
-rootPanel.addDomHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- dialogBox.center();
- dialogBox.show();
- }
-}, ClickEvent.getType());
-----
-
-GWT plugins can come with their own css file. This css file must have a
-unique name and must be registered in the GWT module:
-
-[source,xml]
-----
-<stylesheet src="hello.css"/>
-----
-
-If a GWT plugin wants to invoke the Gerrit REST API it can use
-`com.google.gerrit.plugin.client.rpc.RestApi` to construct the URL
-path and to trigger the REST calls.
-
-Example for invoking a Gerrit core REST endpoint:
-
-[source,java]
-----
-new RestApi("projects").id(projectName).view("description")
- .put("new description", new AsyncCallback<JavaScriptObject>() {
-
- @Override
- public void onSuccess(JavaScriptObject result) {
- // TODO
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // never invoked
- }
-});
-----
-
-Example for invoking a REST endpoint defined by a plugin:
-
-[source,java]
-----
-new RestApi("projects").id(projectName).view("myplugin", "myview")
- .get(new AsyncCallback<JavaScriptObject>() {
-
- @Override
- public void onSuccess(JavaScriptObject result) {
- // TODO
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // never invoked
- }
-});
-----
-
-The `onFailure(Throwable)` of the provided callback is never invoked.
-If an error occurs, it is shown in an error dialog.
-
-In order to be able to do REST calls the GWT module must inherit
-`com.google.gwt.json.JSON`:
-
-[source,xml]
-----
-<inherits name="com.google.gwt.json.JSON"/>
-----
-
-[[screen]]
-== Add Screen
-A link:#gwt_ui_extension[GWT plugin] can link:#top-menu-extensions[add
-a menu item] that opens a screen that is implemented by the plugin.
-This way plugin screens can be fully integrated into the Gerrit UI.
-
-Example menu item:
-[source,java]
-----
-public class MyMenu implements TopMenu {
- private final List<MenuEntry> menuEntries;
-
- @Inject
- public MyMenu(@PluginName String name) {
- menuEntries = new ArrayList<>();
- menuEntries.add(new MenuEntry("My Menu", Collections.singletonList(
- new MenuItem("My Screen", "#/x/" + name + "/my-screen", ""))));
- }
-
- @Override
- public List<MenuEntry> getEntries() {
- return menuEntries;
- }
-}
-----
-
-Example screen:
-[source,java]
-----
-public class MyPlugin extends PluginEntryPoint {
- @Override
- public void onPluginLoad() {
- Plugin.get().screen("my-screen", new Screen.EntryPoint() {
- @Override
- public void onLoad(Screen screen) {
- screen.add(new InlineLabel("My Screen");
- screen.show();
- }
- });
- }
-}
-----
-
-[[user-settings-screen]]
-== Add User Settings Screen
-
-A link:#gwt_ui_extension[GWT plugin] can implement a user settings
-screen that is integrated into the Gerrit user settings menu.
-
-Example settings screen:
-[source,java]
-----
-public class MyPlugin extends PluginEntryPoint {
- @Override
- public void onPluginLoad() {
- Plugin.get().settingsScreen("my-preferences", "My Preferences",
- new Screen.EntryPoint() {
- @Override
- public void onLoad(Screen screen) {
- screen.setPageTitle("Settings");
- screen.add(new InlineLabel("My Preferences"));
- screen.show();
- }
- });
- }
-}
-----
-
-By defining an link:config-gerrit.html#urlAlias[urlAlias] Gerrit
-administrators can map plugin screens into the Gerrit URL namespace or
-even replace Gerrit screens by plugin screens.
-
-Plugins may also programatically add URL aliases in the preferences of
-of a user. This way certain screens can be replaced for certain users.
-E.g. the plugin may offer a user preferences setting for choosing a
-screen that then sets/unsets a URL alias for the user.
-
[[settings-screen]]
== Plugin Settings Screen
@@ -2686,40 +2445,6 @@ command can be used.
Disabled plugins can be re-enabled using the
link:cmd-plugin-enable.html[plugin enable] command.
-== Known issues and bugs
-
-=== Error handling in UI when using the REST API
-
-When a plugin invokes a REST endpoint in the UI, it provides an
-`AsyncCallback` to handle the result. At the moment the
-`onFailure(Throwable)` of the callback is never invoked, even if there
-is an error. Errors are always handled by the Gerrit core UI which
-shows the error dialog. This means currently plugins cannot do any
-error handling and e.g. ignore expected errors.
-
-In the following example the REST endpoint would return '404 Not
-Found' if the user has no username and the Gerrit core UI would
-display an error dialog for this. However having no username is
-not an error and the plugin may like to handle this case.
-
-[source,java]
-----
-new RestApi("accounts").id("self").view("username")
- .get(new AsyncCallback<NativeString>() {
-
- @Override
- public void onSuccess(NativeString username) {
- // TODO
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // never invoked
- }
-});
-----
-
-
[[reviewer-suggestion]]
== Reviewer Suggestion Plugins
@@ -2800,6 +2525,39 @@ class MyCommandInterceptor implements SshCreateCommandInterceptor {
return pluginName + " mycommand";
----
+[[ssh-command-execution-interception]]
+== SSH Command Execution Interception
+Gerrit provides an extension point that enables plugins to check and
+prevent an SSH command from being run.
+
+[source, java]
+----
+import com.google.gerrit.sshd.SshExecuteCommandInterceptor;
+
+@Singleton
+public class SshExecuteCommandInterceptorImpl implements SshExecuteCommandInterceptor {
+ private final Provider<SshSession> sessionProvider;
+
+ @Inject
+ SshExecuteCommandInterceptorImpl(Provider<SshSession> sessionProvider) {
+ this.sessionProvider = sessionProvider;
+ }
+
+ @Override
+ public boolean accept(String command, List<String> arguments) {
+ if (command.startsWith("gerrit") && !"10.1.2.3".equals(sessionProvider.get().getRemoteAddressAsString())) {
+ return false;
+ }
+ return true;
+ }
+}
+----
+
+And then declare it in your SSH module:
+[source, java]
+----
+ DynamicSet.bind(binder(), SshExecuteCommandInterceptor.class).to(SshExecuteCommandInterceptorImpl.class);
+----
[[pre-submit-evaluator]]
== Pre-submit Validation Plugins
@@ -2907,40 +2665,6 @@ public class MyPluginModule extends AbstractModule {
}
----
-[[ssh-command-execution-interception]]
-== SSH Command Execution Interception
-Gerrit provides an extension point that enables plugins to check and
-prevent an SSH command from being run.
-
-[source, java]
-----
-import com.google.gerrit.sshd.SshExecuteCommandInterceptor;
-
-@Singleton
-public class SshExecuteCommandInterceptorImpl implements SshExecuteCommandInterceptor {
- private final Provider<SshSession> sessionProvider;
-
- @Inject
- SshExecuteCommandInterceptorImpl(Provider<SshSession> sessionProvider) {
- this.sessionProvider = sessionProvider;
- }
-
- @Override
- public boolean accept(String command, List<String> arguments) {
- if (command.startsWith("gerrit") && !"10.1.2.3".equals(sessionProvider.get().getRemoteAddressAsString())) {
- return false;
- }
- return true;
- }
-}
-----
-
-And then declare it in your SSH module:
-[source, java]
-----
- DynamicSet.bind(binder(), SshExecuteCommandInterceptor.class).to(SshExecuteCommandInterceptorImpl.class);
-----
-
Plugin authors should also consider binding their SubmitRule using a `Gerrit-BatchModule`.
See link:dev-plugins.html[Batch runtime] for more informations.
@@ -2954,40 +2678,94 @@ 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.
-[[message-of-the-day]]
-== Posting Messages (Of The Day) to the UI
-Gerrit provides an extension point that enables plugins to implement a method to
-collect messages that will then be shown below the main header in the Gerrit UI.
+[[quota-enforcer]]
+== Quota Enforcer
+
+Gerrit provides an extension point that allows a plugin to enforce quota.
+link:quota.html[This documentation page] has a list of all quota requests that
+Gerrit core issues. Plugins can choose to respond to all or just a subset of
+requests. Some implementations might want to keep track of user quota in buckets,
+others might just check against instance or project state to enforce limits on how
+many projects can be created or how large a repository can become.
+
+Checking against instance state can be racy for concurrent requests as the server does not
+refill tokens if the action fails in a later stage (e.g. database failure). If
+plugins want to guarantee an absolute maximum on a resource, they have to do their own
+book-keeping.
[source, java]
----
-import com.google.gerrit.extensions.systemstatus.MessageOfTheDay;
+import com.google.server.quota.QuotaEnforcer;
-@Singleton
-class MessageOfTheDayImpl extends MessageOfTheDay {
+class ProjectLimiter implements QuotaEnforcer {
+ private final long maxNumberOfProjects = 100;
+ @Override
+ QuotaResponse requestTokens(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+ if (!"/projects/create".equals(quotaGroup)) {
+ return QuotaResponse.noOp();
+ }
+ // No deduction because we always check against the instance state (racy but fine for
+ // this plugin)
+ if (currentNumberOfProjects() + numTokens > maxNumberOfProjects) {
+ return QuotaResponse.error("too many projects");
+ }
+ return QuotaResponse.ok();
+ }
+
+ @Override
+ QuotaResponse dryRun(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+ // Since we are not keeping any state in this enforcer, we can simply call requestTokens().
+ return requestTokens(quotaGroup, ctx, numTokens);
+ }
+
+ void refill(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+ // No-op
+ }
+}
+----
- private final String id;
- private final String msg;
+[source, java]
+----
+import com.google.server.quota.QuotaEnforcer;
- public MessageOfTheDayImpl() {
- id = "hello";
- msg = "I just wanted to say <b>hello</b>.";
+class ApiQpsEnforcer implements QuotaEnforcer {
+ // AutoRefillingPerUserBuckets is a imaginary bucket implementation that could be based on
+ // a loading cache or a commonly used bucketing algorithm.
+ private final AutoRefillingPerUserBuckets<CurrentUser, Long> buckets;
+ @Override
+ QuotaResponse requestTokens(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+ if (!quotaGroup.startsWith("/restapi/")) {
+ return QuotaResponse.noOp();
+ }
+ boolean success = buckets.deduct(ctx.user(), numTokens);
+ if (!success) {
+ return QuotaResponse.error("user sent too many qps, please wait for 5 minutes");
+ }
+ return QuotaResponse.ok();
}
@Override
- public String getHtmlMessage() {
- return msg;
+ QuotaResponse dryRun(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+ if (!quotaGroup.startsWith("/restapi/")) {
+ return QuotaResponse.noOp();
+ }
+ boolean success = buckets.checkOnly(ctx.user(), numTokens);
+ if (!success) {
+ return QuotaResponse.error("user sent too many qps, please wait for 5 minutes");
+ }
+ return QuotaResponse.ok();
}
@Override
- public String getMessageId() {
- return id;
+ void refill(String quotaGroup, QuotaRequestContext ctx, long numTokens) {
+ if (!quotaGroup.startsWith("/restapi/")) {
+ return;
+ }
+ buckets.add(ctx.user(), numTokens);
}
}
----
-Note, that the message will be added as HTML and parsed into the DOM. Thus,
-plugins using this extension should ensure that the message content is safe.
== SEE ALSO
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index 862bdef1f8..c014687cb2 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -93,6 +93,7 @@ To create more accounts on your development instance:
. Click 'become' in the upper right corner.
. Select 'Switch User'.
. Register a new account.
+. link:user-upload.html#ssh[Configure your SSH key].
Use the `ssh` protocol to clone from and push to the local server. For
example, to clone a repository that you've created through the admin
@@ -193,27 +194,6 @@ interfaces (including HTTP and SSH) are available.
CAUTION: When using the Inspector, be careful not to modify the internal state
of the system.
-=== Querying the database
-
-The embedded H2 database can be queried and updated from the command line. If
-the daemon is not running, run:
-
-----
- $(bazel info output_base)/external/local_jdk/bin/java \
- -jar bazel-bin/gerrit.war gsql -d ../gerrit_testsite -s
-----
-
-NOTE: To learn why using `java -jar` isn't sufficient, see
-<<special_bazel_java_version,this explanation>>.
-
-Alternatively, if the daemon is running and the database is in use, use an
-administrator user account to connect over SSH:
-
-----
- ssh -p 29418 user@localhost gerrit gsql
-----
-
-
== Switching between branches
When using `git checkout` without `--recurse-submodules` to switch between
diff --git a/Documentation/dev-release-deploy-config.txt b/Documentation/dev-release-deploy-config.txt
index 5f95cb326c..541192775a 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. `gwtjsonrpc` etc.).
+Bucket to store Gerrit Subproject Artifacts (e.g. `gwtorm` etc.).
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-subproject.txt b/Documentation/dev-release-subproject.txt
index 4886849b42..c9369b9570 100644
--- a/Documentation/dev-release-subproject.txt
+++ b/Documentation/dev-release-subproject.txt
@@ -73,7 +73,7 @@ link:dev-release-deploy-config.html#deploy-configuration-subprojects[subprojects
* Deploy the new release:
+
----
- mvn deploy
+ mvn deploy -DperformRelease=true
----
* Push the pom change(s) to the project's repository
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 8297b60204..c10457d84e 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -12,7 +12,6 @@ hopefully serve as both a how to for those new to the process
and as a checklist for those already familiar with these
tasks.
-
== Gerrit Release Type
Here are some guidelines on release approaches depending on the
@@ -25,33 +24,36 @@ type of release you want to make (`stable-fix`, `stable`, `rc0`,
A `stable` release is generally built from the `master` branch and may
need to undergo some stabilization before releasing the final release.
-* Propose the release with any plans/objectives to the mailing list
+* Propose the release with any plans/objectives to the mailing list.
+
+* Release plans usually become a
+ link:https://www.gerritcodereview.com/news.html[news article]
+ to be followed up with.
-* Create a Gerrit `rc0`
+* Create a Gerrit `rc0`.
-* If needed create a Gerrit `rc1`
+* If needed create Gerrit `rc1`, `rc2` and `rc3` (one per week, on Mondays
+ or so; see link:https://www.gerritcodereview.com/news.html[past release plans]).
[NOTE]
-You may let in a few features to this release
+You may let in a few features to these releases.
-* If needed create a Gerrit `rc2`
+* If needed create a Gerrit `rc4`.
[NOTE]
-There should be no new features in this release, only bug fixes
-
-* Finally create the `stable` release (no `rc`)
+There should be no new features in this release, only bug fixes.
+* Finally create the `stable` release (no `rc`).
=== Stable-Fix
`stable-fix` releases should likely only contain bug fixes and doc
updates.
-* Propose the release with any plans/objectives to the mailing list
+* Propose the release with any plans/objectives to the mailing list.
* This type of release does not need any RCs, release when the
-objectives are met
-
+objectives are met.
[[security]]
=== Security-Fix
@@ -70,6 +72,47 @@ a `security-fix` release has been published will the commits/tags made in
the `gerrit-security-fixes` project be taken over into the public
`gerrit` project.
+[[upload-final-release-notes]]
+== Upload the final Release Notes change
+
+Upload a change on the homepage project to:
+
+* Remove 'In Development' caveat from the relevant section.
+
+* Add links to the released documentation and the .war file, and make the
+latest version bold.
+
+The uploaded change is not to be approved yet, but rather act as the
+release content review thread until it can be finalized.
+
+[[update-links]]
+=== Update homepage links
+
+Upload a change on the link:https://gerrit-review.googlesource.com/admin/repos/homepage[
+homepage project] to change the version numbers to the new version.
+
+[[update-issues]]
+=== Update the Issues
+
+Update the issues by hand. There is no script for this.
+
+Our current process is an issue should be updated to say `Status =
+Submitted, FixedIn-$version` once the change is submitted, but before the
+release.
+
+The updated issues are the ones listed in commit messages since the
+previous version tag. Mention each updated issue in the uploaded change,
+following the examples from the previous version notes. Add updated issue
+owners as reviewers of the uploaded change. More reviewers can be added
+or cc'ed, to further coordinate the final release contents.
+
+Similarly to issues, also mention every noteworthy change done after the
+previous release. Again, previous notes should be used as template examples.
+
+You may need to split note update changes from the final change that
+updates the links. This allows non-final update changes to be reviewed and
+submitted timely. The final (links) change may take more time to complete,
+as this underlying release process unfolds.
== Create the Actual Release
@@ -95,6 +138,15 @@ Commit the changes and create a signed release tag on the new commit:
git tag -s -m "v$version" "v$version"
----
+If unable to tag, make sure that git is locally
+link:https://medium.com/@rwbutler/signing-commits-using-gpg-on-macos-7210362d15[configured with your user's key].
+These are the macOS instructions but such commands should be portable enough.
+Setting `GPG_TTY` this way or similar might also be necessary:
+
+----
+ export GPG_TTY=$(tty)
+----
+
Tag the plugins:
----
@@ -104,7 +156,7 @@ Tag the plugins:
[[build-gerrit]]
=== Build Gerrit
-* Build the Gerrit WAR, API JARs and documentation
+* Build the Gerrit WAR, API JARs and documentation:
+
----
bazel build release Documentation:searchfree
@@ -117,11 +169,10 @@ Tag the plugins:
----
java -jar bazel-bin/release.war --version
----
-* Try upgrading a test site and launching the daemon
-* Verify plugin versions
-+
-Verify the versions:
+* Try upgrading a test site and launching the daemon.
+
+* Verify the plugin versions:
+
----
java -jar bazel-bin/release.war init --list-plugins
@@ -135,7 +186,7 @@ Verify the versions:
* Make sure you have done the
link:dev-release-deploy-config.html#deploy-configuration-setting-maven-central[
-configuration] for deploying to Maven Central
+configuration] for deploying to Maven Central.
* Make sure that the version is updated in the `version.bzl` file and in
the `*_pom.xml` files as described in the link:#update-versions[Update
@@ -189,7 +240,7 @@ modified anymore, but you may still drop it if you find any issues.
** Test closed staging repository
+
Once a repository is closed you can find the URL to it in the `Summary`
-section, e.g. https://oss.sonatype.org/content/repositories/comgooglegerrit-1029
+section, e.g. https://oss.sonatype.org/content/repositories/comgooglegerrit-1029.
+
Use this URL for further testing of the artifacts in this repository,
e.g. to try building a plugin against the plugin API in this repository
@@ -218,12 +269,12 @@ link:https://oss.sonatype.org/[Sonatype Nexus Server], select it and
click on `Release`.
** The released artifacts are available in
-https://oss.sonatype.org/content/repositories/releases/com/google/gerrit/
+https://oss.sonatype.org/content/repositories/releases/com/google/gerrit/.
** It may take up to 2 hours until the artifacts appear on Maven
Central:
+
-http://central.maven.org/maven2/com/google/gerrit/
+https://repo1.maven.org/maven2/com/google/gerrit/
* [optional]: View download statistics
@@ -235,14 +286,15 @@ link:https://oss.sonatype.org/[Sonatype Nexus Server].
** Select `com.google.gerrit` as `Project`.
-
[[publish-to-google-storage]]
==== Publish the Gerrit WAR to the Google Cloud Storage
-* go to the link:https://console.cloud.google.com/storage/browser/gerrit-releases/?project=api-project-164060093628[
-gerrit-releases bucket in the Google cloud storage console]
-* make sure you are signed in with your Gmail account
-* manually upload the Gerrit WAR file by using the `Upload` button
+* Go to the link:https://console.cloud.google.com/storage/browser/gerrit-releases/?project=api-project-164060093628[
+gerrit-releases bucket in the Google cloud storage console].
+
+* Make sure you are signed in with your Gmail account.
+
+* Manually upload the Gerrit WAR file by using the `Upload` button.
[[push-stable]]
==== Push the Stable Branch
@@ -257,7 +309,6 @@ get them merged.
* Create a change updating the `defaultbranch` field in the `.gitreview`
to match the branch name created.
-
[[push-tag]]
==== Push the Release Tag
@@ -273,7 +324,6 @@ Push the new Release Tag on the plugins:
git submodule foreach git push gerrit-review tag v$version
----
-
[[upload-documentation]]
==== Upload the Documentation
@@ -288,34 +338,16 @@ gerrit-documentation] storage bucket.
[[finalize-release-notes]]
=== Finalize the Release Notes
-Upload a change on the homepage project to:
+Submit any previously uploaded notes change on the homepage project.
-* Remove 'In Development' caveat from the relevant section.
-
-* Add links to the released documentation and the .war file, and make the
-latest version bold.
-
-[[update-links]]
-==== Update homepage links
-
-Upload a change on the link:https://gerrit-review.googlesource.com/admin/repos/homepage[
-homepage project] to change the version numbers to the new version.
-
-[[update-issues]]
+[[finalize-issues]]
==== Update the Issues
-Update the issues by hand. There is no script for this.
-
-Our current process is an issue should be updated to say `Status =
-Submitted, FixedIn-$version` once the change is submitted, but before the
-release.
-
-After the release is actually made, you can search in Google Code for
+After the release is actually made, you can search (in Monorail) for
`Status=Submitted FixedIn=$version` and then batch update these changes
to say `Status=Released`. Make sure the pulldown says `All Issues`
because `Status=Submitted` is considered a closed issue.
-
[[announce]]
==== Announce on Mailing List
@@ -328,7 +360,7 @@ For details refer to the documentation in the script's header, and/or the
help text:
----
- ~/gerrit-release-tools/release-announcement.py --help
+ ~/gerrit-release-tools/release-announcement.py --help
----
[[increase-version]]
@@ -341,7 +373,7 @@ for the next release.
Use the `version` tool to set the version in the `version.bzl` file:
----
- ./tools/version.py 2.6-SNAPSHOT
+ ./tools/version.py 2.6-SNAPSHOT
----
Verify that the changes made by the tool are sane, then commit them, push
diff --git a/Documentation/error-changeid-above-footer.txt b/Documentation/error-changeid-above-footer.txt
new file mode 100644
index 0000000000..abc0186758
--- /dev/null
+++ b/Documentation/error-changeid-above-footer.txt
@@ -0,0 +1,31 @@
+= commit xxxxxxx: Change-Id must be in message footer
+
+With this error message, Gerrit rejects a push of a commit to a project
+if the commit message of the pushed commit contains a Change-Id line that
+is not in the footer (the last paragraph).
+
+To be picked up by Gerrit, a Change-Id must be in the last paragraph
+of a commit message. For details, see link:user-changeid.html[Change-Id Lines].
+
+You can see the commit messages for existing commits in the history
+by doing a link:http://www.kernel.org/pub/software/scm/git/docs/git-log.html[git log].
+
+
+== Change-Id is contained in the commit message but not in the last paragraph
+
+If the Change-Id is contained in the commit message but not in its
+last paragraph, you have to update the commit message and move the
+Change-Id into the last paragraph. How to update the commit message
+is explained link:error-push-fails-due-to-commit-message.html[here].
+
+To avoid confusion due to a Change-Id that was meant to be picked up by
+Gerrit not being picked up, this is an error whether or not the project
+is configured to always require a Change-Id in the commit message.
+
+
+GERRIT
+------
+Part of link:error-messages.html[Gerrit Error Messages]
+
+SEARCHBOX
+---------
diff --git a/Documentation/error-messages.txt b/Documentation/error-messages.txt
index 37eb1f6827..b52366342b 100644
--- a/Documentation/error-messages.txt
+++ b/Documentation/error-messages.txt
@@ -18,6 +18,7 @@ occurring and what can be done to solve it.
* link:error-invalid-changeid-line.html[invalid Change-Id line format in commit message footer]
* link:error-invalid-committer.html[invalid committer]
* link:error-missing-changeid.html[missing Change-Id in commit message footer]
+* link:error-changeid-above-footer.html[Change-Id must be in commit message footer]
* link:error-missing-subject.html[missing subject; Change-Id must be in commit message footer]
* link:error-multiple-changeid-lines.html[multiple Change-Id lines in commit message footer]
* link:error-no-common-ancestry.html[no common ancestry]
diff --git a/Documentation/error-missing-changeid.txt b/Documentation/error-missing-changeid.txt
index 08f2c09475..27bfea580b 100644
--- a/Documentation/error-missing-changeid.txt
+++ b/Documentation/error-missing-changeid.txt
@@ -3,13 +3,7 @@
With this error message Gerrit rejects to push a commit to a project
which is configured to always require a Change-Id in the commit
message if the commit message of the pushed commit does not contain
-a Change-Id in the footer (the last paragraph).
-
-This error may happen for different reasons:
-
-. missing Change-Id in the commit message
-. Change-Id is contained in the commit message but not in the last
- paragraph
+a Change-Id.
You can see the commit messages for existing commits in the history
by doing a link:http://www.kernel.org/pub/software/scm/git/docs/git-log.html[git log].
@@ -38,17 +32,6 @@ insert it into the commit message. How to update the commit message
is explained link:error-push-fails-due-to-commit-message.html[here].
-== Change-Id is contained in the commit message but not in the last paragraph
-
-To be picked up by Gerrit, a Change-Id must be in the last paragraph
-of a commit message, for details, see link:user-changeid.html[Change-Id Lines].
-
-If the Change-Id is contained in the commit message but not in its
-last paragraph you have to update the commit message and move the
-Change-Id into the last paragraph. How to update the commit message
-is explained link:error-push-fails-due-to-commit-message.html[here].
-
-
GERRIT
------
Part of link:error-messages.html[Gerrit Error Messages]
diff --git a/Documentation/images/inline-edit-create-change-project-screen-dialog.png b/Documentation/images/inline-edit-create-change-project-screen-dialog.png
deleted file mode 100644
index ea5daa97a8..0000000000
--- a/Documentation/images/inline-edit-create-change-project-screen-dialog.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/images/inline-edit-create-change-project-screen.png b/Documentation/images/inline-edit-create-change-project-screen.png
deleted file mode 100644
index e9c7033186..0000000000
--- a/Documentation/images/inline-edit-create-change-project-screen.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/images/inline-edit-create-follow-up-change.png b/Documentation/images/inline-edit-create-follow-up-change.png
deleted file mode 100644
index 3e81eee8a5..0000000000
--- a/Documentation/images/inline-edit-create-follow-up-change.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/images/inline-edit-edit-in-diff-screen-patch-list.png b/Documentation/images/inline-edit-edit-in-diff-screen-patch-list.png
deleted file mode 100644
index bdbc59dd96..0000000000
--- a/Documentation/images/inline-edit-edit-in-diff-screen-patch-list.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/images/inline-edit-edit-in-patch-list.png b/Documentation/images/inline-edit-edit-in-patch-list.png
deleted file mode 100644
index 9a31e0209e..0000000000
--- a/Documentation/images/inline-edit-edit-in-patch-list.png
+++ /dev/null
Binary files differ
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 7fecf73a16..f9e39b5c2a 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -86,6 +86,7 @@
.. 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]
diff --git a/Documentation/install-j2ee.txt b/Documentation/install-j2ee.txt
index 91d73cc902..48751b761c 100644
--- a/Documentation/install-j2ee.txt
+++ b/Documentation/install-j2ee.txt
@@ -14,9 +14,8 @@ any commercial server which supports the J2EE servlet specification.
== Installation
-* Complete the link:install.html#createdb[database setup] and
- link:install.html#init[site initialization] tasks described
- in the standard installation documentation.
+* Complete the link:install.html#init[site initialization] task
+ described in the standard installation documentation.
* Stop the embedded daemon that was automatically started by 'init':
+
@@ -24,13 +23,6 @@ any commercial server which supports the J2EE servlet specification.
review_site/bin/gerrit.sh stop
----
-* Configure JNDI DataSource 'jdbc/ReviewDb'.
-+
-This DataSource must point to the database you created above.
-Don't forget to ensure your JNDI configuration can load the
-necessary JDBC drivers. You may wish to ensure connection pooling
-is configured and enabled within the DataSource.
-
* Deploy the 'gerrit.war' file to your application server.
+
The deployment process differs between servers, but typically this
@@ -70,20 +62,8 @@ Copy Gerrit Code Review into the deployment:
java -jar webapps/gerrit.war cat extra/jetty7/gerrit.xml >contexts/gerrit.xml
----
-Install the required additional libraries by copying them into the
-`'$JETTY_HOME'/lib/ext` directory:
-
-----
- cp ../review_db/lib/* lib/ext/
- java -jar webapps/gerrit.war cat lib/commons-dbcp-1.2.2.jar >lib/ext/commons-dbcp-1.2.2.jar
- java -jar webapps/gerrit.war cat lib/commons-pool-1.5.4.jar >lib/ext/commons-pool-1.5.4.jar
- java -jar webapps/gerrit.war cat lib/h2-1.2.128.jar >lib/ext/h2-1.2.128.jar
- java -jar webapps/gerrit.war cat lib/postgresql-8.4-701.jdbc4.jar >lib/ext/postgresql-8.4-701.jdbc4.jar
-----
-
Edit `'$JETTY_HOME'/contexts/gerrit.xml` to correctly configure
-the database and outgoing SMTP connections, especially the user
-and password fields.
+outgoing SMTP connections.
If OpenID authentication (or certain enterprise single-sign-on
solutions) is being used, you may need to increase the
diff --git a/Documentation/install.txt b/Documentation/install.txt
index 2f004922f0..aaefc86fbc 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -9,10 +9,6 @@ To run the Gerrit service, the following requirement must be met on the host:
+
Gerrit is not yet compatible with Java 13 or newer at this time.
-By default, Gerrit uses link:note-db.html[NoteDB] as the storage backend. (If
-desired, you can _optionally_ use an external database such as MySQL or
-PostgreSQL.)
-
[[cryptography]]
== Configure Java for Strong Cryptography
@@ -60,15 +56,12 @@ rename the downloaded file.
If you would prefer to build Gerrit directly from source, review
the notes under link:dev-readme.html[developer setup].
-include::database-setup.txt[]
-
[[init]]
== Initialize the Site
Gerrit stores configuration files, the server's SSH keys, and the
managed Git repositories under a local directory, typically referred
-to as `'$site_path'`. If the embedded H2 database is being used,
-its data files will also be stored under this directory.
+to as `'$site_path'`.
You also have to decide where to store your server side git repositories. This
can either be a relative path under `'$site_path'` or an absolute path
@@ -93,11 +86,10 @@ have any privileges, you may have to manually create the directory first and
then give ownership of that location to the `'gerrit'` user.
If run from an interactive terminal, the init command will prompt through a
-series of configuration questions, including gathering information
-about the database created above. If the terminal is not interactive,
-running the init command will choose some reasonable default selections,
-and will use the embedded H2 database. Once the init phase is complete,
-you can review your settings in the file `'$site_path/etc/gerrit.config'`.
+series of configuration questions. If the terminal is not interactive,
+running the init command will choose some reasonable default selections.
+Once the init phase is complete, you can review your settings in the file
+`'$site_path/etc/gerrit.config'`.
When running the init command, additional JARs might be downloaded to
support optional selected functionality. If a download fails a URL will
@@ -217,8 +209,7 @@ Sample install command:
--StartPath=C:\MY\GERRIT\SITE ^
--StartMode=jvm --StopMode=jvm ^
--StartClass=com.google.gerrit.launcher.GerritLauncher --StartMethod=daemonStart ^
- --StopClass=com.google.gerrit.launcher.GerritLauncher --StopMethod=daemonStop ^
- ++DependsOn=postgresql-x64-9.4
+ --StopClass=com.google.gerrit.launcher.GerritLauncher --StopMethod=daemonStop
====
[[customize]]
@@ -255,8 +246,6 @@ Place Gerrit plugins in the review_site/plugins directory to have them loaded on
== External Documentation Links
-* http://www.postgresql.org/docs/[PostgreSQL Documentation]
-* http://dev.mysql.com/doc/[MySQL Documentation]
* http://www.kernel.org/pub/software/scm/git/docs/git-daemon.html[git-daemon]
diff --git a/Documentation/intro-project-owner.txt b/Documentation/intro-project-owner.txt
index 08407c25c6..f7aed5ed7b 100644
--- a/Documentation/intro-project-owner.txt
+++ b/Documentation/intro-project-owner.txt
@@ -123,6 +123,13 @@ under `refs/heads/` and all tags under `refs/tags/`. In addition there
are a number of link:access-control.html#references_special[special refs]
and link:access-control.html#references_magic[magic refs].
+Gerrit only supports tags that are reachable by any ref not owned by
+Gerrit. This includes branches (refs/heads/*) or custom ref namespaces
+(refs/my-company/*). Tagging a change ref is not supported.
+When filtering tags by visibility, Gerrit performs a reachability check
+and will present the user ony with tags that are reachable by any ref
+they can see.
+
Access rights can be assigned on a concrete ref, e.g.
`refs/heads/master` but also on ref patterns and regular expressions
for ref names.
@@ -209,7 +216,7 @@ verifications from a build server] before changes are merged. In
addition you can benefit from Gerrit's merge strategies that can
automatically merge/rebase commits on server side if necessary. You can
control the merge strategy by configuring the
-link:project-configuration.html#submit_type[submit type] on the project. If you
+link:config-project-config.html#submit-type[submit type] on the project. If you
bypass code review you always need to merge/rebase manually if the tip
of the destination branch has moved. Please keep this in mind if you
choose to not work with code review because you think it's easier to
@@ -239,7 +246,7 @@ To see the options of your project:
An important decision for a project is the choice of the submit type
and the content merge setting (see the `Allow content merges` option).
-The link:project-configuration.html#submit_type[submit type] is the method
+The link:config-project-config.html#submit-type[submit type] is the method
Gerrit uses to submit a change to the project. The submit type defines
what Gerrit should do on submit of a change if the destination branch
has moved while the change was in review. The
@@ -281,7 +288,7 @@ link:#prolog-submit-type[Prolog]. This way you can use different submit
types for different branches.
Please note that there are other submit types available; they are
-described in the link:project-configuration.html#submit_type[Submit Type]
+described in the link:config-project-config.html#submit-type[Submit Type]
section.
[[labels]]
@@ -600,18 +607,6 @@ Project-specific download commands that are defined on a parent project
are inherited by the child projects. A child project can overwrite an
inherited download command, or remove it by assigning no value to it.
-[[theme]]
-== Theme
-
-Gerrit supports project-specific themes for customizing the appearance
-of the change screen and the diff screens. It is possible to define an
-HTML header and footer and to adapt Gerrit's CSS. Details about themes
-are explained in the link:config-themes.html[Themes] section.
-
-Project-specific themes can only be installed by Gerrit administrators
-since the theme files must be copied into the Gerrit installation
-folder.
-
[[tool-integration]]
== Integration with other tools
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index 74a1a4461e..04778f1e8f 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -386,7 +386,7 @@ rules] to control when a change becomes submittable.
How the code modification is applied to the target branch when a change
is submitted is controlled by the
-link:project-configuration.html#submit_type[submit type] which can be
+link:config-project-config.html#submit-type[submit type] which can be
link:intro-project-owner.html#submit-type[configured on project-level].
Submitting a change may fail with conflicts. In this case you need to
@@ -517,9 +517,6 @@ topics, a change can have multiple hashtags, and they are only used for
informational grouping; changes with the same hashtags are not necessarily
submitted together.
-The hashtag feature is only available when running under
-link:note-db.html[NoteDb].
-
.Set Hashtag on Push
----
$ git push origin HEAD:refs/for/master%t=stable-bugfix
@@ -558,7 +555,9 @@ the command line or the user interface. To use the command line, append
----
$ git push origin HEAD:refs/for/master%wip
----
-Alternatively, click *WIP* from the Change screen.
+Alternatively, click *WIP* from the *More* menu on the Change screen.
+The Change screen updates with a yellow header, indicating that
+the change is in a Work-in-Progress state.
To mark the change as ready for review, append `%ready` to your push
request.
@@ -566,16 +565,12 @@ request.
----
$ git push origin HEAD:refs/for/master%ready
----
-Alternatively, click *Ready* from the Change screen.
-
-Only change owners, project owners and site administrators can mark changes as
-`work-in-progress` and `ready`.
+Alternatively, click *Start Review* from the Change screen.
-[[wip-polygerrit]]
-In the new PolyGerrit UI, you can mark a change as WIP, by selecting *WIP* from
-the *More* menu. The Change screen updates with a yellow header, indicating that
-the change is in a Work-in-Progress state. To mark a change as ready for review,
-click *Start Review*.
+Change owners, project owners, site administrators and members of a group that
+was granted link:access-control.html#category_toggle_work_in_progress_state[
+Toggle Work In Progress state] permission can mark changes as `work-in-progress`
+and `ready`.
[[private-changes]]
== Private Changes
@@ -598,10 +593,9 @@ of cases:
creating a private throwaway change without reviewers, you can push
from one device, and fetch to another device.
-* You want to do code review on a change that has sensitive
- aspects. By reviewing a security fix in a private change,
- outsiders can't discover the fix before it is pushed out. Even after
- merging the change, the review can be kept private.
+Do *not* use private changes for making security fixes (see
+link:#private-changes-pitfalls[pitfalls] below). How to make security
+fixes is explained link:#security-fixes[below].
To create a private change, you push it with the `private` option.
@@ -625,6 +619,25 @@ can be granted
In that case, care should be taken to prevent the CI system from
exposing secret details.
+[[private-changes-pitfalls]]
+=== Pitfalls
+
+If private changes are used, be aware of the following pitfalls:
+
+* If a private change gets merged the corresponding commit gets visible
+ for all users that can access the target branch and the private flag
+ from the change is automatically removed. This makes private changes
+ a bad choice for security fixes, as the security fix will be
+ accessible as soon as the change was merged, but for security issues
+ you want to keep an embargo until new releases have been made
+ available.
+* If you push a non-private change on top of a private change the
+ commit of the private change gets implicitly visible through the
+ parent relationship of the follow-up change.
+* If you have a series of private changes and share one with reviewers,
+ the reviewers can also see the commits of the predecessor private
+ changes through the commit parent relationship.
+
[[ignore]]
== Ignoring Or Marking Changes As 'Reviewed'
@@ -962,6 +975,29 @@ In this case, Gerrit will persist a change message ("Thanks, I'll fix it."),
a file comment ("Rename this file to File.java") as well as a reply to an
inline comment ("Yeah, I see why, let me try again.").
+[[security-fixes]]
+-- Security Fixes
+
+If a security vulnerability is discovered you normally want to have an
+embargo about it until fixed releases have been made available. This
+means you want to develop and review security fixes in private.
+
+If your repository is public or grants broad read access it is
+recommended to fix security issues in a copy of your repository which
+has very restricted read permissions (e.g. `myproject-security-fixes`).
+You can then implement, review and submit the security fix in this
+repository, make and publish a new release and only then integrate the
+security fix back into the normal (public) repository.
+
+Alternatively you can do the security fix in your normal repository in
+a branch with restricted read permissions. We don't recommend this
+because there is a risk of configuring the access rights wrongly and
+unintentionally granting read access to the wrong people.
+
+Using private changes for security fixes is *not* recommended due to
+the link:#private-changes-pitfalls[pitfalls] discussed above.
+Especially you don't want the fix to become visible after submit and
+before you had a chance to make and publish a new release.
GERRIT
------
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 96b5107651..4ef2a6ce8d 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -188,6 +188,12 @@ Supported events:
comments, file-level comments and summary comments, and it may change
with new Gerrit versions.
+* `highlightjs-loaded`: Invoked when the highlight.js library has
+ finished loading. The global `hljs` object (also now accessible via
+ `window.hljs`) is passed as an argument to the callback function.
+ 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
diff --git a/Documentation/js_licenses.txt b/Documentation/js_licenses.txt
new file mode 100644
index 0000000000..bb1399d9d9
--- /dev/null
+++ b/Documentation/js_licenses.txt
@@ -0,0 +1,509 @@
+
+[[Apache2_0]]
+Apache2.0
+
+* fonts:robotofonts
+* js:web-animations-js
+* polymer_externs:polymer_closure
+
+[[Apache2_0_license]]
+----
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+----
+
+
+[[ba-linkify]]
+ba-linkify
+
+* js:ba-linkify
+
+[[ba-linkify_license]]
+----
+Copyright (c) 2009 "Cowboy" Ben Alman
+
+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.
+
+----
+
+
+[[es6-promise]]
+es6-promise
+
+* js:es6-promise
+
+[[es6-promise_license]]
+----
+Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
+
+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.
+
+----
+
+
+[[fetch]]
+fetch
+
+* js:fetch
+
+[[fetch_license]]
+----
+Copyright (c) 2014-2016 GitHub, Inc.
+
+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.
+
+----
+
+
+[[highlightjs]]
+highlightjs
+
+* js:highlightjs
+* js:highlightjs_files
+
+[[highlightjs_license]]
+----
+Copyright (c) 2006, Ivan Sagalaev
+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 highlight.js 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 REGENTS 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 REGENTS AND 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.
+----
+
+
+[[moment]]
+moment
+
+* js:moment
+
+[[moment_license]]
+----
+Copyright (c) 2011-2016 Tim Wood, Iskren Chernev, Moment.js contributors
+
+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.
+
+----
+
+
+[[page_js]]
+page.js
+
+* js:page
+
+[[page_js_license]]
+----
+(The MIT License)
+
+Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
+
+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.
+
+----
+
+
+[[polymer]]
+polymer
+
+* js:font-roboto-local
+* js:iron-a11y-announcer
+* js:iron-a11y-keys-behavior
+* js:iron-autogrow-textarea
+* js:iron-behaviors
+* js:iron-checked-element-behavior
+* js:iron-dropdown
+* js:iron-fit-behavior
+* js:iron-flex-layout
+* js:iron-form-element-behavior
+* js:iron-icon
+* js:iron-iconset-svg
+* js:iron-input
+* js:iron-menu-behavior
+* js:iron-meta
+* js:iron-overlay-behavior
+* js:iron-resizable-behavior
+* js:iron-selector
+* js:iron-validatable-behavior
+* js:neon-animation
+* js:paper-behaviors
+* js:paper-button
+* js:paper-icon-button
+* js:paper-input
+* js:paper-item
+* js:paper-listbox
+* js:paper-ripple
+* js:paper-styles
+* js:paper-tabs
+* js:paper-toggle-button
+* js:polymer
+* js:polymer-resin
+* js:webcomponentsjs
+
+[[polymer_license]]
+----
+Copyright (c) 2014 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.
+
+----
+
+
+[[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.
+
+----
+
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
new file mode 100644
index 0000000000..e94f29741a
--- /dev/null
+++ b/Documentation/licenses.txt
@@ -0,0 +1,3458 @@
+= Gerrit Code Review - Licenses
+
+// DO NOT EDIT - GENERATED AUTOMATICALLY.
+
+Gerrit open source software is licensed under the <<Apache2_0,Apache
+License 2.0>>. Executable distributions also include other software
+components that are provided under additional licenses.
+
+[[cryptography]]
+== Cryptography Notice
+
+This distribution includes cryptographic software. The country
+in which you currently reside may have restrictions on the import,
+possession, use, and/or re-export to another country, of encryption
+software. BEFORE using any encryption software, please check
+your country's laws, regulations and policies concerning the
+import, possession, or use, and re-export of encryption software,
+to see if this is permitted. See the
+link:http://www.wassenaar.org/[Wassenaar Arrangement]
+for more information.
+
+The U.S. Government Department of Commerce, Bureau of Industry
+and Security (BIS), has classified this software as Export
+Commodity Control Number (ECCN) 5D002.C.1, which includes
+information security software using or performing cryptographic
+functions with asymmetric algorithms. The form and manner of
+this distribution makes it eligible for export under the License
+Exception ENC Technology Software Unrestricted (TSU) exception
+(see the BIS Export Administration Regulations, Section 740.13)
+for both object code and source code.
+
+Gerrit includes an SSH daemon (Apache SSHD), to support authenticated
+uploads of changes directly from `git push` command line clients.
+
+Gerrit includes an SSH client (JSch), to support authenticated
+replication of changes to remote systems, such as for automatic
+updates of mirror servers, or realtime backups.
+
+== Licenses
+
+
+[[Apache2_0]]
+Apache2.0
+
+* auto:auto-value
+* auto:auto-value-annotations
+* commons:codec
+* commons:compress
+* commons:dbcp
+* commons:lang
+* commons:net
+* commons:pool
+* commons:validator
+* dropwizard:dropwizard-core
+* flogger:api
+* fonts:robotofonts
+* guice:guice
+* guice:guice-assistedinject
+* guice:guice-library
+* guice:guice-servlet
+* guice:javax_inject
+* httpcomponents:httpasyncclient
+* httpcomponents:httpclient
+* httpcomponents:httpcore
+* httpcomponents:httpcore-nio
+* jackson:jackson-core
+* jetty:continuation
+* jetty:http
+* jetty:io
+* jetty:jmx
+* jetty:security
+* jetty:server
+* jetty:servlet
+* jetty:util
+* jgit/org.eclipse.jgit:javaewah
+* js:web-animations-js
+* log:json-smart
+* log:jsonevent-layout
+* log:log4j
+* lucene:lucene-analyzers-common
+* lucene:lucene-core-and-backward-codecs-merged
+* lucene:lucene-misc
+* lucene:lucene-queryparser
+* mime4j:core
+* mime4j:dom
+* mina:core
+* mina:sshd
+* openid:consumer
+* openid:nekohtml
+* openid:xerces
+* polymer_externs:polymer_closure
+* blame-cache
+* caffeine
+* caffeine-guava
+* gson
+* guava
+* guava-failureaccess
+* guava-retrying
+* html-types
+* j2objc
+* jsr305
+* mime-util
+* servlet-api
+* servlet-api-without-neverlink
+* soy
+
+[[Apache2_0_license]]
+----
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+----
+
+
+[[CC0-1_0]]
+CC0-1.0
+
+* mina:eddsa
+
+[[CC0-1_0_license]]
+----
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
+
+For more information, please see https://creativecommons.org/publicdomain/zero/1.0/
+
+----
+
+
+[[MPL1_1]]
+MPL1.1
+
+* juniversalchardet
+
+[[MPL1_1_license]]
+----
+ MOZILLA PUBLIC LICENSE
+ Version 1.1
+
+ ---------------
+
+1. Definitions.
+
+ 1.0.1. "Commercial Use" means distribution or otherwise making the
+ Covered Code available to a third party.
+
+ 1.1. "Contributor" means each entity that creates or contributes to
+ the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Code, prior Modifications used by a Contributor, and the Modifications
+ made by that particular Contributor.
+
+ 1.3. "Covered Code" means the Original Code or Modifications or the
+ combination of the Original Code and Modifications, in each case
+ including portions thereof.
+
+ 1.4. "Electronic Distribution Mechanism" means a mechanism generally
+ accepted in the software development community for the electronic
+ transfer of data.
+
+ 1.5. "Executable" means Covered Code in any form other than Source
+ Code.
+
+ 1.6. "Initial Developer" means the individual or entity identified
+ as the Initial Developer in the Source Code notice required by Exhibit
+ A.
+
+ 1.7. "Larger Work" means a work which combines Covered Code or
+ portions thereof with code not governed by the terms of this License.
+
+ 1.8. "License" means this document.
+
+ 1.8.1. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed herein.
+
+ 1.9. "Modifications" means any addition to or deletion from the
+ substance or structure of either the Original Code or any previous
+ Modifications. When Covered Code is released as a series of files, a
+ Modification is:
+ A. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+
+ B. Any new file that contains any part of the Original Code or
+ previous Modifications.
+
+ 1.10. "Original Code" means Source Code of computer software code
+ which is described in the Source Code notice required by Exhibit A as
+ Original Code, and which, at the time of its release under this
+ License is not already Covered Code governed by this License.
+
+ 1.10.1. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by grantor.
+
+ 1.11. "Source Code" means the preferred form of the Covered Code for
+ making modifications to it, including all modules it contains, plus
+ any associated interface definition files, scripts used to control
+ compilation and installation of an Executable, or source code
+ differential comparisons against either the Original Code or another
+ well known, available Covered Code of the Contributor's choice. The
+ Source Code can be in a compressed or archival form, provided the
+ appropriate decompression or de-archiving software is widely available
+ for no charge.
+
+ 1.12. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms of, this
+ License or a future version of this License issued under Section 6.1.
+ For legal entities, "You" includes any entity which controls, is
+ controlled by, or is under common control with You. For purposes of
+ this definition, "control" means (a) the power, direct or indirect,
+ to cause the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty percent
+ (50%) of the outstanding shares or beneficial ownership of such
+ entity.
+
+2. Source Code License.
+
+ 2.1. The Initial Developer Grant.
+ The Initial Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license, subject to third party intellectual property
+ claims:
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer to use, reproduce,
+ modify, display, perform, sublicense and distribute the Original
+ Code (or portions thereof) with or without Modifications, and/or
+ as part of a Larger Work; and
+
+ (b) under Patents Claims infringed by the making, using or
+ selling of Original Code, to make, have made, use, practice,
+ sell, and offer for sale, and/or otherwise dispose of the
+ Original Code (or portions thereof).
+
+ (c) the licenses granted in this Section 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ Original Code under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: 1) for code that You delete from the Original Code; 2)
+ separate from the Original Code; or 3) for infringements caused
+ by: i) the modification of the Original Code or ii) the
+ combination of the Original Code with other software or devices.
+
+ 2.2. Contributor Grant.
+ Subject to third party intellectual property claims, each Contributor
+ hereby grants You a world-wide, royalty-free, non-exclusive license
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor, to use, reproduce, modify,
+ display, perform, sublicense and distribute the Modifications
+ created by such Contributor (or portions thereof) either on an
+ unmodified basis, with other Modifications, as Covered Code
+ and/or as part of a Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either alone
+ and/or in combination with its Contributor Version (or portions
+ of such combination), to make, use, sell, offer for sale, have
+ made, and/or otherwise dispose of: 1) Modifications made by that
+ Contributor (or portions thereof); and 2) the combination of
+ Modifications made by that Contributor with its Contributor
+ Version (or portions of such combination).
+
+ (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first makes Commercial Use of
+ the Covered Code.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: 1) for any code that Contributor has deleted from the
+ Contributor Version; 2) separate from the Contributor Version;
+ 3) for infringements caused by: i) third party modifications of
+ Contributor Version or ii) the combination of Modifications made
+ by that Contributor with other software (except as part of the
+ Contributor Version) or other devices; or 4) under Patent Claims
+ infringed by Covered Code in the absence of Modifications made by
+ that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Application of License.
+ The Modifications which You create or to which You contribute are
+ governed by the terms of this License, including without limitation
+ Section 2.2. The Source Code version of Covered Code may be
+ distributed only under the terms of this License or a future version
+ of this License released under Section 6.1, and You must include a
+ copy of this License with every copy of the Source Code You
+ distribute. You may not offer or impose any terms on any Source Code
+ version that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. However, You may include
+ an additional document offering the additional rights described in
+ Section 3.5.
+
+ 3.2. Availability of Source Code.
+ Any Modification which You create or to which You contribute must be
+ made available in Source Code form under the terms of this License
+ either on the same media as an Executable version or via an accepted
+ Electronic Distribution Mechanism to anyone to whom you made an
+ Executable version available; and if made available via Electronic
+ Distribution Mechanism, must remain available for at least twelve (12)
+ months after the date it initially became available, or at least six
+ (6) months after a subsequent version of that particular Modification
+ has been made available to such recipients. You are responsible for
+ ensuring that the Source Code version remains available even if the
+ Electronic Distribution Mechanism is maintained by a third party.
+
+ 3.3. Description of Modifications.
+ You must cause all Covered Code to which You contribute to contain a
+ file documenting the changes You made to create that Covered Code and
+ the date of any change. You must include a prominent statement that
+ the Modification is derived, directly or indirectly, from Original
+ Code provided by the Initial Developer and including the name of the
+ Initial Developer in (a) the Source Code, and (b) in any notice in an
+ Executable version or related documentation in which You describe the
+ origin or ownership of the Covered Code.
+
+ 3.4. Intellectual Property Matters
+ (a) Third Party Claims.
+ If Contributor has knowledge that a license under a third party's
+ intellectual property rights is required to exercise the rights
+ granted by such Contributor under Sections 2.1 or 2.2,
+ Contributor must include a text file with the Source Code
+ distribution titled "LEGAL" which describes the claim and the
+ party making the claim in sufficient detail that a recipient will
+ know whom to contact. If Contributor obtains such knowledge after
+ the Modification is made available as described in Section 3.2,
+ Contributor shall promptly modify the LEGAL file in all copies
+ Contributor makes available thereafter and shall take other steps
+ (such as notifying appropriate mailing lists or newsgroups)
+ reasonably calculated to inform those who received the Covered
+ Code that new knowledge has been obtained.
+
+ (b) Contributor APIs.
+ If Contributor's Modifications include an application programming
+ interface and Contributor has knowledge of patent licenses which
+ are reasonably necessary to implement that API, Contributor must
+ also include this information in the LEGAL file.
+
+ (c) Representations.
+ Contributor represents that, except as disclosed pursuant to
+ Section 3.4(a) above, Contributor believes that Contributor's
+ Modifications are Contributor's original creation(s) and/or
+ Contributor has sufficient rights to grant the rights conveyed by
+ this License.
+
+ 3.5. Required Notices.
+ You must duplicate the notice in Exhibit A in each file of the Source
+ Code. If it is not possible to put such notice in a particular Source
+ Code file due to its structure, then You must include such notice in a
+ location (such as a relevant directory) where a user would be likely
+ to look for such a notice. If You created one or more Modification(s)
+ You may add your name as a Contributor to the notice described in
+ Exhibit A. You must also duplicate this License in any documentation
+ for the Source Code where You describe recipients' rights or ownership
+ rights relating to Covered Code. You may choose to offer, and to
+ charge a fee for, warranty, support, indemnity or liability
+ obligations to one or more recipients of Covered Code. However, You
+ may do so only on Your own behalf, and not on behalf of the Initial
+ Developer or any Contributor. You must make it absolutely clear than
+ any such warranty, support, indemnity or liability obligation is
+ offered by You alone, and You hereby agree to indemnify the Initial
+ Developer and every Contributor for any liability incurred by the
+ Initial Developer or such Contributor as a result of warranty,
+ support, indemnity or liability terms You offer.
+
+ 3.6. Distribution of Executable Versions.
+ You may distribute Covered Code in Executable form only if the
+ requirements of Section 3.1-3.5 have been met for that Covered Code,
+ and if You include a notice stating that the Source Code version of
+ the Covered Code is available under the terms of this License,
+ including a description of how and where You have fulfilled the
+ obligations of Section 3.2. The notice must be conspicuously included
+ in any notice in an Executable version, related documentation or
+ collateral in which You describe recipients' rights relating to the
+ Covered Code. You may distribute the Executable version of Covered
+ Code or ownership rights under a license of Your choice, which may
+ contain terms different from this License, provided that You are in
+ compliance with the terms of this License and that the license for the
+ Executable version does not attempt to limit or alter the recipient's
+ rights in the Source Code version from the rights set forth in this
+ License. If You distribute the Executable version under a different
+ license You must make it absolutely clear that any terms which differ
+ from this License are offered by You alone, not by the Initial
+ Developer or any Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred by
+ the Initial Developer or such Contributor as a result of any such
+ terms You offer.
+
+ 3.7. Larger Works.
+ You may create a Larger Work by combining Covered Code with other code
+ not governed by the terms of this License and distribute the Larger
+ Work as a single product. In such a case, You must make sure the
+ requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+ If it is impossible for You to comply with any of the terms of this
+ License with respect to some or all of the Covered Code due to
+ statute, judicial order, or regulation then You must: (a) comply with
+ the terms of this License to the maximum extent possible; and (b)
+ describe the limitations and the code they affect. Such description
+ must be included in the LEGAL file described in Section 3.4 and must
+ be included with all distributions of the Source Code. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Application of this License.
+
+ This License applies to code to which the Initial Developer has
+ attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+ 6.1. New Versions.
+ Netscape Communications Corporation ("Netscape") may publish revised
+ and/or new versions of the License from time to time. Each version
+ will be given a distinguishing version number.
+
+ 6.2. Effect of New Versions.
+ Once Covered Code has been published under a particular version of the
+ License, You may always continue to use it under the terms of that
+ version. You may also choose to use such Covered Code under the terms
+ of any subsequent version of the License published by Netscape. No one
+ other than Netscape has the right to modify the terms applicable to
+ Covered Code created under this License.
+
+ 6.3. Derivative Works.
+ If You create or use a modified version of this License (which you may
+ only do in order to apply it to code which is not already Covered Code
+ governed by this License), You must (a) rename Your license so that
+ the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+ "MPL", "NPL" or any confusingly similar phrase do not appear in your
+ license (except to note that your license differs from this License)
+ and (b) otherwise make it clear that Your version of the license
+ contains terms which differ from the Mozilla Public License and
+ Netscape Public License. (Filling in the name of the Initial
+ Developer, Original Code or Contributor in the notice described in
+ Exhibit A shall not of themselves be deemed to be modifications of
+ this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+ COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+ DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+ THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+ IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+ YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+ COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+ OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+ 8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to cure
+ such breach within 30 days of becoming aware of the breach. All
+ sublicenses to the Covered Code which are properly granted shall
+ survive any termination of this License. Provisions which, by their
+ nature, must remain in effect beyond the termination of this License
+ shall survive.
+
+ 8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declatory judgment actions) against Initial Developer
+ or a Contributor (the Initial Developer or Contributor against whom
+ You file such action is referred to as "Participant") alleging that:
+
+ (a) such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by such
+ Participant to You under Sections 2.1 and/or 2.2 of this License
+ shall, upon 60 days notice from Participant terminate prospectively,
+ unless if within 60 days after receipt of notice You either: (i)
+ agree in writing to pay Participant a mutually agreeable reasonable
+ royalty for Your past and future use of Modifications made by such
+ Participant, or (ii) withdraw Your litigation claim with respect to
+ the Contributor Version against such Participant. If within 60 days
+ of notice, a reasonable royalty and payment arrangement are not
+ mutually agreed upon in writing by the parties or the litigation claim
+ is not withdrawn, the rights granted by Participant to You under
+ Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+ the 60 day notice period specified above.
+
+ (b) any software, hardware, or device, other than such Participant's
+ Contributor Version, directly or indirectly infringes any patent, then
+ any rights granted to You by such Participant under Sections 2.1(b)
+ and 2.2(b) are revoked effective as of the date You first made, used,
+ sold, distributed, or had made, Modifications made by that
+ Participant.
+
+ 8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly or
+ indirectly infringes any patent where such claim is resolved (such as
+ by license or settlement) prior to the initiation of patent
+ infringement litigation, then the reasonable value of the licenses
+ granted by such Participant under Sections 2.1 or 2.2 shall be taken
+ into account in determining the amount or value of any payment or
+ license.
+
+ 8.4. In the event of termination under Sections 8.1 or 8.2 above,
+ all end user license agreements (excluding distributors and resellers)
+ which have been validly granted by You or any distributor hereunder
+ prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+ DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+ OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+ ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+ CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+ WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+ RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+ PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+ EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+ THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+ The Covered Code is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" and "commercial computer software documentation," as such
+ terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+ C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+ all U.S. Government End Users acquire Covered Code with only those
+ rights set forth herein.
+
+11. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed by
+ California law provisions (except to the extent applicable law, if
+ any, provides otherwise), excluding its conflict-of-law provisions.
+ With respect to disputes in which at least one party is a citizen of,
+ or an entity chartered or registered to do business in the United
+ States of America, any litigation relating to this License shall be
+ subject to the jurisdiction of the Federal Courts of the Northern
+ District of California, with venue lying in Santa Clara County,
+ California, with the losing party responsible for costs, including
+ without limitation, court costs and reasonable attorneys' fees and
+ expenses. The application of the United Nations Convention on
+ Contracts for the International Sale of Goods is expressly excluded.
+ Any law or regulation which provides that the language of a contract
+ shall be construed against the drafter shall not apply to this
+ License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly,
+ out of its utilization of rights under this License and You agree to
+ work with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+ Initial Developer may designate portions of the Covered Code as
+ "Multiple-Licensed". "Multiple-Licensed" means that the Initial
+ Developer permits you to utilize portions of the Covered Code under
+ Your choice of the NPL or the alternative licenses, if any, specified
+ by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+ ``The contents of this file are subject to the Mozilla Public License
+ Version 1.1 (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.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ License for the specific language governing rights and limitations
+ under the License.
+
+ The Original Code is ______________________________________.
+
+ The Initial Developer of the Original Code is ________________________.
+ Portions created by ______________________ are Copyright (C) ______
+ _______________________. All Rights Reserved.
+
+ Contributor(s): ______________________________________.
+
+ Alternatively, the contents of this file may be used under the terms
+ of the _____ license (the "[___] License"), in which case the
+ provisions of [______] License are applicable instead of those
+ above. If you wish to allow use of your version of this file only
+ under the terms of the [____] License and not to allow others to use
+ your version of this file under the MPL, indicate your decision by
+ deleting the provisions above and replace them with the notice and
+ other provisions required by the [___] License. If you do not delete
+ the provisions above, a recipient may use your version of this file
+ under either the MPL or the [___] License."
+
+ [NOTE: The text of this Exhibit A may differ slightly from the text of
+ the notices in the Source Code files of the Original Code. You should
+ use the text of this Exhibit A rather than the text found in the
+ Original Code Source Code for Your Modifications.]
+
+----
+
+
+[[antlr]]
+antlr
+
+* antlr:antlr27
+* antlr:java-runtime
+* antlr:stringtemplate
+* antlr:tool
+
+[[antlr_license]]
+----
+Copyright (c) 2003-2008, Terence Parr
+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 the author 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.
+
+----
+
+
+[[args4j]]
+args4j
+
+* args4j
+
+[[args4j_license]]
+----
+Copyright (c) 2013 Kohsuke Kawaguchi and other contributors
+
+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.
+
+----
+
+
+[[autolink]]
+autolink
+
+* autolink
+
+[[autolink_license]]
+----
+The MIT License (MIT)
+
+Copyright (c) 2015 Robin Stocker
+
+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.
+
+----
+
+
+[[automaton]]
+automaton
+
+* automaton
+
+[[automaton_license]]
+----
+Copyright (c) 2001-2011 Anders Moeller
+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 the JSR305 expert group 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.
+
+----
+
+
+[[ba-linkify]]
+ba-linkify
+
+* js:ba-linkify
+
+[[ba-linkify_license]]
+----
+Copyright (c) 2009 "Cowboy" Ben Alman
+
+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.
+
+----
+
+
+[[bouncycastle]]
+bouncycastle
+
+* bouncycastle:bcpg-neverlink
+* bouncycastle:bcpkix-neverlink
+* bouncycastle:bcprov-neverlink
+
+[[bouncycastle_license]]
+----
+Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc.
+(http://www.bouncycastle.org)
+
+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.
+
+----
+
+
+[[elasticsearch]]
+elasticsearch
+
+* elasticsearch-rest-client:elasticsearch-rest-client
+
+[[elasticsearch_license]]
+----
+Elasticsearch
+Copyright 2009-2015 Elasticsearch
+
+This product includes software developed by The Apache Software
+Foundation (http://www.apache.org/).
+
+----
+
+
+[[es6-promise]]
+es6-promise
+
+* js:es6-promise
+
+[[es6-promise_license]]
+----
+Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
+
+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.
+
+----
+
+
+[[fetch]]
+fetch
+
+* js:fetch
+
+[[fetch_license]]
+----
+Copyright (c) 2014-2016 GitHub, Inc.
+
+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.
+
+----
+
+
+[[flexmark]]
+flexmark
+
+* flexmark
+* flexmark-ext-abbreviation
+* flexmark-ext-anchorlink
+* flexmark-ext-autolink
+* flexmark-ext-definition
+* flexmark-ext-emoji
+* flexmark-ext-escaped-character
+* flexmark-ext-footnotes
+* flexmark-ext-gfm-issues
+* flexmark-ext-gfm-strikethrough
+* flexmark-ext-gfm-tables
+* flexmark-ext-gfm-tasklist
+* flexmark-ext-gfm-users
+* flexmark-ext-ins
+* flexmark-ext-jekyll-front-matter
+* flexmark-ext-superscript
+* flexmark-ext-tables
+* flexmark-ext-toc
+* flexmark-ext-typographic
+* flexmark-ext-wikilink
+* flexmark-ext-yaml-front-matter
+* flexmark-formatter
+* flexmark-html-parser
+* flexmark-profile-pegdown
+* flexmark-util
+
+[[flexmark_license]]
+----
+Copyright (c) 2015-2016, Atlassian Pty Ltd
+All rights reserved.
+
+Copyright (c) 2016, Vladimir Schneider,
+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.
+
+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 HOLDER 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.
+
+----
+
+
+[[h2]]
+h2
+
+* h2
+
+[[h2_license]]
+----
+H2 is dual licensed and available under a modified version of the
+MPL 1.1 (Mozilla Public License) or under the (unmodified) EPL 1.0.
+----
+
+link:http://www.h2database.com/html/license.html[H2 License]
+
+----
+H2 License - Version 1.0
+1. Definitions
+
+1.0.1. "Commercial Use" means distribution or otherwise making the
+ Covered Code available to a third party.
+
+1.1. "Contributor" means each entity that creates or contributes
+ to the creation of Modifications.
+
+1.2. "Contributor Version" means the combination of the Original
+ Code, prior Modifications used by a Contributor, and the
+ Modifications made by that particular Contributor.
+
+1.3. "Covered Code" means the Original Code or Modifications or
+ the combination of the Original Code and Modifications, in each
+ case including portions thereof.
+
+1.4. "Electronic Distribution Mechanism" means a mechanism generally
+ accepted in the software development community for the electronic
+ transfer of data.
+
+1.5. "Executable" means Covered Code in any form other than Source Code.
+
+1.6. "Initial Developer" means the individual or entity identified
+ as the Initial Developer in the Source Code notice required
+ by Exhibit A.
+
+1.7. "Larger Work" means a work which combines Covered Code or
+ portions thereof with code not governed by the terms of this
+ License.
+
+1.8. "License" means this document.
+
+1.8.1. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant
+ or subsequently acquired, any and all of the rights conveyed
+ herein.
+
+1.9. "Modifications" means any addition to or deletion from the
+ substance or structure of either the Original Code or any
+ previous Modifications. When Covered Code is released as a
+ series of files, a Modification is:
+
+1.9.a. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+
+1.9.b. Any new file that contains any part of the Original Code or
+ previous Modifications.
+
+1.10. "Original Code" means Source Code of computer software
+ code which is described in the Source Code notice required
+ by Exhibit A as Original Code, and which, at the time of
+ its release under this License is not already Covered Code
+ governed by this License.
+
+1.10.1. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method,
+ process, and apparatus claims, in any patent Licensable
+ by grantor.
+
+1.11. "Source Code" means the preferred form of the Covered Code
+ for making modifications to it, including all modules it
+ contains, plus any associated interface definition files,
+ scripts used to control compilation and installation of an
+ Executable, or source code differential comparisons against
+ either the Original Code or another well known, available
+ Covered Code of the Contributor's choice. The Source Code can
+ be in a compressed or archival form, provided the appropriate
+ decompression or de-archiving software is widely available
+ for no charge.
+
+1.12. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms
+ of, this License or a future version of this License issued
+ under Section 6.1. For legal entities, "You" includes any
+ entity which controls, is controlled by, or is under common
+ control with You. For purposes of this definition, "control"
+ means (a) the power, direct or indirect, to cause the direction
+ or management of such entity, whether by contract or otherwise,
+ or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+2. Source Code License
+
+2.1. The Initial Developer Grant
+
+The Initial Developer hereby grants You a world-wide, royalty-free,
+non-exclusive license, subject to third party intellectual property
+claims:
+
+2.1.a. under intellectual property rights (other than patent
+ or trademark) Licensable by Initial Developer to use,
+ reproduce, modify, display, perform, sublicense and distribute
+ the Original Code (or portions thereof) with or without
+ Modifications, and/or as part of a Larger Work; and
+
+2.1.b. under Patents Claims infringed by the making, using or selling
+ of Original Code, to make, have made, use, practice, sell,
+ and offer for sale, and/or otherwise dispose of the Original
+ Code (or portions thereof).
+
+2.1.c. the licenses granted in this Section 2.1 (a) and (b) are
+ effective on the date Initial Developer first distributes
+ Original Code under the terms of this License.
+
+2.1.d. Notwithstanding Section 2.1 (b) above, no patent license is
+ granted: 1) for code that You delete from the Original Code;
+ 2) separate from the Original Code; or 3) for infringements
+ caused by: i) the modification of the Original Code or ii)
+ the combination of the Original Code with other software
+ or devices.
+
+2.2. Contributor Grant
+
+Subject to third party intellectual property claims, each Contributor
+hereby grants You a world-wide, royalty-free, non-exclusive license
+
+2.2.a. under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor, to use, reproduce,
+ modify, display, perform, sublicense and distribute the
+ Modifications created by such Contributor (or portions
+ thereof) either on an unmodified basis, with other
+ Modifications, as Covered Code and/or as part of a Larger
+ Work; and
+
+2.2.b. under Patent Claims infringed by the making, using, or selling
+ of Modifications made by that Contributor either alone and/or
+ in combination with its Contributor Version (or portions
+ of such combination), to make, use, sell, offer for sale,
+ have made, and/or otherwise dispose of: 1) Modifications
+ made by that Contributor (or portions thereof); and 2) the
+ combination of Modifications made by that Contributor with
+ its Contributor Version (or portions of such combination).
+
+2.2.c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are
+ effective on the date Contributor first makes Commercial
+ Use of the Covered Code.
+
+2.2.c. Notwithstanding Section 2.2 (b) above, no patent license is
+ granted: 1) for any code that Contributor has deleted from
+ the Contributor Version; 2) separate from the Contributor
+ Version; 3) for infringements caused by: i) third party
+ modifications of Contributor Version or ii) the combination
+ of Modifications made by that Contributor with other software
+ (except as part of the Contributor Version) or other devices;
+ or 4) under Patent Claims infringed by Covered Code in the
+ absence of Modifications made by that Contributor.
+
+3. Distribution Obligations
+
+3.1. Application of License
+
+The Modifications which You create or to which You contribute
+are governed by the terms of this License, including without
+limitation Section 2.2. The Source Code version of Covered Code may
+be distributed only under the terms of this License or a future
+version of this License released under Section 6.1, and You must
+include a copy of this License with every copy of the Source Code
+You distribute. You may not offer or impose any terms on any Source
+Code version that alters or restricts the applicable version of
+this License or the recipients' rights hereunder. However, You
+may include an additional document offering the additional rights
+described in Section 3.5.
+
+3.2. Availability of Source Code
+
+Any Modification which You create or to which You contribute must
+be made available in Source Code form under the terms of this
+License either on the same media as an Executable version or via
+an accepted Electronic Distribution Mechanism to anyone to whom
+you made an Executable version available; and if made available
+via Electronic Distribution Mechanism, must remain available for
+at least twelve (12) months after the date it initially became
+available, or at least six (6) months after a subsequent version
+of that particular Modification has been made available to such
+recipients. You are responsible for ensuring that the Source Code
+version remains available even if the Electronic Distribution
+Mechanism is maintained by a third party.
+
+3.3. Description of Modifications
+
+You must cause all Covered Code to which You contribute to contain
+a file documenting the changes You made to create that Covered
+Code and the date of any change. You must include a prominent
+statement that the Modification is derived, directly or indirectly,
+from Original Code provided by the Initial Developer and including
+the name of the Initial Developer in (a) the Source Code, and (b)
+in any notice in an Executable version or related documentation in
+which You describe the origin or ownership of the Covered Code.
+
+3.4. Intellectual Property Matters
+
+3.4.a. Third Party Claims: If Contributor has knowledge that
+ a license under a third party's intellectual property
+ rights is required to exercise the rights granted by such
+ Contributor under Sections 2.1 or 2.2, Contributor must
+ include a text file with the Source Code distribution titled
+ "LEGAL" which describes the claim and the party making the
+ claim in sufficient detail that a recipient will know whom
+ to contact. If Contributor obtains such knowledge after the
+ Modification is made available as described in Section 3.2,
+ Contributor shall promptly modify the LEGAL file in all
+ copies Contributor makes available thereafter and shall take
+ other steps (such as notifying appropriate mailing lists or
+ newsgroups) reasonably calculated to inform those who received
+ the Covered Code that new knowledge has been obtained.
+
+3.4.b. Contributor APIs: If Contributor's Modifications include
+ an application programming interface and Contributor has
+ knowledge of patent licenses which are reasonably necessary
+ to implement that API, Contributor must also include this
+ information in the legal file.
+
+3.4.c. Representations: Contributor represents that, except as
+ disclosed pursuant to Section 3.4 (a) above, Contributor
+ believes that Contributor's Modifications are Contributor's
+ original creation(s) and/or Contributor has sufficient rights
+ to grant the rights conveyed by this License.
+
+3.5. Required Notices
+
+You must duplicate the notice in Exhibit A in each file of
+the Source Code. If it is not possible to put such notice in a
+particular Source Code file due to its structure, then You must
+include such notice in a location (such as a relevant directory)
+where a user would be likely to look for such a notice. If You
+created one or more Modification(s) You may add your name as a
+Contributor to the notice described in Exhibit A. You must also
+duplicate this License in any documentation for the Source Code
+where You describe recipients' rights or ownership rights relating
+to Covered Code. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations to one or
+more recipients of Covered Code. However, You may do so only on
+Your own behalf, and not on behalf of the Initial Developer or
+any Contributor. You must make it absolutely clear than any such
+warranty, support, indemnity or liability obligation is offered by
+You alone, and You hereby agree to indemnify the Initial Developer
+and every Contributor for any liability incurred by the Initial
+Developer or such Contributor as a result of warranty, support,
+indemnity or liability terms You offer.
+
+3.6. Distribution of Executable Versions
+
+You may distribute Covered Code in Executable form only if the
+requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met
+for that Covered Code, and if You include a notice stating that
+the Source Code version of the Covered Code is available under the
+terms of this License, including a description of how and where
+You have fulfilled the obligations of Section 3.2. The notice
+must be conspicuously included in any notice in an Executable
+version, related documentation or collateral in which You describe
+recipients' rights relating to the Covered Code. You may distribute
+the Executable version of Covered Code or ownership rights under
+a license of Your choice, which may contain terms different from
+this License, provided that You are in compliance with the terms
+of this License and that the license for the Executable version
+does not attempt to limit or alter the recipient's rights in the
+Source Code version from the rights set forth in this License. If
+You distribute the Executable version under a different license You
+must make it absolutely clear that any terms which differ from this
+License are offered by You alone, not by the Initial Developer or any
+Contributor. You hereby agree to indemnify the Initial Developer and
+every Contributor for any liability incurred by the Initial Developer
+or such Contributor as a result of any such terms You offer.
+
+3.7. Larger Works
+
+You may create a Larger Work by combining Covered Code with other
+code not governed by the terms of this License and distribute the
+Larger Work as a single product. In such a case, You must make sure
+the requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+If it is impossible for You to comply with any of the terms of
+this License with respect to some or all of the Covered Code due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description
+must be included in the legal file described in Section 3.4 and
+must be included with all distributions of the Source Code. Except
+to the extent prohibited by statute or regulation, such description
+must be sufficiently detailed for a recipient of ordinary skill to
+be able to understand it.
+
+5. Application of this License.
+
+This License applies to code to which the Initial Developer has
+attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+6.1. New Versions
+
+The H2 Group may publish revised and/or new versions of the License
+from time to time. Each version will be given a distinguishing
+version number.
+
+6.2. Effect of New Versions
+
+Once Covered Code has been published under a particular version of
+the License, You may always continue to use it under the terms of
+that version. You may also choose to use such Covered Code under the
+terms of any subsequent version of the License published by the H2
+Group. No one other than the H2 Group has the right to modify the
+terms applicable to Covered Code created under this License.
+
+6.3. Derivative Works
+
+If You create or use a modified version of this License (which you
+may only do in order to apply it to code which is not already Covered
+Code governed by this License), You must (a) rename Your license so
+that the phrases "H2 Group", "H2" or any confusingly similar phrase
+do not appear in your license (except to note that your license
+differs from this License) and (b) otherwise make it clear that
+Your version of the license contains terms which differ from the
+H2 License. (Filling in the name of the Initial Developer, Original
+Code or Contributor in the notice described in Exhibit A shall not
+of themselves be deemed to be modifications of this License.)
+
+7. Disclaimer of Warranty
+
+Covered code is provided under this license on an "as is" basis,
+without warranty of any kind, either expressed or implied,
+including, without limitation, warranties that the covered code
+is free of defects, merchantable, fit for a particular purpose or
+non-infringing. The entire risk as to the quality and performance
+of the covered code is with you. Should any covered code prove
+defective in any respect, you (not the initial developer or any
+other contributor) assume the cost of any necessary servicing,
+repair or correction. This disclaimer of warranty constitutes
+an essential part of this license. No use of any covered code is
+authorized hereunder except under this disclaimer.
+
+8. Termination
+
+8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and
+ fail to cure such breach within 30 days of becoming aware
+ of the breach. All sublicenses to the Covered Code which
+ are properly granted shall survive any termination of this
+ License. Provisions which, by their nature, must remain in
+ effect beyond the termination of this License shall survive.
+
+8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declaratory judgment actions) against
+ Initial Developer or a Contributor (the Initial Developer or
+ Contributor against whom You file such action is referred to as
+ "Participant") alleging that:
+
+8.2.a. such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by
+ such Participant to You under Sections 2.1 and/or 2.2 of this
+ License shall, upon 60 days notice from Participant terminate
+ prospectively, unless if within 60 days after receipt of
+ notice You either: (i) agree in writing to pay Participant
+ a mutually agreeable reasonable royalty for Your past and
+ future use of Modifications made by such Participant, or (ii)
+ withdraw Your litigation claim with respect to the Contributor
+ Version against such Participant. If within 60 days of notice,
+ a reasonable royalty and payment arrangement are not mutually
+ agreed upon in writing by the parties or the litigation claim
+ is not withdrawn, the rights granted by Participant to You
+ under Sections 2.1 and/or 2.2 automatically terminate at
+ the expiration of the 60 day notice period specified above.
+
+8.2.b. any software, hardware, or device, other than such
+ Participant's Contributor Version, directly or indirectly
+ infringes any patent, then any rights granted to You by
+ such Participant under Sections 2.1(b) and 2.2(b) are
+ revoked effective as of the date You first made, used,
+ sold, distributed, or had made, Modifications made by that
+ Participant.
+
+8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly
+ or indirectly infringes any patent where such claim is resolved
+ (such as by license or settlement) prior to the initiation of
+ patent infringement litigation, then the reasonable value of
+ the licenses granted by such Participant under Sections 2.1
+ or 2.2 shall be taken into account in determining the amount
+ or value of any payment or license.
+
+8.4. In the event of termination under Sections 8.1 or 8.2 above,
+ all end user license agreements (excluding distributors and
+ resellers) which have been validly granted by You or any
+ distributor hereunder prior to termination shall survive
+ termination.
+
+9. Limitation of Liability
+
+Under no circumstances and under no legal theory, whether tort
+(including negligence), contract, or otherwise, shall you, the
+initial developer, any other contributor, or any distributor of
+covered code, or any supplier of any of such parties, be liable to
+any person for any indirect, special, incidental, or consequential
+damages of any character including, without limitation, damages for
+loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses, even if such party
+shall have been informed of the possibility of such damages. This
+limitation of liability shall not apply to liability for death or
+personal injury resulting from such party's negligence to the extent
+applicable law prohibits such limitation. Some jurisdictions do not
+allow the exclusion or limitation of incidental or consequential
+damages, so this exclusion and limitation may not apply to you.
+
+10. United States Government End Users
+
+The Covered Code is a "commercial item", as that term is defined in
+48 C.F.R. 2.101 (October 1995), consisting of "commercial computer
+software" and "commercial computer software documentation", as such
+terms are used in 48 C.F.R. 12.212 (September 1995). Consistent
+with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4
+(June 1995), all U.S. Government End Users acquire Covered Code
+with only those rights set forth herein.
+
+11. Miscellaneous
+
+This License represents the complete agreement concerning subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. This License shall be governed
+by California law provisions (except to the extent applicable
+law, if any, provides otherwise), excluding its conflict-of-law
+provisions. With respect to disputes in which at least one party is
+a citizen of, or an entity chartered or registered to do business in
+United States of America, any litigation relating to this License
+shall be subject to the jurisdiction of the Federal Courts of the
+Northern District of California, with venue lying in Santa Clara
+County, California, with the losing party responsible for costs,
+including without limitation, court costs and reasonable attorneys'
+fees and expenses. The application of the United Nations Convention
+on Contracts for the International Sale of Goods is expressly
+excluded. Any law or regulation which provides that the language of
+a contract shall be construed against the drafter shall not apply
+to this License.
+
+12. Responsibility for Claims
+
+As between Initial Developer and the Contributors, each party is
+responsible for claims and damages arising, directly or indirectly,
+out of its utilization of rights under this License and You agree
+to work with Initial Developer and Contributors to distribute such
+responsibility on an equitable basis. Nothing herein is intended
+or shall be deemed to constitute any admission of liability.
+
+13. Multiple-Licensed Code
+
+Initial Developer may designate portions of the Covered Code as
+"Multiple-Licensed". "Multiple-Licensed" means that the Initial
+Developer permits you to utilize portions of the Covered Code under
+Your choice of this or the alternative licenses, if any, specified
+by the Initial Developer in the file described in Exhibit A.
+
+Exhibit A
+
+Multiple-Licensed under the H2 License, Version 1.0,
+and under the Eclipse Public License, Version 1.0
+(http://h2database.com/html/license.html).
+Initial Developer: H2 Group
+----
+
+----
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+ documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from
+and are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program
+by such Contributor itself or anyone acting on such Contributor's
+behalf. Contributions do not include additions to the Program which:
+(i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are
+not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with
+this Agreement.
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare derivative works of, publicly display,
+ publicly perform, distribute and sublicense the Contribution of such
+ Contributor, if any, and such derivative works, in source code and
+ object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor,
+ if any, in source code and object code form. This patent license
+ shall apply to the combination of the Contribution and the Program
+ if, at the time the Contribution is added by the Contributor, such
+ addition of the Contribution causes such combination to be covered
+ by the Licensed Patents. The patent license shall not apply to any
+ other combinations which include the Contribution. No hardware per
+ se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe
+ the patent or other intellectual property rights of any other
+ entity. Each Contributor disclaims any liability to Recipient
+ for claims brought by any other entity based on infringement
+ of intellectual property rights or otherwise. As a condition to
+ exercising the rights and licenses granted hereunder, each Recipient
+ hereby assumes sole responsibility to secure any other intellectual
+ property rights needed, if any. For example, if a third party patent
+ license is required to allow Recipient to distribute the Program,
+ it is Recipient's responsibility to acquire that license before
+ distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant
+ the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code
+ form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+ and conditions, express and implied, including warranties or
+ conditions of title and non-infringement, and implied warranties or
+ conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability
+ for damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement
+ are offered by that Contributor alone and not by any other
+ party; and
+
+iv) states that source code for the Program is available from such
+ Contributor, and informs licensees how to obtain it in a reasonable
+ manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial
+use of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for other Contributors. Therefore, if a
+Contributor includes the Program in a commercial product offering,
+such Contributor ("Commercial Contributor") hereby agrees to defend
+and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising
+from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by
+the acts or omissions of such Commercial Contributor in connection
+with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must:
+a) promptly notify the Commercial Contributor in writing of such
+claim, and b) allow the Commercial Contributor to control, and
+cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a
+commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes
+performance claims, or offers warranties related to Product X, those
+performance claims and warranties are such Commercial Contributor's
+responsibility alone. Under this section, the Commercial Contributor
+would have to defend claims against the other Contributors related
+to those performance claims and warranties, and if a court requires
+any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with
+its exercise of rights under this Agreement , including but not
+limited to the risks and costs of program errors, compliance with
+applicable laws, damage to or loss of data, programs or equipment,
+and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY
+RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed
+to the minimum extent necessary to make such provision valid and
+enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging
+that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s),
+then such Recipient's rights granted under Section 2(b) shall
+terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if
+it fails to comply with any of the material terms or conditions
+of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all
+Recipient's rights under this Agreement terminate, Recipient agrees
+to cease use and distribution of the Program as soon as reasonably
+practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall
+continue and survive.
+
+Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions
+(including revisions) of this Agreement from time to time. No
+one other than the Agreement Steward has the right to modify
+this Agreement. The Eclipse Foundation is the initial Agreement
+Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each
+new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it
+was received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under
+this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each
+party waives its rights to a jury trial in any resulting litigation.
+----
+
+----
+Export Control Classification Number (ECCN)
+
+As far as we know, the U.S. Export Control Classification Number
+(ECCN) for this software is 5D002. However, for legal reasons, we
+can make no warranty that this information is correct. For details,
+see also the Apache Software Foundation Export Classifications page.
+
+----
+
+
+[[highlightjs]]
+highlightjs
+
+* js:highlightjs
+* js:highlightjs_files
+
+[[highlightjs_license]]
+----
+Copyright (c) 2006, Ivan Sagalaev
+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 highlight.js 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 REGENTS 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 REGENTS AND 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.
+----
+
+
+[[icu4j]]
+icu4j
+
+* icu4j
+
+[[icu4j_license]]
+----
+COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+
+Copyright © 1991-2016 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in http://www.unicode.org/copyright.html
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+
+---------------------
+
+Third-Party Software Licenses
+
+This section contains third-party software notices and/or additional
+terms for licensed third-party software components included within ICU
+libraries.
+
+1. ICU License - ICU 1.8.1 to ICU 57.1
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2016 International Business Machines Corporation and others
+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, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+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
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the
+property of their respective owners.
+
+2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt)
+
+ # The Google Chrome software developed by Google is licensed under
+ # the BSD license. Other software included in this distribution is
+ # provided under other licenses, as set forth below.
+ #
+ # The BSD License
+ # http://opensource.org/licenses/bsd-license.php
+ # Copyright (C) 2006-2008, Google Inc.
+ #
+ # 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 word list in cjdict.txt are generated by combining three word lists
+ # listed below with further processing for compound word breaking. The
+ # frequency is generated with an iterative training against Google web
+ # corpora.
+ #
+ # * Libtabe (Chinese)
+ # - https://sourceforge.net/project/?group_id=1519
+ # - Its license terms and conditions are shown below.
+ #
+ # * IPADIC (Japanese)
+ # - http://chasen.aist-nara.ac.jp/chasen/distribution.html
+ # - Its license terms and conditions are shown below.
+ #
+ # ---------COPYING.libtabe ---- BEGIN--------------------
+ #
+ # /*
+ # * Copyrighy (c) 1999 TaBE Project.
+ # * Copyright (c) 1999 Pai-Hsiang Hsiao.
+ # * 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 the TaBE Project 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
+ # * REGENTS 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.
+ # */
+ #
+ # /*
+ # * Copyright (c) 1999 Computer Systems and Communication Lab,
+ # * Institute of Information Science, Academia
+ # * Sinica. 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 the Computer Systems and Communication Lab
+ # * 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
+ # * REGENTS 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.
+ # */
+ #
+ # Copyright 1996 Chih-Hao Tsai @ Beckman Institute,
+ # University of Illinois
+ # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4
+ #
+ # ---------------COPYING.libtabe-----END--------------------------------
+ #
+ #
+ # ---------------COPYING.ipadic-----BEGIN-------------------------------
+ #
+ # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
+ # and Technology. All Rights Reserved.
+ #
+ # Use, reproduction, and distribution of this software is permitted.
+ # Any copy of this software, whether in its original form or modified,
+ # must include both the above copyright notice and the following
+ # paragraphs.
+ #
+ # Nara Institute of Science and Technology (NAIST),
+ # the copyright holders, disclaims all warranties with regard to this
+ # software, including all implied warranties of merchantability and
+ # fitness, in no event shall NAIST be liable for
+ # any special, indirect or consequential damages or any damages
+ # whatsoever resulting from loss of use, data or profits, whether in an
+ # action of contract, negligence or other tortuous action, arising out
+ # of or in connection with the use or performance of this software.
+ #
+ # A large portion of the dictionary entries
+ # originate from ICOT Free Software. The following conditions for ICOT
+ # Free Software applies to the current dictionary as well.
+ #
+ # Each User may also freely distribute the Program, whether in its
+ # original form or modified, to any third party or parties, PROVIDED
+ # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
+ # on, or be attached to, the Program, which is distributed substantially
+ # in the same form as set out herein and that such intended
+ # distribution, if actually made, will neither violate or otherwise
+ # contravene any of the laws and regulations of the countries having
+ # jurisdiction over the User or the intended distribution itself.
+ #
+ # NO WARRANTY
+ #
+ # The program was produced on an experimental basis in the course of the
+ # research and development conducted during the project and is provided
+ # to users as so produced on an experimental basis. Accordingly, the
+ # program is provided without any warranty whatsoever, whether express,
+ # implied, statutory or otherwise. The term "warranty" used herein
+ # includes, but is not limited to, any warranty of the quality,
+ # performance, merchantability and fitness for a particular purpose of
+ # the program and the nonexistence of any infringement or violation of
+ # any right of any third party.
+ #
+ # Each user of the program will agree and understand, and be deemed to
+ # have agreed and understood, that there is no warranty whatsoever for
+ # the program and, accordingly, the entire risk arising from or
+ # otherwise connected with the program is assumed by the user.
+ #
+ # Therefore, neither ICOT, the copyright holder, or any other
+ # organization that participated in or was otherwise related to the
+ # development of the program and their respective officials, directors,
+ # officers and other employees shall be held liable for any and all
+ # damages, including, without limitation, general, special, incidental
+ # and consequential damages, arising out of or otherwise in connection
+ # with the use or inability to use the program or any product, material
+ # or result produced or otherwise obtained by using the program,
+ # regardless of whether they have been advised of, or otherwise had
+ # knowledge of, the possibility of such damages at any time during the
+ # project or thereafter. Each user will be deemed to have agreed to the
+ # foregoing by his or her commencement of use of the program. The term
+ # "use" as used herein includes, but is not limited to, the use,
+ # modification, copying and distribution of the program and the
+ # production of secondary products from the program.
+ #
+ # In the case where the program, whether in its original form or
+ # modified, was distributed or delivered to or received by a user from
+ # any person, organization or entity other than ICOT, unless it makes or
+ # grants independently of ICOT any specific warranty to the user in
+ # writing, such person, organization or entity, will also be exempted
+ # from and not be held liable to the user for any such damages as noted
+ # above as far as the program is concerned.
+ #
+ # ---------------COPYING.ipadic-----END----------------------------------
+
+3. Lao Word Break Dictionary Data (laodict.txt)
+
+ # Copyright (c) 2013 International Business Machines Corporation
+ # and others. All Rights Reserved.
+ #
+ # Project: http://code.google.com/p/lao-dictionary/
+ # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt
+ # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt
+ # (copied below)
+ #
+ # This file is derived from the above dictionary, with slight
+ # modifications.
+ # ----------------------------------------------------------------------
+ # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell.
+ # 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.
+ #
+ #
+ # 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 HOLDER 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.
+ # --------------------------------------------------------------------------
+
+4. Burmese Word Break Dictionary Data (burmesedict.txt)
+
+ # Copyright (c) 2014 International Business Machines Corporation
+ # and others. All Rights Reserved.
+ #
+ # This list is part of a project hosted at:
+ # github.com/kanyawtech/myanmar-karen-word-lists
+ #
+ # --------------------------------------------------------------------------
+ # Copyright (c) 2013, LeRoy Benjamin Sharon
+ # 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 Myanmar Karen Word Lists, 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 HOLDER 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.
+ # --------------------------------------------------------------------------
+
+5. Time Zone Database
+
+ ICU uses the public domain data and code derived from Time Zone
+Database for its time zone support. The ownership of the TZ database
+is explained in BCP 175: Procedure for Maintaining the Time Zone
+Database section 7.
+
+ # 7. Database Ownership
+ #
+ # The TZ database itself is not an IETF Contribution or an IETF
+ # document. Rather it is a pre-existing and regularly updated work
+ # that is in the public domain, and is intended to remain in the
+ # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do
+ # not apply to the TZ Database or contributions that individuals make
+ # to it. Should any claims be made and substantiated against the TZ
+ # Database, the organization that is providing the IANA
+ # Considerations defined in this RFC, under the memorandum of
+ # understanding with the IETF, currently ICANN, may act in accordance
+ # with all competent court orders. No ownership claims will be made
+ # by ICANN or the IETF Trust on the database or the code. Any person
+ # making a contribution to the database or code waives all rights to
+ # future claims in that contribution or in the TZ Database.
+
+----
+
+
+[[jgit]]
+jgit
+
+* jgit/org.eclipse.jgit.archive:jgit-archive
+* jgit/org.eclipse.jgit.http.server:jgit-servlet
+* jgit/org.eclipse.jgit:jgit
+
+[[jgit_license]]
+----
+This program and the accompanying materials are made available
+under the terms of the Eclipse Distribution License v1.0 which
+accompanies this distribution, is reproduced below, and is
+available at http://www.eclipse.org/org/documents/edl-v10.php
+
+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 the Eclipse Foundation, 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.
+
+----
+
+
+[[jsch]]
+jsch
+
+* jsch
+
+[[jsch_license]]
+----
+Copyright (c) 2002-2012 Atsuhiko Yamanaka, JCraft,Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE 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.
+
+----
+
+
+[[jsoup]]
+jsoup
+
+* jsoup:jsoup
+
+[[jsoup_license]]
+----
+The MIT License
+
+© 2009-2016, Jonathan Hedley <jonathan@hedley.net>
+
+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.
+
+----
+
+
+[[moment]]
+moment
+
+* js:moment
+
+[[moment_license]]
+----
+Copyright (c) 2011-2016 Tim Wood, Iskren Chernev, Moment.js contributors
+
+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.
+
+----
+
+
+[[ow2]]
+ow2
+
+* ow2:ow2-asm
+* ow2:ow2-asm-analysis
+* ow2:ow2-asm-commons
+* ow2:ow2-asm-tree
+* ow2:ow2-asm-util
+
+[[ow2_license]]
+----
+Copyright (c) 2000-2011 INRIA, France Telecom
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holders 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.
+
+----
+
+
+[[page_js]]
+page.js
+
+* js:page
+
+[[page_js_license]]
+----
+(The MIT License)
+
+Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
+
+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.
+
+----
+
+
+[[polymer]]
+polymer
+
+* js:font-roboto-local
+* js:iron-a11y-announcer
+* js:iron-a11y-keys-behavior
+* js:iron-autogrow-textarea
+* js:iron-behaviors
+* js:iron-checked-element-behavior
+* js:iron-dropdown
+* js:iron-fit-behavior
+* js:iron-flex-layout
+* js:iron-form-element-behavior
+* js:iron-icon
+* js:iron-iconset-svg
+* js:iron-input
+* js:iron-menu-behavior
+* js:iron-meta
+* js:iron-overlay-behavior
+* js:iron-resizable-behavior
+* js:iron-selector
+* js:iron-validatable-behavior
+* js:neon-animation
+* js:paper-behaviors
+* js:paper-button
+* js:paper-icon-button
+* js:paper-input
+* js:paper-item
+* js:paper-listbox
+* js:paper-ripple
+* js:paper-styles
+* js:paper-tabs
+* js:paper-toggle-button
+* js:polymer
+* js:polymer-resin
+* js:webcomponentsjs
+
+[[polymer_license]]
+----
+Copyright (c) 2014 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.
+
+----
+
+
+[[prologcafe]]
+prologcafe
+
+* prolog:cafeteria
+* prolog:compiler
+* prolog:io
+* prolog:runtime
+
+[[prologcafe_license]]
+----
+Prolog Cafe (A Prolog to Java Translator System)
+Copyright (C) 1997-2009 by Mutsunori Banbara and Naoyuki Tamura
+
+Prolog Cafe is free software; you can redistribute it and/or modify
+it under the terms of either:
+
+ * the GNU General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option)
+ any later version, or
+
+ * the Eclipse Public License
+----
+
+In the context of Gerrit Code Review, Prolog Cafe is consumed under
+the <<prologcafe_EPL,EPL>>. Gerrit Code Review uses a fork derived
+from the 1.2.5 release and offers the corresponding source code at
+link:https://gerrit.googlesource.com/prolog-cafe[].
+
+----
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+----
+
+[[prologcafe_EPL]]
+----
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+ documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from
+and are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program
+by such Contributor itself or anyone acting on such Contributor's
+behalf. Contributions do not include additions to the Program which:
+(i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are
+not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with
+this Agreement.
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare derivative works of, publicly display,
+ publicly perform, distribute and sublicense the Contribution of such
+ Contributor, if any, and such derivative works, in source code and
+ object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor,
+ if any, in source code and object code form. This patent license
+ shall apply to the combination of the Contribution and the Program
+ if, at the time the Contribution is added by the Contributor, such
+ addition of the Contribution causes such combination to be covered
+ by the Licensed Patents. The patent license shall not apply to any
+ other combinations which include the Contribution. No hardware per
+ se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe
+ the patent or other intellectual property rights of any other
+ entity. Each Contributor disclaims any liability to Recipient
+ for claims brought by any other entity based on infringement
+ of intellectual property rights or otherwise. As a condition to
+ exercising the rights and licenses granted hereunder, each Recipient
+ hereby assumes sole responsibility to secure any other intellectual
+ property rights needed, if any. For example, if a third party patent
+ license is required to allow Recipient to distribute the Program,
+ it is Recipient's responsibility to acquire that license before
+ distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant
+ the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code
+ form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+ and conditions, express and implied, including warranties or
+ conditions of title and non-infringement, and implied warranties or
+ conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability
+ for damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement
+ are offered by that Contributor alone and not by any other
+ party; and
+
+iv) states that source code for the Program is available from such
+ Contributor, and informs licensees how to obtain it in a reasonable
+ manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial
+use of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for other Contributors. Therefore, if a
+Contributor includes the Program in a commercial product offering,
+such Contributor ("Commercial Contributor") hereby agrees to defend
+and indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses") arising
+from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by
+the acts or omissions of such Commercial Contributor in connection
+with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must:
+a) promptly notify the Commercial Contributor in writing of such
+claim, and b) allow the Commercial Contributor to control, and
+cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a
+commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes
+performance claims, or offers warranties related to Product X, those
+performance claims and warranties are such Commercial Contributor's
+responsibility alone. Under this section, the Commercial Contributor
+would have to defend claims against the other Contributors related
+to those performance claims and warranties, and if a court requires
+any other Contributor to pay any damages as a result, the Commercial
+Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with
+its exercise of rights under this Agreement , including but not
+limited to the risks and costs of program errors, compliance with
+applicable laws, damage to or loss of data, programs or equipment,
+and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY
+RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed
+to the minimum extent necessary to make such provision valid and
+enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging
+that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s),
+then such Recipient's rights granted under Section 2(b) shall
+terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if
+it fails to comply with any of the material terms or conditions
+of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all
+Recipient's rights under this Agreement terminate, Recipient agrees
+to cease use and distribution of the Program as soon as reasonably
+practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall
+continue and survive.
+
+Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions
+(including revisions) of this Agreement from time to time. No
+one other than the Agreement Steward has the right to modify
+this Agreement. The Eclipse Foundation is the initial Agreement
+Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each
+new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it
+was received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under
+this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each
+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
+
+* protobuf
+
+[[protobuf_license]]
+----
+Copyright 2008, Google Inc.
+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.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it. This code is not
+standalone and requires a support library to be linked with it. This
+support library is itself covered by the above license.
+
+----
+
+
+[[slf4j]]
+slf4j
+
+* log:api
+* log:jcl-over-slf4j
+
+[[slf4j_license]]
+----
+Copyright (c) 2004-2008 QOS.ch
+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.
+
+----
+
+
+[[xz]]
+xz
+
+* tukaani-xz
+
+[[xz_license]]
+----
+All the files in this package have been written by Lasse Collin
+and/or Igor Pavlov. All these files have been put into the
+public domain. You can do whatever you want with these files.
+This software is provided "as is", without any warranty.
+
+----
+
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index cb3eb2179d..c0e23c5246 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -23,10 +23,12 @@ that ultimately timed out
=== Pushes
* `receivecommits/changes`: histogram of number of changes processed
-in a single upload, split up by update type (new change created,
-existing changed updated, change autoclosed).
+in a single upload, split up by update type (change created/updated,
+change autoclosed).
* `receivecommits/latency`: latency per change for processing a push,
split up by update type (create+replace, and autoclose)
+* `receivecommits/push_latency`: total latency for processing a push,
+split up by update type (create+replace, autoclose, normal)
* `receivecommits/timeout`: number of timeouts during push processing.
=== Process
@@ -134,10 +136,6 @@ Each queue provides the following metrics:
* `sshd/sessions/created`: Rate of new SSH sessions.
* `sshd/sessions/authentication_failures`: Rate of SSH authentication failures.
-=== SQL connections
-
-* `sql/connection_pool/connections`: SQL database connections.
-
=== Topics
* `topic/cross_project_submit`: number of cross-project topic submissions.
@@ -181,9 +179,6 @@ 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/auto_rebuild_latency`: NoteDb auto-rebuilding latency by table.
-* `notedb/auto_rebuild_failure_count`: NoteDb auto-rebuilding attempts that
-failed by table.
* `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.
@@ -218,6 +213,10 @@ suggestion.
* `plugin/latency`: Latency for plugin invocation.
* `plugin/error_count`: Number of plugin errors.
+=== Group
+
+* `group/guess_relevant_groups_latency`: Latency for guessing relevant groups.
+
=== Replication Plugin
* `plugins/replication/replication_latency`: Time spent pushing to remote
diff --git a/Documentation/note-db.txt b/Documentation/note-db.txt
index a9b71c1357..0bacca4860 100644
--- a/Documentation/note-db.txt
+++ b/Documentation/note-db.txt
@@ -1,8 +1,9 @@
= Gerrit Code Review - NoteDb Backend
-NoteDb is the next generation of Gerrit storage backend, which replaces the
-traditional SQL backend for change and account metadata with storing data in the
-same repository as code changes.
+NoteDb is the storage backend for code review metadata. It is based on
+Git, so code reviews are stored together with the code under review.
+NoteDb replaced the traditional SQL backend for change, account and group
+metadata that was used in the 2.x series.
.Advantages
- *Simplicity*: All data is stored in one location in the site directory, rather
@@ -21,35 +22,18 @@ same repository as code changes.
- *New features*: Enables simple federation between Gerrit servers, as well as
offline code review and interoperation with other tools.
-== Current Status
-
-- Storing change metadata is fully implemented in the 2.15 release, and is the
- default for new sites.
-- Admins may use an link:#offline-migration[offline] or
- link:#online-migration[online] tool to migrate change data in an existing
- site from ReviewDb.
-- Storing link:config-accounts.html[account data] is fully implemented in the
- 2.15 release. Account data is migrated automatically during the upgrade
- process by running `gerrit.war init`.
-- Storing link:config-groups.html[group metadata] is fully implemented
- for the 2.16 release. Group data is migrated automatically during
- the upgrade process by running `gerrit.war init`
-- Account, group and change metadata on the servers behind `googlesource.com` is fully
- migrated to NoteDb. In other words, if you use
- link:https://gerrit-review.googlesource.com/[gerrit-review], you're already
- using NoteDb.
-
For an example NoteDb change, poke around at this one:
----
git fetch https://gerrit.googlesource.com/gerrit refs/changes/70/98070/meta \
&& git log -p FETCH_HEAD
----
-== Future Work ("Gerrit 3.0")
+== Current Status
-- NoteDb will be the only database format supported by Gerrit 3.0. The offline
- change data migration tool will be included in Gerrit 3.0, but online
- migration will only be available in the 2.x line.
+NoteDb is the only database format supported by Gerrit 3.0+. The
+change data migration tools are only included in Gerrit 2.16; they are
+not available in 3.0, so any upgrade from Gerrit 2.x to 3.x must go through
+2.16 to effect the NoteDb upgrade.
[[migration]]
== Migration
@@ -64,6 +48,9 @@ and group data is migrated automatically by `gerrit.war init`.
[[online-migration]]
=== Online
+Note that online migration is only available in 2.x. To do the online migration
+from 2.14.x or 2.15.x to 3.0, it is necessary to first upgrade to 2.16.x.
+
To start the online migration, set the `noteDb.changes.autoMigrate` option in
`gerrit.config` and restart Gerrit:
----
@@ -87,7 +74,7 @@ off. Migration progress will be reported to the Gerrit logs.
*Disadvantages*
-* Only available in 2.x; will not be available in Gerrit 3.0.
+* Only available in 2.x; not available in Gerrit 3.0.
* Much slower than offline; uses only a single thread, to leave resources
available for serving traffic.
* Performance may be degraded, particularly of updates; data needs to be written
@@ -121,10 +108,10 @@ requires to reindex changes anyway).
* Much faster than online; can use all available CPUs, since no live traffic
needs to be served.
* No degraded performance of live servers due to writing data to 2 locations.
-* Available in both Gerrit 2.x and 3.0.
*Disadvantages*
+* Available in Gerrit 2.15 and 2.16 only.
* May require substantial downtime; takes about twice as long as an
link:pgm-reindex.html[offline reindex]. (In fact, one of the migration steps is a
full reindex, so it can't possibly take less time.)
diff --git a/Documentation/pg-plugin-admin-api.txt b/Documentation/pg-plugin-admin-api.txt
index 1c724d0cd0..084fa2c78a 100644
--- a/Documentation/pg-plugin-admin-api.txt
+++ b/Documentation/pg-plugin-admin-api.txt
@@ -4,7 +4,7 @@ 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)`
+`adminApi.addMenuLink(text, url, opt_external, opt_capabilities)`
Add a new link to the end of the admin navigation menu.
diff --git a/Documentation/pg-plugin-dev.txt b/Documentation/pg-plugin-dev.txt
index c7aa57c911..8fb5655214 100644
--- a/Documentation/pg-plugin-dev.txt
+++ b/Documentation/pg-plugin-dev.txt
@@ -177,6 +177,14 @@ See list of supported link:pg-plugin-endpoints.html[endpoints].
Note: TODO
+=== registerDynamicCustomComponent
+`plugin.registerDynamicCustomComponent(dynamicEndpointName, opt_moduleName,
+opt_options)`
+
+See list of supported link:pg-plugin-endpoints.html[endpoints].
+
+Note: TODO
+
=== registerStyleModule
`plugin.registerStyleModule(endpointName, moduleName)`
diff --git a/Documentation/pg-plugin-endpoints.txt b/Documentation/pg-plugin-endpoints.txt
index cf9313d56d..3d66dd4aae 100644
--- a/Documentation/pg-plugin-endpoints.txt
+++ b/Documentation/pg-plugin-endpoints.txt
@@ -166,3 +166,52 @@ link:rest-api-changes.html#change-info[ChangeInfo]
+
current revision displayed, an instance of
link:rest-api-changes.html#revision-info[RevisionInfo]
+
+== Dynamic Plugin endpoints
+
+The following endpoints are available to plugins.
+
+=== change-list-header
+The `change-list-header` extension point adds a header to the change list view.
+
+=== change-list-item-cell
+The `change-list-item-cell` extension point adds a cell to the change list item.
+
+In addition to default parameters, the following are available:
+
+* `change`
++
+current change of the row, an instance of
+link:rest-api-changes.html#change-info[ChangeInfo]
+
+=== change-view-tab-header
+The `change-view-tab-header` extension point adds a primary tab to the change
+view. This must be used in conjunction with `change-view-tab-content`.
+
+In addition to default parameters, the following are available:
+
+* `change`
++
+current change displayed, an instance of
+link:rest-api-changes.html#change-info[ChangeInfo]
+
+* `revision`
++
+current revision displayed, an instance of
+link:rest-api-changes.html#revision-info[RevisionInfo]
+
+=== change-view-tab-content
+The `change-view-tab-content` extension point adds primary tab content to
+the change view. This must be used in conjunction with `change-view-tab-header`.
+
+In addition to default parameters, the following are available:
+
+* `change`
++
+current change displayed, an instance of
+link:rest-api-changes.html#change-info[ChangeInfo]
+
+* `revision`
++
+current revision displayed, an instance of
+link:rest-api-changes.html#revision-info[RevisionInfo]
diff --git a/Documentation/pgm-LocalUsernamesToLowerCase.txt b/Documentation/pgm-LocalUsernamesToLowerCase.txt
index 4b50961893..53081a18ba 100644
--- a/Documentation/pgm-LocalUsernamesToLowerCase.txt
+++ b/Documentation/pgm-LocalUsernamesToLowerCase.txt
@@ -47,7 +47,7 @@ it must be run by itself.
== CONTEXT
This command can only be run on a server which has direct
-connectivity to the metadata database.
+connectivity to the managed Git repositories.
== EXAMPLES
To convert the local username of every account to lower case:
diff --git a/Documentation/pgm-daemon.txt b/Documentation/pgm-daemon.txt
index 0b1a3e5b11..25ca4dd706 100644
--- a/Documentation/pgm-daemon.txt
+++ b/Documentation/pgm-daemon.txt
@@ -19,14 +19,8 @@ _java_ -jar gerrit.war _daemon_
== DESCRIPTION
Runs the Gerrit network daemon on the local system, configured as
-per the local copy of link:config-gerrit.html[gerrit.config].
-
-The path to gerrit.config is read from the metadata database,
-which requires that all slaves (and master) reading from the same
-database must place gerrit.config at the same location on the local
-filesystem. However, any option within gerrit.config, including
-link:config-gerrit.html#gerrit.basePath[gerrit.basePath] may be set
-to different values.
+per the local copy of link:config-gerrit.html[gerrit.config] located under
+`<SITE_PATH>/etc`.
== OPTIONS
@@ -55,8 +49,9 @@ to different values.
This option automatically implies '--enable-sshd'.
--console-log::
- Send log messages to the console, instead of to the standard
- log file '$site_path/logs/error_log'.
+ Send log messages to the console. Log files will still be written to
+ the error log file, if log.textLogging and/or log.jsonLogging is set to
+ 'true'.
--headless::
Don't start the default Gerrit UI. May be useful when Gerrit is
diff --git a/Documentation/pgm-gsql.txt b/Documentation/pgm-gsql.txt
deleted file mode 100644
index 4986522191..0000000000
--- a/Documentation/pgm-gsql.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-= gsql
-
-== NAME
-gsql - Administrative interface to idle database
-
-== SYNOPSIS
-[verse]
---
-_java_ -jar gerrit.war _gsql_
- -d <SITE_PATH>
---
-
-== DESCRIPTION
-Interactive query support against the configured SQL database.
-All SQL statements are supported, including SELECT, UPDATE, INSERT,
-DELETE and ALTER.
-
-This command is primarily intended to access a local H2 database
-which is not currently open by a Gerrit daemon. To access an open
-database use link:cmd-gsql.html[gerrit gsql] over SSH.
-
-== OPTIONS
-
--d::
---site-path::
- Location of the gerrit.config file, and all other per-site
- configuration data, supporting libraries and log files.
-
-== CONTEXT
-This command can only be run on a server which has direct
-connectivity to the metadata database, and local access to the
-managed Git repositories.
-
-== EXAMPLES
-To manually correct a user's SSH user name:
-
-----
- $ java -jar gerrit.war gsql
- Welcome to Gerrit Code Review v2.0.25
- (PostgreSQL 8.3.8)
-
- Type '\h' for help. Type '\r' to clear the buffer.
-
- gerrit> update accounts set ssh_user_name = 'alice' where account_id=1;
- UPDATE 1; 1 ms
- gerrit> \q
- Bye
-----
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/pgm-index.txt b/Documentation/pgm-index.txt
index d61cc0b8f8..dde02318b7 100644
--- a/Documentation/pgm-index.txt
+++ b/Documentation/pgm-index.txt
@@ -15,9 +15,6 @@ link:pgm-init.html[init]::
link:pgm-daemon.html[daemon]::
Gerrit HTTP, SSH network server.
-link:pgm-gsql.html[gsql]::
- Administrative interface to idle database.
-
link:pgm-prolog-shell.html[prolog-shell]::
Simple interactive Prolog interpreter.
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index 9a16cdfa13..f6c3c85d3e 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -28,7 +28,7 @@ for some basic setup prior to writing default configuration files
into a newly created `$site_path`.
If run in an existing `$site_path`, init upgrades existing resources
-(e.g. DB schema, plugins) as necessary.
+(e.g. NoteDb schema, plugins) as necessary.
== OPTIONS
-b::
@@ -100,8 +100,7 @@ objects these SQL statements must be executed manually.
folder.
== CONTEXT
-This command can only be run on a server which has direct
-connectivity to the metadata database, and local access to the
+This command can only be run on a server which has direct local access to the
managed Git repositories.
GERRIT
diff --git a/Documentation/pgm-prolog-shell.txt b/Documentation/pgm-prolog-shell.txt
index a669aa758c..3566b8fff7 100644
--- a/Documentation/pgm-prolog-shell.txt
+++ b/Documentation/pgm-prolog-shell.txt
@@ -7,7 +7,7 @@ prolog-shell - Simple interactive Prolog interpreter
[verse]
--
_java_ -jar gerrit.war _prolog-shell_
- [-s FILE.pl ...]
+ [-q] [-s FILE.pl ...]
--
== DESCRIPTION
@@ -15,6 +15,8 @@ Provides a simple interactive Prolog interpreter for development
and testing.
== OPTIONS
+-q::
+ Do not display banner.
-s::
Dynamically load the Prolog source code at startup,
as though the user had entered `['FILE.pl'].` into
diff --git a/Documentation/pgm-rulec.txt b/Documentation/pgm-rulec.txt
index 1b50812aaa..2a987205ac 100644
--- a/Documentation/pgm-rulec.txt
+++ b/Documentation/pgm-rulec.txt
@@ -33,8 +33,7 @@ named `rules-'SHA1'.jar` in `'$site_path'/cache/rules`.
Compile rules for the specified project.
== CONTEXT
-This command can only be run on a server which has direct
-connectivity to the metadata database, and local access to the
+This command can only be run on a server which has local access to the
managed Git repositories.
Caching needs to be enabled. See
diff --git a/Documentation/project-configuration.txt b/Documentation/project-configuration.txt
index 9fa21ffd95..23030a4f86 100644
--- a/Documentation/project-configuration.txt
+++ b/Documentation/project-configuration.txt
@@ -48,201 +48,7 @@ Either restart the server, or flush the `project_list` cache:
[[project_options]]
== Project Options
-[[submit_type]]
-=== Submit Type
-
-The method Gerrit uses to submit a change to a project can be
-modified by any project owner through the project console, `Projects` >
-`List` > my/project. In general, a submitted change is only merged if all
-its dependencies are also submitted, with exceptions documented below.
-The following submit types are supported:
-
-[[submit_type_inherit]]
-* Inherit
-+
-This is the default for new projects, unless overridden by a global
-link:config-gerrit.html#repository.name.defaultSubmitType[`defaultSubmitType` option].
-+
-Inherit the submit type from the parent project. In `All-Projects`, this
-is equivalent to link:#merge_if_necessary[Merge If Necessary].
-
-[[fast_forward_only]]
-* Fast Forward Only
-+
-With this method Gerrit does not create merge commits on submitting a
-change. Merge commits may still be submitted, but they must be created
-on the client prior to uploading to Gerrit for review.
-+
-To submit a change, the change must be a strict superset of the
-destination branch. That is, the change must already contain the
-tip of the destination branch at submit time.
-
-[[merge_if_necessary]]
-* Merge If Necessary
-+
-If the change being submitted is a strict superset of the destination
-branch, then the branch is fast-forwarded to the change. If not,
-then a merge commit is automatically created. This is identical
-to the classical `git merge` behavior, or `git merge --ff`.
-
-[[always_merge]]
-* Always Merge
-+
-Always produce a merge commit, even if the change is a strict
-superset of the destination branch. This is identical to the
-behavior of `git merge --no-ff`, and may be useful if the
-project needs to follow submits with `git log --first-parent`.
-
-[[cherry_pick]]
-* Cherry Pick
-+
-Always cherry pick the patch set, ignoring the parent lineage
-and instead creating a brand new commit on top of the current
-branch head.
-+
-When cherry picking a change, Gerrit automatically appends onto the
-end of the commit message a short summary of the change's approvals,
-and a URL link back to the change on the web. The committer header
-is also set to the submitter, while the author header retains the
-original patch set author.
-+
-Note that Gerrit ignores dependencies between changes when using this
-submit type unless
-link:config-gerrit.html#change.submitWholeTopic[`change.submitWholeTopic`]
-is enabled and depending changes share the same topic. So generally
-submitters must remember to submit changes in the right order when using this
-submit type. If all you want is extra information in the commit message,
-consider using the Rebase Always submit strategy.
-
-[[rebase_if_necessary]]
-* Rebase If Necessary
-+
-If the change being submitted is a strict superset of the destination
-branch, then the branch is fast-forwarded to the change. If not,
-then the change is automatically rebased and then the branch is
-fast-forwarded to the change.
-+
-When Gerrit tries to do a merge, by default the merge will only
-succeed if there is no path conflict. A path conflict occurs when
-the same file has also been changed on the other side of the merge.
-
-[[rebase_always]]
-* Rebase Always
-+
-Basically, the same as Rebase If Necessary, but it creates a new patchset even
-if fast forward is possible AND like Cherry Pick it ensures footers such as
-Change-Id, Reviewed-On, and others are present in resulting commit that is
-merged.
-+
-Thus, Rebase Always can be considered similar to Cherry Pick, but with
-the important distinction that Rebase Always does not ignore dependencies.
-
-[[content_merge]]
-=== Allow content merges
-
-If `Allow content merges` is enabled, Gerrit will try
-to do a content merge when a path conflict occurs.
-
-[[project-state]]
-=== State
-
-This setting defines the state of the project. A project can have the
-following states:
-
-- `Active`:
-+
-The project is active and users can see and modify the project according
-to their access rights on the project.
-
-- `Read Only`:
-+
-The project is read only and all modifying operations on it are
-disabled. E.g. this means that pushing to this project fails for all
-users even if they have push permissions assigned on it.
-+
-Setting a project to this state is an easy way to temporary close a
-project, as you can keep all write access rights in place and they will
-become active again as soon as the project state is set back to
-`Active`.
-+
-This state also makes sense if a project was moved to another location.
-In this case all new development should happen in the new project and
-you want to prevent that somebody accidentally works on the old
-project, while keeping the old project around for old references.
-
-- `Hidden`:
-+
-The project is hidden and only visible to project owners. Other users
-are not able to see the project even if they have read permissions
-granted on the project.
-
-=== Use target branch when determining new changes to open
-
-The `create-new-change-for-all-not-in-target` option provides a
-convenience for selecting link:user-upload.html#base[the merge base]
-by setting it automatically to the target branch's tip so you can
-create new changes for all commits not in the target branch.
-
-This option is disabled if the tip of the push is a merge commit.
-
-This option also only works if there are no merge commits in the
-commit chain, in such cases it fails warning the user that such
-pushes can only be performed by manually specifying
-link:user-upload.html#base[bases]
-
-This option is useful if you want to push a change to your personal
-branch first and for review to another branch for example. Or in cases
-where a commit is already merged into a branch and you want to create
-a new open change for that commit on another branch.
-
-[[require-change-id]]
-=== Require Change-Id
-
-The `Require Change-Id in commit message` option defines whether a
-link:user-changeid.html[Change-Id] in the commit message is required
-for pushing a commit for review. If this option is set, trying to push
-a commit for review that doesn't contain a Change-Id in the commit
-message fails with link:error-missing-changeid.html[missing Change-Id
-in commit message footer].
-
-It is recommended to set this option and use a
-link:user-changeid.html#create[commit-msg hook] (or other client side
-tooling like EGit) to automatically generate Change-Id's for new
-commits. This way the Change-Id is automatically in place when changes
-are reworked or rebased and uploading new patch sets gets easy.
-
-If this option is not set, commits can be uploaded without a Change-Id,
-but then users have to remember to copy the assigned Change-Id from the
-change screen and insert it manually into the commit message when they
-want to upload a second patch set.
-
-=== Maximum Git Object Size Limit
-
-This option defines the maximum allowed Git object size that
-receive-pack will accept. If an object is larger than the given size
-the pack-parsing will abort and the push operation will fail.
-
-With this option users can be prevented from uploading commits that
-contain files which are too large.
-
-Normally the link:config-gerrit.html#receive.maxObjectSizeLimit[maximum
-Git object size limit] is configured globally for a Gerrit server. At
-the project level, the maximum Git object size limit can be further
-reduced, but not extended. The displayed effective limit shows the
-maximum Git object size limit that is actually used on the project.
-
-The defined maximum Git object size limit is inherited by any child
-project.
-
-[[require-signed-off-by]]
-=== Require Signed-off-by
-
-The `Require Signed-off-by in commit message` option defines whether a
-link:user-signedoffby.html[Signed-off-by] line in the commit message is
-required for pushing a commit. If this option is set, trying to push a
-commit that doesn't contain a Signed-off-by line in the commit message
-fails with link:error-not-signed-off-by.html[not Signed-off-by
-author/committer/uploader in commit message footer].
+See details at link:config-project-config.html#project-section[project section].
[[branch-admin]]
== Branch Administration
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index 19ed98aa6f..9a23a27b36 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -725,6 +725,9 @@ is no non author `Code-Review+2`. The second rule will only be reached
if the `cut` in the first rule is not reached and it only happens if a
predicate before the `cut` fails.
+This fact can be bypassed by users who have
+link:access-control.html#category_forge_author[Forge Author] permission.
+
==== Don't use `gerrit:default_submit`
Let's implement the same submit rule the other way, without reusing the
`gerrit:default_submit`:
@@ -1044,10 +1047,7 @@ submit_rule(submit(R)) :-
gerrit:uploader(U),
R = label('Is-Pure-Revert', ok(U)).
-submit_rule(submit(R)) :-
- gerrit:pure_revert(U),
- U /= 1,
- R = label('Is-Pure-Revert', need(_)).
+submit_rule(submit(label('Is-Pure-Revert', need(_)))).
----
Suppose currently a change is submittable if it gets `+2` for `Code-Review`
@@ -1058,21 +1058,20 @@ follows:
[source,prolog]
----
submit_rule(submit(CR, V, R)) :-
- base(CR, V),
- gerrit:pure_revert(1),
- !,
- gerrit:uploader(U),
- R = label('Is-Pure-Revert', ok(U)).
-
-submit_rule(submit(CR, V, R)) :-
- base(CR, V),
- gerrit:pure_revert(U),
- U /= 1,
- R = label('Is-Pure-Revert', need(_)).
+ base(CR, V),
+ set_pure_revert_label(R).
base(CR, V) :-
- gerrit:max_with_block(-2, 2, 'Code-Review', CR),
- gerrit:max_with_block(-1, 1, 'Verified', V).
+ gerrit:max_with_block(-2, 2, 'Code-Review', CR),
+ gerrit:max_with_block(-1, 1, 'Verified', V).
+
+set_pure_revert_label(R) :-
+ gerrit:pure_revert(1),
+ !,
+ gerrit:uploader(U),
+ R = label('Is-Pure-Revert', ok(U)).
+
+set_pure_revert_label(label('Is-Pure-Revert', need(_))).
----
Note that a new label as `Is-Pure-Revert` should not be configured.
diff --git a/Documentation/quota.txt b/Documentation/quota.txt
new file mode 100644
index 0000000000..a647e333fe
--- /dev/null
+++ b/Documentation/quota.txt
@@ -0,0 +1,50 @@
+= Gerrit Code Review - Quota
+
+Gerrit does not provide out of the box quota enforcement. However, it does
+support an extension mechanism for plugins to hook into to provide this
+functionality. The most prominent plugin is the
+link:https://gerrit.googlesource.com/plugins/quota/[Quota Plugin].
+
+This documentation is intended to be read by plugin developers. It contains all
+quota requests implemented in Gerrit-core as well as the metadata that they have
+associated.
+
+== Quota Groups
+
+The following quota groups are defined in core Gerrit:
+
+=== REST API
+[[rest-api]]
+
+The REST API enforces quota after the resource was parsed (if applicable) and before the
+endpoint's logic is executed. This enables quota enforcer implementations to throttle calls
+to specific endpoints while knowing the general context (user and top-level entity such as
+change, project or account).
+
+If the quota enforcer wants to throttle HTTP requests, they should use
+link:quota.html#http-requests[HTTP Requests] instead.
+
+The quota groups used for checking follow the exact definition of the endoint in the REST
+API, but remove all IDs. The schema is:
+
+/restapi/<ENDPOINT>:<HTTP-METHOD>
+
+Examples:
+
+[options="header",cols="1,6"]
+|=======================
+|HTTP call |Quota Group |Metadata
+|GET /a/changes/1/revisions/current/detail |/changes/revisions/detail:GET |CurrentUser, Change.Id, Project.NameKey
+|POST /a/changes/ |/changes/:POST |CurrentUser
+|GET /a/accounts/self/detail |/accounts/detail:GET |CurrentUser, Account.Id
+|=======================
+
+The user provided in the check's metadata is always the calling user (having the
+impersonation bit and real user set in case the user is impersonating another user).
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+
+SEARCHBOX
+---------
diff --git a/Documentation/replace_macros.py b/Documentation/replace_macros.py
index d1b50d31ce..aaa9223bf0 100755
--- a/Documentation/replace_macros.py
+++ b/Documentation/replace_macros.py
@@ -87,6 +87,12 @@ SEARCH_BOX = """
id="searchBox">
Search
</button>
+ %s
+</div>
+++++
+"""
+
+BUILTIN_SEARCH = """
<script type="text/javascript">
var f = function() {
window.location = '../#/Documentation/q/' +
@@ -99,11 +105,25 @@ document.getElementById("docSearch").onkeypress = function(e) {
}
}
</script>
-</div>
-++++
+"""
+GOOGLE_SITE_SEARCH = """
+<script type="text/javascript">
+var f = function() {
+ window.location = 'https://www.google.com/search?q=' +
+ encodeURIComponent(document.getElementById("docSearch").value +
+ ' site:@SITE@');
+}
+document.getElementById("searchBox").onclick = f;
+document.getElementById("docSearch").onkeypress = function(e) {
+ if (13 == (e.keyCode ? e.keyCode : e.which)) {
+ f();
+ }
+}
+</script>
"""
+
LINK_SCRIPT = """
++++
@@ -227,8 +247,19 @@ parser.add_argument('-b', '--searchbox', action="store_true", default=True,
help="generate the search boxes")
parser.add_argument('--no-searchbox', action="store_false", dest='searchbox',
help="don't generate the search boxes")
+parser.add_argument('--site-search', action="store", metavar="SITE",
+ help=("generate the search box using google. SITE should " +
+ "point to the domain/path of the site, eg. " +
+ "gerrit-review.googlesource.com/Documentation"))
args = parser.parse_args()
+if args.site_search:
+ SEARCH_BOX = (SEARCH_BOX %
+ GOOGLE_SITE_SEARCH.replace("@SITE@", args.site_search))
+else:
+ SEARCH_BOX = SEARCH_BOX % BUILTIN_SEARCH
+
+
try:
try:
out_file = open(args.out, 'w', errors='ignore')
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index becc411939..1d4f6fb14a 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1256,7 +1256,6 @@ any account.
"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",
@@ -1313,7 +1312,6 @@ link:#preferences-input[PreferencesInput] entity.
"show_site_header": true,
"use_flash_clipboard": true,
"expand_inline_diffs": true,
- "download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
@@ -1364,7 +1362,6 @@ link:#preferences-info[PreferencesInfo] entity.
"show_site_header": true,
"use_flash_clipboard": true,
"expand_inline_diffs": true,
- "download_command": "CHECKOUT",
"date_format": "STD",
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
@@ -1427,7 +1424,6 @@ link:#diff-preferences-info[DiffPreferencesInfo] entity.
)]}'
{
"context": 10,
- "theme": "DEFAULT",
"ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
@@ -1458,7 +1454,6 @@ link:#diff-preferences-input[DiffPreferencesInput] entity.
{
"context": 10,
- "theme": "ECLIPSE",
"ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
@@ -1484,7 +1479,6 @@ link:#diff-preferences-info[DiffPreferencesInfo] entity.
)]}'
{
"context": 10,
- "theme": "ECLIPSE",
"ignore_whitespace": "IGNORE_ALL",
"intraline_difference": true,
"line_length": 100,
@@ -1521,8 +1515,6 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
)]}'
{
- "theme": "ECLIPSE",
- "key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"indent_unit": 2,
@@ -1554,8 +1546,6 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
Content-Type: application/json;charset=UTF-8
{
- "theme": "ECLIPSE",
- "key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"indent_unit": 2,
@@ -1582,8 +1572,6 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
)]}'
{
- "theme": "ECLIPSE",
- "key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"cursor_blink_rate": 530,
@@ -2147,12 +2135,37 @@ Identifier that uniquely identifies one account.
This can be:
+* `self` or `me` for the calling user
+* a bare account ID ("18419")
+* an account ID following a name in parentheses ("Full Name (18419)")
* a string of the format "Full Name <email@example.com>"
* just the email address ("email@example")
-* a full name if it is unique ("Full Name")
-* an account ID ("18419")
+* a full name ("Full Name")
* a user name ("username")
-* `self` for the calling user
+
+In all cases, accounts that are not
+link:config-gerrit.html#accounts.visibility[visible] to the calling user are not
+considered.
+
+In all cases _except_ a bare account ID and `self`/`me`, inactive accounts are
+not considered. Inactive accounts should only be referenced by bare ID.
+
+If the input is a bare account ID, this will always resolve to exactly
+one account if there is a visible account with that ID, and zero accounts
+otherwise. (This is true even in corner cases like a user having a full name
+which is exactly a numeric account ID belonging to a different user; such a user
+cannot be identified by this number.)
+
+If the identifier is ambiguous or only refers to inactive accounts, the error
+message from the API should contain a human-readable description of how to
+disambiguate the request.
+
+*Note*: Except as noted above, callers should not rely on the particular
+priorities of any of the identifiers in the account resolution algorithm. Any
+other formats may be subject to future deprecation. If callers require specific
+searching semantics, they should use the link:#query-account[Query Account]
+endpoint to resolve a string to one or more accounts, then access the API using
+the account ID.
[[capability-id]]
=== \{capability-id\}
@@ -2423,9 +2436,6 @@ preferences of a user.
|Field Name ||Description
|`context` ||
The number of lines of context when viewing a patch.
-|`theme` ||
-The CodeMirror theme name in upper case, for example `DEFAULT`. All the themes
-from the CodeMirror release that Gerrit is using are available.
|`expand_all_comments` |not set if `false`|
Whether all inline comments should be automatically expanded.
|`ignore_whitespace` ||
@@ -2547,12 +2557,6 @@ preferences of a user.
[options="header",cols="1,^1,5"]
|===========================================
|Field Name ||Description
-|`theme` ||
-The CodeMirror theme name in upper case, for example `DEFAULT`. All the themes
-from the CodeMirror release that Gerrit is using are available.
-|`key_map_type` ||
-The CodeMirror key map. Currently only a subset of key maps are
-supported: `DEFAULT`, `EMACS`, `SUBLIME`, `VIM`.
|`tab_size` ||
Number of spaces that should be used to display one tab.
|`line_length` ||
@@ -2712,8 +2716,6 @@ Whether to expand diffs inline instead of opening as separate page
The type of download URL the user prefers to use. May be any key from
the `schemes` map in
link:rest-api-config.html#download-info[DownloadInfo].
-|`download_command` ||
-The type of download command the user prefers to use.
|`date_format` ||
The format to display the date in.
Allowed values are `STD`, `US`, `ISO`, `EURO`, `UK`.
@@ -2743,9 +2745,6 @@ link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
|`change_table` ||
The columns to display in the change table (PolyGerrit only). The default is
empty, which will default columns as determined by the frontend.
-|`url_aliases` |optional|
-A map of URL path pairs, where the first URL path is an alias for the
-second URL path.
|`email_strategy` ||
The type of email strategy to use. On `ENABLED`, the user will receive emails
from Gerrit. On `CC_ON_OWN_COMMENTS` the user will also receive emails for
@@ -2784,8 +2783,6 @@ Whether to expand diffs inline instead of opening as separate page
(PolyGerrit only).
|`download_scheme` |optional|
The type of download URL the user prefers to use.
-|`download_command` |optional|
-The type of download command the user prefers to use.
|`date_format` |optional|
The format to display the date in.
Allowed values are `STD`, `US`, `ISO`, `EURO`, `UK`.
@@ -2815,9 +2812,6 @@ link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
|`change_table` ||
The columns to display in the change table (PolyGerrit only). The default is
empty, which will default columns as determined by the frontend.
-|`url_aliases` |optional|
-A map of URL path pairs, where the first URL path is an alias for the
-second URL path.
|`email_strategy` |optional|
The type of email strategy to use. On `ENABLED`, the user will receive emails
from Gerrit. On `CC_ON_OWN_COMMENTS` the user will also receive emails for
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index a43b0dc50f..199c13d58a 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -309,6 +309,14 @@ default. Optional fields are:
* `SKIP_MERGEABLE`: skip the `mergeable` field in
link:#change-info[ChangeInfo]. For fast moving projects, this field must
be recomputed often, which is slow for projects with big trees.
++
+When link:config-gerrit.html#change.api.excludeMergeableInChangeInfo[
+`change.api.excludeMergeableInChangeInfo`] is set in the `gerrit.config`,
+the `mergeable` field will always be omitted and `SKIP_MERGEABLE` has no
+effect.
++
+A change's mergeability can be requested separately by calling the
+link:#get-mergeable[get-mergeable] endpoint.
--
[[submittable]]
@@ -350,6 +358,11 @@ be recomputed often, which is slow for projects with big trees.
as link:#tracking-id-info[TrackingIdInfo].
--
+[[no-limit]]
+--
+* `NO-LIMIT`: Return all results
+--
+
.Request
----
GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES&o=DOWNLOAD_COMMANDS HTTP/1.0
@@ -976,8 +989,6 @@ If the change has no assignee the response is "`204 No Content`".
Returns a list of every user ever assigned to a change, in the order in which
they were first assigned.
-[NOTE] Past assignees are only available when NoteDb is enabled.
-
.Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/past_assignees HTTP/1.0
@@ -2200,8 +2211,8 @@ if no review comment is added.
'POST /changes/link:#change-id[\{change-id\}]/private'
--
-Marks the change to be private. Changes may only be marked private by the
-owner or site administrators.
+Marks the change to be private. Only open changes can be marked private.
+Changes may only be marked private by the owner or site administrators.
A message can be specified in the request body inside a
link:#private-input[PrivateInput] entity.
@@ -2333,8 +2344,6 @@ This allows users to "highlight" changes in their dashboard
Gets the hashtags associated with a change.
-[NOTE] Hashtags are only available when NoteDb is enabled.
-
.Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/hashtags HTTP/1.0
@@ -2363,8 +2372,6 @@ As response the change's hashtags are returned as a list of strings.
Adds and/or removes hashtags from a change.
-[NOTE] Hashtags are only available when NoteDb is enabled.
-
The hashtags to add or remove must be provided in the request body inside a
link:#hashtags-input[HashtagsInput] entity.
@@ -5164,9 +5171,6 @@ parent commit number against which the diff should be generated. This is useful
for supporting review of merge commits. The value is the 1-based index of the
parent's position in the commit object.
-[[weblinks-only]]
-If the `weblinks-only` parameter is specified, only the diff web links are returned.
-
.Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/b6b9c10649b9041884046119ab794374470a1b45/files/gerrit-server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fgerrit%2Fserver%2Fproject%2FRefControl.java/diff?base=2 HTTP/1.0
@@ -5764,12 +5768,14 @@ change. The labels are lexicographically sorted.
Whether the change was reviewed by the calling user.
Only set if link:#reviewed[reviewed] is requested.
|`submit_type` |optional|
-The link:project-configuration.html#submit_type[submit type] of the change. +
+The link:config-project-config.html#submit-type[submit type] of the change. +
Not set for merged changes.
|`mergeable` |optional|
Whether the change is mergeable. +
Not set for merged changes, if the change has not yet been tested, or
-if the link:#skip_mergeable[skip_mergeable] option is set.
+if the link:#skip_mergeable[skip_mergeable] option is set or when
+link:config-gerrit.html#change.api.excludeMergeableInChangeInfo[change.api.excludeMergeableInChangeInfo]
+is set.
|`submittable` |optional|
Whether the change has been approved by the project submit rules. +
Only set if link:#submittable[requested].
@@ -5777,8 +5783,12 @@ Only set if link:#submittable[requested].
Number of inserted lines.
|`deletions` ||
Number of deleted lines.
+|`total_comment_count` |optional|
+Total number of inline comments across all patch sets. Not set if the current
+change index doesn't have the data.
|`unresolved_comment_count` |optional|
-Number of unresolved comments. Not set if the current change index doesn't have the data.
+Number of unresolved inline comment threads across all patch sets. Not set if
+the current change index doesn't have the data.
|`_number` ||The legacy numeric ID of the change.
|`owner` ||
The owner of the change as an link:rest-api-accounts.html#account-info[
@@ -5821,8 +5831,7 @@ Only set if link:#detailed-labels[detailed labels] are requested.
|`reviewer_updates`|optional|
Updates to reviewers set for the change as
link:#review-update-info[ReviewerUpdateInfo] entities.
-Only set if link:#reviewer-updates[reviewer updates] are requested and
-if NoteDb is enabled.
+Only set if link:#reviewer-updates[reviewer updates] are requested.
|`messages`|optional|
Messages associated with the change as a list of
link:#change-message-info[ChangeMessageInfo] entities. +
@@ -5886,9 +5895,13 @@ A 40-digit hex SHA-1 of the commit which will be the parent commit of the newly
created change. If set, it must be a merged commit on the destination branch.
Mutually exclusive with `base_change`.
|`new_branch` |optional, default to `false`|
-Allow creating a new branch when set to `true`.
+Allow creating a new branch when set to `true`. Using this option is
+only possible for non-merge commits (if the `merge` field is not set).
|`merge` |optional|
The detail of a merge commit as a link:#merge-input[MergeInput] entity.
+If set, the target branch (see `branch` field) must exist (it is not
+possible to create it automatically by setting the `new_branch` field
+to `true`.
|`notify` |optional|
Notify handling that defines to whom email notifications should be sent
after the change is created. +
@@ -6810,7 +6823,7 @@ The `Requirement` entity contains information about a requirement relative to a
|===========================
|Field Name | |Description
|`status` | | Status of the requirement. Can be either `OK`, `NOT_READY` or `RULE_ERROR`.
-|`fallbackText` | | A human readable reason
+|`fallback_text` | | A human readable reason
|`type` | |
Alphanumerical (plus hyphens or underscores) string to identify what the requirement is and why it
was triggered. Can be seen as a class: requirements sharing the same type were created for a similar
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 905c3ef830..93591120e7 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -126,10 +126,7 @@ As result a link:#server-info[ServerInfo] entity is returned.
"gerrit": {
"all_projects": "All-Projects",
"all_users": "All-Users"
- "doc_search": true,
- "web_uis": [
- "gwt"
- ]
+ "doc_search": true
},
"sshd": {},
"suggest": {
@@ -1486,8 +1483,8 @@ type] is `CUSTOM_EXTENSION`.
|`git_basic_auth_policy` |optional|
The link:config-gerrit.html#auth.gitBasicAuthPolicy[policy] to authenticate
Git over HTTP and REST API requests when
-link:config-gerrit.html#auth.type[authentication type] is `LDAP`.
-Can be `HTTP`, `LDAP` or `HTTP_LDAP`.
+link:config-gerrit.html#auth.type[authentication type] is `LDAP`,
+`LDAP_BIND` or `OAUTH`. Can be `HTTP`, `LDAP`, `HTTP_LDAP` or `OAUTH`.
|==========================================
[[cache-info]]
@@ -1824,12 +1821,6 @@ Gerrit base path even if this value is unset.)
Whether to enable the web UI for editing GPG keys.
|`report_bug_url` |optional|
link:config-gerrit.html#gerrit.reportBugUrl[URL to report bugs].
-|`report_bug_text` |optional, not set if default|
-link:config-gerrit.html#gerrit.reportBugText[Display text for report
-bugs link].
-|`web_uis` ||
-List of web UIs supported by the HTTP server. Possible values are `GWT`
-and `POLYGERRIT`.
|=================================
[[hit-ration-info]]
@@ -1905,22 +1896,6 @@ The maximal memory size. The value is returned with a unit abbreviation
The number of open files.
|============================
-[[message-of-the-day-info]]
-=== MessageOfTheDayInfo
-The `MessageOfTheDayInfo` entity contains information about a message
-that was registered with the
-link:dev-plugins.html#message-of-the-day[MessageOfTheDay]-extension by plugins.
-
-[options="header",cols="1,^1,5"]
-|===========================
-|Field Name ||Description
-|`id` ||ID of the message.
-|`redisplay` ||
-Date and Time, when the message should be displayed again after it was dismissed
-by the user.
-|`html` ||Message in HTML-format.
-|===========================
-
[[plugin-config-info]]
=== PluginConfigInfo
The `PluginConfigInfo` entity contains information about Gerrit
@@ -1974,11 +1949,6 @@ information about Gerrit
Information about the configuration from the
link:config-gerrit.html#gerrit[gerrit] section as link:#gerrit-info[
GerritInfo] entity.
-|`messages` ||
-List of messages registered with the
-link:dev-plugins.html#message-of-the-day[MessageOfTheDay]-extension
-containing link:#message-of-the-day-info[
-MessageOfTheDayInfo] entities.
|`note_db_enabled` |not set if `false`|
Whether the NoteDb storage backend is fully enabled.
|`plugin` ||
@@ -1995,11 +1965,6 @@ entity. Not set if SSHD is disabled.
Information about the configuration from the
link:config-gerrit.html#suggest[suggest] section as link:#suggest-info[
SuggestInfo] entity.
-|`url_aliases` |optional|
-A map of URL aliases, where a regular expression for an URL token is
-mapped to a target URL token. The target URL token can contain
-placeholders for the groups matched by the regular expression: `$1` for
-the first matched group, `$2` for the second matched group, etc.
|`user` ||
Information about the configuration from the
link:config-gerrit.html#user[user] section as link:#user-config-info[
diff --git a/Documentation/rest-api-plugins.txt b/Documentation/rest-api-plugins.txt
index 255704a521..92b692fa33 100644
--- a/Documentation/rest-api-plugins.txt
+++ b/Documentation/rest-api-plugins.txt
@@ -48,6 +48,7 @@ by plugin ID.
"id": "delete-project",
"index_url": "plugins/delete-project/",
"filename": "delete-project.jar",
+ "api_version": "2.9.3-SNAPSHOT",
"version": "2.9-SNAPSHOT"
}
}
@@ -437,12 +438,13 @@ The `PluginInfo` entity describes a plugin.
[options="header",cols="1,^2,4"]
|=======================
-|Field Name ||Description
-|`id` ||The ID of the plugin.
-|`version` ||The version of the plugin.
-|`index_url`|optional|URL of the plugin's default page.
-|`filename` |optional|The plugin's filename.
-|`disabled` |not set if `false`|Whether the plugin is disabled.
+|Field Name ||Description
+|`id` ||The ID of the plugin.
+|`version` ||The version of the plugin.
+|`api_version`|optional|The version of the Gerrit Api used by the plugin.
+|`index_url` |optional|URL of the plugin's default page.
+|`filename` |optional|The plugin's filename.
+|`disabled` |not set if `false`|Whether the plugin is disabled.
|=======================
[[plugin-input]]
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 5006b6d457..76151b40e0 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1356,25 +1356,6 @@ access to at least branch is checked.
Ref(ref)::
The branch for which to check access. This must be given if `perm` is specified.
-[[check-access-post]]
-=== Check Access (POST)
-
-This endpoint can also be accessed as a POST request (deprecated). In
-this case, the input for the access checks must be provided in the
-request body inside a link:#access-check-input[AccessCheckInput]
-entity.
-
-.Request
-----
- POST /projects/MyProject/check.access HTTP/1.0
- Content-Type: application/json; charset=UTF-8
-
- {
- "account": "Kristen.Burns@gerritcodereview.com",
- "ref": "refs/heads/secret/bla"
- }
-----
-
[[index]]
=== Index project
@@ -2934,21 +2915,6 @@ The `AccessCheckInfo` entity is the result of an access check.
|`message` |optional|A clarifying message if `status` is not 200.
|=========================================
-[[access-check-input]]
-=== AccessCheckInput
-The `AccessCheckInput` entity is either an account or
-(account, ref) tuple for which we want to check access.
-
-[options="header",cols="1,^1,5"]
-|=========================================
-|Field Name ||Description
-|`account` ||The account for which to check access
-|`ref` |optional|The refname for which to check
-access
-|`permission` |optional|The ref permission for which to
-check. This defaults to `read`. If given, it `ref` must be given too.
-|=========================================
-
[[auto_closeable_changes_check_input]]
=== AutoCloseableChangesCheckInput
The `AutoCloseableChangesCheckInput` entity contains options for running
@@ -3169,9 +3135,6 @@ Not set if the project state is `ACTIVE`.
Map with the comment link configurations of the project. The name of
the comment link configuration is mapped to a link:#commentlink-info[
CommentlinkInfo] entity.
-|`theme` |optional|
-The theme that is configured for the project as a link:#theme-info[
-ThemeInfo] entity.
|`plugin_config` |optional|
Plugin configuration as map which maps the plugin name to a map of
parameter names to link:#config-parameter-info[ConfigParameterInfo]
@@ -3660,7 +3623,7 @@ statistics of a Git repository.
[[submit-type-info]]
=== SubmitTypeInfo
-Information about the link:project-configuration.html#submit_type[default submit
+Information about the link:config-project-config.html#submit-type[default submit
type of a project], taking into account project inheritance.
Valid values for each field are `MERGE_IF_NECESSARY`, `FAST_FORWARD_ONLY`,
@@ -3723,21 +3686,6 @@ as an annotated tag.
|=========================
-[[theme-info]]
-=== ThemeInfo
-The `ThemeInfo` entity describes a theme.
-
-[options="header",cols="1,^2,4"]
-|=============================
-|Field Name ||Description
-|`css` |optional|
-The path to the `GerritSite.css` file.
-|`header` |optional|
-The path to the `GerritSiteHeader.html` file.
-|`footer` |optional|
-The path to the `GerritSiteFooter.html` file.
-|=============================
-
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index 8f6a47b233..a8ab35347a 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -191,6 +191,11 @@ Preconditions] section.
"`422 Unprocessable Entity`" is returned if the ID of a resource that is
specified in the request body cannot be resolved.
+==== 429 Too Many Requests
+"`429 Too Many Requests`" is returned if the request exhausted any set
+quota limits. Depending on the exhausted quota, the request may be retried
+with exponential backoff.
+
[[tracing]]
=== Request Tracing
For each REST endpoint tracing can be enabled by setting the
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 48d331b74a..cafd5ca7d5 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -277,6 +277,8 @@ should be double quoted when using more complex construction (like
ones using a bracket expression). For example, to match all XML
files named like 'name1.xml', 'name2.xml', and 'name3.xml' use
`file:"^name[1-3].xml"`.
++
+Slash ('/') is used path separator.
[[file]]
file:'NAME', f:'NAME'::
@@ -290,6 +292,46 @@ named exactly `Foo.java` and does not match `AbstractFoo.java`.
Regular expression matching can be enabled by starting the string
with `^`. In this mode `file:` is an alias of `path:` (see above).
+[[extension]]
+extension:'EXT', ext:'EXT'::
++
+Matches any change touching a file with extension 'EXT', case-insensitive. The
+extension is defined as the portion of the filename following the final `.`.
+Files with no `.` in their name have no extension and can be matched by an
+empty string.
+
+[[onlyextensions]]
+onlyextensions:'EXT_LIST', onlyexts:'EXT_LIST'::
++
+Matches any change touching only files with extensions that are listed in
+'EXT_LIST' (comma-separated list). The matching is done case-insensitive.
+An extension is defined as the portion of the filename following the final `.`.
+Files with no `.` in their name have no extension and can be matched by an
+empty string.
+
+[[directory]]
+directory:'DIR', dir:'DIR'::
++
+Matches any change where the current patch set touches a file in the directory
+'DIR'. The matching is done case-insensitive. 'DIR' can be a full directory
+name, a directory prefix or any combination of intermediate directory segments.
+E.g. a change that touches a file in the directory 'a/b/c' matches for 'a/b/c',
+'a', 'a/b', 'b', 'b/c' and 'c'.
++
+Slash ('/') is used path separator. Leading and trailing slashes are allowed
+but are not mandatory.
++
+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:'FOOTER'::
++
+Matches any change that has 'FOOTER' as footer in the commit message of the
+current patch set. 'FOOTER' can be specified verbatim ('<key>: <value>', must
+be quoted) or as '<key>=<value>'. The matching is done case-insensitive.
+
[[star]]
star:'LABEL'::
+
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 751e886a25..56602e27be 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -732,7 +732,7 @@ client invoked by the end-user, or by `repo upload`. This allows
Gerrit to provide magical refs, such as `+refs/for/*+` for new
change submission and `+refs/changes/*+` for change replacement.
When a push request is received to create a ref in one of these
-namespaces Gerrit performs its own logic to update the database,
+namespaces Gerrit performs its own logic to update the review metadata,
and then lies to the client about the result of the operation.
A successful result causes the client to believe that Gerrit has
created the ref, but in reality Gerrit hasn't created the ref at all.
diff --git a/WORKSPACE b/WORKSPACE
index 73e8874099..efc3c73be4 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -59,9 +59,6 @@ rules_closure_dependencies(
rules_closure_toolchains()
-# This has to be done after loading of rules_closure, because it loads rules_java
-load("//lib/codemirror:cm.bzl", "CM_VERSION", "DIFF_MATCH_PATCH_VERSION")
-
# Golang support for PolyGerrit local dev server.
http_archive(
name = "io_bazel_rules_go",
@@ -90,12 +87,6 @@ gazelle_dependencies()
# Dependencies for PolyGerrit local dev server.
go_repository(
- name = "com_github_robfig_soy",
- commit = "82face14ebc0883b4ca9c901b5aaf3738b9f6a24",
- importpath = "github.com/robfig/soy",
-)
-
-go_repository(
name = "com_github_howeyc_fsnotify",
commit = "441bbc86b167f3c1f4786afae9931403b99fdacf",
importpath = "github.com/howeyc/fsnotify",
@@ -165,66 +156,11 @@ maven_jar(
)
maven_jar(
- name = "servlet-api-3_1",
+ name = "servlet-api",
artifact = "org.apache.tomcat:tomcat-servlet-api:8.5.23",
sha1 = "021a212688ec94fe77aff74ab34cc74f6f940e60",
)
-GWT_VERS = "2.8.2"
-
-maven_jar(
- name = "user",
- artifact = "com.google.gwt:gwt-user:" + GWT_VERS,
- sha1 = "a2b9be2c996a658c4e009ba652a9c6a81c88a797",
-)
-
-maven_jar(
- name = "dev",
- artifact = "com.google.gwt:gwt-dev:" + GWT_VERS,
- sha1 = "7a87e060bbf129386b7ae772459fb9f87297c332",
-)
-
-maven_jar(
- name = "javax-validation",
- artifact = "javax.validation:validation-api:1.0.0.GA",
- sha1 = "b6bd7f9d78f6fdaa3c37dae18a4bd298915f328e",
- src_sha1 = "7a561191db2203550fbfa40d534d4997624cd369",
-)
-
-maven_jar(
- name = "jsinterop-annotations",
- artifact = "com.google.jsinterop:jsinterop-annotations:1.0.2",
- sha1 = "abd7319f53d018e11108a88f599bd16492448dd2",
- src_sha1 = "33716f8aef043f2f02b78ab4a1acda6cd90a7602",
-)
-
-maven_jar(
- name = "ant",
- artifact = "ant:ant:1.6.5",
- attach_source = False,
- sha1 = "7d18faf23df1a5c3a43613952e0e8a182664564b",
-)
-
-maven_jar(
- name = "colt",
- artifact = "colt:colt:1.2.0",
- attach_source = False,
- sha1 = "0abc984f3adc760684d49e0f11ddf167ba516d4f",
-)
-
-maven_jar(
- name = "tapestry",
- artifact = "tapestry:tapestry:4.0.2",
- attach_source = False,
- sha1 = "e855a807425d522e958cbce8697f21e9d679b1f7",
-)
-
-maven_jar(
- name = "w3c-css-sac",
- artifact = "org.w3c.css:sac:1.3",
- sha1 = "cdb2dcb4e22b83d6b32b93095f644c3462739e82",
-)
-
load("//lib/jgit:jgit.bzl", "jgit_repos")
jgit_repos()
@@ -236,46 +172,12 @@ maven_jar(
sha1 = "94ad16d728b374d65bd897625f3fbb3da223a2b6",
)
-FLOGGER_VERS = "0.4"
-
-maven_jar(
- name = "flogger",
- artifact = "com.google.flogger:flogger:" + FLOGGER_VERS,
- sha1 = "9c8863dcc913b56291c0c88e6d4ca9715b43df98",
-)
-
-maven_jar(
- name = "flogger-log4j-backend",
- artifact = "com.google.flogger:flogger-log4j-backend:" + FLOGGER_VERS,
- sha1 = "17aa5e31daa1354187e14b6978597d630391c028",
-)
-
-maven_jar(
- name = "flogger-system-backend",
- artifact = "com.google.flogger:flogger-system-backend:" + FLOGGER_VERS,
- sha1 = "287b569d76abcd82f9de87fe41829fbc7ebd8ac9",
-)
-
-maven_jar(
- name = "gwtjsonrpc",
- artifact = "com.google.gerrit:gwtjsonrpc:1.12",
- sha1 = "cade35e5628af56f687d651dd0f43d17d46b20f5",
- src_sha1 = "e4c17ec9a453f4d41d5e0e55f7020e5919725b0d",
-)
-
maven_jar(
name = "gson",
artifact = "com.google.code.gson:gson:2.8.5",
sha1 = "f645ed69d595b24d4cf8b3fbb64cc505bede8829",
)
-maven_jar(
- name = "gwtorm-client",
- artifact = "com.google.gerrit:gwtorm:1.18",
- sha1 = "f326dec463439a92ccb32f05b38345e21d0b5ecf",
- src_sha1 = "e0b973d5cafef3d145fa80cdf032fcead1186d29",
-)
-
load("//lib:guava.bzl", "GUAVA_BIN_SHA1", "GUAVA_VERSION")
maven_jar(
@@ -284,15 +186,15 @@ maven_jar(
sha1 = GUAVA_BIN_SHA1,
)
-CAFFEINE_VERS = "2.8.0"
+CAFFEINE_VERS = "2.8.5"
maven_jar(
name = "caffeine",
artifact = "com.github.ben-manes.caffeine:caffeine:" + CAFFEINE_VERS,
- sha1 = "6000774d7f8412ced005a704188ced78beeed2bb",
+ sha1 = "f0eafef6e1529a44e36549cd9d1fc06d3a57f384",
)
-CAFFEINE_GUAVA_SHA256 = "3a66ee3ec70971dee0bae6e56bda7b8742bc4bedd7489161bfbbaaf7137d89e1"
+CAFFEINE_GUAVA_SHA256 = "a7ce6d29c40bccd688815a6734070c55b20cd326351a06886a6144005aa32299"
# TODO(davido): Rename guava.jar to caffeine-guava.jar on fetch to prevent potential
# naming collision between caffeine guava adapter and guava library itself.
@@ -313,6 +215,12 @@ http_file(
)
maven_jar(
+ name = "guava-failureaccess",
+ artifact = "com.google.guava:failureaccess:1.0.1",
+ sha1 = "1dcf1de382a0bf95a3d8b0849546c88bac1292c9",
+)
+
+maven_jar(
name = "jsch",
artifact = "com.jcraft:jsch:0.1.54",
sha1 = "da3584329a263616e277e15462b387addd1b208d",
@@ -395,8 +303,14 @@ maven_jar(
maven_jar(
name = "commons-lang3",
- artifact = "org.apache.commons:commons-lang3:3.6",
- sha1 = "9d28a6b23650e8a7e9063c04588ace6cf7012c17",
+ artifact = "org.apache.commons:commons-lang3:3.8.1",
+ sha1 = "6505a72a097d9270f7a9e7bf42c4238283247755",
+)
+
+maven_jar(
+ name = "commons-text",
+ artifact = "org.apache.commons:commons-text:1.2",
+ sha1 = "74acdec7237f576c4803fff0c1008ab8a3808b2b",
)
maven_jar(
@@ -431,6 +345,33 @@ maven_jar(
sha1 = "959a0c62f9a5c2309e0ad0b0589c74d69e101241",
)
+COMMONMARK_VERS = "0.10.0"
+
+# commonmark must match the version used in Gitiles
+maven_jar(
+ name = "commonmark",
+ artifact = "com.atlassian.commonmark:commonmark:" + COMMONMARK_VERS,
+ sha1 = "119cb7bedc3570d9ecb64ec69ab7686b5c20559b",
+)
+
+maven_jar(
+ name = "cm-autolink",
+ artifact = "com.atlassian.commonmark:commonmark-ext-autolink:" + COMMONMARK_VERS,
+ sha1 = "a6056a5efbd68f57d420bc51bbc54b28a5d3c56b",
+)
+
+maven_jar(
+ name = "gfm-strikethrough",
+ artifact = "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:" + COMMONMARK_VERS,
+ sha1 = "40837da951b421b545edddac57012e15fcc9e63c",
+)
+
+maven_jar(
+ name = "gfm-tables",
+ artifact = "com.atlassian.commonmark:commonmark-ext-gfm-tables:" + COMMONMARK_VERS,
+ sha1 = "c075db2a3301100cf70c7dced8ecf86b494458a2",
+)
+
FLEXMARK_VERS = "0.34.18"
maven_jar(
@@ -583,7 +524,7 @@ maven_jar(
sha1 = "31e2e1fbe8273d7c913506eafeb06b1a7badb062",
)
-# Transitive dependency of flexmark
+# Transitive dependency of flexmark and gitiles
maven_jar(
name = "autolink",
artifact = "org.nibor.autolink:autolink:0.7.0",
@@ -658,18 +599,18 @@ maven_jar(
sha1 = "a3ae34e57fa8a4040e28247291d0cc3d6b8c7bcf",
)
-AUTO_VALUE_VERSION = "1.6.2"
+AUTO_VALUE_VERSION = "1.6.5"
maven_jar(
name = "auto-value",
artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
- sha1 = "e7eae562942315a983eea3e191b72d755c153620",
+ sha1 = "816872c85048f36a67a276ef7a49cc2e4595711c",
)
maven_jar(
name = "auto-value-annotations",
artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
- sha1 = "ed193d86e0af90cc2342aedbe73c5d86b03fa09b",
+ sha1 = "c3dad10377f0e2242c9a4b88e9704eaf79103679",
)
declare_nongoogle_deps()
@@ -713,7 +654,7 @@ maven_jar(
sha1 = "0c9cfae15c74f62491d4f28def0dff1dabe52a47",
)
-PROLOG_VERS = "1.4.3"
+PROLOG_VERS = "1.4.4"
PROLOG_REPO = GERRIT
@@ -722,7 +663,7 @@ maven_jar(
artifact = "com.googlecode.prolog-cafe:prolog-runtime:" + PROLOG_VERS,
attach_source = False,
repository = PROLOG_REPO,
- sha1 = "d5206556cbc76ffeab21313ffc47b586a1efbcbb",
+ sha1 = "e9a364f4233481cce63239e8e68a6190c8f58acd",
)
maven_jar(
@@ -730,7 +671,7 @@ maven_jar(
artifact = "com.googlecode.prolog-cafe:prolog-compiler:" + PROLOG_VERS,
attach_source = False,
repository = PROLOG_REPO,
- sha1 = "f37032cf1dec3e064427745bc59da5a12757a3b2",
+ sha1 = "570295026f6aa7b905e423d107cb2e081eecdc04",
)
maven_jar(
@@ -738,7 +679,7 @@ maven_jar(
artifact = "com.googlecode.prolog-cafe:prolog-io:" + PROLOG_VERS,
attach_source = False,
repository = PROLOG_REPO,
- sha1 = "d02b2640b26f64036b6ba2b45e4acc79281cea17",
+ sha1 = "1f25c4e27d22bdbc31481ee0c962a2a2853e4428",
)
maven_jar(
@@ -746,7 +687,7 @@ maven_jar(
artifact = "com.googlecode.prolog-cafe:prolog-cafeteria:" + PROLOG_VERS,
attach_source = False,
repository = PROLOG_REPO,
- sha1 = "e3b1860c63e57265e5435f890263ad82dafa724f",
+ sha1 = "0e6c2deeaf5054815a561cbd663566fd59b56c6c",
)
maven_jar(
@@ -761,25 +702,43 @@ maven_jar(
sha1 = "f7be08ec23c21485b9b5a1cf1654c2ec8c58168d",
)
+GITILES_VERS = "0.2-12"
+
+GITILES_REPO = GERRIT
+
maven_jar(
name = "blame-cache",
- artifact = "com/google/gitiles:blame-cache:0.2-7.1",
+ artifact = "com.google.gitiles:blame-cache:" + GITILES_VERS,
attach_source = False,
- repository = GERRIT,
- sha1 = "73915991bb7472a730102ab01ca68776a52466fd",
+ repository = GITILES_REPO,
+ sha1 = "e175e4366d83f20378905ca58a505ba8adac291d",
+)
+
+maven_jar(
+ name = "gitiles-servlet",
+ artifact = "com.google.gitiles:gitiles-servlet:" + GITILES_VERS,
+ repository = GITILES_REPO,
+ sha1 = "53f654f79ec65b9af7fbe645c99bf7888cd1ac48",
+)
+
+# prettify must match the version used in Gitiles
+maven_jar(
+ name = "prettify",
+ artifact = "com.github.twalcari:java-prettify:1.2.2",
+ sha1 = "b8ba1c1eb8b2e45cfd465d01218c6060e887572e",
)
# Keep this version of Soy synchronized with the version used in Gitiles.
maven_jar(
name = "soy",
- artifact = "com.google.template:soy:2018-03-14",
- sha1 = "76a1322705ba5a6d6329ee26e7387417725ce4b3",
+ artifact = "com.google.template:soy:2019-03-11",
+ sha1 = "119ac4b3eb0e2c638526ca99374013965c727097",
)
maven_jar(
name = "html-types",
- artifact = "com.google.common.html.types:types:1.0.4",
- sha1 = "2adf4c8bfccc0ff7346f9186ac5aa57d829ad065",
+ artifact = "com.google.common.html.types:types:1.0.8",
+ sha1 = "9e9cf7bc4b2a60efeb5f5581fe46d17c068e0777",
)
maven_jar(
@@ -858,30 +817,30 @@ maven_jar(
sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
)
-TRUTH_VERS = "0.42"
+TRUTH_VERS = "0.44"
maven_jar(
name = "truth",
artifact = "com.google.truth:truth:" + TRUTH_VERS,
- sha1 = "b5768f644b114e6cf5c3962c2ebcb072f788dcbb",
+ sha1 = "11eff954c0c14da7d43276d7b3bcf71463105368",
)
maven_jar(
name = "truth-java8-extension",
artifact = "com.google.truth.extensions:truth-java8-extension:" + TRUTH_VERS,
- sha1 = "4d01dfa5b3780632a3d109e14e101f01d10cce2c",
+ sha1 = "2081a0721d3101e1cf559f013e59c6129b4b10b0",
)
maven_jar(
name = "truth-liteproto-extension",
artifact = "com.google.truth.extensions:truth-liteproto-extension:" + TRUTH_VERS,
- sha1 = "c231e6735aa6c133c7e411ae1c1c90b124900a8b",
+ sha1 = "64f47e4e3f79b0a582573098b9c3c6b73599f7c6",
)
maven_jar(
name = "truth-proto-extension",
artifact = "com.google.truth.extensions:truth-proto-extension:" + TRUTH_VERS,
- sha1 = "c41d22e8b4a61b4171e57c44a2959ebee0091a14",
+ sha1 = "c03fbc16087d8cb3bf0f3265a04566d4beb88a6d",
)
maven_jar(
@@ -897,110 +856,98 @@ maven_jar(
sha1 = "3e127311a86fc2e8f550ef8ee4abe094bbcf7e7e",
)
-maven_jar(
- name = "derby",
- artifact = "org.apache.derby:derby:10.12.1.1",
- attach_source = False,
- sha1 = "75070c744a8e52a7d17b8b476468580309d5cd09",
-)
-
-JETTY_VERS = "9.4.30.v20200611"
+JETTY_VERS = "9.4.32.v20200930"
maven_jar(
name = "jetty-servlet",
artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VERS,
- sha1 = "ca3dea2cd34ee88cec017001603af0c9e74781d6",
+ sha1 = "4253dd46c099e0bca4dd763fc1e10774e10de00a",
)
maven_jar(
name = "jetty-security",
artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VERS,
- sha1 = "1a5261f6ad4081ad9e9bb01416d639931d391273",
-)
-
-maven_jar(
- name = "jetty-servlets",
- artifact = "org.eclipse.jetty:jetty-servlets:" + JETTY_VERS,
- sha1 = "3404a59996afbecd05d2504b07a765304546af07",
+ sha1 = "16a6110fa40e49050146de5f597ab3a3a3fa83b5",
)
maven_jar(
name = "jetty-server",
artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VERS,
- sha1 = "e5ede3724d062717d0c04e4c77f74fe8115c2a6f",
+ sha1 = "d2d89099be5237cf68254bc943a7d800d3ee1945",
)
maven_jar(
name = "jetty-jmx",
artifact = "org.eclipse.jetty:jetty-jmx:" + JETTY_VERS,
- sha1 = "653559eaec0f9a335a0d12e90bc764b28f341241",
+ sha1 = "5e8e87a6f89b8eabf5b5b1765e3d758209001570",
)
maven_jar(
name = "jetty-continuation",
artifact = "org.eclipse.jetty:jetty-continuation:" + JETTY_VERS,
- sha1 = "2a9cd8c4cf392a7697a57665e7b0caf5bce4cd48",
+ sha1 = "b46713a1b8b2baf951f6514dd621c5a546254d6c",
)
maven_jar(
name = "jetty-http",
artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VERS,
- sha1 = "cd6223382e4f82b9ea807d8cdb04a23e5d629f1c",
+ sha1 = "5fdcefd82178d11f895690f4fe6e843be69394b3",
)
maven_jar(
name = "jetty-io",
artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VERS,
- sha1 = "9c360d08e903b2dbd5d1f8e889a32046948628ce",
+ sha1 = "0d0f32c3b511d6b3a542787f95ed229731588810",
)
maven_jar(
name = "jetty-util",
artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VERS,
- sha1 = "39ec6aa4745952077f5407cb1394d8ba2db88b13",
+ sha1 = "efefd29006dcc9c9960a679263504287ce4e6896",
)
maven_jar(
- name = "postgresql",
- artifact = "org.postgresql:postgresql:42.2.5",
- sha1 = "951b7eda125f3137538a94e2cbdcf744088ad4c2",
+ name = "commons-io",
+ artifact = "commons-io:commons-io:2.2",
+ sha1 = "83b5b8a7ba1c08f9e8c8ff2373724e33d3c1e22a",
)
maven_jar(
- name = "codemirror-minified-gwt",
- artifact = "org.webjars.npm:codemirror-minified:" + CM_VERSION,
- sha1 = "36558ea3b8e30782e1e09c0e7bd781e09614f139",
+ name = "asciidoctor",
+ artifact = "org.asciidoctor:asciidoctorj:1.5.7",
+ sha1 = "8e8c1d8fc6144405700dd8df3b177f2801ac5987",
)
maven_jar(
- name = "codemirror-original-gwt",
- artifact = "org.webjars.npm:codemirror:" + CM_VERSION,
- sha1 = "f1f8fbbc3e2d224fdccc43d2f4180658a92320f9",
+ name = "javax-activation",
+ artifact = "javax.activation:activation:1.1.1",
+ sha1 = "485de3a253e23f645037828c07f1d7f1af40763a",
)
maven_jar(
- name = "diff-match-patch",
- artifact = "org.webjars:google-diff-match-patch:" + DIFF_MATCH_PATCH_VERSION,
- attach_source = False,
- sha1 = "0cf1782dbcb8359d95070da9176059a5a9d37709",
+ name = "mockito",
+ artifact = "org.mockito:mockito-core:2.24.0",
+ sha1 = "969a7bcb6f16e076904336ebc7ca171d412cc1f9",
)
+BYTE_BUDDY_VERSION = "1.9.7"
+
maven_jar(
- name = "commons-io",
- artifact = "commons-io:commons-io:2.2",
- sha1 = "83b5b8a7ba1c08f9e8c8ff2373724e33d3c1e22a",
+ name = "byte-buddy",
+ artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
+ sha1 = "8fea78fea6449e1738b675cb155ce8422661e237",
)
maven_jar(
- name = "asciidoctor",
- artifact = "org.asciidoctor:asciidoctorj:1.5.7",
- sha1 = "8e8c1d8fc6144405700dd8df3b177f2801ac5987",
+ name = "byte-buddy-agent",
+ artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
+ sha1 = "8e7d1b599f4943851ffea125fd9780e572727fc0",
)
maven_jar(
- name = "javax-activation",
- artifact = "javax.activation:activation:1.1.1",
- sha1 = "485de3a253e23f645037828c07f1d7f1af40763a",
+ name = "objenesis",
+ artifact = "org.objenesis:objenesis:2.6",
+ sha1 = "639033469776fd37c08358c6b92a4761feb2af4b",
)
load("//tools/bzl:js.bzl", "bower_archive", "npm_binary")
@@ -1174,8 +1121,8 @@ bower_archive(
bower_archive(
name = "codemirror-minified",
package = "Dominator008/codemirror-minified",
- sha1 = "1524e19087d8223edfe4a5b1ccf04c1e3707235d",
- version = "5.37.0",
+ sha1 = "e6bda82afc7cf3493f4282c6f17265d40e1485e5",
+ version = "5.43.0",
)
# bower test stuff
diff --git a/antlr3/BUILD b/antlr3/BUILD
index 4af6006396..549946a2a0 100644
--- a/antlr3/BUILD
+++ b/antlr3/BUILD
@@ -21,7 +21,8 @@ java_library(
name = "query_parser",
srcs = [":query"],
visibility = [
- "//java/com/google/gerrit/index:__pkg__",
+ "//java/com/google/gerrit/index:__subpackages__",
+ "//javatests/com/google/gerrit:__subpackages__",
"//javatests/com/google/gerrit/index:__pkg__",
"//plugins:__pkg__",
],
diff --git a/antlr3/com/google/gerrit/index/query/Query.g b/antlr3/com/google/gerrit/index/query/Query.g
index 953a473f03..1bf20aa97b 100644
--- a/antlr3/com/google/gerrit/index/query/Query.g
+++ b/antlr3/com/google/gerrit/index/query/Query.g
@@ -120,12 +120,24 @@ conditionNot
;
conditionBase
: '('! conditionOr ')'!
- | (FIELD_NAME ':') => FIELD_NAME^ ':'! fieldValue
+ | (FIELD_NAME COLON) => FIELD_NAME^ COLON! fieldValue
| fieldValue -> ^(DEFAULT_FIELD fieldValue)
;
fieldValue
- : n=FIELD_NAME -> SINGLE_WORD[n]
+ // Rewrite by invoking SINGLE_WORD fragment lexer rule, passing the field name as an argument.
+ : n=FIELD_NAME -> SINGLE_WORD[n]
+
+ // Allow field values to contain a colon. We can't do this at the lexer level, because we need to
+ // emit a separate token for the field name. If we were to allow ':' in SINGLE_WORD, then
+ // everything would just lex as DEFAULT_FIELD.
+ //
+ // Field values with a colon may be lexed either as <field>:<rest> or <word>:<rest>, depending on
+ // whether the part before the colon looks like a field name.
+ // TODO(dborowitz): Field values ending in colon still don't work.
+ | (FIELD_NAME COLON) => n=FIELD_NAME COLON fieldValue -> SINGLE_WORD[n] COLON fieldValue
+ | (SINGLE_WORD COLON) => SINGLE_WORD COLON fieldValue
+
| SINGLE_WORD
| EXACT_PHRASE
;
@@ -134,6 +146,8 @@ AND: 'AND' ;
OR: 'OR' ;
NOT: 'NOT' ;
+COLON: ':' ;
+
WS
: ( ' ' | '\r' | '\t' | '\n' ) { $channel=HIDDEN; }
;
@@ -172,7 +186,7 @@ fragment NON_WORD
// '-' permit
// '.' permit
// '/' permit
- | ':'
+ | COLON
| ';'
// '<' permit
// '=' permit
diff --git a/contrib/mitm-ui/README.md b/contrib/mitm-ui/README.md
new file mode 100644
index 0000000000..1ec8dd4a78
--- /dev/null
+++ b/contrib/mitm-ui/README.md
@@ -0,0 +1,61 @@
+# Scripts for PolyGerrit local development against prod using MitmProxy.
+
+## Installation (OSX)
+
+1. Install Docker from http://docker.com
+2. Start the proxy and create a new proxied browser instance
+ ```
+ cd ~/gerrit
+ ~/mitm-gerrit/mitm-serve-app-dev.sh
+ ```
+3. Make sure that the browser uses the proxy provided by the command line,
+ e.g. if you are a Googler check that the BeyondCorp extension uses the
+ "System/Alternative" proxy.
+4. Install MITM certificates
+ - Open http://mitm.it in the proxied browser window
+ - Follow the instructions to install MITM certs
+
+## Usage
+
+### Add or replace a single plugin containing static content
+
+To develop unminified plugin that loads multiple files, use this.
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ ~/mitm-gerrit/mitm-single-plugin.sh ./path/to/static/plugin.html
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. plugin.html and ./path/to/static/* will be served
+
+### Add or replace a minified plugin for *.googlesource.com
+
+This flow assumes no additional .html/.js are needed, i.e. the plugin is a single file.
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ ~/mitm-gerrit/mitm-plugins.sh ./path/to/plugin.html,./maybe/one/more.js
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. plugin.html and more.js are served
+
+### Force or replace default site theme for *.googlesource.com
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ ~/mitm-gerrit/mitm-theme.sh ./path/to/theme.html
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. Default site themes are enabled.
+4. Local `theme.html` content replaces `/static/gerrit-theme.html`
+5. `/static/*` URLs are served from local theme directory, i.e. `./path/to/`
+
+### Serve uncompiled PolyGerrit
+
+1. Create a new proxied browser window and start mitmproxy via Docker:
+ ```
+ cd ~/gerrit
+ ~/mitm-gerrit/mitm-serve-app-dev.sh
+ ```
+2. Open any *.googlesource.com domain in proxied window
+3. Instead of prod UI (gr-app.html, gr-app.js), local source files will be served
diff --git a/contrib/mitm-ui/add-header.py b/contrib/mitm-ui/add-header.py
new file mode 100644
index 0000000000..f9b2b121e0
--- /dev/null
+++ b/contrib/mitm-ui/add-header.py
@@ -0,0 +1,5 @@
+# mitmdump -s add-header.py
+def response(flow):
+ if flow.request.host == 'gerrit-review.googlesource.com' and flow.request.path == "/c/92000?1":
+ #flow.response.headers['any'] = '<meta.rdf>; rel=meta'
+ flow.response.headers['Link'] = '</changes/98000/detail?O=11640c>;rel="preload";crossorigin;'
diff --git a/contrib/mitm-ui/dev-chrome.sh b/contrib/mitm-ui/dev-chrome.sh
new file mode 100755
index 0000000000..adcb296d9b
--- /dev/null
+++ b/contrib/mitm-ui/dev-chrome.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [[ "$OSTYPE" != "darwin"* ]]; then
+ echo Only works on OSX.
+ exit 1
+fi
+
+/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir=${HOME}/devchrome --proxy-server="127.0.0.1:8888"
diff --git a/contrib/mitm-ui/force-version.py b/contrib/mitm-ui/force-version.py
new file mode 100644
index 0000000000..a69c885b09
--- /dev/null
+++ b/contrib/mitm-ui/force-version.py
@@ -0,0 +1,22 @@
+# mitmdump -q -p 8888 -s "force-version.py --version $1"
+# Request URL is not changed, only the response context
+from mitmproxy import http
+import argparse
+import re
+
+class Server:
+ def __init__(self, version):
+ self.version = version
+
+ def request(self, flow: http.HTTPFlow) -> None:
+ if "gr-app." in flow.request.pretty_url:
+ flow.request.url = re.sub(
+ r"polygerrit_ui/([\d.]+)/elements",
+ "polygerrit_ui/" + self.version + "/elements",
+ flow.request.url)
+
+def start():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--version", type=str, help="Rapid release version, e.g. 432.0")
+ args = parser.parse_args()
+ return Server(args.version)
diff --git a/contrib/mitm-ui/mitm-docker.sh b/contrib/mitm-ui/mitm-docker.sh
new file mode 100755
index 0000000000..a1206f71f4
--- /dev/null
+++ b/contrib/mitm-ui/mitm-docker.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+extra_volume='/tmp:/tmp'
+
+POSITIONAL=()
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+ -v|--volume)
+ extra_volume="$2"
+ shift # past argument
+ shift # past value
+ ;;
+ *) # unknown option
+ POSITIONAL+=("$1") # save it in an array for later
+ shift # past argument
+ ;;
+esac
+done
+set -- "${POSITIONAL[@]}" # restore positional parameters
+
+if [[ -z "$1" ]]; then
+ echo This is a runner for higher-level scripts, e.g. mitm-serve-app-dev.sh
+ echo Alternatively, pass mitmproxy script from the same dir as a parameter, e.g. serve-app-dev.py
+ exit 1
+fi
+
+gerrit_dir=$(pwd)
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+CMD="${mitm_dir}/$1"
+
+docker run --rm -it \
+ -v ~/.mitmproxy:/home/mitmproxy/.mitmproxy \
+ -v ${mitm_dir}:${mitm_dir} \
+ -v ${gerrit_dir}:${gerrit_dir} \
+ -v ${gerrit_dir}/bazel-out:${gerrit_dir}/bazel-out \
+ -v ${extra_volume} \
+ -p 8888:8888 \
+ mitmproxy/mitmproxy:2.0.2 \
+ mitmdump -q -p 8888 -s "${CMD}"
diff --git a/contrib/mitm-ui/mitm-plugins.sh b/contrib/mitm-ui/mitm-plugins.sh
new file mode 100755
index 0000000000..fc542bb94c
--- /dev/null
+++ b/contrib/mitm-ui/mitm-plugins.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+if [[ -z "$1" ]]; then
+ echo This script injects plugins for *.googlesource.com.
+ echo Provide plugin paths, comma-separated, as a parameter.
+ echo This script assumes files do not have dependencies, i.e. minified.
+ exit 1
+fi
+
+realpath() {
+ [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
+}
+
+join () {
+ local IFS="$1"
+ shift
+ echo "$*"
+}
+
+plugins=$1
+plugin_paths=()
+for plugin in $(echo ${plugins} | sed "s/,/ /g")
+do
+ plugin_paths+=($(realpath ${plugin}))
+done
+
+absolute_plugin_paths=$(join , "${plugin_paths[@]}")
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+${mitm_dir}/dev-chrome.sh &
+
+bazel build //polygerrit-ui/app:test_components &
+
+${mitm_dir}/mitm-docker.sh \
+ "serve-app-dev.py \
+ --plugins ${absolute_plugin_paths} \
+ --strip_assets \
+ --components $(pwd)/bazel-bin/polygerrit-ui/app/"
diff --git a/contrib/mitm-ui/mitm-serve-app-dev.sh b/contrib/mitm-ui/mitm-serve-app-dev.sh
new file mode 100755
index 0000000000..d4c72cc1c8
--- /dev/null
+++ b/contrib/mitm-ui/mitm-serve-app-dev.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+workspace="./WORKSPACE"
+if [[ ! -f ${workspace} ]] || [[ ! $(head -n 1 ${workspace}) == *"gerrit"* ]]; then
+ echo Please change to cloned Gerrit repo from https://gerrit.googlesource.com/gerrit/
+ exit 1
+fi
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+bazel build //polygerrit-ui/app:test_components &
+
+${mitm_dir}/dev-chrome.sh &
+
+${mitm_dir}/mitm-docker.sh "serve-app-dev.py --app $(pwd)/polygerrit-ui/app/ --components $(pwd)/bazel-bin/polygerrit-ui/app/"
diff --git a/contrib/mitm-ui/mitm-single-plugin.sh b/contrib/mitm-ui/mitm-single-plugin.sh
new file mode 100755
index 0000000000..895822984d
--- /dev/null
+++ b/contrib/mitm-ui/mitm-single-plugin.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+if [[ -z "$1" ]]; then
+ echo This script serves one plugin with the rest of static content.
+ echo Provide path to index plugin file, e.g. buildbucket.html for buildbucket plugin
+ exit 1
+fi
+
+realpath() {
+ OURPWD=$PWD
+ cd "$(dirname "$1")"
+ LINK=$(basename "$1")
+ while [ -L "$LINK" ]; do
+ LINK=$(readlink "$LINK")
+ cd "$(dirname "$LINK")"
+ LINK="$(basename "$1")"
+ done
+ REAL_DIR=`pwd -P`
+ RESULT=$REAL_DIR/$LINK
+ cd "$OURPWD"
+ echo "$RESULT"
+}
+
+plugin=$(realpath $1)
+plugin_root=$(dirname ${plugin})
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+${mitm_dir}/dev-chrome.sh &
+
+bazel build //polygerrit-ui/app:test_components &
+
+${mitm_dir}/mitm-docker.sh -v ${plugin_root}:${plugin_root} \
+ "serve-app-dev.py \
+ --plugins ${plugin} \
+ --strip_assets \
+ --plugin_root ${plugin_root} \
+ --components $(pwd)/bazel-bin/polygerrit-ui/app/"
diff --git a/contrib/mitm-ui/mitm-theme.sh b/contrib/mitm-ui/mitm-theme.sh
new file mode 100755
index 0000000000..9290235e5e
--- /dev/null
+++ b/contrib/mitm-ui/mitm-theme.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+if [[ -z "$1" ]]; then
+ echo This script forces or replaces default site theme on *.googlesource.com
+ echo Provide path to the theme.html as a parameter.
+ exit 1
+fi
+
+realpath() {
+ OURPWD=$PWD
+ cd "$(dirname "$1")"
+ LINK=$(basename "$1")
+ while [ -L "$LINK" ]; do
+ LINK=$(readlink "$LINK")
+ cd "$(dirname "$LINK")"
+ LINK="$(basename "$1")"
+ done
+ REAL_DIR=`pwd -P`
+ RESULT=$REAL_DIR/$LINK
+ cd "$OURPWD"
+ echo "$RESULT"
+}
+
+theme=$(realpath "$1")
+theme_dir=$(dirname "${theme}")
+
+mitm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+
+"${mitm_dir}"/dev-chrome.sh &
+
+"${mitm_dir}"/mitm-docker.sh -v "${theme_dir}":"${theme_dir}" "serve-app-dev.py --strip_assets --theme \"${theme}\""
diff --git a/contrib/mitm-ui/serve-app-dev.py b/contrib/mitm-ui/serve-app-dev.py
new file mode 100644
index 0000000000..cdf7bfcec2
--- /dev/null
+++ b/contrib/mitm-ui/serve-app-dev.py
@@ -0,0 +1,169 @@
+# 1. install and setup mitmproxy v2.0.2: https://mitmproxy.readthedocs.io/en/v2.0.2/install.html
+# (In case of python versions trouble, use https://www.anaconda.com/)
+# 2. mitmdump -q -s -p 8888 \
+# "serve-app-dev.py --app /path/to/polygerrit-ui/app/"
+# 3. start Chrome with --proxy-server="127.0.0.1:8888" --user-data-dir=/tmp/devchrome
+# 4. open, say, gerrit-review.googlesource.com. Or chromium-review.googlesource.com. Any.
+# 5. uncompiled source files are served and you can log in, too.
+# 6. enjoy!
+#
+# P.S. For replacing plugins, use --plugins or --plugin_root
+#
+# --plugin takes comma-separated list of plugins to add or replace.
+#
+# Example: Adding a new plugin to the server response:
+# --plugins ~/gerrit-testsite/plugins/myplugin.html
+#
+# Example: Replace all matching plugins with local versions:
+# --plugins ~/gerrit-testsite/plugins/
+# Following files will be served if they exist for /plugins/tricium/static/tricium.html:
+# ~/gerrit-testsite/plugins/tricium.html
+# ~/gerrit-testsite/plugins/tricium/static/tricium.html
+#
+# --assets takes assets bundle.html, expecting rest of the assets files to be in the same folder
+#
+# Example:
+# --assets ~/gerrit-testsite/assets/a3be19f.html
+#
+
+from mitmproxy import http
+from mitmproxy.script import concurrent
+import argparse
+import json
+import mimetypes
+import os.path
+import re
+import zipfile
+
+class Server:
+ def __init__(self, devpath, components, plugins, pluginroot, assets, strip_assets, theme):
+ if devpath:
+ print("Serving app from " + devpath)
+ if components:
+ print("Serving components from " + components)
+ if pluginroot:
+ print("Serving plugins from " + pluginroot)
+ if assets:
+ self.assets_root, self.assets_file = os.path.split(assets)
+ print("Assets: using " + self.assets_file + " from " + self.assets_root)
+ else:
+ self.assets_root = None
+ if plugins:
+ self.plugins = {path.split("/")[-1:][0]: path for path in map(expandpath, plugins.split(","))}
+ for filename, path in self.plugins.items():
+ print("Serving " + filename + " from " + path)
+ else:
+ self.plugins = {}
+ self.devpath = devpath
+ self.components = components
+ self.pluginroot = pluginroot
+ self.strip_assets = strip_assets
+ self.theme = theme
+
+ def readfile(self, path):
+ with open(path, 'rb') as contentfile:
+ return contentfile.read()
+
+@concurrent
+def response(flow: http.HTTPFlow) -> None:
+ if server.strip_assets:
+ assets_bundle = 'googlesource.com/polygerrit_assets'
+ assets_pos = flow.response.text.find(assets_bundle)
+ if assets_pos != -1:
+ t = flow.response.text
+ flow.response.text = t[:t.rfind('<', 0, assets_pos)] + t[t.find('>', assets_pos) + 1:]
+ return
+
+ if server.assets_root:
+ marker = 'webcomponents-lite.js"></script>'
+ pos = flow.response.text.find(marker)
+ if pos != -1:
+ pos += len(marker)
+ flow.response.text = ''.join([
+ flow.response.text[:pos],
+ '<link rel="import" href="/gerrit_assets/123.0/' + server.assets_file + '">',
+ flow.response.text[pos:]
+ ])
+
+ assets_prefix = "/gerrit_assets/123.0/"
+ if flow.request.path.startswith(assets_prefix):
+ assets_file = flow.request.path[len(assets_prefix):]
+ flow.response.content = server.readfile(server.assets_root + '/' + assets_file)
+ flow.response.status_code = 200
+ if assets_file.endswith('.js'):
+ flow.response.headers['Content-type'] = 'text/javascript'
+ return
+ m = re.match(".+polygerrit_ui/\d+\.\d+/(.+)", flow.request.path)
+ pluginmatch = re.match("^/plugins/(.+)", flow.request.path)
+ localfile = ""
+ content = ""
+ if flow.request.path == "/config/server/info":
+ config = json.loads(flow.response.content[5:].decode('utf8'))
+ if server.theme:
+ config['default_theme'] = '/static/gerrit-theme.html'
+ for filename, path in server.plugins.items():
+ pluginname = filename.split(".")[0]
+ payload = config["plugin"]["js_resource_paths" if filename.endswith(".js") else "html_resource_paths"]
+ if list(filter(lambda url: filename in url, payload)):
+ continue
+ payload.append("plugins/" + pluginname + "/static/" + filename)
+ flow.response.content = str.encode(")]}'\n" + json.dumps(config))
+ if m is not None:
+ filepath = m.groups()[0]
+ if (filepath.startswith("bower_components/")):
+ with zipfile.ZipFile(server.components + "test_components.zip") as bower_zip:
+ content = bower_zip.read(filepath)
+ localfile = server.devpath + filepath
+ elif pluginmatch is not None:
+ pluginfile = flow.request.path_components[-1]
+ if server.plugins and pluginfile in server.plugins:
+ if os.path.isfile(server.plugins[pluginfile]):
+ localfile = server.plugins[pluginfile]
+ else:
+ print("Can't find file " + server.plugins[pluginfile] + " for " + flow.request.path)
+ elif server.pluginroot:
+ pluginurl = pluginmatch.groups()[0]
+ if os.path.isfile(server.pluginroot + pluginfile):
+ localfile = server.pluginroot + pluginfile
+ elif os.path.isfile(server.pluginroot + pluginurl):
+ localfile = server.pluginroot + pluginurl
+
+ if server.theme:
+ if flow.request.path.endswith('/gerrit-theme.html'):
+ localfile = server.theme
+ else:
+ match = re.match("^/static(/[\w\.]+)$", flow.request.path)
+ if match is not None:
+ localfile = os.path.dirname(server.theme) + match.group(1)
+
+ if localfile and os.path.isfile(localfile):
+ if pluginmatch is not None:
+ print("Serving " + flow.request.path + " from " + localfile)
+ content = server.readfile(localfile)
+
+ if content:
+ flow.response.content = content
+ flow.response.status_code = 200
+ localtype = mimetypes.guess_type(localfile)
+ if localtype and localtype[0]:
+ flow.response.headers['Content-type'] = localtype[0]
+
+def expandpath(path):
+ return os.path.realpath(os.path.expanduser(path))
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--app", type=str, default="", help="Path to /polygerrit-ui/app/")
+parser.add_argument("--components", type=str, default="", help="Path to test_components.zip")
+parser.add_argument("--plugins", type=str, default="", help="Comma-separated list of plugin files to add/replace")
+parser.add_argument("--plugin_root", type=str, default="", help="Path containing individual plugin files to replace")
+parser.add_argument("--assets", type=str, default="", help="Path containing assets file to import.")
+parser.add_argument("--strip_assets", action="store_true", help="Strip plugin bundles from the response.")
+parser.add_argument("--theme", default="", type=str, help="Path to the default site theme to be used.")
+args = parser.parse_args()
+server = Server(expandpath(args.app) + '/',
+ expandpath(args.components) + '/',
+ args.plugins,
+ expandpath(args.plugin_root) + '/',
+ args.assets and expandpath(args.assets),
+ args.strip_assets,
+ expandpath(args.theme))
diff --git a/contrib/mitm-ui/serve-app-locally.py b/contrib/mitm-ui/serve-app-locally.py
new file mode 100644
index 0000000000..636c68478e
--- /dev/null
+++ b/contrib/mitm-ui/serve-app-locally.py
@@ -0,0 +1,46 @@
+# bazel build polygerrit-ui/app:gr-app
+# mitmdump -s "serve-app-locally.py ~/gerrit/bazel-bin/polygerrit-ui/app"
+from mitmproxy import http
+import argparse
+import os
+import zipfile
+
+class Server:
+ def __init__(self, bundle):
+ self.bundle = bundle
+ self.bundlemtime = 0
+ self.files = {
+ 'polygerrit_ui/elements/gr-app.js': '',
+ 'polygerrit_ui/elements/gr-app.html': '',
+ 'polygerrit_ui/styles/main.css': '',
+ }
+ self.read_files()
+
+ def read_files(self):
+ if not os.path.isfile(self.bundle):
+ print("bundle not found!")
+ return
+ mtime = os.stat(self.bundle).st_mtime
+ if mtime <= self.bundlemtime:
+ return
+ self.bundlemtime = mtime
+ with zipfile.ZipFile(self.bundle) as z:
+ for fname in self.files:
+ print('Reading new content for ' + fname)
+ with z.open(fname, 'r') as content_file:
+ self.files[fname] = content_file.read()
+
+ def response(self, flow: http.HTTPFlow) -> None:
+ self.read_files()
+ for name in self.files:
+ if name.rsplit('/', 1)[1] in flow.request.pretty_url:
+ flow.response.content = self.files[name]
+
+def expandpath(path):
+ return os.path.expanduser(path)
+
+def start():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("bundle", type=str)
+ args = parser.parse_args()
+ return Server(expandpath(args.bundle))
diff --git a/contrib/themes/diffy/etc/GerritSite.css b/contrib/themes/diffy/etc/GerritSite.css
deleted file mode 100644
index 76c595a2b7..0000000000
--- a/contrib/themes/diffy/etc/GerritSite.css
+++ /dev/null
@@ -1,29 +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.
- */
-#gerrit_topmenu {
- left: 60px;
- margin-right: 60px;
- position: relative;
- margin-bottom: 5px;
-}
-
-#diffy_logo {
- display: block !important;
- margin-bottom: -55px;
- padding-left: 10px;
- position: relative;
- top: -55px;
- width: 60px;
-}
diff --git a/contrib/themes/diffy/etc/GerritSiteHeader.html b/contrib/themes/diffy/etc/GerritSiteHeader.html
deleted file mode 100644
index 89b4db5149..0000000000
--- a/contrib/themes/diffy/etc/GerritSiteHeader.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div>
- <div id="diffy_logo">
- <img width="50" height="46" src="static/logo.png"/>
- </div>
-</div>
diff --git a/contrib/themes/diffy/static/diffy.svg b/contrib/themes/diffy/static/diffy.svg
deleted file mode 100644
index 3e6e6c28d5..0000000000
--- a/contrib/themes/diffy/static/diffy.svg
+++ /dev/null
@@ -1,326 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="600.06732"
- height="558.20709"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.3.1 r9886"
- sodipodi:docname="diffy.svg"
- inkscape:export-filename="/home/sarah/art/diffy.png"
- inkscape:export-xdpi="500"
- inkscape:export-ydpi="500">
- <defs
- id="defs4" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.66618777"
- inkscape:cx="300.03365"
- inkscape:cy="258.35521"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="1366"
- inkscape:window-height="744"
- inkscape:window-x="0"
- inkscape:window-y="24"
- inkscape:window-maximized="1"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-3069.8852,2251.5084)">
- <text
- xml:space="preserve"
- style="font-size:51.65934372px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans Bold"
- x="475.83173"
- y="900.47076"
- id="text4257"
- sodipodi:linespacing="125%"
- transform="scale(1.1411753,0.87628955)"><tspan
- sodipodi:role="line"
- x="475.83173"
- y="900.47076"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;text-anchor:end;font-family:Acme;-inkscape-font-specification:Acme Bold"
- id="tspan3008" /></text>
- <flowRoot
- xml:space="preserve"
- id="flowRoot4263"
- style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
- id="flowRegion4265"><rect
- id="rect4267"
- width="313.14728"
- height="343.45187"
- x="-335.37064"
- y="205.85435" /></flowRegion><flowPara
- id="flowPara4269" /></flowRoot> <g
- id="g10119"
- transform="matrix(0.99201906,-0.12608805,0.12608805,0.99201906,273.85438,408.58042)">
- <path
- sodipodi:nodetypes="csccscac"
- inkscape:connector-curvature="0"
- id="path4130"
- d="m 3378.9965,-2206.2972 c 0,0 13.4039,-8.8398 27.0022,-17.1718 13.5984,-8.3319 22.2322,-21.276 22.2322,-21.276 l 34.2799,28.1467 c 0,0 -16.0641,15.553 -24.7742,21.6893 -3.709,2.6129 -10.9463,9.1577 -10.9463,9.1577 0,0 -14.3617,-10.0769 -22.3623,-13.5225 -8.0773,-3.4786 -25.4315,-7.0234 -25.4315,-7.0234 z"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cac"
- inkscape:connector-curvature="0"
- d="m 3414.7387,-2204.9828 c 0,0 8.2233,-5.8571 11.9326,-9.2622 3.8224,-3.5089 10.5596,-11.437 10.5596,-11.437"
- style="fill:#ffffff;stroke:#ff0000;stroke-width:8.97858429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="path4185" />
- <path
- sodipodi:nodetypes="cascacsc"
- inkscape:connector-curvature="0"
- id="path3307"
- d="m 3247.6516,-2171.4037 c 0,0 4.3348,-19.2835 10.7071,-26.2701 7.4284,-8.1446 19.072,-14.1542 29.3495,-15.24 27.8177,-2.9386 63.2371,6.1908 63.2371,6.1908 0,0 -30.8042,0.7262 -45.6022,4.5134 -5.1922,1.3289 -14.7941,6.2977 -14.7941,6.2977 0,0 -16.1166,0.8374 -29.8971,12.0927 -13.7804,11.2552 -3.0344,9.7831 -3.0344,9.7831"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="ccsccscscccccscc"
- inkscape:connector-curvature="0"
- id="path3011"
- d="m 3332.9985,-2012.4352 -20.0742,24.9886 c 0,0 -16.3479,-5.082 -40.3255,4.9725 -23.9776,10.0546 -39.2193,26.4042 -39.2193,26.4042 l 7.8897,-0.7975 c 0,0 20.4818,-14.4371 31.9614,-17.1294 11.4796,-2.6923 23.6197,-2.8388 23.6197,-2.8388 0,0 -25.492,10.0264 -30.7497,12.3759 -6.2584,2.7968 -15.0874,9.199 -15.0874,9.199 l 10.4761,-0.2965 c 17.6867,-7.2528 41.3221,-16.0187 59.9685,-15.047 15.5919,1.1274 33.9405,16.7543 33.9405,16.7543 l -3.8165,-9.9895 c 0,0 -10.8307,-8.1932 -16.3866,-13.7491 -5.5558,-5.5558 17.5848,-25.8193 17.5848,-25.8193 -14.3098,-2.8351 -2.1172,-7.9424 -19.7815,-9.0274 z"
- style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 3397.9929,-1984.0461 -15.5774,33.0846 c 0,0 -14.4499,-2.2165 -39.5097,4.7139 -25.0597,6.9303 -44.9575,20.0476 -44.9575,20.0476 l 10.5706,1.822 c 0,0 19.7276,-11.1182 30.516,-14.1955 7.747,-2.2097 23.9934,-2.9008 23.9934,-2.9008 0,0 -20.2687,6.6869 -30.057,10.9033 -5.4067,2.329 -15.7817,7.9273 -15.7817,7.9273 l 10.8391,1.5269 c 0,0 31.056,-11.8625 47.4047,-13.3504 11.1103,-1.0112 11.8331,-2.962 33.324,3.1079 10.0509,2.8388 16.3928,6.252 16.3928,6.252 l -3.3122,-5.8696 c 0,0 -5.3909,-4.5711 -8.2712,-6.6111 -3.5982,-2.5484 -11.2202,-7.0059 -11.2202,-7.0059 l 15.4484,-30.858 c -13.3255,-5.9362 -2.8177,-3.6211 -19.8021,-8.5942 z"
- id="path3013"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccsccacaccasccaccc" />
- <path
- sodipodi:nodetypes="aaczsccsssca"
- inkscape:connector-curvature="0"
- id="path3014"
- d="m 3138.8926,-2124.2277 c 9.8375,-20.5358 27.6337,-38.9533 48.4705,-48.136 22.7321,-10.018 51.1582,-10.0879 74.4382,-3.5941 7.6944,6.5 10.3115,-17.1804 46.9059,-27.4208 36.5944,-10.2403 91.6429,-6.509 142.1505,22.7854 54.0494,31.3487 -23.7386,41.9213 -8.0812,94.4492 32.3844,113.578 -63.1345,101.5204 -63.1345,101.5204 0,0 -25.2539,-16.1625 -35.3554,-17.1726 -10.1015,-1.0102 -46.6429,-25.1251 -68.8662,-28.6607 -22.2234,-3.5355 -36.9003,18.6222 -107.991,-9.9282 -24.3178,-9.7661 -33.8401,-33.335 -33.8401,-33.335 0,0 -2.0102,-35.2405 5.3033,-50.5076 z"
- style="fill:#0000ff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cssscscsac"
- inkscape:connector-curvature="0"
- id="path3028"
- d="m 3179.7636,-2035.4822 c 0,0 33.988,-7.9036 57.2215,-14.9746 23.2335,-7.0711 36.1637,-43.7474 71.519,-48.7981 35.3554,-5.0508 68.528,22.1624 80.6498,47.4162 12.1218,25.2539 50.6701,55.6193 50.6701,55.6193 0,0 1.0101,11.1117 -14.1422,18.1828 -15.1522,7.0711 -10.4989,7.6628 -10.4989,7.6628 0,0 -43.1103,-11.4139 -62.396,-21.4139 -19.2857,-10 -61.236,-23.044 -92.9377,-30.7963 -26.2653,-6.4228 -35.0334,2.5255 -80.0856,-12.8982 z"
- style="fill:#ff6600;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cscccccscc"
- inkscape:connector-curvature="0"
- id="path3009"
- d="m 3470.1209,-2002.8128 c 0,0 13.8937,99.0002 33.6829,157.8517 19.7893,58.8514 78.4464,180.4742 78.4464,180.4742 l 28.1031,-31.8521 -52.5625,-109.7203 60.5152,95.9016 26.0124,-35.7271 c 0,0 -70.663,-88.4878 -93.8954,-140.4597 -23.2324,-51.9718 -37.4945,-152.9082 -37.4945,-152.9082 z"
- style="fill:#0000ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="csscsccccccccccsc"
- inkscape:connector-curvature="0"
- id="path3016"
- d="m 3359.2154,-2191.502 c 0,0 -40.7142,5 -57.1428,42.1428 -16.4286,37.1429 50.7143,115.7143 60.7143,132.1429 10,16.4286 24.2857,38.5714 24.2857,38.5714 0,0 47.6766,32.0335 82.9062,53.8444 32.6567,20.2179 65.2266,33.1109 65.2266,33.1109 0,0 1.1687,-0.4937 7.0265,-19.1963 -12.1218,-21.7183 -38.3216,-53.2543 -54.5036,-69.192 24.2792,14.1167 43.346,28.1254 60.1041,56.5482 0,0 4.9588,-21.3916 6.5659,-30.7894 -3.0357,-9.5763 -47.4771,-56.5685 -47.4771,-56.5685 20.5517,8.7613 33.7508,23.0255 50.0892,44.7732 0,0 3.3263,-21.5598 3.8366,-32.9472 -2.5253,-6.566 -33.7228,-35.0595 -33.7228,-35.0595 13.1654,6.6374 19.714,11.0661 34.4127,24.0476 0,0 -3.0357,-60.7142 -66.6072,-130.7142 -63.5714,-70 -135.7143,-50.7143 -135.7143,-50.7143 z"
- style="fill:#000080;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <g
- transform="translate(2935.644,-2513.1499)"
- id="g3138">
- <path
- style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 337.66259,394.0585 c 0,0 -28.86328,-30.61257 -60.78781,-25.80202 -31.92454,4.81054 -41.10831,33.67382 -39.79634,39.79633 1.31197,6.12252 13.557,11.80771 22.30345,15.74361 8.74644,3.9359 32.79917,-0.43732 45.48152,-6.55984 12.68235,-6.12251 32.79918,-23.17808 32.79918,-23.17808 z"
- id="path3022"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-miterlimit:4"
- d="m 286.65383,372.49887 c 12.50235,2.18615 20.86528,14.09354 18.67912,26.59589 -2.18615,12.50234 -14.09354,20.86527 -26.59589,18.67911 -12.50235,-2.18616 -20.86528,-14.09354 -18.67912,-26.59588 z"
- id="path3024"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csscc" />
- <path
- sodipodi:type="arc"
- style="fill:#000000;fill-opacity:1;stroke:none"
- id="path3026"
- sodipodi:cx="229.64285"
- sodipodi:cy="403.79074"
- sodipodi:rx="8.2142859"
- sodipodi:ry="8.2142859"
- d="m 237.85714,403.79074 c 0,4.53663 -3.67766,8.21429 -8.21429,8.21429 -4.53662,0 -8.21428,-3.67766 -8.21428,-8.21429 0,-4.53662 3.67766,-8.21428 8.21428,-8.21428 4.53663,0 8.21429,3.67766 8.21429,8.21428 z"
- transform="matrix(1.5694327,0,0,1.5694327,-85.566867,-233.30737)" />
- </g>
- <path
- sodipodi:nodetypes="cssssssc"
- style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 3110.2692,-2103.8575 c -32.3249,20.7081 -31.4522,56.0845 -27.4116,60.6301 4.0406,4.5457 8.0812,1.0102 15.6574,-1.5152 7.5761,-2.5254 26.2639,-10.1015 42.4264,-11.1117 16.1624,-1.0101 21.2132,3.0305 23.7386,-0.505 2.5254,-3.5356 4.0406,-15.1523 3.0304,-25.7589 -1.0101,-10.6066 -1.0101,-23.2335 -7.071,-25.2538 -6.061,-2.0203 -38.2484,-5.0718 -50.3702,3.5145 z"
- id="path3296"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path3305"
- d="m 3251.1404,-2178.2729 c 0,0 12.1428,21.0714 67.1428,32.5 55,11.4286 125.7143,-38.2143 125.7143,-38.2143 l 38.7857,38.6429 c 0,0 -113.0714,25.6428 -150.2143,21 -37.1428,-4.6429 -81.4285,-50.3572 -81.4285,-50.3572"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="ccccc"
- inkscape:connector-curvature="0"
- id="path4099"
- d="m 3453.1169,-2166.1191 -30.2186,14.3667 6.8378,5.5184 29.9689,-14.0733 z"
- style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
- <path
- sodipodi:nodetypes="cacscacsc"
- inkscape:connector-curvature="0"
- id="path3303"
- d="m 3154.8351,-2149.0637 c 0,0 -6.1891,7.2925 -8.7928,11.2888 -2.8625,4.3936 -7.4472,13.8569 -7.4472,13.8569 0,0 49.8667,-24.0831 67.6065,-30.2972 18.2787,-6.4028 54.803,-21.7873 54.803,-21.7873 0,0 -3.8451,-1.8876 -5.9103,-2.3872 -3.1018,-0.7503 -9.5343,-0.868 -9.5343,-0.868 0,0 -25.7837,-0.1235 -42.8032,6.1343 -17.0196,6.2579 -47.9217,24.0597 -47.9217,24.0597 z"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
- d="m 3433.0379,-2169.2212 24.4739,22.8165 -8.1844,3.1976 -24.3243,-22.4616 z"
- id="path27241"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccc" />
- </g>
- <g
- id="g10229"
- transform="translate(31.403024,6.4439689)">
- <g
- id="text4253"
- style="font-size:140.50161743px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans Bold"
- transform="scale(1.066563,0.93759113)">
- <path
- id="path10219"
- style="font-family:Acme;-inkscape-font-specification:Acme Bold"
- d="m 2917.8887,-1930.8568 c -6.6505,0 -16.2514,-0.5152 -28.8029,-1.5455 l 1.6861,-34.1419 -1.6861,-62.8043 37.514,0 c 12.645,10e-5 22.527,3.7937 29.6458,11.3807 7.1187,7.5871 10.678,18.1247 10.6781,31.6128 -10e-5,17.0476 -4.4024,30.5826 -13.2071,40.605 -8.8049,9.9288 -20.7475,14.8932 -35.8279,14.8932 m 3.653,-83.3175 c -6.1821,10e-5 -10.5376,0.047 -13.0666,0.1405 l -1.4051,45.101 0.9836,22.7613 c 7.868,0.562 13.1602,0.843 15.8766,0.843 7.3997,0 13.2071,-2.9973 17.4222,-8.9921 4.3087,-5.9947 6.463,-14.7058 6.4631,-26.1333 -1e-4,-11.4274 -2.1076,-19.9043 -6.3226,-25.4308 -4.1214,-5.5263 -10.7718,-8.2895 -19.9512,-8.2896"
- inkscape:connector-curvature="0" />
- <path
- id="path10221"
- style="font-family:Acme;-inkscape-font-specification:Acme Bold"
- d="m 3002.3389,-2030.051 -1.967,61.1182 1.686,36.5305 -19.6702,0 1.686,-34.1419 -1.686,-59.9942 19.9512,-3.5126"
- inkscape:connector-curvature="0" />
- <path
- id="path10223"
- style="font-family:Acme;-inkscape-font-specification:Acme Bold"
- d="m 3074.2384,-2014.3148 -29.9268,0 -0.843,25.8523 26.8358,0 -1.5455,14.8932 -25.7118,0 -0.1405,4.6365 1.686,36.5305 -19.6703,0 1.6861,-34.1419 -1.6861,-62.8043 50.8616,0 -1.5455,15.0337"
- inkscape:connector-curvature="0" />
- <path
- id="path10225"
- style="font-family:Acme;-inkscape-font-specification:Acme Bold"
- d="m 3136.2567,-2014.3148 -29.9268,0 -0.843,25.8523 26.8358,0 -1.5456,14.8932 -25.7117,0 -0.1405,4.6365 1.686,36.5305 -19.6703,0 1.6861,-34.1419 -1.6861,-62.8043 50.8616,0 -1.5455,15.0337"
- inkscape:connector-curvature="0" />
- <path
- id="path10227"
- style="font-family:Acme;-inkscape-font-specification:Acme Bold"
- d="m 3177.4808,-1987.9005 1.686,0 10.3971,-19.1082 10.5376,-23.1828 17.1412,2.5291 -12.9261,23.4637 -18.2652,35.6874 1.5455,36.109 -19.6702,0 1.405,-33.7204 -17.7032,-35.9684 -13.4882,-24.5878 19.8107,-3.5126 9.1326,21.3563 10.3972,20.9347"
- inkscape:connector-curvature="0" />
- </g>
- <g
- id="text3010"
- style="font-size:62.54807663px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Acme;-inkscape-font-specification:Acme"
- transform="scale(1.1647576,0.85854772)">
- <path
- id="path10175"
- d="m 2645.5222,-2090.4097 0.688,-7.1931 29.7104,0 -0.688,7.1931 -10.8834,-0.9382 -0.2502,20.6408 0.7506,16.2625 -8.7568,0 0.7506,-15.1992 -0.2502,-21.6416 -11.071,0.8757"
- inkscape:connector-curvature="0" />
- <path
- id="path10177"
- d="m 2697.8329,-2070.2692 c 0,-2.8772 -0.2502,-4.7954 -0.7505,-5.7545 -0.5005,-0.959 -1.668,-1.4385 -3.5027,-1.4386 -1.8348,1e-4 -3.9197,0.7298 -6.2548,2.1892 l 0,4.566 0.688,14.6988 -8.444,1.5637 0.7506,-15.1992 -0.7506,-31.6493 8.5691,-1.5637 -0.8131,16.2 0,7.3181 c 0.6254,-0.834 1.3343,-1.6471 2.1266,-2.4394 0.7923,-0.7922 2.0224,-1.6888 3.6903,-2.6896 1.668,-1.0007 3.4401,-1.5011 5.3166,-1.5011 1.9181,0 3.5235,0.7506 4.8162,2.2517 1.2926,1.4595 1.939,3.461 1.939,6.0046 l -0.3753,7.0054 0.688,15.0116 -8.4439,1.5637 0.7505,-16.1374"
- inkscape:connector-curvature="0" />
- <path
- id="path10179"
- d="m 2723.6897,-2060.8245 c 1.2093,0 2.4811,-0.3127 3.8155,-0.9382 1.3343,-0.6255 2.3768,-1.251 3.1274,-1.8764 l 1.1258,-0.9383 2.8772,3.5027 c -0.417,0.7089 -1.0633,1.522 -1.939,2.4394 -0.8757,0.9174 -1.7722,1.7096 -2.6895,2.3768 -0.8757,0.6255 -2.0641,1.2093 -3.5653,1.7514 -1.4594,0.5004 -2.9814,0.7505 -4.566,0.7505 -3.3776,0 -6.1297,-1.2718 -8.2563,-3.8154 -2.1267,-2.5853 -3.19,-5.9003 -3.19,-9.9451 0,-5.0873 1.4803,-9.549 4.4409,-13.3853 2.9606,-3.8363 6.4216,-5.7544 10.383,-5.7544 3.044,0 5.4,0.8548 7.068,2.5644 1.7096,1.7097 2.5644,4.1074 2.5644,7.1931 0,1.8347 -0.3127,3.9197 -0.9382,6.2548 l -1.251,1.3135 -15.637,1.4386 c 0.7089,4.7119 2.9189,7.0679 6.6301,7.0679 m 0,-19.3899 c -1.8347,0 -3.3776,0.7506 -4.6285,2.2517 -1.251,1.4595 -1.9807,3.336 -2.1892,5.6294 l 10.6332,-1.3135 c 0.125,-0.9591 0.1876,-1.7514 0.1876,-2.3769 0,-2.7938 -1.3344,-4.1907 -4.0031,-4.1907"
- inkscape:connector-curvature="0" />
- <path
- id="path10181"
- d="m 2760.1934,-2097.9155 -0.6881,20.3907 15.9498,-20.2656 6.3799,4.2533 -14.3861,16.7003 15.2618,18.0764 -8.0062,5.0038 -15.2617,-21.579 -0.1251,4.6285 0.7506,16.2625 -8.7568,0 0.7506,-15.1992 -0.7506,-26.708 8.8819,-1.5637"
- inkscape:connector-curvature="0" />
- <path
- id="path10183"
- d="m 2793.6018,-2054.4446 c -2.2517,0 -4.0864,-0.7297 -5.5042,-2.1892 -1.376,-1.5011 -2.0641,-3.461 -2.0641,-5.8795 l 0.3753,-7.193 -0.688,-15.0115 8.3814,-1.5638 -0.8131,19.0147 c 0,1.793 0.3127,3.1065 0.9382,3.9405 0.6672,0.834 1.7097,1.251 3.1274,1.251 1.4178,0 3.3776,-0.7089 5.8796,-2.1267 l 0,-3.7529 -0.6881,-16.4501 8.444,-1.5637 -0.7506,15.637 0.063,5.129 c 0,2.6687 0.688,5.3582 2.0641,8.0687 l -6.505,2.8772 c -1.3761,-2.7104 -2.2101,-4.8788 -2.502,-6.505 -3.7529,4.2115 -7.0053,6.3173 -9.7575,6.3173"
- inkscape:connector-curvature="0" />
- <path
- id="path10185"
- d="m 2836.1199,-2070.2692 c 0,-2.8772 -0.2502,-4.7954 -0.7506,-5.7545 -0.5004,-0.959 -1.668,-1.4385 -3.5027,-1.4386 -1.8347,1e-4 -3.9197,0.7298 -6.2548,2.1892 l 0,4.566 0.688,14.6988 -8.3814,1.5637 0.7506,-15.1992 c -0.1668,-5.5876 -0.6255,-10.5914 -1.3761,-15.0115 l 7.5683,-1.6263 c 0.2919,2.4186 0.5004,4.7746 0.6255,7.068 0.6255,-0.834 1.3344,-1.6471 2.1266,-2.4394 0.7923,-0.8339 2.0433,-1.7513 3.7529,-2.7521 1.7097,-1.0424 3.5027,-1.5637 5.3792,-1.5637 1.9181,0 3.5235,0.7506 4.8162,2.2517 1.2926,1.4595 1.9389,3.461 1.939,6.0046 l -0.3753,7.0054 0.688,15.0116 -8.444,1.5637 0.7506,-16.1374"
- inkscape:connector-curvature="0" />
- <path
- id="path10187"
- d="m 2848.5318,-2048.2523 c 0,-3.6695 3.4193,-6.5467 10.2579,-8.6317 -1.6263,-0.8339 -2.4394,-1.9181 -2.4394,-3.2525 0,-0.542 0.2085,-1.0841 0.6255,-1.6262 0.4169,-0.5421 1.0216,-1.105 1.8139,-1.6888 l 0,-0.5004 c -3.0024,0 -5.4,-0.834 -7.1931,-2.5019 -1.7513,-1.6679 -2.627,-3.8988 -2.627,-6.6927 0,-3.6277 1.4803,-6.776 4.4409,-9.4447 2.9606,-2.7104 6.4633,-4.0656 10.5081,-4.0656 1.0425,0 2.5645,0.2085 4.566,0.6254 2.0015,0.4171 3.9614,0.6256 5.8795,0.6255 l 4.8162,0 -0.6254,5.4417 -4.6912,-0.5629 c 0.417,1.2093 0.6255,2.5853 0.6255,4.1281 0,1.5429 -0.3753,3.0858 -1.1258,4.6286 -0.7089,1.5012 -1.5846,2.7104 -2.6271,3.6278 -1.0007,0.9174 -2.0224,1.7305 -3.0648,2.4394 -1.0008,0.6672 -1.8765,1.2718 -2.627,1.8139 -0.7089,0.5004 -1.0634,0.959 -1.0633,1.376 -1e-4,0.7923 0.8339,1.4803 2.5019,2.0641 4.42,1.7931 7.4015,3.3151 8.9443,4.566 1.5846,1.251 2.3768,2.7938 2.3769,4.6286 -10e-5,3.0857 -1.5638,5.6084 -4.6911,7.5683 -3.0858,2.0015 -6.7761,3.0023 -11.071,3.0023 -4.295,0 -7.6309,-0.688 -10.0077,-2.0641 -2.3352,-1.3761 -3.5027,-3.2108 -3.5027,-5.5042 m 18.8895,-26.2702 c 0,-2.0432 -0.4587,-3.5444 -1.3761,-4.5035 -0.9174,-0.959 -2.356,-1.4386 -4.3158,-1.4386 -3.7946,0 -5.6919,1.8765 -5.6919,5.6293 0,3.7112 2.0224,5.5668 6.0672,5.5668 3.5444,0 5.3166,-1.7513 5.3166,-5.254 m 2.2517,24.2061 c 0,-0.7506 -0.3961,-1.4595 -1.1884,-2.1266 -0.7923,-0.6255 -2.21,-1.3761 -4.2533,-2.2518 -3.044,0.834 -5.1498,1.6054 -6.3173,2.3143 -1.1259,0.7089 -1.6888,1.5846 -1.6888,2.627 0,1.0425 0.5629,1.8765 1.6888,2.5019 1.1675,0.6672 2.7729,1.0008 4.8162,1.0008 2.0432,0 3.7112,-0.3753 5.0038,-1.1258 1.2927,-0.7506 1.939,-1.7305 1.939,-2.9398"
- inkscape:connector-curvature="0" />
- <path
- id="path10189"
- d="m 2915.4651,-2090.9101 -13.3228,0 -0.3753,11.5088 11.9467,0 -0.688,6.6301 -11.4463,0 -0.063,2.0641 0.7506,16.2625 -8.7567,0 0.7506,-15.1992 -0.7506,-27.959 22.6424,0 -0.688,6.6927"
- inkscape:connector-curvature="0" />
- <path
- id="path10191"
- d="m 2927.2495,-2054.4446 c -2.2517,0 -4.0865,-0.7297 -5.5042,-2.1892 -1.3761,-1.5011 -2.0641,-3.461 -2.0641,-5.8795 l 0.3753,-7.193 -0.6881,-15.0115 8.3815,-1.5638 -0.8131,19.0147 c -10e-5,1.793 0.3127,3.1065 0.9382,3.9405 0.6671,0.834 1.7096,1.251 3.1274,1.251 1.4177,0 3.3776,-0.7089 5.8795,-2.1267 l 0,-3.7529 -0.688,-16.4501 8.444,-1.5637 -0.7506,15.637 0.063,5.129 c 0,2.6687 0.688,5.3582 2.0641,8.0687 l -6.505,2.8772 c -1.3761,-2.7104 -2.21,-4.8788 -2.5019,-6.505 -3.7529,4.2115 -7.0054,6.3173 -9.7575,6.3173"
- inkscape:connector-curvature="0" />
- </g>
- <g
- id="text3016"
- style="font-size:55.5345993px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Acme;-inkscape-font-specification:Acme"
- transform="scale(1.0640965,0.93976441)">
- <path
- id="path10194"
- d="m 2910.1662,-1832.5692 -3.1655,0 0,0.3332 0.6664,14.439 -7.4416,0 0.6664,-13.4949 -0.6664,-24.8239 12.051,0 c 3.5542,0 6.3864,0.9626 8.4968,2.8878 2.1103,1.9252 3.1654,4.4798 3.1655,7.6637 -10e-5,2.4066 -0.6295,4.6094 -1.8882,6.6087 -1.2218,1.9622 -2.8878,3.5172 -4.9981,4.6649 l 9.663,13.6615 -6.7197,3.4431 -8.6634,-15.4386 c -0.2592,0.037 -0.6479,0.056 -1.1662,0.056 m 0.111,-17.9376 -2.7212,0 -0.3887,12.5508 3.7764,0 c 1.8511,0 3.2765,-0.6109 4.2761,-1.8327 1.0366,-1.2217 1.555,-2.9618 1.555,-5.2202 0,-3.6653 -2.1659,-5.4979 -6.4976,-5.4979"
- inkscape:connector-curvature="0" />
- <path
- id="path10196"
- d="m 2941.1059,-1823.4615 c 1.0736,0 2.2028,-0.2777 3.3876,-0.833 1.1847,-0.5553 2.1103,-1.1107 2.7767,-1.666 l 0.9997,-0.8331 2.5545,3.11 c -0.3702,0.6294 -0.9441,1.3513 -1.7215,2.1658 -0.7775,0.8145 -1.5735,1.518 -2.388,2.1103 -0.7775,0.5554 -1.8327,1.0737 -3.1655,1.555 -1.2958,0.4443 -2.6471,0.6664 -4.054,0.6664 -2.9989,0 -5.4424,-1.1292 -7.3306,-3.3876 -1.8882,-2.2954 -2.8322,-5.2387 -2.8322,-8.83 0,-4.5168 1.3143,-8.4783 3.9429,-11.8844 2.6286,-3.4061 5.7016,-5.1092 9.2188,-5.1092 2.7026,0 4.7944,0.759 6.2754,2.2769 1.5179,1.518 2.2769,3.6468 2.2769,6.3865 0,1.6291 -0.2777,3.4802 -0.833,5.5535 l -1.1107,1.1662 -13.8837,1.2773 c 0.6294,4.1836 2.5916,6.2754 5.8867,6.2754 m 0,-17.2157 c -1.629,0 -2.9989,0.6664 -4.1096,1.9992 -1.1107,1.2959 -1.7586,2.9619 -1.9437,4.9981 l 9.4409,-1.1662 c 0.1111,-0.8515 0.1666,-1.5549 0.1666,-2.1103 0,-2.4805 -1.1847,-3.7208 -3.5542,-3.7208"
- inkscape:connector-curvature="0" />
- <path
- id="path10198"
- d="m 2965.2808,-1824.7388 0.7219,0 1.8327,-7.6082 2.9989,-13.7171 6.8862,0.833 -4.165,13.7726 -3.4987,12.9951 -9.4964,0.833 -3.2766,-13.4949 -3.9985,-13.4949 7.5527,-1.3883 2.388,12.2176 2.0548,9.0521"
- inkscape:connector-curvature="0" />
- <path
- id="path10200"
- d="m 2988.8795,-1846.0641 -0.6664,13.8281 0.6109,13.0507 -7.4416,1.3883 0.6664,-13.4949 -0.6109,-13.3838 7.4416,-1.3884 m -7.8303,-8.108 c 0,-1.2588 0.4812,-2.3695 1.4439,-3.3321 0.9625,-0.9996 2.0732,-1.4994 3.332,-1.4994 1.2588,0 2.2214,0.3517 2.8878,1.0551 0.6664,0.6665 0.9996,1.6846 0.9997,3.0544 -10e-5,1.3699 -0.4814,2.5547 -1.4439,3.5542 -0.9256,0.9627 -2.0178,1.444 -3.2766,1.4439 -1.2218,10e-5 -2.1844,-0.4072 -2.8878,-1.2217 -0.7034,-0.8145 -1.0551,-1.8326 -1.0551,-3.0544"
- inkscape:connector-curvature="0" />
- <path
- id="path10202"
- d="m 3004.9924,-1823.4615 c 1.0736,0 2.2028,-0.2777 3.3876,-0.833 1.1847,-0.5553 2.1103,-1.1107 2.7767,-1.666 l 0.9996,-0.8331 2.5546,3.11 c -0.3702,0.6294 -0.9441,1.3513 -1.7215,2.1658 -0.7775,0.8145 -1.5735,1.518 -2.388,2.1103 -0.7775,0.5554 -1.8327,1.0737 -3.1655,1.555 -1.2958,0.4443 -2.6472,0.6664 -4.054,0.6664 -2.9989,0 -5.4424,-1.1292 -7.3306,-3.3876 -1.8882,-2.2954 -2.8323,-5.2387 -2.8323,-8.83 0,-4.5168 1.3144,-8.4783 3.943,-11.8844 2.6286,-3.4061 5.7015,-5.1092 9.2187,-5.1092 2.7027,0 4.7945,0.759 6.2754,2.2769 1.518,1.518 2.2769,3.6468 2.277,6.3865 -1e-4,1.6291 -0.2777,3.4802 -0.8331,5.5535 l -1.1106,1.1662 -13.8837,1.2773 c 0.6294,4.1836 2.5916,6.2754 5.8867,6.2754 m 0,-17.2157 c -1.6291,0 -2.9989,0.6664 -4.1096,1.9992 -1.1107,1.2959 -1.7586,2.9619 -1.9437,4.9981 l 9.4409,-1.1662 c 0.111,-0.8515 0.1666,-1.5549 0.1666,-2.1103 0,-2.4805 -1.1848,-3.7208 -3.5542,-3.7208"
- inkscape:connector-curvature="0" />
- <path
- id="path10204"
- d="m 3044.4948,-1824.7388 0.722,0 1.7771,-7.6082 2.8878,-13.7171 6.7752,0.833 -4.054,13.7726 -3.3876,12.9951 -9.1077,0.833 -3.0544,-13.4949 -0.2777,-0.8885 -3.7208,13.5504 -8.83,0.833 -3.221,-13.4949 -3.8319,-13.4949 7.275,-1.3883 2.277,12.2176 1.9992,9.0521 0.7219,0 4.776,-15.8273 -1.1662,-4.0541 7.164,-1.3883 2.3324,12.2176 1.9437,9.0521"
- inkscape:connector-curvature="0" />
- <path
- id="path10206"
- d="m 3081.9121,-1817.1861 c -4.1095,0 -7.5712,-1.7216 -10.3849,-5.1647 -2.8138,-3.4431 -4.2207,-7.6638 -4.2207,-12.6619 0,-6.1088 1.6846,-11.255 5.0537,-15.4386 3.4061,-4.1836 7.5712,-6.2754 12.4953,-6.2754 1.9251,0 3.7022,0.2037 5.3313,0.6109 1.666,0.3702 2.8507,0.759 3.5542,1.1662 l 0.9996,0.6109 -3.7763,7.7193 c -0.8516,-0.7404 -2.0548,-1.4254 -3.6098,-2.0548 -1.555,-0.6664 -2.9618,-0.9996 -4.2206,-0.9996 -2.5546,0 -4.6094,1.0552 -6.1644,3.1655 -1.5179,2.1103 -2.2769,5.1277 -2.2769,9.0521 0,3.8874 0.796,7.0899 2.388,9.6075 1.592,2.4805 3.7578,3.7208 6.4976,3.7208 1.7771,0 3.3876,-0.5553 4.8315,-1.666 1.4438,-1.1107 2.462,-2.5916 3.0544,-4.4428 l 4.6093,2.6656 c -1.2217,3.2581 -3.0914,5.8127 -5.6089,7.6638 -2.5176,1.8142 -5.3684,2.7212 -8.5524,2.7212"
- inkscape:connector-curvature="0" />
- <path
- id="path10208"
- d="m 3106.1131,-1817.797 c -1.9993,0 -3.6283,-0.6479 -4.8871,-1.9437 -1.2217,-1.3328 -1.8326,-3.0729 -1.8326,-5.2202 l 0.3332,-6.3865 -0.6109,-13.3283 7.4416,-1.3884 -0.7219,16.8825 c 0,1.592 0.2777,2.7583 0.833,3.4987 0.5924,0.7405 1.5179,1.1107 2.7767,1.1107 1.2588,0 2.9989,-0.6294 5.2203,-1.8882 l 0,-3.332 -0.6109,-14.6056 7.4972,-1.3884 -0.6664,13.8837 0.055,4.5538 c 0,2.3695 0.6109,4.7575 1.8326,7.164 l -5.7756,2.5545 c -1.2217,-2.4065 -1.9622,-4.3317 -2.2213,-5.7756 -3.3321,3.7394 -6.2199,5.609 -8.6634,5.609"
- inkscape:connector-curvature="0" />
- <path
- id="path10210"
- d="m 3137.3105,-1839.1778 c -1.629,0 -2.8878,0.5924 -3.7764,1.7771 -0.8885,1.1478 -1.3328,2.9249 -1.3328,5.3313 0,2.4066 0.4443,4.3503 1.3328,5.8312 0.9256,1.4439 2.2584,2.1658 3.9985,2.1658 0.8886,0 1.7586,-0.2776 2.6102,-0.833 0.8515,-0.5553 1.4994,-1.1107 1.9437,-1.666 l 0.6109,-0.8886 2.7767,2.8878 c -0.1111,0.1851 -0.2592,0.4443 -0.4443,0.7775 -0.1481,0.3332 -0.5554,0.9256 -1.2218,1.7771 -0.6294,0.8515 -1.3143,1.6105 -2.0547,2.2769 -0.7035,0.6294 -1.6291,1.2218 -2.7768,1.7771 -1.1477,0.5184 -2.3509,0.7775 -3.6097,0.7775 -2.7768,0 -5.0352,-1.1292 -6.7752,-3.3876 -1.7401,-2.2584 -2.6102,-5.2017 -2.6102,-8.83 0,-4.5168 1.3144,-8.4783 3.943,-11.8844 2.6286,-3.4061 5.7015,-5.1092 9.2187,-5.1092 1.0737,0 2.1474,0.1667 3.221,0.4998 1.0737,0.2962 2.1659,0.7775 3.2766,1.4439 l -3.8874,6.8308 c -1.3329,-1.0366 -2.8138,-1.555 -4.4428,-1.555"
- inkscape:connector-curvature="0" />
- <path
- id="path10212"
- d="m 3166.4532,-1817.2416 -10.4406,-15.3831 0,0.3887 0.6109,13.0507 -7.4971,1.3883 0.6664,-13.4949 -0.6664,-28.1005 7.6082,-1.3883 -0.722,26.8232 11.107,-12.1621 5.2202,3.5542 -9.7185,9.1077 10.1628,11.9955 -6.3309,4.2206"
- inkscape:connector-curvature="0" />
- <path
- id="path10214"
- d="m 3174.2514,-1829.4037 c 0,-4.7389 1.2773,-8.7559 3.8319,-12.051 2.5546,-3.295 5.5349,-4.9426 8.9411,-4.9426 3.4431,0 6.1088,1.1107 7.997,3.3321 1.9251,2.2214 2.8877,5.1647 2.8877,8.83 0,4.8871 -1.2402,8.9596 -3.7208,12.2176 -2.4435,3.221 -5.5349,4.8315 -9.2742,4.8315 -3.073,0 -5.6276,-1.1477 -7.6638,-3.4431 -1.9993,-2.3325 -2.9989,-5.2573 -2.9989,-8.7745 m 17.4379,-1.9437 c 0,-5.3683 -1.8327,-8.0525 -5.498,-8.0525 -3.8133,0 -5.72,2.5176 -5.72,7.5527 0,2.6286 0.5368,4.6834 1.6105,6.1643 1.0736,1.481 2.4805,2.2214 4.2206,2.2214 1.7771,0 3.1099,-0.6664 3.9985,-1.9992 0.9256,-1.3699 1.3884,-3.3321 1.3884,-5.8867"
- inkscape:connector-curvature="0" />
- <path
- id="path10216"
- d="m 3201.0425,-1829.4037 c 0,-4.7389 1.2773,-8.7559 3.8319,-12.051 2.5546,-3.295 5.5349,-4.9426 8.9411,-4.9426 3.4431,0 6.1088,1.1107 7.997,3.3321 1.9251,2.2214 2.8877,5.1647 2.8878,8.83 -10e-5,4.8871 -1.2403,8.9596 -3.7209,12.2176 -2.4435,3.221 -5.5349,4.8315 -9.2742,4.8315 -3.073,0 -5.6276,-1.1477 -7.6638,-3.4431 -1.9993,-2.3325 -2.9989,-5.2573 -2.9989,-8.7745 m 17.4379,-1.9437 c 0,-5.3683 -1.8327,-8.0525 -5.4979,-8.0525 -3.8134,0 -5.7201,2.5176 -5.7201,7.5527 0,2.6286 0.5368,4.6834 1.6105,6.1643 1.0737,1.481 2.4805,2.2214 4.2206,2.2214 1.7771,0 3.1099,-0.6664 3.9985,-1.9992 0.9256,-1.3699 1.3884,-3.3321 1.3884,-5.8867"
- inkscape:connector-curvature="0" />
- </g>
- </g>
- </g>
-</svg>
diff --git a/contrib/themes/diffy/static/diffymute.svg b/contrib/themes/diffy/static/diffymute.svg
deleted file mode 100644
index 6833ba6a9f..0000000000
--- a/contrib/themes/diffy/static/diffymute.svg
+++ /dev/null
@@ -1,198 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="600.06732"
- height="558.20709"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.3.1 r9886"
- sodipodi:docname="diffy.svg"
- inkscape:export-filename="/home/sarah/art/diffy.png"
- inkscape:export-xdpi="500"
- inkscape:export-ydpi="500">
- <defs
- id="defs4" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.66618777"
- inkscape:cx="300.03365"
- inkscape:cy="258.35521"
- inkscape:document-units="px"
- inkscape:current-layer="g10119"
- showgrid="false"
- inkscape:window-width="1366"
- inkscape:window-height="744"
- inkscape:window-x="0"
- inkscape:window-y="24"
- inkscape:window-maximized="1"
- fit-margin-top="0"
- fit-margin-left="0"
- fit-margin-right="0"
- fit-margin-bottom="0" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-3069.8852,2251.5084)">
- <text
- xml:space="preserve"
- style="font-size:51.65934372px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans Bold"
- x="475.83173"
- y="900.47076"
- id="text4257"
- sodipodi:linespacing="125%"
- transform="scale(1.1411753,0.87628955)"><tspan
- sodipodi:role="line"
- x="475.83173"
- y="900.47076"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:end;text-anchor:end;font-family:Acme;-inkscape-font-specification:Acme Bold"
- id="tspan3008" /></text>
- <flowRoot
- xml:space="preserve"
- id="flowRoot4263"
- style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
- id="flowRegion4265"><rect
- id="rect4267"
- width="313.14728"
- height="343.45187"
- x="-335.37064"
- y="205.85435" /></flowRegion><flowPara
- id="flowPara4269" /></flowRoot> <g
- id="g10119"
- transform="matrix(0.99201906,-0.12608805,0.12608805,0.99201906,273.85438,408.58042)">
- <path
- sodipodi:nodetypes="csccscac"
- inkscape:connector-curvature="0"
- id="path4130"
- d="m 3378.9965,-2206.2972 c 0,0 13.4039,-8.8398 27.0022,-17.1718 13.5984,-8.3319 22.2322,-21.276 22.2322,-21.276 l 34.2799,28.1467 c 0,0 -16.0641,15.553 -24.7742,21.6893 -3.709,2.6129 -10.9463,9.1577 -10.9463,9.1577 0,0 -14.3617,-10.0769 -22.3623,-13.5225 -8.0773,-3.4786 -25.4315,-7.0234 -25.4315,-7.0234 z"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cac"
- inkscape:connector-curvature="0"
- d="m 3414.7387,-2204.9828 c 0,0 8.2233,-5.8571 11.9326,-9.2622 3.8224,-3.5089 10.5596,-11.437 10.5596,-11.437"
- style="fill:#ffffff;stroke:#ff0000;stroke-width:8.97858429;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="path4185" />
- <path
- sodipodi:nodetypes="cascacsc"
- inkscape:connector-curvature="0"
- id="path3307"
- d="m 3247.6516,-2171.4037 c 0,0 4.3348,-19.2835 10.7071,-26.2701 7.4284,-8.1446 19.072,-14.1542 29.3495,-15.24 27.8177,-2.9386 63.2371,6.1908 63.2371,6.1908 0,0 -30.8042,0.7262 -45.6022,4.5134 -5.1922,1.3289 -14.7941,6.2977 -14.7941,6.2977 0,0 -16.1166,0.8374 -29.8971,12.0927 -13.7804,11.2552 -3.0344,9.7831 -3.0344,9.7831"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="ccsccscscccccscc"
- inkscape:connector-curvature="0"
- id="path3011"
- d="m 3332.9985,-2012.4352 -20.0742,24.9886 c 0,0 -16.3479,-5.082 -40.3255,4.9725 -23.9776,10.0546 -39.2193,26.4042 -39.2193,26.4042 l 7.8897,-0.7975 c 0,0 20.4818,-14.4371 31.9614,-17.1294 11.4796,-2.6923 23.6197,-2.8388 23.6197,-2.8388 0,0 -25.492,10.0264 -30.7497,12.3759 -6.2584,2.7968 -15.0874,9.199 -15.0874,9.199 l 10.4761,-0.2965 c 17.6867,-7.2528 41.3221,-16.0187 59.9685,-15.047 15.5919,1.1274 33.9405,16.7543 33.9405,16.7543 l -3.8165,-9.9895 c 0,0 -10.8307,-8.1932 -16.3866,-13.7491 -5.5558,-5.5558 17.5848,-25.8193 17.5848,-25.8193 -14.3098,-2.8351 -2.1172,-7.9424 -19.7815,-9.0274 z"
- style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 3397.9929,-1984.0461 -15.5774,33.0846 c 0,0 -14.4499,-2.2165 -39.5097,4.7139 -25.0597,6.9303 -44.9575,20.0476 -44.9575,20.0476 l 10.5706,1.822 c 0,0 19.7276,-11.1182 30.516,-14.1955 7.747,-2.2097 23.9934,-2.9008 23.9934,-2.9008 0,0 -20.2687,6.6869 -30.057,10.9033 -5.4067,2.329 -15.7817,7.9273 -15.7817,7.9273 l 10.8391,1.5269 c 0,0 31.056,-11.8625 47.4047,-13.3504 11.1103,-1.0112 11.8331,-2.962 33.324,3.1079 10.0509,2.8388 16.3928,6.252 16.3928,6.252 l -3.3122,-5.8696 c 0,0 -5.3909,-4.5711 -8.2712,-6.6111 -3.5982,-2.5484 -11.2202,-7.0059 -11.2202,-7.0059 l 15.4484,-30.858 c -13.3255,-5.9362 -2.8177,-3.6211 -19.8021,-8.5942 z"
- id="path3013"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccsccacaccasccaccc" />
- <path
- sodipodi:nodetypes="aaczsccsssca"
- inkscape:connector-curvature="0"
- id="path3014"
- d="m 3138.8926,-2124.2277 c 9.8375,-20.5358 27.6337,-38.9533 48.4705,-48.136 22.7321,-10.018 51.1582,-10.0879 74.4382,-3.5941 7.6944,6.5 10.3115,-17.1804 46.9059,-27.4208 36.5944,-10.2403 91.6429,-6.509 142.1505,22.7854 54.0494,31.3487 -23.7386,41.9213 -8.0812,94.4492 32.3844,113.578 -63.1345,101.5204 -63.1345,101.5204 0,0 -25.2539,-16.1625 -35.3554,-17.1726 -10.1015,-1.0102 -46.6429,-25.1251 -68.8662,-28.6607 -22.2234,-3.5355 -36.9003,18.6222 -107.991,-9.9282 -24.3178,-9.7661 -33.8401,-33.335 -33.8401,-33.335 0,0 -2.0102,-35.2405 5.3033,-50.5076 z"
- style="fill:#0000ff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cssscscsac"
- inkscape:connector-curvature="0"
- id="path3028"
- d="m 3179.7636,-2035.4822 c 0,0 33.988,-7.9036 57.2215,-14.9746 23.2335,-7.0711 36.1637,-43.7474 71.519,-48.7981 35.3554,-5.0508 68.528,22.1624 80.6498,47.4162 12.1218,25.2539 50.6701,55.6193 50.6701,55.6193 0,0 1.0101,11.1117 -14.1422,18.1828 -15.1522,7.0711 -10.4989,7.6628 -10.4989,7.6628 0,0 -43.1103,-11.4139 -62.396,-21.4139 -19.2857,-10 -61.236,-23.044 -92.9377,-30.7963 -26.2653,-6.4228 -35.0334,2.5255 -80.0856,-12.8982 z"
- style="fill:#ff6600;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="cscccccscc"
- inkscape:connector-curvature="0"
- id="path3009"
- d="m 3470.1209,-2002.8128 c 0,0 13.8937,99.0002 33.6829,157.8517 19.7893,58.8514 78.4464,180.4742 78.4464,180.4742 l 28.1031,-31.8521 -52.5625,-109.7203 60.5152,95.9016 26.0124,-35.7271 c 0,0 -70.663,-88.4878 -93.8954,-140.4597 -23.2324,-51.9718 -37.4945,-152.9082 -37.4945,-152.9082 z"
- style="fill:#0000ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="csscsccccccccccsc"
- inkscape:connector-curvature="0"
- id="path3016"
- d="m 3359.2154,-2191.502 c 0,0 -40.7142,5 -57.1428,42.1428 -16.4286,37.1429 50.7143,115.7143 60.7143,132.1429 10,16.4286 24.2857,38.5714 24.2857,38.5714 0,0 47.6766,32.0335 82.9062,53.8444 32.6567,20.2179 65.2266,33.1109 65.2266,33.1109 0,0 1.1687,-0.4937 7.0265,-19.1963 -12.1218,-21.7183 -38.3216,-53.2543 -54.5036,-69.192 24.2792,14.1167 43.346,28.1254 60.1041,56.5482 0,0 4.9588,-21.3916 6.5659,-30.7894 -3.0357,-9.5763 -47.4771,-56.5685 -47.4771,-56.5685 20.5517,8.7613 33.7508,23.0255 50.0892,44.7732 0,0 3.3263,-21.5598 3.8366,-32.9472 -2.5253,-6.566 -33.7228,-35.0595 -33.7228,-35.0595 13.1654,6.6374 19.714,11.0661 34.4127,24.0476 0,0 -3.0357,-60.7142 -66.6072,-130.7142 -63.5714,-70 -135.7143,-50.7143 -135.7143,-50.7143 z"
- style="fill:#000080;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <g
- transform="translate(2935.644,-2513.1499)"
- id="g3138">
- <path
- style="fill:#ff0000;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 337.66259,394.0585 c 0,0 -28.86328,-30.61257 -60.78781,-25.80202 -31.92454,4.81054 -41.10831,33.67382 -39.79634,39.79633 1.31197,6.12252 13.557,11.80771 22.30345,15.74361 8.74644,3.9359 32.79917,-0.43732 45.48152,-6.55984 12.68235,-6.12251 32.79918,-23.17808 32.79918,-23.17808 z"
- id="path3022"
- inkscape:connector-curvature="0" />
- <path
- style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-miterlimit:4"
- d="m 286.65383,372.49887 c 12.50235,2.18615 20.86528,14.09354 18.67912,26.59589 -2.18615,12.50234 -14.09354,20.86527 -26.59589,18.67911 -12.50235,-2.18616 -20.86528,-14.09354 -18.67912,-26.59588 z"
- id="path3024"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="csscc" />
- <path
- sodipodi:type="arc"
- style="fill:#000000;fill-opacity:1;stroke:none"
- id="path3026"
- sodipodi:cx="229.64285"
- sodipodi:cy="403.79074"
- sodipodi:rx="8.2142859"
- sodipodi:ry="8.2142859"
- d="m 237.85714,403.79074 c 0,4.53663 -3.67766,8.21429 -8.21429,8.21429 -4.53662,0 -8.21428,-3.67766 -8.21428,-8.21429 0,-4.53662 3.67766,-8.21428 8.21428,-8.21428 4.53663,0 8.21429,3.67766 8.21429,8.21428 z"
- transform="matrix(1.5694327,0,0,1.5694327,-85.566867,-233.30737)" />
- </g>
- <path
- sodipodi:nodetypes="cssssssc"
- style="fill:#ffff00;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 3110.2692,-2103.8575 c -32.3249,20.7081 -31.4522,56.0845 -27.4116,60.6301 4.0406,4.5457 8.0812,1.0102 15.6574,-1.5152 7.5761,-2.5254 26.2639,-10.1015 42.4264,-11.1117 16.1624,-1.0101 21.2132,3.0305 23.7386,-0.505 2.5254,-3.5356 4.0406,-15.1523 3.0304,-25.7589 -1.0101,-10.6066 -1.0101,-23.2335 -7.071,-25.2538 -6.061,-2.0203 -38.2484,-5.0718 -50.3702,3.5145 z"
- id="path3296"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path3305"
- d="m 3251.1404,-2178.2729 c 0,0 12.1428,21.0714 67.1428,32.5 55,11.4286 125.7143,-38.2143 125.7143,-38.2143 l 38.7857,38.6429 c 0,0 -113.0714,25.6428 -150.2143,21 -37.1428,-4.6429 -81.4285,-50.3572 -81.4285,-50.3572"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- sodipodi:nodetypes="ccccc"
- inkscape:connector-curvature="0"
- id="path4099"
- d="m 3453.1169,-2166.1191 -30.2186,14.3667 6.8378,5.5184 29.9689,-14.0733 z"
- style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
- <path
- sodipodi:nodetypes="cacscacsc"
- inkscape:connector-curvature="0"
- id="path3303"
- d="m 3154.8351,-2149.0637 c 0,0 -6.1891,7.2925 -8.7928,11.2888 -2.8625,4.3936 -7.4472,13.8569 -7.4472,13.8569 0,0 49.8667,-24.0831 67.6065,-30.2972 18.2787,-6.4028 54.803,-21.7873 54.803,-21.7873 0,0 -3.8451,-1.8876 -5.9103,-2.3872 -3.1018,-0.7503 -9.5343,-0.868 -9.5343,-0.868 0,0 -25.7837,-0.1235 -42.8032,6.1343 -17.0196,6.2579 -47.9217,24.0597 -47.9217,24.0597 z"
- style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
- d="m 3433.0379,-2169.2212 24.4739,22.8165 -8.1844,3.1976 -24.3243,-22.4616 z"
- id="path27241"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccc" />
- </g>
- </g>
-</svg>
diff --git a/contrib/themes/diffy/static/logo.png b/contrib/themes/diffy/static/logo.png
deleted file mode 100644
index 989c30304c..0000000000
--- a/contrib/themes/diffy/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/contrib/themes/spotify/etc/GerritSite.css b/contrib/themes/spotify/etc/GerritSite.css
deleted file mode 100644
index 489452a949..0000000000
--- a/contrib/themes/spotify/etc/GerritSite.css
+++ /dev/null
@@ -1,113 +0,0 @@
-#gerrit_topmenu {
- font-size: 9pt !important;
- padding-top: 5px !important;
- padding-left: 15px !important;
- padding-right: 15px !important;
- background: url(static/background-spotigreen.jpg) !important;
- float: left !important;
- margin-left: 250px !important;
- margin-right: 150px !important;
- border-radius: 0 0 10px 10px !important;
- padding-bottom: 10px !important;
- border-bottom: 1px solid #abc506 !important;
- border-right: 1px solid #abc506 !important;
- border-left: 1px solid #abc506 !important;
-}
-
-body, .gwt-DialogBox .dialogMiddleCenter {
- background: #FFF url(static/background-gradient.png) no-repeat !important;
-}
-
-#logo-spotify {
- background: url(static/logo.png) no-repeat;
- height: 98px;
- width: 228px;
- margin-left: 2px;
-}
-
-#gerrit_header {
- background: #FCFEEF;
- padding-bottom: 10px;
- border-bottom: dashed 1px rgba(0, 0, 0, 0.05);
-}
-
-#gerrit_topmenu .gwt-TextBox {
- margin-top: 15px;
- width: 240px;
-}
-
-#gerrit_topmenu > table {
- float: left;
-}
-
-#gerrit_topmenu > table > tbody > tr > td:nth-child(3) a {
- margin-left: 5px;
- background-color: #FFC;
- border-radius: 3px;
- padding: 5px 5px;
- border-right: none !important;
-}
-
-.gwt-TabPanelBottom {
- background: #FFC !important;
- border-radius: 0 0 3px 3px !important;
- padding-bottom: 6px !important;
- padding-top: 6px !important;
-}
-
-.gwt-TabBar .gwt-TabBarItem, .gwt-TabBar .gwt-TabBarRest, .gwt-TabBar .gwt-TabPanelBottom {
- background: transparent !important;
-}
-
-.gwt-TabBarItem-wrapper {
- background: rgba(255, 255, 255, 0.2) !important;
-}
-
-#gerrit_topmenu .gwt-TabBar .gwt-TabBarItem-selected {
- background: #FFC !important;
- border-radius: 3px 3px 0 0 !important;
-}
-
-#gerrit_topmenu .gwt-TabBar .gwt-TabBarItem-selected:hover {
- background: #FFC !important;
-}
-
-#gerrit_topmenu .gwt-TabBar .gwt-TabBarItem-selected:focus {
- outline: none !important;
-}
-
-#gerrit_topmenu > table > tbody > tr > td > table {
- border: none !important;
-}
-
-.gwt-TabBar {
- border-bottom: 1px solid #E2E2AD !important;
-}
-
-.gwt-TabBarItem {
- border-right: 1px solid rgba(79, 58, 0, 0.3) !important;
- background: rgba(255, 255, 255, 0.2) !important;
-}
-
-.gwt-TabBarItem:hover {
- background: rgba(255, 255, 255, 0.1) !important;
-}
-
-a, a:visited {
- text-decoration: none !important;
- color: #3F4D00 !important;
-}
-
-a:hover {
- color: #5d7200 !important;
- text-decoration: underline !important;
-}
-
-.gwt-Label {
-height: 30px !important;
-line-height: 30px !important;
-}
-
-#gerrit_btmmenu > div {
- color: rgba(0, 0, 0, 0.3) !important;
-} \ No newline at end of file
diff --git a/contrib/themes/spotify/etc/GerritSiteHeader.html b/contrib/themes/spotify/etc/GerritSiteHeader.html
deleted file mode 100644
index 8af84be723..0000000000
--- a/contrib/themes/spotify/etc/GerritSiteHeader.html
+++ /dev/null
@@ -1 +0,0 @@
-<div id="logo-spotify"></div> \ No newline at end of file
diff --git a/contrib/themes/spotify/static/background-gradient.png b/contrib/themes/spotify/static/background-gradient.png
deleted file mode 100644
index 3b9422eaec..0000000000
--- a/contrib/themes/spotify/static/background-gradient.png
+++ /dev/null
Binary files differ
diff --git a/contrib/themes/spotify/static/background-spotigreen.jpg b/contrib/themes/spotify/static/background-spotigreen.jpg
deleted file mode 100644
index fcc7983309..0000000000
--- a/contrib/themes/spotify/static/background-spotigreen.jpg
+++ /dev/null
Binary files differ
diff --git a/contrib/themes/spotify/static/logo.png b/contrib/themes/spotify/static/logo.png
deleted file mode 100644
index c231031c29..0000000000
--- a/contrib/themes/spotify/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/e2e-tests/build.sbt b/e2e-tests/build.sbt
index a322970b81..294212cf16 100644
--- a/e2e-tests/build.sbt
+++ b/e2e-tests/build.sbt
@@ -2,7 +2,6 @@ import Dependencies._
enablePlugins(GatlingPlugin)
-lazy val gatlingGitExtension = RootProject(uri("git://github.com/GerritForge/gatling-git.git"))
lazy val root = (project in file("."))
.settings(
inThisBuild(List(
@@ -12,8 +11,8 @@ lazy val root = (project in file("."))
)),
name := "gerrit",
libraryDependencies ++=
- gatling ++
+ gatling ++ gatlingGit ++
Seq("io.gatling" % "gatling-core" % GatlingVersion) ++
Seq("io.gatling" % "gatling-app" % GatlingVersion),
scalacOptions += "-language:postfixOps"
- ) dependsOn gatlingGitExtension
+ )
diff --git a/e2e-tests/project/Dependencies.scala b/e2e-tests/project/Dependencies.scala
index 63328f938d..56ef74040d 100644
--- a/e2e-tests/project/Dependencies.scala
+++ b/e2e-tests/project/Dependencies.scala
@@ -2,9 +2,17 @@ import sbt._
object Dependencies {
val GatlingVersion = "3.2.0"
+ val GatlingGitVersion = "1.0.12"
lazy val gatling = Seq(
"io.gatling.highcharts" % "gatling-charts-highcharts",
"io.gatling" % "gatling-test-framework",
).map(_ % GatlingVersion % Test)
+
+ lazy val gatlingGit = Seq(
+ "com.gerritforge" %% "gatling-git" % GatlingGitVersion excludeAll(
+ ExclusionRule(organization = "io.gatling"),
+ ExclusionRule(organization = "io.gatling.highcharts")
+ )
+ )
}
diff --git a/e2e-tests/project/plugins.sbt b/e2e-tests/project/plugins.sbt
index 36cd201542..9ed0f894bf 100644
--- a/e2e-tests/project/plugins.sbt
+++ b/e2e-tests/project/plugins.sbt
@@ -1 +1,2 @@
addSbtPlugin("io.gatling" % "gatling-sbt" % "3.0.0")
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
index 3577a6a35b..665cc4d0b4 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
"number": "NUMBER"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
new file mode 100644
index 0000000000..5b892aad8e
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckMasterBranchReplica1.json
@@ -0,0 +1,5 @@
+[
+ {
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/a/projects/PROJECT/branches/master"
+ }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json
new file mode 100644
index 0000000000..f15ddaeb91
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckNewProjectReplica1.json
@@ -0,0 +1,6 @@
+[
+ {
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT1/_PROJECT",
+ "cmd": "clone"
+ }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
index 6210deb684..467661b217 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects",
"entries": "PROJECTS_ENTRIES"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
index 23891246d1..30f5f23f8e 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
@@ -4,7 +4,7 @@
"cmd": "clone"
},
{
- "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
"cmd": "clone"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
index b4ee549d26..70e79ca991 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
"project": "PROJECT"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject-body.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject-body.json
index bcf470820d..282ac99cde 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject-body.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject-body.json
@@ -1,3 +1,4 @@
{
- "create_empty_commit": "true"
+ "create_empty_commit": "true",
+ "parent": "${parent}"
}
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
index 40e5a450ad..c141bb820f 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
@@ -1,5 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT",
+ "parent": "PARENT"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
index 3577a6a35b..665cc4d0b4 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
@@ -1,6 +1,6 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/",
"number": "NUMBER"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
index 7cc829304f..5720f53bcd 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/delete-project~delete"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
index 9ff15a79b4..e30a2cfa63 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCache.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json
new file mode 100644
index 0000000000..e30a2cfa63
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.json
@@ -0,0 +1,5 @@
+[
+ {
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects/flush"
+ }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
new file mode 100644
index 0000000000..86a3c28bcd
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetMasterBranchRevision.json
@@ -0,0 +1,5 @@
+[
+ {
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/PROJECT/branches/master"
+ }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
index fcf4bc9ada..e4e2643fff 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/GetProjectsCacheEntries.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/config/server/caches/projects"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json
new file mode 100644
index 0000000000..f6350bedd2
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ListProjects.json
@@ -0,0 +1,5 @@
+[
+ {
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/projects/"
+ }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
index 23891246d1..30f5f23f8e 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.json
@@ -4,7 +4,7 @@
"cmd": "clone"
},
{
- "url": "http://HOSTNAME:HTTP_PORT/_PROJECT",
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/_PROJECT",
"cmd": "clone"
}
]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
index a371757d23..301c65b0d3 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
@@ -1,5 +1,5 @@
[
{
- "url": "http://HOSTNAME:HTTP_PORT/a/changes/"
+ "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/"
}
]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
index fe46bd6506..8ae69d70d7 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
@@ -28,7 +28,7 @@ class ApproveChange extends GerritSimulation {
this.createChange = Some(createChange)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(session => {
if (createChange.nonEmpty) {
@@ -37,12 +37,12 @@ class ApproveChange extends GerritSimulation {
session
}
})
- .exec(http(unique)
+ .exec(http(uniqueName)
.post("${url}${number}/revisions/current/review")
.body(ElFileBody(body)).asJson)
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CacheFlushSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CacheFlushSimulation.scala
index 98d0190a8a..b98ae21b55 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CacheFlushSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CacheFlushSimulation.scala
@@ -20,7 +20,7 @@ class CacheFlushSimulation extends GerritSimulation {
protected var producer: Option[CacheFlushSimulation] = None
protected var consumer: Option[CacheFlushSimulation] = None
- private var cacheEntriesBeforeFlush: Int = 0
+ private var cacheEntriesBeforeFlush = 0
def entriesBeforeFlush(entries: Int): Unit = {
cacheEntriesBeforeFlush = entries
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckMasterBranchReplica1.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckMasterBranchReplica1.scala
new file mode 100644
index 0000000000..81274de566
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckMasterBranchReplica1.scala
@@ -0,0 +1,73 @@
+// 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.scenarios
+
+import com.typesafe.config.ConfigFactory
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef._
+
+import scala.concurrent.duration._
+
+class CheckMasterBranchReplica1 extends ProjectSimulation {
+ private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+
+ override def replaceOverride(in: String): String = {
+ val next = replaceProperty("http_port1", 8081, in)
+ super.replaceOverride(next)
+ }
+
+ private val httpForReplica = http.basicAuth(
+ conf.httpConfiguration.userName,
+ ConfigFactory.load().getString("http.password_replica"))
+
+ private val createChange = new CreateChange
+ private val approveChange = new ApproveChange(createChange)
+ private val submitChange = new SubmitChange(createChange)
+ private val getBranch = new GetMasterBranchRevision
+
+ private val test: ScenarioBuilder = scenario(uniqueName)
+ .feed(data)
+ .exec(session => {
+ session.set(getBranch.revisionKey, getBranch.revision.get)
+ })
+ .exec(http(uniqueName).get("${url}")
+ .check(regex(getBranch.revisionPattern)
+ .is(session => session(getBranch.revisionKey).as[String])))
+
+ setUp(
+ createChange.test.inject(
+ nothingFor(stepWaitTime(createChange) seconds),
+ atOnceUsers(single)
+ ),
+ approveChange.test.inject(
+ nothingFor(stepWaitTime(approveChange) seconds),
+ atOnceUsers(single)
+ ),
+ submitChange.test.inject(
+ nothingFor(stepWaitTime(submitChange) seconds),
+ atOnceUsers(single)
+ ),
+ getBranch.test.inject(
+ nothingFor(stepWaitTime(getBranch) seconds),
+ atOnceUsers(single)
+ ),
+ test.inject(
+ nothingFor(stepWaitTime(this) seconds),
+ atOnceUsers(single)
+ ).protocols(httpForReplica),
+ ).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala
new file mode 100644
index 0000000000..ae4fa801c4
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckNewProjectReplica1.scala
@@ -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.scenarios
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+
+import scala.concurrent.duration._
+
+class CheckNewProjectReplica1 extends GitSimulation {
+ private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+ private val projectName = className
+
+ private lazy val replicationDuration = replicationDelay + SecondsPerWeightUnit
+
+ override def relativeRuntimeWeight: Int = replicationDuration / SecondsPerWeightUnit + 2
+
+ override def replaceOverride(in: String): String = {
+ var next = replaceProperty("http_port1", 8081, in)
+ next = replaceKeyWith("_project", projectName, next)
+ super.replaceOverride(next)
+ }
+
+ private val test: ScenarioBuilder = scenario(uniqueName)
+ .feed(data)
+ .exec(gitRequest)
+
+ private val createProject = new CreateProject(projectName)
+ private val deleteProject = new DeleteProject(projectName)
+
+ setUp(
+ createProject.test.inject(
+ nothingFor(stepWaitTime(createProject) seconds),
+ atOnceUsers(single)
+ ),
+ test.inject(
+ nothingFor(stepWaitTime(this) + replicationDuration seconds),
+ atOnceUsers(single)
+ ).protocols(gitProtocol),
+ deleteProject.test.inject(
+ nothingFor(stepWaitTime(deleteProject) seconds),
+ atOnceUsers(single)
+ ),
+ ).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.scala
index 2424209e2d..96943ce1b4 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CheckProjectsCacheFlushEntries.scala
@@ -27,7 +27,7 @@ class CheckProjectsCacheFlushEntries extends CacheFlushSimulation {
this.producer = Some(producer)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(session => {
if (producer.nonEmpty) {
@@ -36,12 +36,12 @@ class CheckProjectsCacheFlushEntries extends CacheFlushSimulation {
session
}
})
- .exec(http(unique).get("${url}")
+ .exec(http(uniqueName).get("${url}")
.check(regex("\"" + memKey + "\": (\\d+)")
.is(session => session(entriesKey).as[String])))
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
index c70c39335b..08966a89d3 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
@@ -22,32 +22,32 @@ import scala.concurrent.duration._
class CloneUsingBothProtocols extends GitSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- private val default: String = name
- private val duration: Int = 2
+ private val projectName = className
+ private val duration = 2
override def replaceOverride(in: String): String = {
- replaceKeyWith("_project", default, in)
+ replaceKeyWith("_project", projectName, in)
}
- private val test: ScenarioBuilder = scenario(unique)
+ private val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(gitRequest)
- private val createProject = new CreateProject(default)
- private val deleteProject = new DeleteProject(default)
+ private val createProject = new CreateProject(projectName)
+ private val deleteProject = new DeleteProject(projectName)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
test.inject(
nothingFor(stepWaitTime(this) seconds),
- constantUsersPerSec(1) during (duration seconds)
- ),
+ constantUsersPerSec(single) during (duration seconds)
+ ).protocols(gitProtocol),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) + duration seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
- ).protocols(gitProtocol, httpProtocol)
+ ).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
index c7fb8ed279..f3692a96d9 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
@@ -28,12 +28,12 @@ class CreateChange extends ProjectSimulation {
override def relativeRuntimeWeight = 2
- def this(default: String) {
+ def this(projectName: String) {
this()
- this.default = default
+ this.projectName = projectName
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest
.body(ElFileBody(body)).asJson
@@ -43,26 +43,26 @@ class CreateChange extends ProjectSimulation {
session
})
- private val createProject = new CreateProject(default)
- private val deleteProject = new DeleteProject(default)
+ private val createProject = new CreateProject(projectName)
+ private val deleteProject = new DeleteProject(projectName)
private val deleteChange = new DeleteChange(this)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
test.inject(
nothingFor(stepWaitTime(this) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteChange.test.inject(
nothingFor(stepWaitTime(deleteChange) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala
index 3d5e677dff..25e50b3f03 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala
@@ -21,17 +21,17 @@ import io.gatling.core.structure.ScenarioBuilder
class CreateProject extends ProjectSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- def this(default: String) {
+ def this(projectName: String) {
this()
- this.default = default
+ this.projectName = projectName
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
- .exec(httpRequest.body(RawFileBody(body)).asJson)
+ .exec(httpRequest.body(ElFileBody(body)).asJson)
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
index aa6fe0d0dc..d832bde3d7 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
@@ -30,7 +30,7 @@ class DeleteChange extends GerritSimulation {
this.createChange = Some(createChange)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(session => {
if (createChange.nonEmpty) {
@@ -39,10 +39,10 @@ class DeleteChange extends GerritSimulation {
session
}
})
- .exec(http(unique).delete("${url}${number}"))
+ .exec(http(uniqueName).delete("${url}${number}"))
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala
index 983ac0b0c4..17526348bc 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala
@@ -21,17 +21,17 @@ import io.gatling.core.structure.ScenarioBuilder
class DeleteProject extends ProjectSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- def this(default: String) {
+ def this(projectName: String) {
this()
- this.default = default
+ this.projectName = projectName
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest)
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala
index 94f4ae3e8e..9fef2cfbc9 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCache.scala
@@ -22,39 +22,39 @@ import scala.concurrent.duration._
class FlushProjectsCache extends CacheFlushSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- private val default: String = name
+ private val projectName = className
override def relativeRuntimeWeight = 2
- private val flushCache: ScenarioBuilder = scenario(unique)
+ private val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(httpRequest)
- private val createProject = new CreateProject(default)
+ private val createProject = new CreateProject(projectName)
private val getCacheEntriesAfterProject = new GetProjectsCacheEntries(this)
private val checkCacheEntriesAfterFlush = new CheckProjectsCacheFlushEntries(this)
- private val deleteProject = new DeleteProject(default)
+ private val deleteProject = new DeleteProject(projectName)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
getCacheEntriesAfterProject.test.inject(
nothingFor(stepWaitTime(getCacheEntriesAfterProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
- flushCache.inject(
+ test.inject(
nothingFor(stepWaitTime(this) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
checkCacheEntriesAfterFlush.test.inject(
nothingFor(stepWaitTime(checkCacheEntriesAfterFlush) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.scala
new file mode 100644
index 0000000000..07b0c0b86b
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/FlushProjectsCacheThenRebuild.scala
@@ -0,0 +1,47 @@
+// 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.scenarios
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+
+import scala.concurrent.duration._
+
+class FlushProjectsCacheThenRebuild extends GerritSimulation {
+ private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+
+ private val test: ScenarioBuilder = scenario(uniqueName)
+ .feed(data)
+ .exec(httpRequest)
+
+ private val checkCacheEntriesAfterFlush = new CheckProjectsCacheFlushEntries
+ private val rebuildCache = new ListProjects
+
+ setUp(
+ test.inject(
+ nothingFor(stepWaitTime(this) seconds),
+ atOnceUsers(single)
+ ),
+ checkCacheEntriesAfterFlush.test.inject(
+ nothingFor(stepWaitTime(checkCacheEntriesAfterFlush) seconds),
+ atOnceUsers(single)
+ ),
+ rebuildCache.test.inject(
+ nothingFor(stepWaitTime(rebuildCache) seconds),
+ atOnceUsers(single)
+ ),
+ ).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index 5d6176d1c7..7b31b3d386 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -23,18 +23,22 @@ import io.gatling.http.request.builder.HttpRequestBuilder
class GerritSimulation extends Simulation {
implicit val conf: GatlingGitConfiguration = GatlingGitConfiguration()
- private val pack: String = this.getClass.getPackage.getName
- private val path: String = pack.replaceAllLiterally(".", "/")
- protected val name: String = this.getClass.getSimpleName
- private val pathName: String = s"data/$path/$name"
- protected val resource: String = s"$pathName.json"
- protected val body: String = s"$pathName-body.json"
- protected val unique: String = name + "-" + this.hashCode()
-
- private val powerFactor: Double = replaceProperty("power_factor", 1.0).toDouble
- protected val SecondsPerWeightUnit: Int = 2
+ private val packageName = getClass.getPackage.getName
+ private val path = packageName.replaceAllLiterally(".", "/")
+
+ protected val className: String = getClass.getSimpleName
+ private val pathName = s"data/$path/$className"
+ protected val resource = s"$pathName.json"
+ protected val body = s"$pathName-body.json"
+
+ protected val uniqueName: String = className + "-" + hashCode()
+ protected val single = 1
+
+ val replicationDelay: Int = replaceProperty("replication_delay", 15).toInt
+ private val powerFactor = replaceProperty("power_factor", 1.0).toDouble
+ protected val SecondsPerWeightUnit = 2
val maxExecutionTime: Int = (SecondsPerWeightUnit * relativeRuntimeWeight * powerFactor).toInt
- private var cumulativeWaitTime: Int = 0
+ private var cumulativeWaitTime = 0
/**
* How long a scenario step should wait before starting to execute.
@@ -51,26 +55,29 @@ class GerritSimulation extends Simulation {
currentWaitTime
}
- protected val httpRequest: HttpRequestBuilder = http(unique).post("${url}")
+ protected val httpRequest: HttpRequestBuilder = http(uniqueName).post("${url}")
protected val httpProtocol: HttpProtocolBuilder = http.basicAuth(
conf.httpConfiguration.userName,
conf.httpConfiguration.password)
protected val keys: PartialFunction[(String, Any), Any] = {
- case ("url", url) =>
- var in = replaceOverride(url.toString)
- in = replaceProperty("hostname", "localhost", in)
- in = replaceProperty("http_port", 8080, in)
- replaceProperty("ssh_port", 29418, in)
+ case ("entries", entries) =>
+ replaceProperty("projects_entries", "1", entries.toString)
case ("number", number) =>
val precedes = replaceKeyWith("_number", 0, number.toString)
replaceProperty("number", 1, precedes)
+ case ("parent", parent) =>
+ replaceProperty("parent", "All-Projects", parent.toString)
case ("project", project) =>
- var precedes = replaceKeyWith("_project", name, project.toString)
+ var precedes = replaceKeyWith("_project", className, project.toString)
precedes = replaceOverride(precedes)
replaceProperty("project", precedes)
- case ("entries", entries) =>
- replaceProperty("projects_entries", "1", entries.toString)
+ case ("url", url) =>
+ var in = replaceOverride(url.toString)
+ in = replaceProperty("hostname", "localhost", in)
+ in = replaceProperty("http_port", 8080, in)
+ in = replaceProperty("http_scheme", "http", in)
+ replaceProperty("ssh_port", 29418, in)
}
private def replaceProperty(term: String, in: String): String = {
@@ -82,7 +89,7 @@ class GerritSimulation extends Simulation {
}
protected def replaceProperty(term: String, default: Any, in: String): String = {
- val property = pack + "." + term
+ val property = packageName + "." + term
var value = default
default match {
case _: String | _: Double =>
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetMasterBranchRevision.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetMasterBranchRevision.scala
new file mode 100644
index 0000000000..1137ad53b9
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetMasterBranchRevision.scala
@@ -0,0 +1,41 @@
+// 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.scenarios
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef._
+
+class GetMasterBranchRevision extends ProjectSimulation {
+ private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+ var revision: Option[String] = None
+ val revisionKey = "revision"
+ val revisionPattern: String = "\"" + revisionKey + "\": \"(.+)\""
+
+ val test: ScenarioBuilder = scenario(uniqueName)
+ .feed(data)
+ .exec(http(uniqueName).get("${url}")
+ .check(regex(revisionPattern).saveAs(revisionKey)))
+ .exec(session => {
+ revision = Some(session(revisionKey).as[String])
+ session
+ })
+
+ setUp(
+ test.inject(
+ atOnceUsers(single)
+ )).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetProjectsCacheEntries.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetProjectsCacheEntries.scala
index 27e3f190bc..266c0b9da3 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetProjectsCacheEntries.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GetProjectsCacheEntries.scala
@@ -27,9 +27,9 @@ class GetProjectsCacheEntries extends CacheFlushSimulation {
this.consumer = Some(consumer)
}
- val test: ScenarioBuilder = scenario(unique)
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
- .exec(http(unique).get("${url}")
+ .exec(http(uniqueName).get("${url}")
.check(regex("\"" + memKey + "\": (\\d+)").saveAs(entriesKey)))
.exec(session => {
if (consumer.nonEmpty) {
@@ -40,6 +40,6 @@ class GetProjectsCacheEntries extends CacheFlushSimulation {
setUp(
test.inject(
- atOnceUsers(1)
+ atOnceUsers(single)
)).protocols(httpProtocol)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ListProjects.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ListProjects.scala
new file mode 100644
index 0000000000..26aed0bad3
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ListProjects.scala
@@ -0,0 +1,33 @@
+// 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.scenarios
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef.http
+
+class ListProjects extends GerritSimulation {
+ private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+
+ val test: ScenarioBuilder = scenario(uniqueName)
+ .feed(data)
+ .exec(http(uniqueName).get("${url}"))
+
+ setUp(
+ test.inject(
+ atOnceUsers(single)
+ )).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
index 141c3cf8b8..d6cb937814 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ProjectSimulation.scala
@@ -15,9 +15,9 @@
package com.google.gerrit.scenarios
class ProjectSimulation extends GerritSimulation {
- protected var default: String = "project"
+ protected var projectName: String = "defaultTestProject"
override def replaceOverride(in: String): String = {
- replaceProperty("project", default, in)
+ replaceProperty("project", projectName, in)
}
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
index e5b41b3c9b..0bd9e4a510 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ReplayRecordsFromFeeder.scala
@@ -22,28 +22,28 @@ import scala.concurrent.duration._
class ReplayRecordsFromFeeder extends GitSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
- private val default: String = name
+ private val projectName = className
override def relativeRuntimeWeight = 30
override def replaceOverride(in: String): String = {
- replaceKeyWith("_project", default, in)
+ replaceKeyWith("_project", projectName, in)
}
- private val test: ScenarioBuilder = scenario(unique)
+ private val test: ScenarioBuilder = scenario(uniqueName)
.repeat(10) {
feed(data)
.exec(gitRequest)
}
- private val createProject = new CreateProject(default)
- private val deleteProject = new DeleteProject(default)
+ private val createProject = new CreateProject(projectName)
+ private val deleteProject = new DeleteProject(projectName)
private val maxBeforeDelete: Int = maxExecutionTime - deleteProject.maxExecutionTime
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
test.inject(
nothingFor(stepWaitTime(this) seconds),
@@ -51,11 +51,11 @@ class ReplayRecordsFromFeeder extends GitSimulation {
rampUsers(10) during (5 seconds),
constantUsersPerSec(20) during (15 seconds),
constantUsersPerSec(20) during (15 seconds) randomized
- ),
+ ).protocols(gitProtocol),
deleteProject.test.inject(
nothingFor(maxBeforeDelete seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
- ).protocols(gitProtocol, httpProtocol)
+ ).protocols(httpProtocol)
.maxDuration(maxExecutionTime seconds)
}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
index 2f67274d3f..067496ac5e 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
@@ -23,40 +23,47 @@ import scala.concurrent.duration._
class SubmitChange extends GerritSimulation {
private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
- private val default: String = name
+ private val projectName = className
+ private var createChange = new CreateChange(projectName)
- private val test: ScenarioBuilder = scenario(unique)
+ override def relativeRuntimeWeight = 10
+
+ def this(createChange: CreateChange) {
+ this()
+ this.createChange = createChange
+ }
+
+ val test: ScenarioBuilder = scenario(uniqueName)
.feed(data)
.exec(session => {
session.set("number", createChange.number)
})
- .exec(http(unique).post("${url}${number}/submit"))
+ .exec(http(uniqueName).post("${url}${number}/submit"))
- private val createProject = new CreateProject(default)
- private val createChange = new CreateChange(default)
+ private val createProject = new CreateProject(projectName)
private val approveChange = new ApproveChange(createChange)
- private val deleteProject = new DeleteProject(default)
+ private val deleteProject = new DeleteProject(projectName)
setUp(
createProject.test.inject(
nothingFor(stepWaitTime(createProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
createChange.test.inject(
nothingFor(stepWaitTime(createChange) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
approveChange.test.inject(
nothingFor(stepWaitTime(approveChange) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
test.inject(
nothingFor(stepWaitTime(this) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
deleteProject.test.inject(
nothingFor(stepWaitTime(deleteProject) seconds),
- atOnceUsers(1)
+ atOnceUsers(single)
),
).protocols(httpProtocol)
}
diff --git a/gerrit-gwtdebug/BUILD b/gerrit-gwtdebug/BUILD
deleted file mode 100644
index ecf525ed1a..0000000000
--- a/gerrit-gwtdebug/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
- name = "gwtdebug",
- srcs = glob(["src/main/java/**/*.java"]),
- visibility = ["//visibility:public"],
- deps = [
- "//java/com/google/gerrit/pgm",
- "//java/com/google/gerrit/pgm/util",
- "//java/com/google/gerrit/util/cli",
- "//lib/flogger:api",
- "//lib/gwt:dev",
- "//lib/jetty:server",
- "//lib/jetty:servlet",
- "//lib/jetty:servlets",
- "//lib/log:log4j",
- ],
-)
diff --git a/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritGwtDebugLauncher.java b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritGwtDebugLauncher.java
deleted file mode 100644
index cf84919714..0000000000
--- a/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritGwtDebugLauncher.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2014 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.gwtdebug;
-
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.pgm.Daemon;
-import com.google.gwt.dev.codeserver.CodeServer;
-import com.google.gwt.dev.codeserver.Options;
-import java.util.ArrayList;
-import java.util.List;
-
-class GerritGwtDebugLauncher {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public static void main(String[] argv) throws Exception {
- GerritGwtDebugLauncher launcher = new GerritGwtDebugLauncher();
- launcher.mainImpl(argv);
- }
-
- private int mainImpl(String[] argv) {
- List<String> sdmLauncherOptions = new ArrayList<>();
- List<String> daemonLauncherOptions = new ArrayList<>();
-
- // Separator between Daemon and Codeserver parameters is "--"
- boolean daemonArgumentSeparator = false;
- int i = 0;
- for (; i < argv.length; i++) {
- if (!argv[i].equals("--")) {
- sdmLauncherOptions.add(argv[i]);
- } else {
- daemonArgumentSeparator = true;
- break;
- }
- }
- if (daemonArgumentSeparator) {
- ++i;
- for (; i < argv.length; i++) {
- daemonLauncherOptions.add(argv[i]);
- }
- }
-
- Options options = new Options();
- if (!options.parseArgs(sdmLauncherOptions.toArray(new String[sdmLauncherOptions.size()]))) {
- logger.atSevere().log("Failed to parse codeserver arguments");
- return 1;
- }
-
- CodeServer.main(options);
-
- try {
- int r =
- new Daemon()
- .main(daemonLauncherOptions.toArray(new String[daemonLauncherOptions.size()]));
- if (r != 0) {
- logger.atSevere().log("Daemon exited with return code: %d", r);
- return 1;
- }
- } catch (Exception e) {
- logger.atSevere().withCause(e).log("Cannot start daemon");
- return 1;
- }
-
- return 0;
- }
-}
diff --git a/gerrit-gwtui-common/BUILD b/gerrit-gwtui-common/BUILD
deleted file mode 100644
index e9ed36312d..0000000000
--- a/gerrit-gwtui-common/BUILD
+++ /dev/null
@@ -1,63 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//tools/bzl:gwt.bzl", "gwt_module")
-load("//tools/bzl:java.bzl", "java_library2")
-load("//tools/bzl:junit.bzl", "junit_tests")
-
-EXPORTED_DEPS = [
- "//java/com/google/gerrit/common:client",
- "//java/com/google/gwtexpui/clippy",
- "//java/com/google/gwtexpui/globalkey",
- "//java/com/google/gwtexpui/progress",
- "//java/com/google/gwtexpui/safehtml",
- "//java/com/google/gwtexpui/user:agent",
-]
-
-DEPS = ["//lib/gwt:user-neverlink"]
-
-SRC = "src/main/java/com/google/gerrit/"
-
-gwt_module(
- name = "client",
- srcs = glob(["src/main/**/*.java"]),
- exported_deps = EXPORTED_DEPS,
- gwt_xml = SRC + "GerritGwtUICommon.gwt.xml",
- resources = glob(
- ["src/main/**/*"],
- exclude = [SRC + "client/**/*.java"] + [
- SRC + "GerritGwtUICommon.gwt.xml",
- ],
- ),
- visibility = ["//visibility:public"],
- deps = DEPS,
-)
-
-java_library2(
- name = "client-lib",
- srcs = glob(["src/main/**/*.java"]),
- exported_deps = EXPORTED_DEPS,
- resources = glob(["src/main/**/*"]),
- visibility = ["//visibility:public"],
- deps = DEPS,
-)
-
-java_library(
- name = "diffy_logo",
- data = [
- "//lib:LICENSE-CC-BY3.0-unported",
- "//lib:LICENSE-diffy",
- ],
- resources = glob(["src/main/resources/com/google/gerrit/client/diffy*.png"]),
- visibility = ["//visibility:public"],
-)
-
-junit_tests(
- name = "client_tests",
- srcs = glob(["src/test/java/**/*.java"]),
- visibility = ["//visibility:public"],
- deps = [
- ":client",
- "//lib:junit",
- "//lib/gwt:dev",
- "//lib/jgit/org.eclipse.jgit:jgit",
- ],
-)
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/GerritGwtUICommon.gwt.xml b/gerrit-gwtui-common/src/main/java/com/google/gerrit/GerritGwtUICommon.gwt.xml
deleted file mode 100644
index dc478fc5f4..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/GerritGwtUICommon.gwt.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
- Copyright (C) 2014 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.
--->
-<module>
- <inherits name='org.eclipse.jgit.JGit'/>
- <inherits name='com.google.gerrit.common.Common'/>
- <inherits name='com.google.gerrit.extensions.Extensions'/>
- <inherits name='com.google.gerrit.prettify.PrettyFormatter'/>
- <inherits name='com.google.gwtexpui.clippy.Clippy'/>
- <inherits name='com.google.gwtexpui.globalkey.GlobalKey'/>
- <inherits name='com.google.gwtexpui.progress.Progress'/>
- <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
- <source path='client'>
- <include name='AccountFormatter.java'/>
- <include name='CommonConstants.java'/>
- <include name='CommonMessages.java'/>
- <include name='DateFormatter.java'/>
- <include name='GerritUiExtensionPoint.java'/>
- <include name='RelativeDateFormatter.java'/>
- <include name='Resources.java'/>
- <include name='CommonConstants.properties'/>
- <include name='CommonMessages.properties'/>
- <include name='info/*.java'/>
- <include name='rpc/NativeMap.java'/>
- <include name='rpc/Natives.java'/>
- <include name='rpc/NativeString.java'/>
- <include name='rpc/TransformCallback.java'/>
- <include name='ui/HighlightSuggestion.java'/>
- <include name='ui/RemoteSuggestOracle.java'/>
- </source>
-</module>
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/AccountFormatter.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/AccountFormatter.java
deleted file mode 100644
index 3058971b3a..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/AccountFormatter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-import com.google.gerrit.client.info.AccountInfo;
-
-public class AccountFormatter {
- private final String anonymousCowardName;
-
- public AccountFormatter(String anonymousCowardName) {
- this.anonymousCowardName = anonymousCowardName;
- }
-
- /**
- * Formats an account as a name and an email address.
- *
- * <p>Example output:
- *
- * <ul>
- * <li>{@code A U. Thor &lt;author@example.com&gt;}: full populated
- * <li>{@code A U. Thor (12)}: missing email address
- * <li>{@code Anonymous Coward &lt;author@example.com&gt;}: missing name
- * <li>{@code Anonymous Coward (12)}: missing name and email address
- * </ul>
- */
- public String nameEmail(AccountInfo info) {
- String name = info.name();
- if (name == null || name.trim().isEmpty()) {
- name = anonymousCowardName;
- }
-
- StringBuilder b = new StringBuilder().append(name);
- if (info.email() != null) {
- b.append(" <").append(info.email()).append(">");
- } else if (info._accountId() > 0) {
- b.append(" (").append(info._accountId()).append(")");
- }
- return b.toString();
- }
-
- /**
- * Formats an account name.
- *
- * <p>If the account has a full name, it returns only the full name. Otherwise it returns a longer
- * form that includes the email address.
- */
- public String name(AccountInfo ai) {
- if (ai.name() != null && !ai.name().trim().isEmpty()) {
- return ai.name();
- }
- String email = ai.email();
- if (email != null) {
- int at = email.indexOf('@');
- return 0 < at ? email.substring(0, at) : email;
- }
- return nameEmail(ai);
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.java
deleted file mode 100644
index e769730a0f..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Constants;
-
-public interface CommonConstants extends Constants {
- CommonConstants C = GWT.create(CommonConstants.class);
-
- String inTheFuture();
-
- String month();
-
- String months();
-
- String year();
-
- String years();
-
- String oneSecondAgo();
-
- String oneMinuteAgo();
-
- String oneHourAgo();
-
- String oneDayAgo();
-
- String oneWeekAgo();
-
- String oneMonthAgo();
-
- String oneYearAgo();
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.properties b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.properties
deleted file mode 100644
index 3202bfce0c..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonConstants.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-inTheFuture = in the future
-month = month
-months = months
-years = years
-year = year
-
-oneSecondAgo = 1 second ago
-oneMinuteAgo = 1 minute ago
-oneHourAgo = 1 hour ago
-oneDayAgo = 1 day ago
-oneWeekAgo = 1 week ago
-oneMonthAgo = 1 month ago
-oneYearAgo = 1 year ago
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.java
deleted file mode 100644
index 5314254e9d..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Messages;
-
-public interface CommonMessages extends Messages {
- CommonMessages M = GWT.create(CommonMessages.class);
-
- String secondsAgo(long seconds);
-
- String minutesAgo(long minutes);
-
- String hoursAgo(long hours);
-
- String daysAgo(long days);
-
- String weeksAgo(long weeks);
-
- String monthsAgo(long months);
-
- String yearsAgo(long years);
-
- String years0MonthsAgo(long years, String yearLabel);
-
- String yearsMonthsAgo(long years, String yearLabel, long months, String monthLabel);
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.properties b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.properties
deleted file mode 100644
index 738602effa..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/CommonMessages.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-secondsAgo = {0} seconds ago
-minutesAgo = {0} minutes ago
-hoursAgo = {0} hours ago
-daysAgo = {0} days ago
-weeksAgo = {0} weeks ago
-monthsAgo = {0} months ago
-years0MonthsAgo = {0} {1} ago
-yearsMonthsAgo = {0} {1}, {2} {3} ago
-yearsAgo = {0} years ago
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java
deleted file mode 100644
index 4df2f5fb49..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gwt.i18n.client.DateTimeFormat;
-import java.util.Date;
-
-public class DateFormatter {
- private static final long ONE_YEAR = 182L * 24 * 60 * 60 * 1000;
-
- private final DateTimeFormat sTime;
- private final DateTimeFormat sDate;
- private final DateTimeFormat sdtFmt;
- private final DateTimeFormat mDate;
- private final DateTimeFormat dtfmt;
-
- public DateFormatter(GeneralPreferences prefs) {
- String fmt_sTime = prefs.timeFormat().getFormat();
- String fmt_sDate = prefs.dateFormat().getShortFormat();
- String fmt_mDate = prefs.dateFormat().getLongFormat();
-
- sTime = DateTimeFormat.getFormat(fmt_sTime);
- sDate = DateTimeFormat.getFormat(fmt_sDate);
- sdtFmt = DateTimeFormat.getFormat(fmt_sDate + " " + fmt_sTime);
- mDate = DateTimeFormat.getFormat(fmt_mDate);
- dtfmt = DateTimeFormat.getFormat(fmt_mDate + " " + fmt_sTime);
- }
-
- /** Format a date using a really short format. */
- public String shortFormat(Date dt) {
- if (dt == null) {
- return "";
- }
-
- Date now = new Date();
- dt = new Date(dt.getTime());
- if (mDate.format(now).equals(mDate.format(dt))) {
- // Same day as today, report only the time.
- //
- return sTime.format(dt);
-
- } else if (Math.abs(now.getTime() - dt.getTime()) < ONE_YEAR) {
- // Within the last year, show a shorter date.
- //
- return sDate.format(dt);
-
- } else {
- // Report only date and year, its far away from now.
- //
- return mDate.format(dt);
- }
- }
-
- /** Format a date using a really short format. */
- public String shortFormatDayTime(Date dt) {
- if (dt == null) {
- return "";
- }
-
- Date now = new Date();
- dt = new Date(dt.getTime());
- if (mDate.format(now).equals(mDate.format(dt))) {
- // Same day as today, report only the time.
- //
- return sTime.format(dt);
-
- } else if (Math.abs(now.getTime() - dt.getTime()) < ONE_YEAR) {
- // Within the last year, show a shorter date.
- //
- return sdtFmt.format(dt);
-
- } else {
- // Report only date and year, its far away from now.
- //
- return mDate.format(dt);
- }
- }
-
- /** Format a date using the locale's medium length format. */
- public String mediumFormat(Date dt) {
- if (dt == null) {
- return "";
- }
- return dtfmt.format(new Date(dt.getTime()));
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/GerritUiExtensionPoint.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/GerritUiExtensionPoint.java
deleted file mode 100644
index 66a3b6b294..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/GerritUiExtensionPoint.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-public enum GerritUiExtensionPoint {
- /* ChangeScreen */
- CHANGE_SCREEN_HEADER,
- CHANGE_SCREEN_HEADER_RIGHT_OF_BUTTONS,
- CHANGE_SCREEN_HEADER_RIGHT_OF_POP_DOWNS,
- CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
- CHANGE_SCREEN_BELOW_RELATED_INFO_BLOCK,
- CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK,
- CHANGE_SCREEN_HISTORY_RIGHT_OF_BUTTONS,
-
- /* MyPasswordScreen */
- PASSWORD_SCREEN_BOTTOM,
-
- /* MyPreferencesScreen */
- PREFERENCES_SCREEN_BOTTOM,
-
- /* MyProfileScreen */
- PROFILE_SCREEN_BOTTOM,
-
- /* ProjectInfoScreen */
- PROJECT_INFO_SCREEN_TOP,
- PROJECT_INFO_SCREEN_BOTTOM;
-
- public enum Key {
- ACCOUNT_INFO,
- CHANGE_INFO,
- PROJECT_NAME,
- REVISION_INFO
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
deleted file mode 100644
index e0cc9ca65e..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
+++ /dev/null
@@ -1,142 +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.client;
-
-import java.util.Date;
-
-/**
- * Formatter to format timestamps relative to the current time using time units in the format
- * defined by {@code git log --relative-date}.
- */
-public class RelativeDateFormatter {
- private static CommonConstants constants;
- private static CommonMessages messages;
-
- static final long SECOND_IN_MILLIS = 1000;
- static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
- static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
- static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
- static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
- static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
- static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
-
- static void setConstants(CommonConstants c, CommonMessages m) {
- constants = c;
- messages = m;
- }
-
- private static CommonConstants c() {
- return constants != null ? constants : CommonConstants.C;
- }
-
- private static CommonMessages m() {
- return messages != null ? messages : CommonMessages.M;
- }
-
- /**
- * @param when {@link Date} to format
- * @return age of given {@link Date} compared to now formatted in the same relative format as
- * returned by {@code git log --relative-date}
- */
- public static String format(Date when) {
- long ageMillis = (new Date()).getTime() - when.getTime();
-
- // shouldn't happen in a perfect world
- if (ageMillis < 0) {
- return c().inTheFuture();
- }
-
- // seconds
- if (ageMillis < upperLimit(MINUTE_IN_MILLIS)) {
- long seconds = round(ageMillis, SECOND_IN_MILLIS);
- if (seconds == 1) {
- return c().oneSecondAgo();
- }
- return m().secondsAgo(seconds);
- }
-
- // minutes
- if (ageMillis < upperLimit(HOUR_IN_MILLIS)) {
- long minutes = round(ageMillis, MINUTE_IN_MILLIS);
- if (minutes == 1) {
- return c().oneMinuteAgo();
- }
- return m().minutesAgo(minutes);
- }
-
- // hours
- if (ageMillis < upperLimit(DAY_IN_MILLIS)) {
- long hours = round(ageMillis, HOUR_IN_MILLIS);
- if (hours == 1) {
- return c().oneHourAgo();
- }
- return m().hoursAgo(hours);
- }
-
- // up to 14 days use days
- if (ageMillis < 14 * DAY_IN_MILLIS) {
- long days = round(ageMillis, DAY_IN_MILLIS);
- if (days == 1) {
- return c().oneDayAgo();
- }
- return m().daysAgo(days);
- }
-
- // up to 10 weeks use weeks
- if (ageMillis < 10 * WEEK_IN_MILLIS) {
- long weeks = round(ageMillis, WEEK_IN_MILLIS);
- if (weeks == 1) {
- return c().oneWeekAgo();
- }
- return m().weeksAgo(weeks);
- }
-
- // months
- if (ageMillis < YEAR_IN_MILLIS) {
- long months = round(ageMillis, MONTH_IN_MILLIS);
- if (months == 1) {
- return c().oneMonthAgo();
- }
- return m().monthsAgo(months);
- }
-
- // up to 5 years use "year, months" rounded to months
- if (ageMillis < 5 * YEAR_IN_MILLIS) {
- long years = round(ageMillis, MONTH_IN_MILLIS) / 12;
- String yearLabel = (years > 1) ? c().years() : c().year();
- long months = round(ageMillis - years * YEAR_IN_MILLIS, MONTH_IN_MILLIS);
- String monthLabel = (months > 1) ? c().months() : (months == 1 ? c().month() : "");
- if (months == 0) {
- return m().years0MonthsAgo(years, yearLabel);
- }
- return m().yearsMonthsAgo(years, yearLabel, months, monthLabel);
- }
-
- // years
- long years = round(ageMillis, YEAR_IN_MILLIS);
- if (years == 1) {
- return c().oneYearAgo();
- }
- return m().yearsAgo(years);
- }
-
- private static long upperLimit(long unit) {
- return unit + unit / 2;
- }
-
- private static long round(long n, long unit) {
- return (n + unit / 2) / unit;
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java
deleted file mode 100644
index 67627c3d98..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/Resources.java
+++ /dev/null
@@ -1,126 +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.client;
-
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.ImageResource;
-
-public interface Resources extends ClientBundle {
- /** silk icons (CC-BY3.0): http://famfamfam.com/lab/icons/silk/ */
- @Source("note_add.png")
- ImageResource addFileComment();
-
- @Source("tag_blue_add.png")
- ImageResource addHashtag();
-
- @Source("user_add.png")
- ImageResource addUser();
-
- @Source("user_edit.png")
- ImageResource editUser();
-
- // derived from resultset_next.png
- @Source("resultset_down_gray.png")
- ImageResource arrowDown();
-
- // derived from resultset_next.png
- @Source("resultset_next_gray.png")
- ImageResource arrowRight();
-
- // derived from resultset_next.png
- @Source("resultset_up_gray.png")
- ImageResource arrowUp();
-
- @Source("lightbulb.png")
- ImageResource blame();
-
- @Source("page_white_put.png")
- ImageResource downloadIcon();
-
- // derived from comment.png
- @Source("comment_draft.png")
- ImageResource draftComments();
-
- @Source("page_edit.png")
- ImageResource edit();
-
- @Source("arrow_undo.png")
- ImageResource editUndo();
-
- @Source("cog.png")
- ImageResource gear();
-
- @Source("tick.png")
- ImageResource greenCheck();
-
- @Source("tag_blue.png")
- ImageResource hashtag();
-
- @Source("lightbulb.png")
- ImageResource info();
-
- @Source("find.png")
- ImageResource queryIcon();
-
- @Source("lock.png")
- ImageResource readOnly();
-
- @Source("cross.png")
- ImageResource redNot();
-
- @Source("disk.png")
- ImageResource save();
-
- @Source("star.png")
- ImageResource starFilled();
-
- // derived from star.png
- @Source("star-open.png")
- ImageResource starOpen();
-
- @Source("exclamation.png")
- ImageResource warning();
-
- @Source("help.png")
- ImageResource question();
-
- /** tango icon library (public domain): http://tango.freedesktop.org/Tango_Icon_Library */
- @Source("goNext.png")
- ImageResource goNext();
-
- @Source("goPrev.png")
- ImageResource goPrev();
-
- @Source("goUp.png")
- ImageResource goUp();
-
- @Source("listAdd.png")
- ImageResource listAdd();
-
- // derived from important.png
- @Source("merge.png")
- ImageResource merge();
-
- /** contributed by the artist under Apache2.0 */
- @Source("sideBySideDiff.png")
- ImageResource sideBySideDiff();
-
- @Source("unifiedDiff.png")
- ImageResource unifiedDiff();
-
- /** contributed by the artist under CC-BY3.0 */
- @Source("diffy26.png")
- ImageResource gerritAvatar26();
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java
deleted file mode 100644
index e4c008c354..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java
+++ /dev/null
@@ -1,93 +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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwtjsonrpc.client.impl.ser.JavaSqlTimestamp_JsonSerializer;
-import java.sql.Timestamp;
-
-public class AccountInfo extends JavaScriptObject {
- public final native int _accountId() /*-{ return this._account_id || 0; }-*/;
-
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String email() /*-{ return this.email; }-*/;
-
- public final native JsArrayString secondaryEmails() /*-{ return this.secondary_emails; }-*/;
-
- public final native String username() /*-{ return this.username; }-*/;
-
- public final Timestamp registeredOn() {
- Timestamp ts = _getRegisteredOn();
- if (ts == null) {
- ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(registeredOnRaw());
- _setRegisteredOn(ts);
- }
- return ts;
- }
-
- private native String registeredOnRaw() /*-{ return this.registered_on; }-*/;
-
- private native Timestamp _getRegisteredOn() /*-{ return this._cts; }-*/;
-
- private native void _setRegisteredOn(Timestamp ts) /*-{ this._cts = ts; }-*/;
-
- /**
- * @return true if the server supplied avatar information about this account. The information may
- * be an empty list, indicating no avatars are available, such as when no plugin is installed.
- * This method returns false if the server did not check on avatars for the account.
- */
- public final native boolean hasAvatarInfo() /*-{ return this.hasOwnProperty('avatars') }-*/;
-
- public final AvatarInfo avatar(int sz) {
- JsArray<AvatarInfo> a = avatars();
- for (int i = 0; a != null && i < a.length(); i++) {
- AvatarInfo r = a.get(i);
- if (r.height() == sz) {
- return r;
- }
- }
- return null;
- }
-
- private native JsArray<AvatarInfo> avatars() /*-{ return this.avatars }-*/;
-
- public final native void name(String n) /*-{ this.name = n }-*/;
-
- public final native void email(String e) /*-{ this.email = e }-*/;
-
- public final native void username(String n) /*-{ this.username = n }-*/;
-
- public static native AccountInfo create(int id, String name, String email, String username) /*-{
- return {'_account_id': id, 'name': name, 'email': email,
- 'username': username};
- }-*/;
-
- protected AccountInfo() {}
-
- public static class AvatarInfo extends JavaScriptObject {
- public static final int DEFAULT_SIZE = 26;
-
- public final native String url() /*-{ return this.url }-*/;
-
- public final native int height() /*-{ return this.height || 0 }-*/;
-
- public final native int width() /*-{ return this.width || 0 }-*/;
-
- protected AvatarInfo() {}
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ActionInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ActionInfo.java
deleted file mode 100644
index d09d5b7d6e..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ActionInfo.java
+++ /dev/null
@@ -1,32 +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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ActionInfo extends JavaScriptObject {
-
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String method() /*-{ return this.method; }-*/;
-
- public final native String label() /*-{ return this.label; }-*/;
-
- public final native String title() /*-{ return this.title; }-*/;
-
- public final native boolean enabled() /*-{ return this.enabled || false; }-*/;
-
- protected ActionInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AgreementInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AgreementInfo.java
deleted file mode 100644
index 04fba4f2a5..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AgreementInfo.java
+++ /dev/null
@@ -1,29 +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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class AgreementInfo extends JavaScriptObject {
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String description() /*-{ return this.description; }-*/;
-
- public final native String url() /*-{ return this.url; }-*/;
-
- public final native GroupInfo autoVerifyGroup() /*-{ return this.auto_verify_group; }-*/;
-
- protected AgreementInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java
deleted file mode 100644
index 43281bd259..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2015 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.client.info;
-
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gerrit.extensions.client.AuthType;
-import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import java.util.ArrayList;
-import java.util.List;
-
-public class AuthInfo extends JavaScriptObject {
- public final AuthType authType() {
- return AuthType.valueOf(authTypeRaw());
- }
-
- public final boolean isLdap() {
- return authType() == AuthType.LDAP || authType() == AuthType.LDAP_BIND;
- }
-
- public final boolean isOpenId() {
- return authType() == AuthType.OPENID;
- }
-
- public final boolean isOAuth() {
- return authType() == AuthType.OAUTH;
- }
-
- public final boolean isDev() {
- return authType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
- }
-
- public final boolean isClientSslCertLdap() {
- return authType() == AuthType.CLIENT_SSL_CERT_LDAP;
- }
-
- public final boolean isCustomExtension() {
- return authType() == AuthType.CUSTOM_EXTENSION;
- }
-
- public final boolean canEdit(AccountFieldName f) {
- return editableAccountFields().contains(f);
- }
-
- public final List<AccountFieldName> editableAccountFields() {
- List<AccountFieldName> fields = new ArrayList<>();
- for (String f : Natives.asList(_editableAccountFields())) {
- fields.add(AccountFieldName.valueOf(f));
- }
- return fields;
- }
-
- public final List<AgreementInfo> contributorAgreements() {
- List<AgreementInfo> agreements = new ArrayList<>();
- JsArray<AgreementInfo> contributorAgreements = _contributorAgreements();
- if (contributorAgreements != null) {
- agreements.addAll(Natives.asList(contributorAgreements));
- }
- return agreements;
- }
-
- public final boolean siteHasUsernames() {
- if (isCustomExtension() && httpPasswordUrl() != null && !canEdit(AccountFieldName.USER_NAME)) {
- return false;
- }
- return true;
- }
-
- public final boolean isHttpPasswordSettingsEnabled() {
- return gitBasicAuthPolicy() == GitBasicAuthPolicy.HTTP
- || gitBasicAuthPolicy() == GitBasicAuthPolicy.HTTP_LDAP;
- }
-
- public final GitBasicAuthPolicy gitBasicAuthPolicy() {
- return GitBasicAuthPolicy.valueOf(gitBasicAuthPolicyRaw());
- }
-
- public final native boolean useContributorAgreements()
- /*-{ return this.use_contributor_agreements || false; }-*/ ;
-
- public final native String loginUrl() /*-{ return this.login_url; }-*/;
-
- public final native String loginText() /*-{ return this.login_text; }-*/;
-
- public final native String switchAccountUrl() /*-{ return this.switch_account_url; }-*/;
-
- public final native String registerUrl() /*-{ return this.register_url; }-*/;
-
- public final native String registerText() /*-{ return this.register_text; }-*/;
-
- public final native String editFullNameUrl() /*-{ return this.edit_full_name_url; }-*/;
-
- public final native String httpPasswordUrl() /*-{ return this.http_password_url; }-*/;
-
- private native String gitBasicAuthPolicyRaw() /*-{ return this.git_basic_auth_policy; }-*/;
-
- private native String authTypeRaw() /*-{ return this.auth_type; }-*/;
-
- private native JsArrayString _editableAccountFields()
- /*-{ return this.editable_account_fields; }-*/ ;
-
- private native JsArray<AgreementInfo> _contributorAgreements()
- /*-{ return this.contributor_agreements; }-*/ ;
-
- protected AuthInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
deleted file mode 100644
index 0f786a6d66..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
+++ /dev/null
@@ -1,575 +0,0 @@
-// Copyright (C) 2012 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.client.info;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.extensions.client.ReviewerState;
-import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwtjsonrpc.client.impl.ser.JavaSqlTimestamp_JsonSerializer;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-public class ChangeInfo extends JavaScriptObject {
- public final void init() {
- if (allLabels() != null) {
- allLabels().copyKeysIntoChildren("_name");
- }
- }
-
- public final Project.NameKey projectNameKey() {
- return new Project.NameKey(project());
- }
-
- public final Change.Id legacyId() {
- return new Change.Id(_number());
- }
-
- public final Timestamp created() {
- Timestamp ts = _getCts();
- if (ts == null) {
- ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(createdRaw());
- _setCts(ts);
- }
- return ts;
- }
-
- public final boolean hasEditBasedOnCurrentPatchSet() {
- JsArray<RevisionInfo> revList = revisions().values();
- RevisionInfo.sortRevisionInfoByNumber(revList);
- return revList.get(revList.length() - 1).isEdit();
- }
-
- private native Timestamp _getCts() /*-{ return this._cts; }-*/;
-
- private native void _setCts(Timestamp ts) /*-{ this._cts = ts; }-*/;
-
- public final Timestamp updated() {
- return JavaSqlTimestamp_JsonSerializer.parseTimestamp(updatedRaw());
- }
-
- public final Timestamp submitted() {
- return JavaSqlTimestamp_JsonSerializer.parseTimestamp(submittedRaw());
- }
-
- public final String idAbbreviated() {
- return new Change.Key(changeId()).abbreviate();
- }
-
- public final Change.Status status() {
- return Change.Status.valueOf(statusRaw());
- }
-
- public final Set<String> labels() {
- return allLabels().keySet();
- }
-
- public final Set<Integer> removableReviewerIds() {
- Set<Integer> removable = new HashSet<>();
- if (removableReviewers() != null) {
- for (AccountInfo a : Natives.asList(removableReviewers())) {
- removable.add(a._accountId());
- }
- }
- return removable;
- }
-
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String project() /*-{ return this.project; }-*/;
-
- public final native String branch() /*-{ return this.branch; }-*/;
-
- public final native String topic() /*-{ return this.topic; }-*/;
-
- public final native String changeId() /*-{ return this.change_id; }-*/;
-
- public final native boolean mergeable() /*-{ return this.mergeable ? true : false; }-*/;
-
- public final native int insertions() /*-{ return this.insertions; }-*/;
-
- public final native int deletions() /*-{ return this.deletions; }-*/;
-
- private native String statusRaw() /*-{ return this.status; }-*/;
-
- public final native String subject() /*-{ return this.subject; }-*/;
-
- public final native AccountInfo owner() /*-{ return this.owner; }-*/;
-
- public final native AccountInfo assignee() /*-{ return this.assignee; }-*/;
-
- private native String createdRaw() /*-{ return this.created; }-*/;
-
- private native String updatedRaw() /*-{ return this.updated; }-*/;
-
- private native String submittedRaw() /*-{ return this.submitted; }-*/;
-
- public final native AccountInfo submitter() /*-{ return this.submitter; }-*/;
-
- public final native boolean starred() /*-{ return this.starred ? true : false; }-*/;
-
- public final native boolean reviewed() /*-{ return this.reviewed ? true : false; }-*/;
-
- public final native boolean isPrivate() /*-{ return this.is_private ? true : false; }-*/;
-
- public final native boolean
- isWorkInProgress() /*-{ return this.work_in_progress ? true : false; }-*/;
-
- public final native NativeMap<LabelInfo> allLabels() /*-{ return this.labels; }-*/;
-
- public final native LabelInfo label(String n) /*-{ return this.labels[n]; }-*/;
-
- public final native String currentRevision() /*-{ return this.current_revision; }-*/;
-
- public final native void setCurrentRevision(String r) /*-{ this.current_revision = r; }-*/;
-
- public final native NativeMap<RevisionInfo> revisions() /*-{ return this.revisions; }-*/;
-
- public final native RevisionInfo revision(String n) /*-{ return this.revisions[n]; }-*/;
-
- public final native JsArray<MessageInfo> messages() /*-{ return this.messages; }-*/;
-
- public final native void setEdit(EditInfo edit) /*-{ this.edit = edit; }-*/;
-
- public final native EditInfo edit() /*-{ return this.edit; }-*/;
-
- public final native boolean hasEdit() /*-{ return this.hasOwnProperty('edit') }-*/;
-
- public final native JsArrayString hashtags() /*-{ return this.hashtags; }-*/;
-
- public final native boolean hasPermittedLabels()
- /*-{ return this.hasOwnProperty('permitted_labels') }-*/ ;
-
- public final native NativeMap<JsArrayString> permittedLabels()
- /*-{ return this.permitted_labels; }-*/ ;
-
- public final native JsArrayString permittedValues(String n)
- /*-{ return this.permitted_labels[n]; }-*/ ;
-
- public final native JsArray<AccountInfo> removableReviewers()
- /*-{ return this.removable_reviewers; }-*/ ;
-
- private native NativeMap<JsArray<AccountInfo>> _reviewers() /*-{ return this.reviewers; }-*/;
-
- public final Map<ReviewerState, List<AccountInfo>> reviewers() {
- NativeMap<JsArray<AccountInfo>> reviewers = _reviewers();
- Map<ReviewerState, List<AccountInfo>> result = new HashMap<>();
- for (String k : reviewers.keySet()) {
- ReviewerState state = ReviewerState.valueOf(k.toUpperCase());
- List<AccountInfo> accounts = result.get(state);
- if (accounts == null) {
- accounts = new ArrayList<>();
- result.put(state, accounts);
- }
- accounts.addAll(Natives.asList(reviewers.get(k)));
- }
- return result;
- }
-
- public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/;
-
- public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
-
- public final native int _number() /*-{ return this._number; }-*/;
-
- public final native boolean _more_changes() /*-{ return this._more_changes ? true : false; }-*/;
-
- public final SubmitType submitType() {
- String submitType = _submitType();
- if (submitType == null) {
- return null;
- }
- return SubmitType.valueOf(submitType);
- }
-
- private native String _submitType() /*-{ return this.submit_type; }-*/;
-
- public final boolean submittable() {
- init();
- return _submittable();
- }
-
- private native boolean _submittable() /*-{ return this.submittable ? true : false; }-*/;
-
- /**
- * @return the index of the missing label or -1 if no label is missing, or if more than one label
- * is missing.
- */
- public final int getMissingLabelIndex() {
- int i = -1;
- int ret = -1;
- List<LabelInfo> labels = Natives.asList(allLabels().values());
- for (LabelInfo label : labels) {
- i++;
- if (!permittedLabels().containsKey(label.name())) {
- continue;
- }
-
- JsArrayString values = permittedValues(label.name());
- if (values.length() == 0) {
- continue;
- }
-
- switch (label.status()) {
- case NEED: // Label is required for submit.
- if (ret != -1) {
- // more than one label is missing, so it's unclear which to quick
- // approve, return -1
- return -1;
- }
- ret = i;
- continue;
-
- case OK: // Label already applied.
- case MAY: // Label is not required.
- continue;
-
- case REJECT: // Submit cannot happen, do not quick approve.
- case IMPOSSIBLE:
- return -1;
- }
- }
- return ret;
- }
-
- protected ChangeInfo() {}
-
- public static class LabelInfo extends JavaScriptObject {
- public final SubmitRecord.Label.Status status() {
- if (approved() != null) {
- return SubmitRecord.Label.Status.OK;
- } else if (rejected() != null) {
- return SubmitRecord.Label.Status.REJECT;
- } else if (optional()) {
- return SubmitRecord.Label.Status.MAY;
- } else {
- return SubmitRecord.Label.Status.NEED;
- }
- }
-
- public final native String name() /*-{ return this._name; }-*/;
-
- public final native AccountInfo approved() /*-{ return this.approved; }-*/;
-
- public final native AccountInfo rejected() /*-{ return this.rejected; }-*/;
-
- public final native AccountInfo recommended() /*-{ return this.recommended; }-*/;
-
- public final native AccountInfo disliked() /*-{ return this.disliked; }-*/;
-
- public final native JsArray<ApprovalInfo> all() /*-{ return this.all; }-*/;
-
- public final ApprovalInfo forUser(int user) {
- JsArray<ApprovalInfo> all = all();
- for (int i = 0; all != null && i < all.length(); i++) {
- if (all.get(i)._accountId() == user) {
- return all.get(i);
- }
- }
- return null;
- }
-
- private native NativeMap<NativeString> _values() /*-{ return this.values; }-*/;
-
- public final Set<String> values() {
- return Natives.keys(_values());
- }
-
- public final native String valueText(String n) /*-{ return this.values[n]; }-*/;
-
- public final native boolean optional() /*-{ return this.optional ? true : false; }-*/;
-
- public final native boolean blocking() /*-{ return this.blocking ? true : false; }-*/;
-
- public final native short defaultValue() /*-{ return this.default_value; }-*/;
-
- public final native short _value() /*-{
- if (this.value) return this.value;
- if (this.disliked) return -1;
- if (this.recommended) return 1;
- return 0;
- }-*/;
-
- public final String maxValue() {
- return LabelValue.formatValue(valueSet().last());
- }
-
- public final SortedSet<Short> valueSet() {
- SortedSet<Short> values = new TreeSet<>();
- for (String v : values()) {
- values.add(parseValue(v));
- }
- return values;
- }
-
- public static final short parseValue(String formatted) {
- if (formatted.startsWith("+")) {
- formatted = formatted.substring(1);
- } else if (formatted.startsWith(" ")) {
- formatted = formatted.trim();
- }
- return Short.parseShort(formatted);
- }
-
- protected LabelInfo() {}
- }
-
- public static class ApprovalInfo extends AccountInfo {
- public final native boolean hasValue() /*-{ return this.hasOwnProperty('value'); }-*/;
-
- public final native short value() /*-{ return this.value || 0; }-*/;
-
- public final native VotingRangeInfo
- permittedVotingRange() /*-{ return this.permitted_voting_range; }-*/;
-
- protected ApprovalInfo() {}
- }
-
- public static class VotingRangeInfo extends AccountInfo {
- public final native short min() /*-{ return this.min || 0; }-*/;
-
- public final native short max() /*-{ return this.max || 0; }-*/;
-
- protected VotingRangeInfo() {}
- }
-
- public static class EditInfo extends JavaScriptObject {
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String setName(String n) /*-{ this.name = n; }-*/;
-
- public final native String baseRevision() /*-{ return this.base_revision; }-*/;
-
- public final native CommitInfo commit() /*-{ return this.commit; }-*/;
-
- public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/;
-
- public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
-
- public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
-
- public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/;
-
- public final native boolean hasFiles() /*-{ return this.hasOwnProperty('files') }-*/;
-
- public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/;
-
- protected EditInfo() {}
- }
-
- public static class RevisionInfo extends JavaScriptObject {
- public static RevisionInfo fromEdit(EditInfo edit) {
- RevisionInfo revisionInfo = createObject().cast();
- revisionInfo.takeFromEdit(edit);
- return revisionInfo;
- }
-
- public static RevisionInfo forParent(int number, CommitInfo commit) {
- RevisionInfo revisionInfo = createObject().cast();
- revisionInfo.takeFromParent(number, commit);
- return revisionInfo;
- }
-
- private native void takeFromEdit(EditInfo edit) /*-{
- this._number = 0;
- this.name = edit.name;
- this.commit = edit.commit;
- this.edit_base = edit.base_revision;
- }-*/;
-
- private native void takeFromParent(int number, CommitInfo commit) /*-{
- this._number = number;
- this.commit = commit;
- this.name = this._number;
- }-*/;
-
- public final native int _number() /*-{ return this._number; }-*/;
-
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native AccountInfo uploader() /*-{ return this.uploader; }-*/;
-
- public final native boolean isEdit() /*-{ return this._number == 0; }-*/;
-
- public final native CommitInfo commit() /*-{ return this.commit; }-*/;
-
- public final native void setCommit(CommitInfo c) /*-{ this.commit = c; }-*/;
-
- public final native String editBase() /*-{ return this.edit_base; }-*/;
-
- public final native boolean hasFiles() /*-{ return this.hasOwnProperty('files') }-*/;
-
- public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/;
-
- public final native boolean hasActions() /*-{ return this.hasOwnProperty('actions') }-*/;
-
- public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
-
- public final native boolean hasFetch() /*-{ return this.hasOwnProperty('fetch') }-*/;
-
- public final native NativeMap<FetchInfo> fetch() /*-{ return this.fetch; }-*/;
-
- public final native boolean
- hasPushCertificate() /*-{ return this.hasOwnProperty('push_certificate'); }-*/;
-
- public final native PushCertificateInfo
- pushCertificate() /*-{ return this.push_certificate; }-*/;
-
- public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
- final int editParent = findEditParent(list);
- Natives.asList(list)
- .sort(comparing(r -> !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent));
- }
-
- public static int findEditParent(JsArray<RevisionInfo> list) {
- RevisionInfo r = findEditParentRevision(list);
- return r == null ? -1 : r._number();
- }
-
- public static RevisionInfo findEditParentRevision(JsArray<RevisionInfo> list) {
- for (int i = 0; i < list.length(); i++) {
- // edit under revisions?
- RevisionInfo editInfo = list.get(i);
- if (editInfo.isEdit()) {
- String parentRevision = editInfo.editBase();
- // find parent
- for (int j = 0; j < list.length(); j++) {
- RevisionInfo parentInfo = list.get(j);
- String name = parentInfo.name();
- if (name.equals(parentRevision)) {
- // found parent pacth set number
- return parentInfo;
- }
- }
- }
- }
- return null;
- }
-
- public final String id() {
- return PatchSet.Id.toId(_number());
- }
-
- public final boolean isMerge() {
- return commit().parents().length() > 1;
- }
-
- protected RevisionInfo() {}
- }
-
- public static class FetchInfo extends JavaScriptObject {
- public final native String url() /*-{ return this.url }-*/;
-
- public final native String ref() /*-{ return this.ref }-*/;
-
- public final native NativeMap<NativeString> commands() /*-{ return this.commands }-*/;
-
- public final native String command(String n) /*-{ return this.commands[n]; }-*/;
-
- protected FetchInfo() {}
- }
-
- public static class CommitInfo extends JavaScriptObject {
- public final native String commit() /*-{ return this.commit; }-*/;
-
- public final native JsArray<CommitInfo> parents() /*-{ return this.parents; }-*/;
-
- public final native GitPerson author() /*-{ return this.author; }-*/;
-
- public final native GitPerson committer() /*-{ return this.committer; }-*/;
-
- public final native String subject() /*-{ return this.subject; }-*/;
-
- public final native String message() /*-{ return this.message; }-*/;
-
- public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- protected CommitInfo() {}
- }
-
- public static class GitPerson extends JavaScriptObject {
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String email() /*-{ return this.email; }-*/;
-
- private native String dateRaw() /*-{ return this.date; }-*/;
-
- public final Timestamp date() {
- return JavaSqlTimestamp_JsonSerializer.parseTimestamp(dateRaw());
- }
-
- protected GitPerson() {}
- }
-
- public static class MessageInfo extends JavaScriptObject {
- public final native AccountInfo author() /*-{ return this.author; }-*/;
-
- public final native String message() /*-{ return this.message; }-*/;
-
- public final native int _revisionNumber() /*-{ return this._revision_number || 0; }-*/;
-
- public final native String tag() /*-{ return this.tag; }-*/;
-
- private native String dateRaw() /*-{ return this.date; }-*/;
-
- public final Timestamp date() {
- return JavaSqlTimestamp_JsonSerializer.parseTimestamp(dateRaw());
- }
-
- protected MessageInfo() {}
- }
-
- public static class MergeableInfo extends JavaScriptObject {
- public final native String submitType() /*-{ return this.submit_type }-*/;
-
- public final native boolean mergeable() /*-{ return this.mergeable }-*/;
-
- protected MergeableInfo() {}
- }
-
- public static class IncludedInInfo extends JavaScriptObject {
- public final Set<String> externalNames() {
- return Natives.keys(external());
- }
-
- public final native JsArrayString branches() /*-{ return this.branches; }-*/;
-
- public final native JsArrayString tags() /*-{ return this.tags; }-*/;
-
- public final native JsArrayString external(String n) /*-{ return this.external[n]; }-*/;
-
- private native NativeMap<JsArrayString> external() /*-{ return this.external; }-*/;
-
- protected IncludedInInfo() {}
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/DownloadInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/DownloadInfo.java
deleted file mode 100644
index a22a1e8be1..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/DownloadInfo.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (C) 2015 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.client.info;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class DownloadInfo extends JavaScriptObject {
- public final List<String> schemes() {
- return _schemes().sortedKeys();
- }
-
- public final List<String> archives() {
- List<String> archives = new ArrayList<>();
- archives.addAll(Natives.asList(_archives()));
- return archives;
- }
-
- public final native DownloadSchemeInfo scheme(String n) /*-{ return this.schemes[n]; }-*/;
-
- private native NativeMap<DownloadSchemeInfo> _schemes() /*-{ return this.schemes; }-*/;
-
- private native JsArrayString _archives() /*-{ return this.archives; }-*/;
-
- protected DownloadInfo() {}
-
- public static class DownloadSchemeInfo extends JavaScriptObject {
- public final List<String> commandNames() {
- return _commands().sortedKeys();
- }
-
- public final Set<DownloadCommandInfo> commands(String project) {
- Set<DownloadCommandInfo> commands = new HashSet<>();
- for (String commandName : commandNames()) {
- commands.add(new DownloadCommandInfo(commandName, command(commandName, project)));
- }
- return commands;
- }
-
- public final String command(String commandName, String project) {
- return command(commandName).replaceAll("\\$\\{project\\}", project);
- }
-
- private static String projectBaseName(String project) {
- return project.substring(project.lastIndexOf('/') + 1);
- }
-
- public final List<String> cloneCommandNames() {
- return _cloneCommands().sortedKeys();
- }
-
- public final List<DownloadCommandInfo> cloneCommands(String project) {
- List<String> commandNames = cloneCommandNames();
- List<DownloadCommandInfo> commands = new ArrayList<>(commandNames.size());
- for (String commandName : commandNames) {
- commands.add(new DownloadCommandInfo(commandName, cloneCommand(commandName, project)));
- }
- return commands;
- }
-
- public final String cloneCommand(String commandName, String project) {
- return cloneCommand(commandName)
- .replaceAll("\\$\\{project\\}", project)
- .replaceAll("\\$\\{project-base-name\\}", projectBaseName(project));
- }
-
- public final String getUrl(String project) {
- return url().replaceAll("\\$\\{project\\}", project);
- }
-
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String url() /*-{ return this.url; }-*/;
-
- public final native boolean isAuthRequired() /*-{ return this.is_auth_required || false; }-*/;
-
- public final native boolean isAuthSupported() /*-{ return this.is_auth_supported || false; }-*/;
-
- public final native String command(String n) /*-{ return this.commands[n]; }-*/;
-
- public final native String cloneCommand(String n) /*-{ return this.clone_commands[n]; }-*/;
-
- private native NativeMap<NativeString> _commands() /*-{ return this.commands; }-*/;
-
- private native NativeMap<NativeString> _cloneCommands() /*-{ return this.clone_commands; }-*/;
-
- protected DownloadSchemeInfo() {}
- }
-
- public static class DownloadCommandInfo {
- private final String name;
- private final String command;
-
- DownloadCommandInfo(String name, String command) {
- this.name = name;
- this.command = command;
- }
-
- public String name() {
- return name;
- }
-
- public String command() {
- return command;
- }
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
deleted file mode 100644
index fc3dbf1b04..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
+++ /dev/null
@@ -1,75 +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.client.info;
-
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.data.FilenameComparator;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import java.util.Comparator;
-
-public class FileInfo extends JavaScriptObject {
- public final native String path() /*-{ return this.path; }-*/;
-
- public final native String oldPath() /*-{ return this.old_path; }-*/;
-
- public final native int linesInserted() /*-{ return this.lines_inserted || 0; }-*/;
-
- public final native int linesDeleted() /*-{ return this.lines_deleted || 0; }-*/;
-
- public final native boolean binary() /*-{ return this.binary || false; }-*/;
-
- public final native String status() /*-{ return this.status; }-*/;
-
- // JSNI methods cannot have 'long' as a parameter type or a return type and
- // it's suggested to use double in this case:
- // http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html#important
- public final long size() {
- return (long) _size();
- }
-
- private native double _size() /*-{ return this.size || 0; }-*/;
-
- public final long sizeDelta() {
- return (long) _sizeDelta();
- }
-
- private native double _sizeDelta() /*-{ return this.size_delta || 0; }-*/;
-
- public final native int _row() /*-{ return this._row }-*/;
-
- public final native void _row(int r) /*-{ this._row = r }-*/;
-
- public static void sortFileInfoByPath(JsArray<FileInfo> list) {
- Natives.asList(list).sort(Comparator.comparing(FileInfo::path, FilenameComparator.INSTANCE));
- }
-
- public static String getFileName(String path) {
- String fileName;
- if (Patch.COMMIT_MSG.equals(path)) {
- fileName = "Commit Message";
- } else if (Patch.MERGE_LIST.equals(path)) {
- fileName = "Merge List";
- } else {
- fileName = path;
- }
-
- int s = fileName.lastIndexOf('/');
- return s >= 0 ? fileName.substring(s + 1) : fileName;
- }
-
- protected FileInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GeneralPreferences.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GeneralPreferences.java
deleted file mode 100644
index fbdf52cbfb..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GeneralPreferences.java
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright (C) 2014 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.client.info;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-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.DownloadCommand;
-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.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class GeneralPreferences extends JavaScriptObject {
- public static GeneralPreferences create() {
- return createObject().cast();
- }
-
- public static GeneralPreferences createDefault() {
- GeneralPreferencesInfo d = GeneralPreferencesInfo.defaults();
- GeneralPreferences p = createObject().cast();
- p.changesPerPage(d.changesPerPage);
- p.showSiteHeader(d.showSiteHeader);
- p.useFlashClipboard(d.useFlashClipboard);
- p.downloadScheme(d.downloadScheme);
- p.downloadCommand(d.downloadCommand);
- p.dateFormat(d.getDateFormat());
- p.timeFormat(d.getTimeFormat());
- p.highlightAssigneeInChangeTable(d.highlightAssigneeInChangeTable);
- p.relativeDateInChangeTable(d.relativeDateInChangeTable);
- p.sizeBarInChangeTable(d.sizeBarInChangeTable);
- p.legacycidInChangeTable(d.legacycidInChangeTable);
- p.muteCommonPathPrefixes(d.muteCommonPathPrefixes);
- p.signedOffBy(d.signedOffBy);
- p.emailFormat(d.emailFormat);
- p.reviewCategoryStrategy(d.getReviewCategoryStrategy());
- p.diffView(d.getDiffView());
- p.emailStrategy(d.emailStrategy);
- p.defaultBaseForMerges(d.defaultBaseForMerges);
- return p;
- }
-
- public final int changesPerPage() {
- int changesPerPage = get("changes_per_page", GeneralPreferencesInfo.DEFAULT_PAGESIZE);
- return 0 < changesPerPage ? changesPerPage : GeneralPreferencesInfo.DEFAULT_PAGESIZE;
- }
-
- private native short get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/;
-
- public final native boolean showSiteHeader() /*-{ return this.show_site_header || false }-*/;
-
- public final native boolean useFlashClipboard()
- /*-{ return this.use_flash_clipboard || false }-*/ ;
-
- public final native String downloadScheme() /*-{ return this.download_scheme }-*/;
-
- public final DownloadCommand downloadCommand() {
- String s = downloadCommandRaw();
- return s != null ? DownloadCommand.valueOf(s) : null;
- }
-
- private native String downloadCommandRaw() /*-{ return this.download_command }-*/;
-
- public final DateFormat dateFormat() {
- String s = dateFormatRaw();
- return s != null ? DateFormat.valueOf(s) : null;
- }
-
- private native String dateFormatRaw() /*-{ return this.date_format }-*/;
-
- public final TimeFormat timeFormat() {
- String s = timeFormatRaw();
- return s != null ? TimeFormat.valueOf(s) : null;
- }
-
- private native String timeFormatRaw() /*-{ return this.time_format }-*/;
-
- public final native boolean highlightAssigneeInChangeTable()
- /*-{ return this.highlight_assignee_in_change_table || false }-*/ ;
-
- public final native boolean relativeDateInChangeTable()
- /*-{ return this.relative_date_in_change_table || false }-*/ ;
-
- public final native boolean sizeBarInChangeTable()
- /*-{ return this.size_bar_in_change_table || false }-*/ ;
-
- public final native boolean legacycidInChangeTable()
- /*-{ return this.legacycid_in_change_table || false }-*/ ;
-
- public final native boolean muteCommonPathPrefixes()
- /*-{ return this.mute_common_path_prefixes || false }-*/ ;
-
- public final native boolean signedOffBy() /*-{ return this.signed_off_by || false }-*/;
-
- public final ReviewCategoryStrategy reviewCategoryStrategy() {
- String s = reviewCategeoryStrategyRaw();
- return s != null ? ReviewCategoryStrategy.valueOf(s) : ReviewCategoryStrategy.NONE;
- }
-
- private native String reviewCategeoryStrategyRaw() /*-{ return this.review_category_strategy }-*/;
-
- public final DiffView diffView() {
- String s = diffViewRaw();
- return s != null ? DiffView.valueOf(s) : null;
- }
-
- private native String diffViewRaw() /*-{ return this.diff_view }-*/;
-
- public final EmailStrategy emailStrategy() {
- String s = emailStrategyRaw();
- return s != null ? EmailStrategy.valueOf(s) : null;
- }
-
- private native String emailStrategyRaw() /*-{ return this.email_strategy }-*/;
-
- public final EmailFormat emailFormat() {
- String s = emailFormatRaw();
- return s != null ? EmailFormat.valueOf(s) : null;
- }
-
- private native String emailFormatRaw() /*-{ return this.email_format }-*/;
-
- public final DefaultBase defaultBaseForMerges() {
- String s = defaultBaseForMergesRaw();
- return s != null ? DefaultBase.valueOf(s) : null;
- }
-
- private native String defaultBaseForMergesRaw() /*-{ return this.default_base_for_merges }-*/;
-
- public final native boolean
- publishCommentsOnPush() /*-{ return this.publish_comments_on_push || false }-*/;
-
- public final native boolean
- workInProgressByDefault() /*-{ return this.work_in_progress_by_default || false }-*/;
-
- public final native JsArray<TopMenuItem> my() /*-{ return this.my; }-*/;
-
- public final native void changesPerPage(int n) /*-{ this.changes_per_page = n }-*/;
-
- public final native void showSiteHeader(boolean s) /*-{ this.show_site_header = s }-*/;
-
- public final native void useFlashClipboard(boolean u) /*-{ this.use_flash_clipboard = u }-*/;
-
- public final native void downloadScheme(String d) /*-{ this.download_scheme = d }-*/;
-
- public final void downloadCommand(DownloadCommand d) {
- downloadCommandRaw(d != null ? d.toString() : null);
- }
-
- public final native void downloadCommandRaw(String d) /*-{ this.download_command = d }-*/;
-
- public final void dateFormat(DateFormat f) {
- dateFormatRaw(f != null ? f.toString() : null);
- }
-
- private native void dateFormatRaw(String f) /*-{ this.date_format = f }-*/;
-
- public final void timeFormat(TimeFormat f) {
- timeFormatRaw(f != null ? f.toString() : null);
- }
-
- private native void timeFormatRaw(String f) /*-{ this.time_format = f }-*/;
-
- public final native void highlightAssigneeInChangeTable(boolean d)
- /*-{ this.highlight_assignee_in_change_table = d }-*/ ;
-
- public final native void relativeDateInChangeTable(boolean d)
- /*-{ this.relative_date_in_change_table = d }-*/ ;
-
- public final native void sizeBarInChangeTable(boolean s)
- /*-{ this.size_bar_in_change_table = s }-*/ ;
-
- public final native void legacycidInChangeTable(boolean s)
- /*-{ this.legacycid_in_change_table = s }-*/ ;
-
- public final native void muteCommonPathPrefixes(boolean s)
- /*-{ this.mute_common_path_prefixes = s }-*/ ;
-
- public final native void signedOffBy(boolean s) /*-{ this.signed_off_by = s }-*/;
-
- public final void reviewCategoryStrategy(ReviewCategoryStrategy s) {
- reviewCategoryStrategyRaw(s != null ? s.toString() : null);
- }
-
- private native void reviewCategoryStrategyRaw(String s)
- /*-{ this.review_category_strategy = s }-*/ ;
-
- public final void diffView(DiffView d) {
- diffViewRaw(d != null ? d.toString() : null);
- }
-
- private native void diffViewRaw(String d) /*-{ this.diff_view = d }-*/;
-
- public final void emailStrategy(EmailStrategy s) {
- emailStrategyRaw(s != null ? s.toString() : null);
- }
-
- private native void emailStrategyRaw(String s) /*-{ this.email_strategy = s }-*/;
-
- public final void emailFormat(EmailFormat f) {
- emailFormatRaw(f != null ? f.toString() : null);
- }
-
- private native void emailFormatRaw(String s) /*-{ this.email_format = s }-*/;
-
- public final void defaultBaseForMerges(DefaultBase b) {
- defaultBaseForMergesRaw(b != null ? b.toString() : null);
- }
-
- private native void defaultBaseForMergesRaw(String b) /*-{ this.default_base_for_merges = b }-*/;
-
- public final native void publishCommentsOnPush(
- boolean p) /*-{ this.publish_comments_on_push = p }-*/;
-
- public final native void workInProgressByDefault(
- boolean p) /*-{ this.work_in_progress_by_default = p }-*/;
-
- public final void setMyMenus(List<TopMenuItem> myMenus) {
- initMy();
- for (TopMenuItem n : myMenus) {
- addMy(n);
- }
- }
-
- final native void initMy() /*-{ this.my = []; }-*/;
-
- final native void addMy(TopMenuItem m) /*-{ this.my.push(m); }-*/;
-
- public final Map<String, String> urlAliases() {
- Map<String, String> urlAliases = new HashMap<>();
- for (String k : Natives.keys(_urlAliases())) {
- urlAliases.put(k, urlAliasToken(k));
- }
- return urlAliases;
- }
-
- private native String urlAliasToken(String m) /*-{ return this.url_aliases[m]; }-*/;
-
- private native NativeMap<NativeString> _urlAliases() /*-{ return this.url_aliases; }-*/;
-
- public final void setUrlAliases(Map<String, String> urlAliases) {
- initUrlAliases();
- for (Map.Entry<String, String> e : urlAliases.entrySet()) {
- putUrlAlias(e.getKey(), e.getValue());
- }
- }
-
- private native void putUrlAlias(String m, String t) /*-{ this.url_aliases[m] = t; }-*/;
-
- private native void initUrlAliases() /*-{ this.url_aliases = {}; }-*/;
-
- protected GeneralPreferences() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GerritInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GerritInfo.java
deleted file mode 100644
index 78ca417a4b..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GerritInfo.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2015 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.client.info;
-
-import com.google.gerrit.extensions.client.UiType;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-import java.util.ArrayList;
-import java.util.List;
-
-public class GerritInfo extends JavaScriptObject {
- public final Project.NameKey allProjectsNameKey() {
- return new Project.NameKey(allProjects());
- }
-
- public final boolean isAllProjects(Project.NameKey p) {
- return allProjectsNameKey().equals(p);
- }
-
- public final Project.NameKey allUsersNameKey() {
- return new Project.NameKey(allUsers());
- }
-
- public final boolean isAllUsers(Project.NameKey p) {
- return allUsersNameKey().equals(p);
- }
-
- public final native String allProjects() /*-{ return this.all_projects; }-*/;
-
- public final native String allUsers() /*-{ return this.all_users; }-*/;
-
- public final native boolean docSearch() /*-{ return this.doc_search; }-*/;
-
- public final native String docUrl() /*-{ return this.doc_url; }-*/;
-
- public final native boolean editGpgKeys() /*-{ return this.edit_gpg_keys || false; }-*/;
-
- public final native String reportBugUrl() /*-{ return this.report_bug_url; }-*/;
-
- public final native String reportBugText() /*-{ return this.report_bug_text; }-*/;
-
- private native JsArrayString _webUis() /*-{ return this.web_uis; }-*/;
-
- public final List<UiType> webUis() {
- JsArrayString webUis = _webUis();
- List<UiType> result = new ArrayList<>(webUis.length());
- for (int i = 0; i < webUis.length(); i++) {
- UiType t = UiType.parse(webUis.get(i));
- if (t != null) {
- result.add(t);
- }
- }
- return result;
- }
-
- protected GerritInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GpgKeyInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GpgKeyInfo.java
deleted file mode 100644
index fd4fde70f4..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GpgKeyInfo.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-
-public class GpgKeyInfo extends JavaScriptObject {
- public enum Status {
- BAD,
- OK,
- TRUSTED;
- }
-
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String fingerprint() /*-{ return this.fingerprint; }-*/;
-
- public final native JsArrayString userIds() /*-{ return this.user_ids; }-*/;
-
- public final native String key() /*-{ return this.key; }-*/;
-
- private native String statusRaw() /*-{ return this.status; }-*/;
-
- public final Status status() {
- String s = statusRaw();
- if (s == null) {
- return null;
- }
- return Status.valueOf(s);
- }
-
- public final native boolean hasProblems() /*-{ return this.hasOwnProperty('problems'); }-*/;
-
- public final native JsArrayString problems() /*-{ return this.problems; }-*/;
-
- protected GpgKeyInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupBaseInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupBaseInfo.java
deleted file mode 100644
index 94905c092e..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupBaseInfo.java
+++ /dev/null
@@ -1,31 +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.client.info;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.http.client.URL;
-
-public class GroupBaseInfo extends JavaScriptObject {
- public final AccountGroup.UUID getGroupUUID() {
- return new AccountGroup.UUID(URL.decodeQueryString(id()));
- }
-
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String name() /*-{ return this.name; }-*/;
-
- protected GroupBaseInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupInfo.java
deleted file mode 100644
index 9bf3411a0e..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/GroupInfo.java
+++ /dev/null
@@ -1,67 +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.client.info;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.http.client.URL;
-
-public class GroupInfo extends GroupBaseInfo {
- public final AccountGroup.Id getGroupId() {
- return new AccountGroup.Id(group_id());
- }
-
- public final native GroupOptionsInfo options() /*-{ return this.options; }-*/;
-
- public final native String description() /*-{ return this.description; }-*/;
-
- public final native String url() /*-{ return this.url; }-*/;
-
- public final native String owner() /*-{ return this.owner; }-*/;
-
- public final native void owner(String o) /*-{ if(o)this.owner=o; }-*/;
-
- public final native JsArray<AccountInfo> members() /*-{ return this.members; }-*/;
-
- public final native JsArray<GroupInfo> includes() /*-{ return this.includes; }-*/;
-
- private native int group_id() /*-{ return this.group_id; }-*/;
-
- private native String owner_id() /*-{ return this.owner_id; }-*/;
-
- private native void owner_id(String o) /*-{ if(o)this.owner_id=o; }-*/;
-
- public final AccountGroup.UUID getOwnerUUID() {
- String owner = owner_id();
- if (owner != null) {
- return new AccountGroup.UUID(URL.decodeQueryString(owner));
- }
- return null;
- }
-
- public final void setOwnerUUID(AccountGroup.UUID uuid) {
- owner_id(URL.encodeQueryString(uuid.get()));
- }
-
- protected GroupInfo() {}
-
- public static class GroupOptionsInfo extends JavaScriptObject {
- public final native boolean
- isVisibleToAll() /*-{ return this['visible_to_all'] ? true : false; }-*/;
-
- protected GroupOptionsInfo() {}
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java
deleted file mode 100644
index d96adaa8dc..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java
+++ /dev/null
@@ -1,34 +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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class OAuthTokenInfo extends JavaScriptObject {
-
- protected OAuthTokenInfo() {}
-
- public final native String username() /*-{ return this.username; }-*/;
-
- public final native String resourceHost() /*-{ return this.resource_host; }-*/;
-
- public final native String accessToken() /*-{ return this.access_token; }-*/;
-
- public final native String providerId() /*-{ return this.provider_id; }-*/;
-
- public final native String expiresAt() /*-{ return this.expires_at; }-*/;
-
- public final native String type() /*-{ return this.type; }-*/;
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/PushCertificateInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/PushCertificateInfo.java
deleted file mode 100644
index fb5d932d75..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/PushCertificateInfo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class PushCertificateInfo extends JavaScriptObject {
- public final native String certificate() /*-{ return this.certificate; }-*/;
-
- public final native GpgKeyInfo key() /*-{ return this.key; }-*/;
-
- protected PushCertificateInfo() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java
deleted file mode 100644
index d3274e60fe..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2015 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.client.info;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-import java.util.HashMap;
-import java.util.Map;
-
-public class ServerInfo extends JavaScriptObject {
- public final native AuthInfo auth() /*-{ return this.auth; }-*/;
-
- public final native ChangeConfigInfo change() /*-{ return this.change; }-*/;
-
- public final native DownloadInfo download() /*-{ return this.download; }-*/;
-
- public final native GerritInfo gerrit() /*-{ return this.gerrit; }-*/;
-
- public final native PluginConfigInfo plugin() /*-{ return this.plugin; }-*/;
-
- public final native SshdInfo sshd() /*-{ return this.sshd; }-*/;
-
- public final native SuggestInfo suggest() /*-{ return this.suggest; }-*/;
-
- public final native UserConfigInfo user() /*-{ return this.user; }-*/;
-
- public final native ReceiveInfo receive() /*-{ return this.receive; }-*/;
-
- public final Map<String, String> urlAliases() {
- Map<String, String> urlAliases = new HashMap<>();
- for (String k : Natives.keys(_urlAliases())) {
- urlAliases.put(k, urlAliasToken(k));
- }
- return urlAliases;
- }
-
- public final native String urlAliasToken(String n) /*-{ return this.url_aliases[n]; }-*/;
-
- private native NativeMap<NativeString> _urlAliases() /*-{ return this.url_aliases; }-*/;
-
- public final boolean hasSshd() {
- return sshd() != null;
- }
-
- protected ServerInfo() {}
-
- public static class ChangeConfigInfo extends JavaScriptObject {
- public final native boolean allowBlame() /*-{ return this.allow_blame || false; }-*/;
-
- public final native int largeChange() /*-{ return this.large_change || 0; }-*/;
-
- public final native String replyLabel() /*-{ return this.reply_label; }-*/;
-
- public final native String replyTooltip() /*-{ return this.reply_tooltip; }-*/;
-
- public final native boolean
- showAssigneeInChangesTable() /*-{ return this.show_assignee_in_changes_table || false; }-*/;
-
- public final native int updateDelay() /*-{ return this.update_delay || 0; }-*/;
-
- public final native boolean isSubmitWholeTopicEnabled() /*-{
- return this.submit_whole_topic; }-*/;
-
- protected ChangeConfigInfo() {}
- }
-
- public static class PluginConfigInfo extends JavaScriptObject {
- public final native boolean hasAvatars() /*-{ return this.has_avatars || false; }-*/;
-
- public final native JsArrayString jsResourcePaths() /*-{
- return this.js_resource_paths || []; }-*/;
-
- protected PluginConfigInfo() {}
- }
-
- public static class SshdInfo extends JavaScriptObject {
- protected SshdInfo() {}
- }
-
- public static class SuggestInfo extends JavaScriptObject {
- public final native int from() /*-{ return this.from || 0; }-*/;
-
- protected SuggestInfo() {}
- }
-
- public static class UserConfigInfo extends JavaScriptObject {
- public final native String anonymousCowardName() /*-{ return this.anonymous_coward_name; }-*/;
-
- protected UserConfigInfo() {}
- }
-
- public static class ReceiveInfo extends JavaScriptObject {
- public final native boolean enableSignedPush()
- /*-{ return this.enable_signed_push || false; }-*/ ;
-
- protected ReceiveInfo() {}
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenu.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenu.java
deleted file mode 100644
index 7e25af2d86..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenu.java
+++ /dev/null
@@ -1,27 +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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-
-public class TopMenu extends JavaScriptObject {
-
- protected TopMenu() {}
-
- public final native String getName() /*-{ return this.name; }-*/;
-
- public final native JsArray<TopMenuItem> getItems() /*-{ return this.items; }-*/;
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuItem.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuItem.java
deleted file mode 100644
index 3a286a28c6..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuItem.java
+++ /dev/null
@@ -1,40 +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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class TopMenuItem extends JavaScriptObject {
- public static TopMenuItem create(String name, String url) {
- TopMenuItem i = createObject().cast();
- i.name(name);
- i.url(url);
- return i;
- }
-
- public final native String getName() /*-{ return this.name; }-*/;
-
- public final native String getUrl() /*-{ return this.url; }-*/;
-
- public final native String getTarget() /*-{ return this.target; }-*/;
-
- public final native String getId() /*-{ return this.id; }-*/;
-
- public final native void name(String n) /*-{ this.name = n }-*/;
-
- public final native void url(String u) /*-{ this.url = u }-*/;
-
- protected TopMenuItem() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuList.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuList.java
deleted file mode 100644
index d7df1f7b39..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/TopMenuList.java
+++ /dev/null
@@ -1,22 +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.client.info;
-
-import com.google.gwt.core.client.JsArray;
-
-public class TopMenuList extends JsArray<TopMenu> {
-
- protected TopMenuList() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/WebLinkInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/WebLinkInfo.java
deleted file mode 100644
index bcf3dde13b..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/WebLinkInfo.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2014 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.client.info;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Image;
-
-public class WebLinkInfo extends JavaScriptObject {
-
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String imageUrl() /*-{ return this.image_url; }-*/;
-
- public final native String url() /*-{ return this.url; }-*/;
-
- public final native String target() /*-{ return this.target; }-*/;
-
- protected WebLinkInfo() {}
-
- public final Anchor toAnchor() {
- Anchor a = new Anchor();
- a.setHref(url());
- if (target() != null && !target().isEmpty()) {
- a.setTarget(target());
- }
- if (imageUrl() != null && !imageUrl().isEmpty()) {
- Image img = new Image();
- img.setAltText(name());
- img.setUrl(imageUrl());
- img.setTitle(name());
- a.getElement().appendChild(img.getElement());
- } else {
- a.setText("(" + name() + ")");
- }
- return a;
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
deleted file mode 100644
index 41306ff2fd..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2012 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.client.rpc;
-
-import static java.util.stream.Collectors.toCollection;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/** A map of native JSON objects, keyed by a string. */
-public class NativeMap<T extends JavaScriptObject> extends JavaScriptObject {
- public static <T extends JavaScriptObject> NativeMap<T> create() {
- return createObject().cast();
- }
-
- /**
- * Loop through the result map's entries and copy the key strings into the "name" property of the
- * corresponding child object. This only runs on the top level map of the result, and requires the
- * children to be JSON objects and not a JSON primitive (e.g. boolean or string).
- */
- public static <T extends JavaScriptObject, M extends NativeMap<T>>
- AsyncCallback<M> copyKeysIntoChildren(AsyncCallback<M> callback) {
- return copyKeysIntoChildren("name", callback);
- }
-
- /** Loop through the result map and set asProperty on the children. */
- public static <T extends JavaScriptObject, M extends NativeMap<T>>
- AsyncCallback<M> copyKeysIntoChildren(String asProperty, AsyncCallback<M> callback) {
- return new TransformCallback<M, M>(callback) {
- @Override
- protected M transform(M result) {
- result.copyKeysIntoChildren(asProperty);
- return result;
- }
- };
- }
-
- protected NativeMap() {}
-
- public final Set<String> keySet() {
- return Natives.keys(this);
- }
-
- public final List<String> sortedKeys() {
- return keySet().stream().sorted().collect(toCollection(ArrayList::new));
- }
-
- public final native JsArray<T> values() /*-{
- var s = this;
- var v = [];
- var i = 0;
- for (var k in s) {
- if (s.hasOwnProperty(k)) {
- v[i++] = s[k];
- }
- }
- return v;
- }-*/;
-
- public final int size() {
- return keySet().size();
- }
-
- public final boolean isEmpty() {
- return size() == 0;
- }
-
- public final boolean containsKey(String n) {
- return get(n) != null;
- }
-
- public final native T get(String n) /*-{ return this[n]; }-*/;
-
- public final native void put(String n, T v) /*-{ this[n] = v; }-*/;
-
- public final native void copyKeysIntoChildren(String p) /*-{
- var s = this;
- for (var k in s) {
- if (s.hasOwnProperty(k)) {
- var c = s[k];
- c[p] = k;
- }
- }
- }-*/;
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeString.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeString.java
deleted file mode 100644
index e0bca0ebd9..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeString.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2012 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.client.rpc;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Wraps a String that was returned from a JSON API. */
-public final class NativeString extends JavaScriptObject {
- public static final JavaScriptObject TYPE = init();
-
- // Used from core and plugins
- private static native JavaScriptObject init() /*-{
- if ($wnd.Gerrit === undefined || $wnd.Gerrit.JsonString === undefined) {
- return function(s){this.s=s};
- } else {
- return $wnd.Gerrit.JsonString;
- }
- }-*/;
-
- static NativeString wrap(String s) {
- return wrap0(TYPE, s);
- }
-
- private static native NativeString wrap0(JavaScriptObject T, String s) /*-{ return new T(s) }-*/;
-
- public native String asString() /*-{ return this.s; }-*/;
-
- public static AsyncCallback<NativeString> unwrap(AsyncCallback<String> cb) {
- return new AsyncCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {
- cb.onSuccess(result != null ? result.asString() : null);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- };
- }
-
- public static boolean is(JavaScriptObject o) {
- return is(TYPE, o);
- }
-
- private static native boolean is(JavaScriptObject T, JavaScriptObject o)
- /*-{ return o instanceof T }-*/ ;
-
- protected NativeString() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/Natives.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/Natives.java
deleted file mode 100644
index 1421386a2e..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/Natives.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2012 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.client.rpc;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.json.client.JSONObject;
-import java.util.AbstractList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-public class Natives {
- /**
- * Get the names of defined properties on the object. The returned set iterates in the native
- * iteration order, which may match the source order.
- */
- public static Set<String> keys(JavaScriptObject obj) {
- if (obj != null) {
- return new JSONObject(obj).keySet();
- }
- return Collections.emptySet();
- }
-
- public static List<String> asList(JsArrayString arr) {
- if (arr == null) {
- return null;
- }
- return new AbstractList<String>() {
- @Override
- public String set(int index, String element) {
- String old = arr.get(index);
- arr.set(index, element);
- return old;
- }
-
- @Override
- public String get(int index) {
- return arr.get(index);
- }
-
- @Override
- public int size() {
- return arr.length();
- }
- };
- }
-
- public static <T extends JavaScriptObject> List<T> asList(JsArray<T> arr) {
- if (arr == null) {
- return null;
- }
- return new AbstractList<T>() {
- @Override
- public T set(int index, T element) {
- T old = arr.get(index);
- arr.set(index, element);
- return old;
- }
-
- @Override
- public T get(int index) {
- return arr.get(index);
- }
-
- @Override
- public int size() {
- return arr.length();
- }
- };
- }
-
- public static <T extends JavaScriptObject> JsArray<T> arrayOf(T element) {
- JsArray<T> arr = JavaScriptObject.createArray().cast();
- arr.push(element);
- return arr;
- }
-
- public static JsArrayString arrayOf(Iterable<String> elements) {
- JsArrayString arr = JavaScriptObject.createArray().cast();
- for (String elem : elements) {
- arr.push(elem);
- }
- return arr;
- }
-
- public static JsArrayString arrayOf(String element) {
- JsArrayString arr = JavaScriptObject.createArray().cast();
- arr.push(element);
- return arr;
- }
-
- private Natives() {}
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java
deleted file mode 100644
index 00e6bb1d9c..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/TransformCallback.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2012 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.client.rpc;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Transforms a value and passes it on to another callback. */
-public abstract class TransformCallback<I, O> implements AsyncCallback<I> {
- private final AsyncCallback<O> callback;
-
- protected TransformCallback(AsyncCallback<O> callback) {
- this.callback = callback;
- }
-
- @Override
- public void onSuccess(I result) {
- callback.onSuccess(transform(result));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- callback.onFailure(caught);
- }
-
- protected abstract O transform(I result);
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/HighlightSuggestion.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/HighlightSuggestion.java
deleted file mode 100644
index 513f5705f9..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/HighlightSuggestion.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2015 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.client.ui;
-
-import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-
-/** A {@code Suggestion} with highlights. */
-public class HighlightSuggestion implements Suggestion {
-
- private final String keyword;
- private final String value;
-
- public HighlightSuggestion(String keyword, String value) {
- this.keyword = keyword;
- this.value = value;
- }
-
- @Override
- public String getDisplayString() {
- int start = 0;
- int keyLen = keyword.length();
- SafeHtmlBuilder builder = new SafeHtmlBuilder();
- for (; ; ) {
- int index = value.indexOf(keyword, start);
- if (index == -1) {
- builder.appendEscaped(value.substring(start));
- break;
- }
- builder.appendEscaped(value.substring(start, index));
- builder.appendHtmlConstant("<strong>");
- start = index + keyLen;
- builder.appendEscaped(value.substring(index, start));
- builder.appendHtmlConstant("</strong>");
- }
- return builder.toSafeHtml().asString();
- }
-
- @Override
- public String getReplacementString() {
- return value;
- }
-}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/RemoteSuggestOracle.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/RemoteSuggestOracle.java
deleted file mode 100644
index d66d6a60b2..0000000000
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/ui/RemoteSuggestOracle.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.SuggestOracle;
-
-/**
- * Delegates to a slow SuggestOracle, such as a remote server API.
- *
- * <p>A response is only supplied to the UI if no requests were made after the oracle begin that
- * request.
- *
- * <p>When a request is made while the delegate is still processing a prior request all intermediate
- * requests are discarded and the most recent request is queued. The pending request's response is
- * discarded and the most recent request is started.
- */
-public class RemoteSuggestOracle extends SuggestOracle {
- private final SuggestOracle oracle;
- private Query query;
- private String last;
- private Timer requestRetentionTimer;
- private boolean cancelOutstandingRequest;
-
- private boolean serveSuggestions;
-
- public RemoteSuggestOracle(SuggestOracle src) {
- oracle = src;
- }
-
- public String getLast() {
- return last;
- }
-
- @Override
- public void requestSuggestions(Request req, Callback cb) {
- if (!serveSuggestions) {
- return;
- }
-
- // Use a timer for key stroke retention, such that we don't query the
- // backend for each and every keystroke we receive.
- if (requestRetentionTimer != null) {
- requestRetentionTimer.cancel();
- }
- requestRetentionTimer =
- new Timer() {
- @Override
- public void run() {
- Query q = new Query(req, cb);
- if (query == null) {
- query = q;
- q.start();
- } else {
- query = q;
- }
- }
- };
- requestRetentionTimer.schedule(200);
- }
-
- @Override
- public void requestDefaultSuggestions(Request req, Callback cb) {
- requestSuggestions(req, cb);
- }
-
- @Override
- public boolean isDisplayStringHTML() {
- return oracle.isDisplayStringHTML();
- }
-
- public void cancelOutstandingRequest() {
- if (requestRetentionTimer != null) {
- requestRetentionTimer.cancel();
- }
- if (query != null) {
- cancelOutstandingRequest = true;
- }
- }
-
- public void setServeSuggestions(boolean serveSuggestions) {
- this.serveSuggestions = serveSuggestions;
- }
-
- private class Query implements Callback {
- final Request request;
- final Callback callback;
-
- Query(Request req, Callback cb) {
- request = req;
- callback = cb;
- }
-
- void start() {
- oracle.requestSuggestions(request, this);
- }
-
- @Override
- public void onSuggestionsReady(Request req, Response res) {
- if (cancelOutstandingRequest || !serveSuggestions) {
- // If cancelOutstandingRequest() was called, we ignore this response
- cancelOutstandingRequest = false;
- query = null;
- } else if (query == this) {
- // No new request was started while this query was running.
- // Propose this request's response as the suggestions.
- query = null;
- last = request.getQuery();
- callback.onSuggestionsReady(req, res);
- } else {
- // Another query came in while this one was running. Skip
- // this response and start the most recent query.
- query.start();
- }
- }
- }
-}
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/arrow_undo.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/arrow_undo.png
deleted file mode 100644
index 6972c5e594..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/arrow_undo.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cog.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cog.png
deleted file mode 100644
index 67de2c6ccb..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cog.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/comment_draft.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/comment_draft.png
deleted file mode 100644
index 3408ddf910..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/comment_draft.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cross.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cross.png
deleted file mode 100644
index 1514d51a3c..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/cross.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/diffy26.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/diffy26.png
deleted file mode 100644
index 88b59d87b8..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/diffy26.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/disk.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/disk.png
deleted file mode 100644
index 99d532e8b1..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/disk.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/exclamation.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/exclamation.png
deleted file mode 100644
index c37bd062e6..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/exclamation.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/find.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/find.png
deleted file mode 100644
index 1547479646..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/find.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goDown.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goDown.png
deleted file mode 100644
index 5d87e45f66..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goDown.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goNext.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goNext.png
deleted file mode 100644
index 872c1979e0..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goNext.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goPrev.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goPrev.png
deleted file mode 100644
index d68f29b34c..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goPrev.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goUp.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goUp.png
deleted file mode 100644
index f75bed4478..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/goUp.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/help.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/help.png
deleted file mode 100644
index 5c870176d4..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/help.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lightbulb.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lightbulb.png
deleted file mode 100644
index d22fde8ba4..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lightbulb.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/listAdd.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/listAdd.png
deleted file mode 100644
index 1aa7f095c6..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/listAdd.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lock.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lock.png
deleted file mode 100644
index 2ebc4f6f96..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/lock.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/merge.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/merge.png
deleted file mode 100644
index 9c892dbb1e..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/merge.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/note_add.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/note_add.png
deleted file mode 100644
index abdad91eb2..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/note_add.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_edit.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_edit.png
deleted file mode 100644
index 046811ed7a..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_edit.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_white_put.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_white_put.png
deleted file mode 100644
index 884ffd6f0a..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/page_white_put.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_down_gray.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_down_gray.png
deleted file mode 100644
index 7bdd8ea068..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_down_gray.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_next_gray.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_next_gray.png
deleted file mode 100644
index 3049ef4626..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_next_gray.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_up_gray.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_up_gray.png
deleted file mode 100644
index 966a25efbb..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/resultset_up_gray.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/sideBySideDiff.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/sideBySideDiff.png
deleted file mode 100755
index ee70080bd6..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/sideBySideDiff.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star-open.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star-open.png
deleted file mode 100644
index edd577c5bd..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star-open.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star.png
deleted file mode 100644
index b88c857895..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/star.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue.png
deleted file mode 100644
index 9757fc6ed6..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue_add.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue_add.png
deleted file mode 100644
index f135248f82..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tag_blue_add.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tick.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tick.png
deleted file mode 100644
index a9925a06ab..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/tick.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/unifiedDiff.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/unifiedDiff.png
deleted file mode 100755
index ec5f97ae12..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/unifiedDiff.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_add.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_add.png
deleted file mode 100644
index deae99bcff..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_add.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_edit.png b/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_edit.png
deleted file mode 100644
index c1974cda74..0000000000
--- a/gerrit-gwtui-common/src/main/resources/com/google/gerrit/client/user_edit.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java b/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java
deleted file mode 100644
index 6915ba749a..0000000000
--- a/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/RelativeDateFormatterTest.java
+++ /dev/null
@@ -1,216 +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.client;
-
-import static com.google.gerrit.client.RelativeDateFormatter.DAY_IN_MILLIS;
-import static com.google.gerrit.client.RelativeDateFormatter.HOUR_IN_MILLIS;
-import static com.google.gerrit.client.RelativeDateFormatter.MINUTE_IN_MILLIS;
-import static com.google.gerrit.client.RelativeDateFormatter.SECOND_IN_MILLIS;
-import static com.google.gerrit.client.RelativeDateFormatter.YEAR_IN_MILLIS;
-import static org.junit.Assert.assertEquals;
-
-import java.util.Date;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class RelativeDateFormatterTest {
-
- @BeforeClass
- public static void setConstants() {
- Constants c = new Constants();
- RelativeDateFormatter.setConstants(c, c);
- }
-
- @AfterClass
- public static void unsetConstants() {
- RelativeDateFormatter.setConstants(null, null);
- }
-
- private static void assertFormat(long ageFromNow, long timeUnit, String expectedFormat) {
- Date d = new Date(System.currentTimeMillis() - ageFromNow * timeUnit);
- String s = RelativeDateFormatter.format(d);
- assertEquals(expectedFormat, s);
- }
-
- @Test
- public void future() {
- assertFormat(-100, YEAR_IN_MILLIS, "in the future");
- assertFormat(-1, SECOND_IN_MILLIS, "in the future");
- }
-
- @Test
- public void formatSeconds() {
- assertFormat(1, SECOND_IN_MILLIS, "1 second ago");
- assertFormat(89, SECOND_IN_MILLIS, "89 seconds ago");
- }
-
- @Test
- public void formatMinutes() {
- assertFormat(90, SECOND_IN_MILLIS, "2 minutes ago");
- assertFormat(3, MINUTE_IN_MILLIS, "3 minutes ago");
- assertFormat(60, MINUTE_IN_MILLIS, "60 minutes ago");
- assertFormat(89, MINUTE_IN_MILLIS, "89 minutes ago");
- }
-
- @Test
- public void formatHours() {
- assertFormat(90, MINUTE_IN_MILLIS, "2 hours ago");
- assertFormat(149, MINUTE_IN_MILLIS, "2 hours ago");
- assertFormat(35, HOUR_IN_MILLIS, "35 hours ago");
- }
-
- @Test
- public void formatDays() {
- assertFormat(36, HOUR_IN_MILLIS, "2 days ago");
- assertFormat(13, DAY_IN_MILLIS, "13 days ago");
- }
-
- @Test
- public void formatWeeks() {
- assertFormat(14, DAY_IN_MILLIS, "2 weeks ago");
- assertFormat(69, DAY_IN_MILLIS, "10 weeks ago");
- }
-
- @Test
- public void formatMonths() {
- assertFormat(70, DAY_IN_MILLIS, "2 months ago");
- assertFormat(75, DAY_IN_MILLIS, "3 months ago");
- assertFormat(364, DAY_IN_MILLIS, "12 months ago");
- }
-
- @Test
- public void formatYearsMonths() {
- assertFormat(366, DAY_IN_MILLIS, "1 year ago");
- assertFormat(380, DAY_IN_MILLIS, "1 year, 1 month ago");
- assertFormat(410, DAY_IN_MILLIS, "1 year, 2 months ago");
- assertFormat(2, YEAR_IN_MILLIS, "2 years ago");
- assertFormat(1824, DAY_IN_MILLIS, "5 years ago");
- assertFormat(2 * 365 - 10, DAY_IN_MILLIS, "2 years ago");
- }
-
- @Test
- public void formatYears() {
- assertFormat(5, YEAR_IN_MILLIS, "5 years ago");
- assertFormat(60, YEAR_IN_MILLIS, "60 years ago");
- }
-
- private static class Constants implements CommonConstants, CommonMessages {
- @Override
- public String inTheFuture() {
- return "in the future";
- }
-
- @Override
- public String month() {
- return "month";
- }
-
- @Override
- public String months() {
- return "months";
- }
-
- @Override
- public String year() {
- return "year";
- }
-
- @Override
- public String years() {
- return "years";
- }
-
- @Override
- public String oneSecondAgo() {
- return "1 second ago";
- }
-
- @Override
- public String oneMinuteAgo() {
- return "1 minute ago";
- }
-
- @Override
- public String oneHourAgo() {
- return "1 hour ago";
- }
-
- @Override
- public String oneDayAgo() {
- return "1 day ago";
- }
-
- @Override
- public String oneWeekAgo() {
- return "1 week ago";
- }
-
- @Override
- public String oneMonthAgo() {
- return "1 month ago";
- }
-
- @Override
- public String oneYearAgo() {
- return "1 year ago";
- }
-
- @Override
- public String secondsAgo(long seconds) {
- return seconds + " seconds ago";
- }
-
- @Override
- public String minutesAgo(long minutes) {
- return minutes + " minutes ago";
- }
-
- @Override
- public String hoursAgo(long hours) {
- return hours + " hours ago";
- }
-
- @Override
- public String daysAgo(long days) {
- return days + " days ago";
- }
-
- @Override
- public String weeksAgo(long weeks) {
- return weeks + " weeks ago";
- }
-
- @Override
- public String monthsAgo(long months) {
- return months + " months ago";
- }
-
- @Override
- public String yearsAgo(long years) {
- return years + " years ago";
- }
-
- @Override
- public String years0MonthsAgo(long years, String yearLabel) {
- return years + " " + yearLabel + " ago";
- }
-
- @Override
- public String yearsMonthsAgo(long years, String yearLabel, long months, String monthLabel) {
- return years + " " + yearLabel + ", " + months + " " + monthLabel + " ago";
- }
- }
-}
diff --git a/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/ui/HighlightSuggestionTest.java b/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/ui/HighlightSuggestionTest.java
deleted file mode 100644
index 9bf2d95236..0000000000
--- a/gerrit-gwtui-common/src/test/java/com/google/gerrit/client/ui/HighlightSuggestionTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2015 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.client.ui;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class HighlightSuggestionTest {
-
- @Test
- public void singleHighlight() throws Exception {
- String keyword = "key";
- String value = "somethingkeysomething";
- HighlightSuggestion suggestion = new HighlightSuggestion(keyword, value);
- assertEquals("something<strong>key</strong>something", suggestion.getDisplayString());
- assertEquals(value, suggestion.getReplacementString());
- }
-
- @Test
- public void noHighlight() throws Exception {
- String keyword = "key";
- String value = "something";
- HighlightSuggestion suggestion = new HighlightSuggestion(keyword, value);
- assertEquals(value, suggestion.getDisplayString());
- assertEquals(value, suggestion.getReplacementString());
- }
-
- @Test
- public void doubleHighlight() throws Exception {
- String keyword = "key";
- String value = "somethingkeysomethingkeysomething";
- HighlightSuggestion suggestion = new HighlightSuggestion(keyword, value);
- assertEquals(
- "something<strong>key</strong>something<strong>key</strong>something",
- suggestion.getDisplayString());
- assertEquals(value, suggestion.getReplacementString());
- }
-}
diff --git a/gerrit-gwtui/BUILD b/gerrit-gwtui/BUILD
deleted file mode 100644
index 24718e5ab8..0000000000
--- a/gerrit-gwtui/BUILD
+++ /dev/null
@@ -1,55 +0,0 @@
-load(
- "//tools/bzl:gwt.bzl",
- "gen_ui_module",
- "gwt_genrule",
- "gwt_user_agent_permutations",
-)
-load("//tools/bzl:java.bzl", "java_library2")
-load("//tools/bzl:junit.bzl", "junit_tests")
-load("//tools/bzl:license.bzl", "license_test")
-
-gwt_genrule()
-
-gwt_genrule(suffix = "_r")
-
-gen_ui_module(name = "ui_module")
-
-gen_ui_module(
- name = "ui_module",
- suffix = "_r",
-)
-
-gwt_user_agent_permutations()
-
-java_library2(
- name = "client-lib",
- srcs = glob(["src/main/**/*.java"]),
- exported_deps = [":ui_module"],
- resources = glob(["src/main/**/*"]),
- visibility = ["//visibility:public"],
- deps = [
- "//gerrit-gwtui-common:client-lib",
- "//lib/gwt:dev",
- "//lib/gwt:user",
- ],
-)
-
-license_test(
- name = "ui_module_license_test",
- target = ":ui_module",
-)
-
-junit_tests(
- name = "ui_tests",
- srcs = glob(["src/test/java/**/*.java"]),
- visibility = ["//visibility:public"],
- deps = [
- ":ui_module",
- "//java/com/google/gerrit/common:client",
- "//java/com/google/gerrit/extensions:client",
- "//lib:junit",
- "//lib/gwt:dev",
- "//lib/gwt:user",
- "//lib/truth",
- ],
-)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
deleted file mode 100644
index 9ac919b099..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
+++ /dev/null
@@ -1,39 +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.
--->
-<module rename-to="gerrit_ui">
- <inherits name='com.google.gwt.editor.Editor'/>
- <inherits name='com.google.gwt.user.User'/>
- <inherits name='com.google.gwt.resources.Resources'/>
- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
- <inherits name='com.google.gwtexpui.css.CSS'/>
- <inherits name='com.google.gerrit.GerritGwtUICommon'/>
- <inherits name='com.google.gerrit.UserAgent'/>
- <inherits name='net.codemirror.CodeMirror'/>
-
- <extend-property name='locale' values='en'/>
- <set-property-fallback name='locale' value='en'/>
- <set-property name='locale' value='en'/>
- <set-configuration-property name='UiBinder.useSafeHtmlTemplates' value='true'/>
- <set-configuration-property name='CssResource.style' value='stable'/>
- <add-linker name='xsiframe'/>
-
- <set-property name='gwt.logging.logLevel' value='SEVERE'/>
-
- <!-- Disable GSS -->
- <set-configuration-property name='CssResource.enableGss' value='false'/>
-
- <entry-point class='com.google.gerrit.client.Gerrit'/>
-</module>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml
deleted file mode 100644
index 9644093311..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml
+++ /dev/null
@@ -1,41 +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.
--->
-<module>
- <replace-with class="com.google.gerrit.client.api.PluginName.PluginNameMoz">
- <when-type-is class="com.google.gerrit.client.api.PluginName" />
- <when-property-is name="compiler.stackMode" value="native" />
- <when-property-is name="user.agent" value="safari" />
- <when-property-is name="user.agent" value="gecko1_8" />
- </replace-with>
-
- <replace-with class="com.google.gerrit.client.ui.FancyFlexTableImplIE8">
- <when-type-is class="com.google.gerrit.client.ui.FancyFlexTableImpl" />
- <any>
- <when-property-is name="user.agent" value="ie8"/>
- </any>
- </replace-with>
-
- <replace-with class="com.google.gerrit.client.Themer.ThemerIE">
- <when-type-is class="com.google.gerrit.client.Themer" />
- <any>
- <when-property-is name="user.agent" value="ie8"/>
- <when-property-is name="user.agent" value="ie9"/>
- <when-property-is name="user.agent" value="ie10"/>
- <when-property-is name="user.agent" value="ie11"/>
- <when-property-is name="user.agent" value="edge"/>
- </any>
- </replace-with>
-</module>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java
deleted file mode 100644
index 107d6634b7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/AvatarImage.java
+++ /dev/null
@@ -1,202 +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.client;
-
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.AccountInfo.AvatarInfo;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.event.dom.client.LoadEvent;
-import com.google.gwt.event.dom.client.LoadHandler;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.dom.client.MouseOverHandler;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.UIObject;
-
-public class AvatarImage extends Image implements LoadHandler {
- public AvatarImage() {
- setVisible(false);
- addLoadHandler(this);
- }
-
- /** A default sized avatar image. */
- public AvatarImage(AccountInfo account) {
- this(account, AccountInfo.AvatarInfo.DEFAULT_SIZE, true);
- }
-
- /**
- * An avatar image for the given account using the requested size.
- *
- * @param account The account in which we are interested
- * @param size A requested size. Note that the size can be ignored depending on the avatar
- * provider. A size <= 0 indicates to let the provider decide a default size.
- * @param addPopup show avatar popup with user info on hovering over the avatar image
- */
- public AvatarImage(AccountInfo account, int size, boolean addPopup) {
- addLoadHandler(this);
- setAccount(account, size, addPopup);
- }
-
- public void setAccount(AccountInfo account, int size, boolean addPopup) {
- if (account == null) {
- setVisible(false);
- } else if (isGerritServer(account)) {
- setVisible(true);
- setResource(Gerrit.RESOURCES.gerritAvatar26());
- } else if (account.hasAvatarInfo()) {
- setVisible(false);
- AvatarInfo info = account.avatar(size);
- if (info != null) {
- setWidth(info.width() > 0 ? info.width() + "px" : "");
- setHeight(info.height() > 0 ? info.height() + "px" : "");
- setUrl(info.url());
- popup(account, addPopup);
- } else if (account.email() != null) {
- loadAvatar(account, size, addPopup);
- }
- } else if (account.email() != null) {
- loadAvatar(account, size, addPopup);
- } else {
- setVisible(false);
- }
- }
-
- private void loadAvatar(AccountInfo account, int size, boolean addPopup) {
- if (!Gerrit.info().plugin().hasAvatars()) {
- setVisible(false);
- return;
- }
-
- // TODO Kill /accounts/*/avatar URL.
- String u = account.email();
- if (Gerrit.isSignedIn() && u.equals(Gerrit.getUserAccount().email())) {
- u = "self";
- }
- RestApi api = new RestApi("/accounts/").id(u).view("avatar");
- if (size > 0) {
- api.addParameter("s", size);
- setSize("", size + "px");
- }
- setVisible(false);
- setUrl(api.url());
- popup(account, addPopup);
- }
-
- private void popup(AccountInfo account, boolean addPopup) {
- if (addPopup) {
- PopupHandler popupHandler = new PopupHandler(account, this);
- addMouseOverHandler(popupHandler);
- addMouseOutHandler(popupHandler);
- }
- }
-
- @Override
- public void onLoad(LoadEvent event) {
- setVisible(true);
- }
-
- private static boolean isGerritServer(AccountInfo account) {
- return account._accountId() == 0 && Util.C.messageNoAuthor().equals(account.name());
- }
-
- private static class PopupHandler implements MouseOverHandler, MouseOutHandler {
- private final AccountInfo account;
- private final UIObject target;
-
- private UserPopupPanel popup;
- private Timer showTimer;
- private Timer hideTimer;
-
- PopupHandler(AccountInfo account, UIObject target) {
- this.account = account;
- this.target = target;
- }
-
- private UserPopupPanel createPopupPanel(AccountInfo account) {
- UserPopupPanel popup = new UserPopupPanel(account, false, false);
- popup.addDomHandler(
- new MouseOverHandler() {
- @Override
- public void onMouseOver(MouseOverEvent event) {
- scheduleShow();
- }
- },
- MouseOverEvent.getType());
- popup.addDomHandler(
- new MouseOutHandler() {
- @Override
- public void onMouseOut(MouseOutEvent event) {
- scheduleHide();
- }
- },
- MouseOutEvent.getType());
- return popup;
- }
-
- @Override
- public void onMouseOver(MouseOverEvent event) {
- scheduleShow();
- }
-
- @Override
- public void onMouseOut(MouseOutEvent event) {
- scheduleHide();
- }
-
- private void scheduleShow() {
- if (hideTimer != null) {
- hideTimer.cancel();
- hideTimer = null;
- }
- if ((popup != null && popup.isShowing() && popup.isVisible()) || showTimer != null) {
- return;
- }
- showTimer =
- new Timer() {
- @Override
- public void run() {
- if (popup == null) {
- popup = createPopupPanel(account);
- }
- if (!popup.isShowing() || !popup.isVisible()) {
- popup.showRelativeTo(target);
- }
- }
- };
- showTimer.schedule(600);
- }
-
- private void scheduleHide() {
- if (showTimer != null) {
- showTimer.cancel();
- showTimer = null;
- }
- if (popup == null || !popup.isShowing() || !popup.isVisible() || hideTimer != null) {
- return;
- }
- hideTimer =
- new Timer() {
- @Override
- public void run() {
- popup.hide();
- }
- };
- hideTimer.schedule(50);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationCallback.java
deleted file mode 100644
index cc30873f22..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationCallback.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2010 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.client;
-
-/**
- * Interface that a caller must implement to react on the result of a {@link ConfirmationDialog}.
- */
-public abstract class ConfirmationCallback {
-
- /**
- * Called when the {@link ConfirmationDialog} is finished with OK. To be overwritten by
- * subclasses.
- */
- public abstract void onOk();
-
- /**
- * Called when the {@link ConfirmationDialog} is finished with Cancel. To be overwritten by
- * subclasses.
- */
- public void onCancel() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java
deleted file mode 100644
index 438df34888..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ConfirmationDialog.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2010 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.client;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-
-public class ConfirmationDialog extends AutoCenterDialogBox {
-
- private Button cancelButton;
- private Button okButton;
-
- public ConfirmationDialog(
- final String dialogTitle, SafeHtml message, ConfirmationCallback callback) {
- super(/* auto hide */ false, /* modal */ true);
- setGlassEnabled(true);
- setText(dialogTitle);
-
- final FlowPanel buttons = new FlowPanel();
-
- okButton = new Button();
- okButton.setText(Gerrit.C.confirmationDialogOk());
- okButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- callback.onOk();
- }
- });
- buttons.add(okButton);
-
- cancelButton = new Button();
- cancelButton.getElement().getStyle().setProperty("marginLeft", "300px");
- cancelButton.setText(Gerrit.C.confirmationDialogCancel());
- cancelButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- callback.onCancel();
- }
- });
- buttons.add(cancelButton);
-
- final FlowPanel center = new FlowPanel();
- final Widget msgWidget = message.toBlockWidget();
- center.add(msgWidget);
- center.add(buttons);
- add(center);
-
- msgWidget.setWidth("400px");
-
- setWidget(center);
- }
-
- @Override
- public void center() {
- super.center();
- GlobalKey.dialog(this);
- cancelButton.setFocus(true);
- }
-
- public void setCancelVisible(boolean visible) {
- cancelButton.setVisible(visible);
- if (!visible) {
- okButton.setFocus(true);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffObject.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffObject.java
deleted file mode 100644
index 21ced4c395..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffObject.java
+++ /dev/null
@@ -1,183 +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.client;
-
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DefaultBase;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-
-/**
- * Represent an object that can be diffed. This can be either a regular patch set, the base of a
- * patch set, the parent of a merge, the auto-merge of a merge or an edit patch set.
- */
-public class DiffObject {
- public static final String AUTO_MERGE = "AutoMerge";
-
- /**
- * Parses a string that represents a diff object.
- *
- * <p>The following string representations are supported:
- *
- * <ul>
- * <li>a positive integer: represents a patch set
- * <li>a negative integer: represents a parent of a merge patch set
- * <li>'0': represents the edit patch set
- * <li>empty string or null: represents the parent of a 1-parent patch set, also called base
- * <li>'AutoMerge': represents the auto-merge of a merge patch set
- * </ul>
- *
- * @param changeId the ID of the change to which the diff object belongs
- * @param str the string representation of the diff object
- * @return the parsed diff object, {@code null} if str cannot be parsed as diff object
- */
- public static DiffObject parse(Change.Id changeId, String str) {
- if (str == null || str.isEmpty()) {
- return new DiffObject(false);
- }
-
- if (AUTO_MERGE.equals(str)) {
- return new DiffObject(true);
- }
-
- return new DiffObject(Dispatcher.toPsId(changeId, str));
- }
-
- /** Create a DiffObject that represents the parent of a 1-parent patch set. */
- public static DiffObject base() {
- return new DiffObject(false);
- }
-
- /** Create a DiffObject that represents the auto-merge for a merge patch set. */
- public static DiffObject autoMerge() {
- return new DiffObject(true);
- }
-
- /** Create a DiffObject that represents a patch set. */
- public static DiffObject patchSet(PatchSet.Id psId) {
- return new DiffObject(psId);
- }
-
- private final PatchSet.Id psId;
- private final boolean autoMerge;
-
- private DiffObject(PatchSet.Id psId) {
- this.psId = psId;
- this.autoMerge = false;
- }
-
- private DiffObject(boolean autoMerge) {
- this.psId = null;
- this.autoMerge = autoMerge;
- }
-
- public boolean isBase() {
- return psId == null && !autoMerge;
- }
-
- public boolean isAutoMerge() {
- return psId == null && autoMerge;
- }
-
- public boolean isBaseOrAutoMerge() {
- return psId == null;
- }
-
- public boolean isPatchSet() {
- return psId != null && psId.get() > 0;
- }
-
- public boolean isParent() {
- return psId != null && psId.get() < 0;
- }
-
- public boolean isEdit() {
- return psId != null && psId.get() == 0;
- }
-
- /**
- * Returns the DiffObject as PatchSet.Id.
- *
- * @return PatchSet.Id with an id > 0 for a regular patch set; PatchSet.Id with an id < 0 for a
- * parent of a merge; PatchSet.Id with id == 0 for an edit patch set; {@code null} for the
- * base of a 1-parent patch set and for the auto-merge of a merge patch set
- */
- public PatchSet.Id asPatchSetId() {
- return psId;
- }
-
- /**
- * Returns the parent number for a parent of a merge.
- *
- * @return 1-based parent number, 0 if this DiffObject is not a parent of a merge
- */
- public int getParentNum() {
- if (!isParent()) {
- return 0;
- }
-
- return -psId.get();
- }
-
- /**
- * Returns a string representation of this DiffObject that can be used in URLs.
- *
- * <p>The following string representations are returned:
- *
- * <ul>
- * <li>a positive integer for a patch set
- * <li>a negative integer for a parent of a merge patch set
- * <li>'0' for the edit patch set
- * <li>{@code null} for the parent of a 1-parent patch set, also called base
- * <li>'AutoMerge' for the auto-merge of a merge patch set
- * </ul>
- *
- * @return string representation of this DiffObject
- */
- public String asString() {
- if (autoMerge) {
- if (Gerrit.getUserPreferences().defaultBaseForMerges() != DefaultBase.AUTO_MERGE) {
- return AUTO_MERGE;
- }
- return null;
- }
-
- if (psId != null) {
- return psId.getId();
- }
-
- return null;
- }
-
- @Override
- public String toString() {
- if (isPatchSet()) {
- return "Patch Set " + psId.getId();
- }
-
- if (isParent()) {
- return "Parent " + psId.getId();
- }
-
- if (isEdit()) {
- return "Edit Patch Set";
- }
-
- if (isAutoMerge()) {
- return "Auto Merge";
- }
-
- return "Base";
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffWebLinkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffWebLinkInfo.java
deleted file mode 100644
index fe7016f854..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/DiffWebLinkInfo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2014 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.client;
-
-import com.google.gerrit.client.info.WebLinkInfo;
-
-public class DiffWebLinkInfo extends WebLinkInfo {
- public final native boolean showOnSideBySideDiffView()
- /*-{ return this.show_on_side_by_side_diff_view || false; }-*/ ;
-
- public final native boolean showOnUnifiedDiffView()
- /*-{ return this.show_on_unified_diff_view || false; }-*/ ;
-
- protected DiffWebLinkInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
deleted file mode 100644
index 4b84864767..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ /dev/null
@@ -1,879 +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.client;
-
-import static com.google.gerrit.common.PageLinks.ADMIN_CREATE_GROUP;
-import static com.google.gerrit.common.PageLinks.ADMIN_CREATE_PROJECT;
-import static com.google.gerrit.common.PageLinks.ADMIN_GROUPS;
-import static com.google.gerrit.common.PageLinks.ADMIN_PLUGINS;
-import static com.google.gerrit.common.PageLinks.ADMIN_PROJECTS;
-import static com.google.gerrit.common.PageLinks.DASHBOARDS;
-import static com.google.gerrit.common.PageLinks.MINE;
-import static com.google.gerrit.common.PageLinks.MY_GROUPS;
-import static com.google.gerrit.common.PageLinks.PROJECTS;
-import static com.google.gerrit.common.PageLinks.QUERY;
-import static com.google.gerrit.common.PageLinks.REGISTER;
-import static com.google.gerrit.common.PageLinks.SETTINGS;
-import static com.google.gerrit.common.PageLinks.SETTINGS_AGREEMENTS;
-import static com.google.gerrit.common.PageLinks.SETTINGS_CONTACT;
-import static com.google.gerrit.common.PageLinks.SETTINGS_DIFF_PREFERENCES;
-import static com.google.gerrit.common.PageLinks.SETTINGS_EDIT_PREFERENCES;
-import static com.google.gerrit.common.PageLinks.SETTINGS_EXTENSION;
-import static com.google.gerrit.common.PageLinks.SETTINGS_GPGKEYS;
-import static com.google.gerrit.common.PageLinks.SETTINGS_HTTP_PASSWORD;
-import static com.google.gerrit.common.PageLinks.SETTINGS_MYGROUPS;
-import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
-import static com.google.gerrit.common.PageLinks.SETTINGS_OAUTH_TOKEN;
-import static com.google.gerrit.common.PageLinks.SETTINGS_PREFERENCES;
-import static com.google.gerrit.common.PageLinks.SETTINGS_PROJECTS;
-import static com.google.gerrit.common.PageLinks.SETTINGS_SSHKEYS;
-import static com.google.gerrit.common.PageLinks.SETTINGS_WEBIDENT;
-
-import com.google.gerrit.client.account.MyAgreementsScreen;
-import com.google.gerrit.client.account.MyContactInformationScreen;
-import com.google.gerrit.client.account.MyDiffPreferencesScreen;
-import com.google.gerrit.client.account.MyEditPreferencesScreen;
-import com.google.gerrit.client.account.MyGpgKeysScreen;
-import com.google.gerrit.client.account.MyGroupsScreen;
-import com.google.gerrit.client.account.MyIdentitiesScreen;
-import com.google.gerrit.client.account.MyOAuthTokenScreen;
-import com.google.gerrit.client.account.MyPasswordScreen;
-import com.google.gerrit.client.account.MyPreferencesScreen;
-import com.google.gerrit.client.account.MyProfileScreen;
-import com.google.gerrit.client.account.MySshKeysScreen;
-import com.google.gerrit.client.account.MyWatchedProjectsScreen;
-import com.google.gerrit.client.account.NewAgreementScreen;
-import com.google.gerrit.client.account.RegisterScreen;
-import com.google.gerrit.client.account.ValidateEmailScreen;
-import com.google.gerrit.client.admin.AccountGroupAuditLogScreen;
-import com.google.gerrit.client.admin.AccountGroupInfoScreen;
-import com.google.gerrit.client.admin.AccountGroupMembersScreen;
-import com.google.gerrit.client.admin.AccountGroupScreen;
-import com.google.gerrit.client.admin.CreateGroupScreen;
-import com.google.gerrit.client.admin.CreateProjectScreen;
-import com.google.gerrit.client.admin.GroupListScreen;
-import com.google.gerrit.client.admin.PluginListScreen;
-import com.google.gerrit.client.admin.ProjectAccessScreen;
-import com.google.gerrit.client.admin.ProjectBranchesScreen;
-import com.google.gerrit.client.admin.ProjectDashboardsScreen;
-import com.google.gerrit.client.admin.ProjectInfoScreen;
-import com.google.gerrit.client.admin.ProjectListScreen;
-import com.google.gerrit.client.admin.ProjectScreen;
-import com.google.gerrit.client.admin.ProjectTagsScreen;
-import com.google.gerrit.client.api.ExtensionScreen;
-import com.google.gerrit.client.api.ExtensionSettingsScreen;
-import com.google.gerrit.client.change.ChangeScreen;
-import com.google.gerrit.client.change.FileTable;
-import com.google.gerrit.client.change.ProjectChangeId;
-import com.google.gerrit.client.changes.AccountDashboardScreen;
-import com.google.gerrit.client.changes.CustomDashboardScreen;
-import com.google.gerrit.client.changes.ProjectDashboardScreen;
-import com.google.gerrit.client.changes.QueryScreen;
-import com.google.gerrit.client.dashboards.DashboardInfo;
-import com.google.gerrit.client.dashboards.DashboardList;
-import com.google.gerrit.client.diff.DisplaySide;
-import com.google.gerrit.client.diff.SideBySide;
-import com.google.gerrit.client.diff.Unified;
-import com.google.gerrit.client.documentation.DocScreen;
-import com.google.gerrit.client.editor.EditScreen;
-import com.google.gerrit.client.groups.GroupApi;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-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.gwt.core.client.GWT;
-import com.google.gwt.core.client.RunAsyncCallback;
-import com.google.gwt.http.client.URL;
-import com.google.gwtexpui.user.client.UserAgent;
-import com.google.gwtorm.client.KeyUtil;
-
-public class Dispatcher {
- public static String toPatch(
- @Nullable Project.NameKey project,
- DiffObject diffBase,
- PatchSet.Id revision,
- String fileName) {
- return toPatch("", project, diffBase, revision, fileName, null, 0);
- }
-
- public static String toPatch(
- @Nullable Project.NameKey project,
- DiffObject diffBase,
- PatchSet.Id revision,
- String fileName,
- DisplaySide side,
- int line) {
- return toPatch("", project, diffBase, revision, fileName, side, line);
- }
-
- public static String toSideBySide(
- @Nullable Project.NameKey project,
- DiffObject diffBase,
- PatchSet.Id revision,
- String fileName) {
- return toPatch("sidebyside", project, diffBase, revision, fileName, null, 0);
- }
-
- public static String toUnified(
- @Nullable Project.NameKey project,
- DiffObject diffBase,
- PatchSet.Id revision,
- String fileName) {
- return toPatch("unified", project, diffBase, revision, fileName, null, 0);
- }
-
- public static String toPatch(
- @Nullable Project.NameKey project, String type, DiffObject diffBase, Patch.Key id) {
- return toPatch(type, project, diffBase, id.getParentKey(), id.get(), null, 0);
- }
-
- public static String toEditScreen(
- @Nullable Project.NameKey project, PatchSet.Id revision, String fileName) {
- return toEditScreen(project, revision, fileName, 0);
- }
-
- public static String toEditScreen(
- @Nullable Project.NameKey project, PatchSet.Id revision, String fileName, int line) {
- return toPatch("edit", project, DiffObject.base(), revision, fileName, null, line);
- }
-
- private static String toPatch(
- String type,
- @Nullable Project.NameKey project,
- DiffObject diffBase,
- PatchSet.Id revision,
- String fileName,
- DisplaySide side,
- int line) {
- Change.Id c = revision.getParentKey();
- StringBuilder p = new StringBuilder(PageLinks.toChange(project, c));
- if (diffBase != null && diffBase.asString() != null) {
- p.append(diffBase.asString()).append("..");
- }
- p.append(revision.getId()).append("/").append(KeyUtil.encode(fileName));
- if (type != null && !type.isEmpty() && (!"sidebyside".equals(type) || preferUnified())) {
- p.append(",").append(type);
- }
- if (side == DisplaySide.A && line > 0) {
- p.append("@a").append(line);
- } else if (line > 0) {
- p.append("@").append(line);
- }
- return p.toString();
- }
-
- public static String toGroup(AccountGroup.Id id) {
- return ADMIN_GROUPS + id.toString();
- }
-
- public static String toGroup(AccountGroup.Id id, String panel) {
- return ADMIN_GROUPS + id.toString() + "," + panel;
- }
-
- public static String toGroup(AccountGroup.UUID uuid) {
- return PageLinks.toGroup(uuid);
- }
-
- public static String toGroup(AccountGroup.UUID uuid, String panel) {
- return toGroup(uuid) + "," + panel;
- }
-
- public static String toProject(Project.NameKey n) {
- return toProjectAdmin(n, ProjectScreen.getSavedPanel());
- }
-
- public static String toProjectAdmin(Project.NameKey n, String panel) {
- if (panel == null || ProjectScreen.INFO.equals(panel)) {
- return ADMIN_PROJECTS + n.toString();
- }
- return ADMIN_PROJECTS + n.toString() + "," + panel;
- }
-
- static final String RELOAD_UI = "/reload-ui/";
- private static boolean wasStartedByReloadUI;
-
- void display(String token) {
- assert token != null;
- try {
- try {
- if (matchPrefix(RELOAD_UI, token)) {
- wasStartedByReloadUI = true;
- token = skip(token);
- }
- select(token);
- } finally {
- wasStartedByReloadUI = false;
- }
- } catch (RuntimeException err) {
- GWT.log("Error parsing history token: " + token, err);
- Gerrit.display(token, new NotFoundScreen());
- }
- }
-
- private static void select(String token) {
- token = Gerrit.getUrlAliasMatcher().replace(token);
-
- if (matchPrefix(QUERY, token)) {
- query(token);
-
- } else if (matchPrefix("/Documentation/q/", token)) {
- docSearch(token);
-
- } else if (matchPrefix("/c/", token)) {
- change(token);
-
- } else if (matchPrefix("/x/", token)) {
- extension(token);
-
- } else if (matchExact(MINE, token)) {
- String defaultScreenToken = Gerrit.getDefaultScreenToken();
- if (defaultScreenToken != null && !MINE.equals(defaultScreenToken)) {
- select(defaultScreenToken);
- } else {
- Gerrit.display(token, mine());
- }
-
- } else if (matchPrefix("/dashboard/", token)) {
- dashboard(token);
-
- } else if (matchPrefix(PROJECTS, token)) {
- projects(token);
-
- } else if (matchExact(SETTINGS, token)
- || matchPrefix("/settings/", token)
- || matchExact(MY_GROUPS, token)
- || matchExact("register", token)
- || matchExact(REGISTER, token)
- || matchPrefix("/register/", token)
- || matchPrefix("/VE/", token)
- || matchPrefix("VE,", token)
- || matchPrefix("/SignInFailure,", token)) {
- settings(token);
-
- } else if (matchPrefix("/admin/", token)) {
- admin(token);
-
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- }
-
- private static void query(String token) {
- String s = skip(token);
- int c = s.indexOf(',');
- Screen screen;
- if (c >= 0) {
- String prefix = s.substring(0, c);
- if (s.substring(c).equals(",n,z")) {
- // Respect legacy token with max sortkey.
- screen = new QueryScreen(prefix, 0);
- } else {
- screen = new QueryScreen(prefix, Integer.parseInt(s.substring(c + 1)));
- }
- } else {
- screen = new QueryScreen(s, 0);
- }
- Gerrit.display(token, screen);
- }
-
- private static Screen mine() {
- if (Gerrit.isSignedIn()) {
- return new AccountDashboardScreen(Gerrit.getUserAccount()._accountId());
- }
- Screen r = new AccountDashboardScreen(null);
- r.setRequiresSignIn(true);
- return r;
- }
-
- private static void dashboard(String token) {
- String rest = skip(token);
- if (rest.matches("[0-9]+")) {
- int accountId = Integer.parseInt(rest);
- Gerrit.display(token, new AccountDashboardScreen(accountId));
- return;
- }
-
- if (rest.equals("self")) {
- if (Gerrit.isSignedIn()) {
- Gerrit.display(token, new AccountDashboardScreen(Gerrit.getUserAccount()._accountId()));
- } else {
- Screen s = new AccountDashboardScreen(null);
- s.setRequiresSignIn(true);
- Gerrit.display(token, s);
- }
- return;
- }
-
- if (rest.startsWith("?")) {
- Gerrit.display(token, new CustomDashboardScreen(rest.substring(1)));
- return;
- }
-
- Gerrit.display(token, new NotFoundScreen());
- }
-
- private static void projects(String token) {
- String rest = skip(token);
- int c = rest.indexOf(DASHBOARDS);
- if (0 <= c) {
- final String project = URL.decodePathSegment(rest.substring(0, c));
- rest = rest.substring(c);
- if (matchPrefix(DASHBOARDS, rest)) {
- final String dashboardId = skip(rest);
- GerritCallback<DashboardInfo> cb =
- new GerritCallback<DashboardInfo>() {
- @Override
- public void onSuccess(DashboardInfo result) {
- if (matchPrefix("/dashboard/", result.url())) {
- String params = skip(result.url()).substring(1);
- ProjectDashboardScreen dash =
- new ProjectDashboardScreen(new Project.NameKey(project), params);
- Gerrit.display(token, dash);
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if ("default".equals(dashboardId) && RestApi.isNotFound(caught)) {
- Gerrit.display(
- PageLinks.toChangeQuery(
- PageLinks.projectQuery(new Project.NameKey(project))));
- } else {
- super.onFailure(caught);
- }
- }
- };
- if ("default".equals(dashboardId)) {
- DashboardList.getDefault(new Project.NameKey(project), cb);
- return;
- }
- c = dashboardId.indexOf(":");
- if (0 <= c) {
- final String ref = URL.decodeQueryString(dashboardId.substring(0, c));
- final String path = URL.decodeQueryString(dashboardId.substring(c + 1));
- DashboardList.get(new Project.NameKey(project), ref + ":" + path, cb);
- return;
- }
- }
- }
-
- Gerrit.display(token, new NotFoundScreen());
- }
-
- private static void change(String token) {
- String rest = skip(token);
- int c = rest.lastIndexOf(',');
- String panel = null;
- if (0 <= c) {
- panel = rest.substring(c + 1);
- rest = rest.substring(0, c);
- int at = panel.lastIndexOf('@');
- if (at > 0) {
- rest += panel.substring(at);
- panel = panel.substring(0, at);
- }
- }
-
- ProjectChangeId id = ProjectChangeId.create(rest);
- rest = rest.length() > id.identifierLength() ? rest.substring(id.identifierLength() + 1) : "";
-
- if (rest.isEmpty()) {
- FileTable.Mode mode = FileTable.Mode.REVIEW;
- if (panel != null && (panel.equals("edit") || panel.startsWith("edit/"))) {
- mode = FileTable.Mode.EDIT;
- panel = null;
- }
- Gerrit.display(
- token,
- panel == null
- ? new ChangeScreen(
- id.getProject(), id.getChangeId(), DiffObject.base(), null, false, mode)
- : new NotFoundScreen());
- return;
- }
-
- String psIdStr;
- int s = rest.indexOf('/');
- if (0 <= s) {
- psIdStr = rest.substring(0, s);
- rest = rest.substring(s + 1);
- } else {
- psIdStr = rest;
- rest = "";
- }
-
- DiffObject base = DiffObject.base();
- PatchSet.Id ps;
- int dotdot = psIdStr.indexOf("..");
- if (1 <= dotdot) {
- base = DiffObject.parse(id.getChangeId(), psIdStr.substring(0, dotdot));
- if (base == null) {
- Gerrit.display(token, new NotFoundScreen());
- }
- psIdStr = psIdStr.substring(dotdot + 2);
- }
- ps = toPsId(id.getChangeId(), psIdStr);
-
- if (!rest.isEmpty()) {
- DisplaySide side = DisplaySide.B;
- int line = 0;
- int at = rest.lastIndexOf('@');
- if (at > 0) {
- String l = rest.substring(at + 1);
- if (l.startsWith("a")) {
- side = DisplaySide.A;
- l = l.substring(1);
- }
- line = Integer.parseInt(l);
- rest = rest.substring(0, at);
- }
- Patch.Key p = new Patch.Key(ps, KeyUtil.decode(rest));
- patch(token, id.getProject(), base, p, side, line, panel);
- } else {
- if (panel == null) {
- Gerrit.display(
- token,
- new ChangeScreen(
- id.getProject(),
- id.getChangeId(),
- base,
- String.valueOf(ps.get()),
- false,
- FileTable.Mode.REVIEW));
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- }
- }
-
- public static PatchSet.Id toPsId(Change.Id id, String psIdStr) {
- return new PatchSet.Id(id, psIdStr.equals("edit") ? 0 : Integer.parseInt(psIdStr));
- }
-
- private static void extension(String token) {
- ExtensionScreen view = new ExtensionScreen(skip(token));
- if (view.isFound()) {
- Gerrit.display(token, view);
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- }
-
- private static void patch(
- String token,
- @Nullable Project.NameKey project,
- DiffObject base,
- Patch.Key id,
- DisplaySide side,
- int line,
- String panelType) {
- String panel = panelType;
- if (panel == null) {
- int c = token.lastIndexOf(',');
- panel = 0 <= c ? token.substring(c + 1) : "";
- }
-
- if ("".equals(panel) || /* DEPRECATED URL */ "cm".equals(panel)) {
- if (preferUnified()) {
- unified(token, project, base, id, side, line);
- } else {
- codemirror(token, base, project, id, side, line);
- }
- } else if ("sidebyside".equals(panel)) {
- codemirror(token, base, project, id, side, line);
- } else if ("unified".equals(panel)) {
- unified(token, project, base, id, side, line);
- } else if ("edit".equals(panel)) {
- if (!Patch.isMagic(id.get()) || Patch.COMMIT_MSG.equals(id.get())) {
- codemirrorForEdit(token, project, id, line);
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- }
-
- private static boolean preferUnified() {
- return DiffView.UNIFIED_DIFF.equals(Gerrit.getUserPreferences().diffView())
- || (UserAgent.isPortrait() && UserAgent.isMobile());
- }
-
- private static void unified(
- final String token,
- final Project.NameKey project,
- final DiffObject base,
- final Patch.Key id,
- final DisplaySide side,
- final int line) {
- GWT.runAsync(
- new AsyncSplit(token) {
- @Override
- public void onSuccess() {
- Gerrit.display(
- token,
- new Unified(
- project, base, DiffObject.patchSet(id.getParentKey()), id.get(), side, line));
- }
- });
- }
-
- private static void codemirror(
- final String token,
- final DiffObject base,
- @Nullable final Project.NameKey project,
- final Patch.Key id,
- final DisplaySide side,
- final int line) {
- GWT.runAsync(
- new AsyncSplit(token) {
- @Override
- public void onSuccess() {
- Gerrit.display(
- token,
- new SideBySide(
- project, base, DiffObject.patchSet(id.getParentKey()), id.get(), side, line));
- }
- });
- }
-
- private static void codemirrorForEdit(
- final String token,
- @Nullable final Project.NameKey project,
- final Patch.Key id,
- final int line) {
- GWT.runAsync(
- new AsyncSplit(token) {
- @Override
- public void onSuccess() {
- Gerrit.display(token, new EditScreen(project, id, line));
- }
- });
- }
-
- private static void settings(String token) {
- GWT.runAsync(
- new AsyncSplit(token) {
- @Override
- public void onSuccess() {
- Gerrit.display(token, select());
- }
-
- private Screen select() {
- if (matchExact(SETTINGS, token)) {
- return new MyProfileScreen();
- }
-
- if (matchExact(SETTINGS_PREFERENCES, token)) {
- return new MyPreferencesScreen();
- }
-
- if (matchExact(SETTINGS_DIFF_PREFERENCES, token)) {
- return new MyDiffPreferencesScreen();
- }
-
- if (matchExact(SETTINGS_EDIT_PREFERENCES, token)) {
- return new MyEditPreferencesScreen();
- }
-
- if (matchExact(SETTINGS_PROJECTS, token)) {
- return new MyWatchedProjectsScreen();
- }
-
- if (matchExact(SETTINGS_CONTACT, token)) {
- return new MyContactInformationScreen();
- }
-
- if (matchExact(SETTINGS_SSHKEYS, token)) {
- return new MySshKeysScreen();
- }
-
- if (matchExact(SETTINGS_GPGKEYS, token) && Gerrit.info().gerrit().editGpgKeys()) {
- return new MyGpgKeysScreen();
- }
-
- if (matchExact(SETTINGS_WEBIDENT, token)) {
- return new MyIdentitiesScreen();
- }
-
- if (matchExact(SETTINGS_HTTP_PASSWORD, token)) {
- return new MyPasswordScreen();
- }
-
- if (matchExact(SETTINGS_OAUTH_TOKEN, token) && Gerrit.info().auth().isOAuth()) {
- return new MyOAuthTokenScreen();
- }
-
- if (matchExact(MY_GROUPS, token) || matchExact(SETTINGS_MYGROUPS, token)) {
- return new MyGroupsScreen();
- }
-
- if (matchExact(SETTINGS_AGREEMENTS, token)
- && Gerrit.info().auth().useContributorAgreements()) {
- return new MyAgreementsScreen();
- }
-
- if (matchExact(REGISTER, token)
- || matchExact("/register/", token)
- || matchExact("register", token)) {
- return new RegisterScreen(MINE);
- } else if (matchPrefix("/register/", token)) {
- return new RegisterScreen("/" + skip(token));
- }
-
- if (matchPrefix("/VE/", token) || matchPrefix("VE,", token)) {
- return new ValidateEmailScreen(skip(token));
- }
-
- if (matchExact(SETTINGS_NEW_AGREEMENT, token)) {
- return new NewAgreementScreen();
- }
-
- if (matchPrefix(SETTINGS_NEW_AGREEMENT + "/", token)) {
- return new NewAgreementScreen(skip(token));
- }
-
- if (matchPrefix(SETTINGS_EXTENSION, token)) {
- ExtensionSettingsScreen view = new ExtensionSettingsScreen(skip(token));
- if (view.isFound()) {
- return view;
- }
- return new NotFoundScreen();
- }
-
- return new NotFoundScreen();
- }
- });
- }
-
- private static void admin(String token) {
- GWT.runAsync(
- new AsyncSplit(token) {
- @Override
- public void onSuccess() {
- if (matchExact(ADMIN_GROUPS, token) || matchExact("/admin/groups", token)) {
- Gerrit.display(token, new GroupListScreen());
-
- } else if (matchPrefix(ADMIN_GROUPS, token)) {
- String rest = skip(token);
- if (rest.startsWith("?")) {
- Gerrit.display(token, new GroupListScreen(rest.substring(1)));
- } else {
- group();
- }
-
- } else if (matchPrefix("/admin/groups", token)) {
- String rest = skip(token);
- if (rest.startsWith("?")) {
- Gerrit.display(token, new GroupListScreen(rest.substring(1)));
- }
-
- } else if (matchExact(ADMIN_PROJECTS, token) || matchExact("/admin/projects", token)) {
- Gerrit.display(token, new ProjectListScreen());
-
- } else if (matchPrefix(ADMIN_PROJECTS, token)) {
- String rest = skip(token);
- if (rest.startsWith("?")) {
- Gerrit.display(token, new ProjectListScreen(rest.substring(1)));
- } else {
- Gerrit.display(token, selectProject());
- }
-
- } else if (matchPrefix("/admin/projects", token)) {
- String rest = skip(token);
- if (rest.startsWith("?")) {
- Gerrit.display(token, new ProjectListScreen(rest.substring(1)));
- }
-
- } else if (matchPrefix(ADMIN_PLUGINS, token) || matchExact("/admin/plugins", token)) {
- Gerrit.display(token, new PluginListScreen());
-
- } else if (matchExact(ADMIN_CREATE_PROJECT, token)
- || matchExact("/admin/create-project", token)) {
- Gerrit.display(token, new CreateProjectScreen());
-
- } else if (matchExact(ADMIN_CREATE_GROUP, token)
- || matchExact("/admin/create-group", token)) {
- Gerrit.display(token, new CreateGroupScreen());
-
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- }
-
- private void group() {
- final String panel;
- final String group;
-
- if (matchPrefix("/admin/groups/uuid-", token)) {
- String p = skip(token);
- int c = p.indexOf(',');
- if (c < 0) {
- group = p;
- panel = null;
- } else {
- group = p.substring(0, c);
- panel = p.substring(c + 1);
- }
- } else if (matchPrefix(ADMIN_GROUPS, token)) {
- String p = skip(token);
- int c = p.indexOf(',');
- if (c < 0) {
- group = p;
- panel = null;
- } else {
- group = p.substring(0, c);
- panel = p.substring(c + 1);
- }
- } else {
- Gerrit.display(token, new NotFoundScreen());
- return;
- }
-
- GroupApi.getGroupDetail(
- group,
- new GerritCallback<GroupInfo>() {
- @Override
- public void onSuccess(GroupInfo group) {
- if (panel == null || panel.isEmpty()) {
- // The token does not say which group screen should be shown,
- // as default for internal groups show the members, as default
- // for external and system groups show the info screen (since
- // for external and system groups the members cannot be
- // shown in the web UI).
- //
- if (AccountGroup.isInternalGroup(group.getGroupUUID())) {
- String newToken = toGroup(group.getGroupId(), AccountGroupScreen.MEMBERS);
- Gerrit.display(newToken, new AccountGroupMembersScreen(group, newToken));
- } else {
- String newToken = toGroup(group.getGroupId(), AccountGroupScreen.INFO);
- Gerrit.display(newToken, new AccountGroupInfoScreen(group, newToken));
- }
- } else if (AccountGroupScreen.INFO.equals(panel)) {
- Gerrit.display(token, new AccountGroupInfoScreen(group, token));
- } else if (AccountGroupScreen.MEMBERS.equals(panel)) {
- Gerrit.display(token, new AccountGroupMembersScreen(group, token));
- } else if (AccountGroupScreen.AUDIT_LOG.equals(panel)) {
- Gerrit.display(token, new AccountGroupAuditLogScreen(group, token));
- } else {
- Gerrit.display(token, new NotFoundScreen());
- }
- }
- });
- }
-
- private Screen selectProject() {
- if (matchPrefix(ADMIN_PROJECTS, token)) {
- String rest = skip(token);
- int c = rest.lastIndexOf(',');
- if (c < 0) {
- return new ProjectInfoScreen(Project.NameKey.parse(rest));
- } else if (c == 0) {
- return new NotFoundScreen();
- }
-
- int q = rest.lastIndexOf('?');
- if (q > 0 && rest.lastIndexOf(',', q) > 0) {
- c = rest.substring(0, q - 1).lastIndexOf(',');
- }
-
- Project.NameKey k = Project.NameKey.parse(rest.substring(0, c));
- String panel = rest.substring(c + 1);
-
- if (ProjectScreen.INFO.equals(panel)) {
- return new ProjectInfoScreen(k);
- }
-
- if (ProjectScreen.BRANCHES.equals(panel)
- || matchPrefix(ProjectScreen.BRANCHES, panel)) {
- return new ProjectBranchesScreen(k);
- }
-
- if (ProjectScreen.TAGS.equals(panel) || matchPrefix(ProjectScreen.TAGS, panel)) {
- return new ProjectTagsScreen(k);
- }
-
- if (ProjectScreen.ACCESS.equals(panel)) {
- return new ProjectAccessScreen(k);
- }
-
- if (ProjectScreen.DASHBOARDS.equals(panel)) {
- return new ProjectDashboardsScreen(k);
- }
- }
- return new NotFoundScreen();
- }
- });
- }
-
- private static boolean matchExact(String want, String token) {
- return token.equals(want);
- }
-
- private static int prefixlen;
-
- private static boolean matchPrefix(String want, String token) {
- if (token.startsWith(want)) {
- prefixlen = want.length();
- return true;
- }
- return false;
- }
-
- private static String skip(String token) {
- return token.substring(prefixlen);
- }
-
- private abstract static class AsyncSplit implements RunAsyncCallback {
- private final boolean isReloadUi;
- protected final String token;
-
- protected AsyncSplit(String token) {
- this.isReloadUi = wasStartedByReloadUI;
- this.token = token;
- }
-
- @Override
- public final void onFailure(Throwable reason) {
- if (!isReloadUi && "HTTP download failed with status 404".equals(reason.getMessage())) {
- // The server was upgraded since we last download the main script,
- // so the pointers to the splits aren't valid anymore. Force the
- // page to reload itself and pick up the new code.
- //
- Gerrit.upgradeUI(token);
- } else {
- new ErrorDialog(reason).center();
- }
- }
- }
-
- private static void docSearch(String token) {
- GWT.runAsync(
- new AsyncSplit(token) {
- @Override
- public void onSuccess() {
- Gerrit.display(token, new DocScreen(skip(token)));
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
deleted file mode 100644
index c116d767d8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
+++ /dev/null
@@ -1,168 +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.client;
-
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.rpc.RpcConstants;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-
-/** A dialog box showing an error message, when bad things happen. */
-public class ErrorDialog extends PopupPanel {
- private final Label text;
- private final FlowPanel body;
- private final Button closey;
-
- protected ErrorDialog() {
- super(/* auto hide */ false, /* modal */ true);
- setGlassEnabled(true);
- getGlassElement().addClassName(Gerrit.RESOURCES.css().errorDialogGlass());
-
- text = new Label();
- text.setStyleName(Gerrit.RESOURCES.css().errorDialogTitle());
-
- body = new FlowPanel();
-
- final FlowPanel buttons = new FlowPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().errorDialogButtons());
-
- closey = new Button();
- closey.setText(Gerrit.C.errorDialogContinue());
- closey.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- }
- });
- closey.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- // if the close button is triggered by a key we need to consume the key
- // event, otherwise the key event would be propagated to the parent
- // screen and eventually trigger some unwanted action there after the
- // error dialog was closed
- event.stopPropagation();
- }
- });
- buttons.add(closey);
-
- final FlowPanel center = new FlowPanel();
- center.add(text);
- center.add(body);
- center.add(buttons);
-
- setText(Gerrit.C.errorTitle());
- addStyleName(Gerrit.RESOURCES.css().errorDialog());
- add(center);
-
- int l = Window.getScrollLeft() + 20;
- int t = Window.getScrollTop() + 20;
- setPopupPosition(l, t);
- }
-
- /** Create a dialog box to show a single message string. */
- public ErrorDialog(String message) {
- this();
- body.add(createErrorMsgLabel(message));
- }
-
- /** Create a dialog box to show a single message string. */
- public ErrorDialog(SafeHtml message) {
- this();
- body.add(message.toBlockWidget());
- }
-
- /** Create a dialog box to nicely format an exception. */
- public ErrorDialog(Throwable what) {
- this();
-
- String hdr;
- String msg;
-
- if (what instanceof StatusCodeException) {
- StatusCodeException sc = (StatusCodeException) what;
- if (RestApi.isExpected(sc.getStatusCode())) {
- hdr = null;
- msg = sc.getEncodedResponse();
- } else if (sc.getStatusCode() == Response.SC_INTERNAL_SERVER_ERROR) {
- hdr = null;
- msg = what.getMessage();
- } else {
- hdr = RpcConstants.C.errorServerUnavailable();
- msg = what.getMessage();
- }
-
- } else if (what instanceof RemoteJsonException) {
- // TODO Remove RemoteJsonException from Gerrit sources.
- hdr = RpcConstants.C.errorRemoteJsonException();
- msg = what.getMessage();
-
- } else {
- // TODO Fix callers of ErrorDialog to stop passing random types.
- hdr = what.getClass().getName();
- if (hdr.startsWith("java.lang.")) {
- hdr = hdr.substring("java.lang.".length());
- } else if (hdr.startsWith("com.google.gerrit.")) {
- hdr = hdr.substring(hdr.lastIndexOf('.') + 1);
- }
- if (hdr.endsWith("Exception")) {
- hdr = hdr.substring(0, hdr.length() - "Exception".length());
- } else if (hdr.endsWith("Error")) {
- hdr = hdr.substring(0, hdr.length() - "Error".length());
- }
- msg = what.getMessage();
- }
-
- if (hdr != null) {
- final Label r = new Label(hdr);
- r.setStyleName(Gerrit.RESOURCES.css().errorDialogErrorType());
- body.add(r);
- }
-
- if (msg != null) {
- body.add(createErrorMsgLabel(msg));
- }
- }
-
- private Label createErrorMsgLabel(String message) {
- Label m = new Label(message);
- m.getElement().getStyle().setProperty("white-space", "pre");
- return m;
- }
-
- public ErrorDialog setText(String t) {
- text.setText(t);
- return this;
- }
-
- @Override
- public void center() {
- show();
- closey.setFocus(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
deleted file mode 100644
index c8d052e27c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
+++ /dev/null
@@ -1,132 +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.client;
-
-import com.google.gerrit.client.change.Resources;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gwt.i18n.client.NumberFormat;
-import java.util.Date;
-
-/** Misc. formatting functions. */
-public class FormatUtil {
- private static DateFormatter dateFormatter;
-
- public static void setPreferences(GeneralPreferences prefs) {
- dateFormatter = new DateFormatter(prefs);
- }
-
- /** Format a date using a really short format. */
- public static String shortFormat(Date dt) {
- ensureInited();
- return dateFormatter.shortFormat(dt);
- }
-
- /** Format a date using a really short format. */
- public static String shortFormatDayTime(Date dt) {
- ensureInited();
- return dateFormatter.shortFormatDayTime(dt);
- }
-
- /** Format a date using the locale's medium length format. */
- public static String mediumFormat(Date dt) {
- ensureInited();
- return dateFormatter.mediumFormat(dt);
- }
-
- private static void ensureInited() {
- if (dateFormatter == null) {
- setPreferences(Gerrit.getUserPreferences());
- }
- }
-
- /** Format a date using git log's relative date format. */
- public static String relativeFormat(Date dt) {
- return RelativeDateFormatter.format(dt);
- }
-
- /**
- * Formats an account as a name and an email address.
- *
- * <p>Example output:
- *
- * <ul>
- * <li>{@code A U. Thor &lt;author@example.com&gt;}: full populated
- * <li>{@code A U. Thor (12)}: missing email address
- * <li>{@code Anonymous Coward &lt;author@example.com&gt;}: missing name
- * <li>{@code Anonymous Coward (12)}: missing name and email address
- * </ul>
- */
- public static String nameEmail(AccountInfo info) {
- return createAccountFormatter().nameEmail(info);
- }
-
- /**
- * Formats an account name.
- *
- * <p>If the account has a full name, it returns only the full name. Otherwise it returns a longer
- * form that includes the email address.
- */
- public static String name(AccountInfo info) {
- return createAccountFormatter().name(info);
- }
-
- private static AccountFormatter createAccountFormatter() {
- return new AccountFormatter(Gerrit.info().user().anonymousCowardName());
- }
-
- /** The returned format string doesn't contain any +/- sign. */
- public static String formatAbsBytes(long bytes) {
- return formatBytes(bytes, true);
- }
-
- public static String formatBytes(long bytes) {
- return formatBytes(bytes, false);
- }
-
- private static String formatBytes(long bytes, boolean abs) {
- bytes = abs ? Math.abs(bytes) : bytes;
-
- if (bytes == 0) {
- return abs ? "0 B" : "+/- 0 B";
- }
-
- if (Math.abs(bytes) < 1024) {
- return (bytes > 0 && !abs ? "+" : "") + bytes + " B";
- }
-
- int exp = (int) (Math.log(Math.abs(bytes)) / Math.log(1024));
- return (bytes > 0 && !abs ? "+" : "")
- + NumberFormat.getFormat("#.0").format(bytes / Math.pow(1024, exp))
- + " "
- + "KMGTPE".charAt(exp - 1)
- + "iB";
- }
-
- public static String formatPercentage(long size, long delta) {
- if (size == 0) {
- return Resources.C.notAvailable();
- }
- return (delta > 0 ? "+" : "-") + formatAbsPercentage(size, delta);
- }
-
- public static String formatAbsPercentage(long size, long delta) {
- if (size == 0) {
- return Resources.C.notAvailable();
- }
- long percentage = Math.abs(Math.round(delta * 100.0 / size));
- return percentage + "%";
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
deleted file mode 100644
index afc65c9d37..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ /dev/null
@@ -1,1134 +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.client;
-
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
-import static com.google.gerrit.common.data.GlobalCapability.VIEW_PLUGINS;
-import static com.google.gerrit.common.data.HostPageData.XSRF_COOKIE_NAME;
-
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.account.AccountCapabilities;
-import com.google.gerrit.client.account.EditPreferences;
-import com.google.gerrit.client.admin.ProjectScreen;
-import com.google.gerrit.client.api.ApiGlue;
-import com.google.gerrit.client.api.PluginLoader;
-import com.google.gerrit.client.change.LocalComments;
-import com.google.gerrit.client.changes.ChangeListScreen;
-import com.google.gerrit.client.config.ConfigServerApi;
-import com.google.gerrit.client.documentation.DocInfo;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.AuthInfo;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.info.ServerInfo;
-import com.google.gerrit.client.info.TopMenu;
-import com.google.gerrit.client.info.TopMenuItem;
-import com.google.gerrit.client.info.TopMenuList;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.LinkMenuBar;
-import com.google.gerrit.client.ui.LinkMenuItem;
-import com.google.gerrit.client.ui.MorphingTabPanel;
-import com.google.gerrit.client.ui.ProjectLinkMenuItem;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.data.HostPageData;
-import com.google.gerrit.common.data.SystemInfoService;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.EditPreferencesInfo;
-import com.google.gerrit.extensions.client.GerritTopMenu;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.core.client.EntryPoint;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.event.shared.SimpleEventBus;
-import com.google.gwt.http.client.Request;
-import com.google.gwt.http.client.RequestBuilder;
-import com.google.gwt.http.client.RequestCallback;
-import com.google.gwt.http.client.RequestException;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.http.client.URL;
-import com.google.gwt.http.client.UrlBuilder;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Cookies;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FocusPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.InlineHTML;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.user.client.UserAgent;
-import com.google.gwtexpui.user.client.ViewSite;
-import com.google.gwtjsonrpc.client.JsonDefTarget;
-import com.google.gwtjsonrpc.client.JsonUtil;
-import com.google.gwtjsonrpc.client.XsrfManager;
-import com.google.gwtorm.client.KeyUtil;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class Gerrit implements EntryPoint {
- public static final GerritConstants C = GWT.create(GerritConstants.class);
- public static final GerritMessages M = GWT.create(GerritMessages.class);
- public static final GerritResources RESOURCES = GWT.create(GerritResources.class);
- public static final SystemInfoService SYSTEM_SVC;
- public static final EventBus EVENT_BUS = GWT.create(SimpleEventBus.class);
- public static final Themer THEMER = GWT.create(Themer.class);
- public static final String PROJECT_NAME_MENU_VAR = "${projectName}";
- public static final String INDEX = "Documentation/index.html";
-
- private static String myHost;
- private static ServerInfo myServerInfo;
- private static AccountInfo myAccount;
- private static GeneralPreferences myPrefs;
- private static UrlAliasMatcher urlAliasMatcher;
- private static boolean hasDocumentation;
- private static boolean docSearch;
- private static String docUrl;
- private static HostPageData.Theme myTheme;
- private static String defaultScreenToken;
- private static DiffPreferencesInfo myAccountDiffPref;
- private static EditPreferencesInfo editPrefs;
- private static String xGerritAuth;
- private static boolean isNoteDbEnabled;
-
- private static Map<String, LinkMenuBar> menuBars;
-
- private static MorphingTabPanel menuLeft;
- private static LinkMenuBar menuRight;
- private static RootPanel topMenu;
- private static RootPanel siteHeader;
- private static RootPanel siteFooter;
- private static RootPanel bottomMenu;
- private static SearchPanel searchPanel;
- private static final Dispatcher dispatcher = new Dispatcher();
- private static ViewSite<Screen> body;
- private static String lastChangeListToken;
- private static String lastViewToken;
- private static Anchor uiSwitcherLink;
-
- static {
- SYSTEM_SVC = GWT.create(SystemInfoService.class);
- JsonUtil.bind(SYSTEM_SVC, "rpc/SystemInfoService");
- }
-
- static void upgradeUI(String token) {
- History.newItem(Dispatcher.RELOAD_UI + token, false);
- Window.Location.reload();
- }
-
- public static void displayLastChangeList() {
- if (lastChangeListToken != null) {
- display(lastChangeListToken);
- } else if (isSignedIn()) {
- display(PageLinks.MINE);
- } else {
- display(PageLinks.toChangeQuery("status:open"));
- }
- }
-
- public static String getPriorView() {
- return lastViewToken;
- }
-
- /**
- * Load the screen at the given location, displaying when ready.
- *
- * <p>If the URL is not already pointing at this location, a new item will be added to the
- * browser's history when the screen is fully loaded and displayed on the UI.
- *
- * @param token location to parse, load, and render.
- */
- public static void display(String token) {
- if (body.getView() == null || !body.getView().displayToken(token)) {
- dispatcher.display(token);
- updateUiLink(token);
- }
- }
-
- /**
- * Load the screen passed, assuming token can be used to locate it.
- *
- * <p>The screen is loaded in the background. When it is ready to be visible a new item will be
- * added to the browser's history, the screen will be made visible, and the window title may be
- * updated.
- *
- * <p>If {@link Screen#isRequiresSignIn()} is true and the user is not signed in yet the screen
- * instance will be discarded, sign-in will take place, and will redirect to this location upon
- * success.
- *
- * @param token location that refers to {@code view}.
- * @param view the view to load.
- */
- public static void display(String token, Screen view) {
- if (view.isRequiresSignIn() && !isSignedIn()) {
- doSignIn(token);
- } else {
- view.setToken(token);
- if (isSignedIn()) {
- LocalComments.saveInlineComments();
- }
- body.setView(view);
- updateUiLink(token);
- }
- }
-
- public static void selectMenu(LinkMenuBar bar) {
- menuLeft.selectTab(menuLeft.getWidgetIndex(bar));
- }
-
- /**
- * Update the current history token after a screen change.
- *
- * <p>The caller has already updated the UI, but wants to publish a different history token for
- * the current browser state. This really only makes sense if the caller is a {@code TabPanel} and
- * is firing an event when the tab changed to a different part.
- *
- * @param token new location that is already visible.
- */
- public static void updateImpl(String token) {
- History.newItem(token, false);
- dispatchHistoryHooks(token);
- }
-
- public static void setQueryString(String query) {
- searchPanel.setText(query);
- }
-
- public static void setWindowTitle(Screen screen, String text) {
- if (screen == body.getView()) {
- if (text == null || text.length() == 0) {
- Window.setTitle(M.windowTitle1(myHost));
- } else {
- Window.setTitle(M.windowTitle2(text, myHost));
- }
- }
- }
-
- public static int getHeaderFooterHeight() {
- int h = bottomMenu.getOffsetHeight();
- if (topMenu.isVisible()) {
- h += topMenu.getOffsetHeight();
- }
- if (siteHeader.isVisible()) {
- h += siteHeader.getOffsetHeight();
- }
- if (siteFooter.isVisible()) {
- h += siteFooter.getOffsetHeight();
- }
- return h;
- }
-
- public static void setHeaderVisible(boolean visible) {
- topMenu.setVisible(visible);
- siteHeader.setVisible(visible && getUserPreferences().showSiteHeader());
- }
-
- public static boolean isHeaderVisible() {
- return topMenu.isVisible();
- }
-
- public static String getDefaultScreenToken() {
- return defaultScreenToken;
- }
-
- public static RootPanel getBottomMenu() {
- return bottomMenu;
- }
-
- /** Get the public configuration data used by this Gerrit instance. */
- public static ServerInfo info() {
- return myServerInfo;
- }
-
- public static UrlAliasMatcher getUrlAliasMatcher() {
- return urlAliasMatcher;
- }
-
- /** Site theme information (site specific colors)/ */
- public static HostPageData.Theme getTheme() {
- return myTheme;
- }
-
- /** @return the currently signed in user's account data; empty account data if no account */
- public static AccountInfo getUserAccount() {
- return myAccount;
- }
-
- /** @return access token to prove user identity during REST API calls. */
- @Nullable
- public static String getXGerritAuth() {
- return xGerritAuth;
- }
-
- /**
- * @return the preferences of the currently signed in user, the default preferences if not signed
- * in
- */
- public static GeneralPreferences getUserPreferences() {
- return myPrefs;
- }
-
- /** @return the currently signed in users's diff preferences, or default values */
- public static DiffPreferencesInfo getDiffPreferences() {
- return myAccountDiffPref;
- }
-
- public static void setDiffPreferences(DiffPreferencesInfo accountDiffPref) {
- myAccountDiffPref = accountDiffPref;
- }
-
- /** @return the edit preferences of the current user, null if not signed-in */
- public static EditPreferencesInfo getEditPreferences() {
- return editPrefs;
- }
-
- public static void setEditPreferences(EditPreferencesInfo p) {
- editPrefs = p;
- }
-
- /** @return true if the user is currently authenticated */
- public static boolean isSignedIn() {
- return xGerritAuth != null;
- }
-
- /** Sign the user into the application. */
- public static void doSignIn(String token) {
- Location.assign(loginRedirect(token));
- }
-
- public static boolean isNoteDbEnabled() {
- return isNoteDbEnabled;
- }
-
- public static String loginRedirect(String token) {
- if (token == null) {
- token = "";
- } else if (token.startsWith("/")) {
- token = token.substring(1);
- }
-
- return selfRedirect("login/") + URL.encodePathSegment("#/" + token);
- }
-
- public static String selfRedirect(String suffix) {
- // Clean up the path. Users seem to like putting extra slashes into the URL
- // which can break redirections by misinterpreting at either client or server.
- String path = Location.getPath();
- if (path == null || path.isEmpty()) {
- path = "/";
- } else {
- while (path.startsWith("//")) {
- path = path.substring(1);
- }
- while (path.endsWith("//")) {
- path = path.substring(0, path.length() - 1);
- }
- if (!path.endsWith("/")) {
- path = path + "/";
- }
- }
-
- if (suffix != null) {
- while (suffix.startsWith("/")) {
- suffix = suffix.substring(1);
- }
- path += suffix;
- }
-
- UrlBuilder builder = new UrlBuilder();
- builder.setProtocol(Location.getProtocol());
- builder.setHost(Location.getHost());
- String port = Location.getPort();
- if (port != null && !port.isEmpty()) {
- builder.setPort(Integer.parseInt(port));
- }
- builder.setPath(path);
- return builder.buildString();
- }
-
- static void deleteSessionCookie() {
- myAccount = AccountInfo.create(0, null, null, null);
- myAccountDiffPref = null;
- editPrefs = null;
- myPrefs = GeneralPreferences.createDefault();
- urlAliasMatcher.clearUserAliases();
- xGerritAuth = null;
- refreshMenuBar();
-
- // If the cookie was HttpOnly, this request to delete it will
- // most likely not be successful. We can try anyway though.
- //
- Cookies.removeCookie("GerritAccount");
- }
-
- private void setXsrfToken() {
- xGerritAuth = Cookies.getCookie(XSRF_COOKIE_NAME);
- JsonUtil.setDefaultXsrfManager(
- new XsrfManager() {
- @Override
- public String getToken(JsonDefTarget proxy) {
- return xGerritAuth;
- }
-
- @Override
- public void setToken(JsonDefTarget proxy, String token) {
- // Ignore the request, we always rely upon the cookie.
- }
- });
- }
-
- @Override
- public void onModuleLoad() {
- if (!canLoadInIFrame()) {
- UserAgent.assertNotInIFrame();
- }
- setXsrfToken();
-
- KeyUtil.setEncoderImpl(
- new KeyUtil.Encoder() {
- @Override
- public String encode(String e) {
- e = URL.encodeQueryString(e);
- e = fixPathImpl(e);
- e = fixColonImpl(e);
- e = fixDoubleQuote(e);
- return e;
- }
-
- @Override
- public String decode(String e) {
- return URL.decodeQueryString(e);
- }
-
- private native String fixPathImpl(String path)
- /*-{ return path.replace(/%2F/g, "/"); }-*/ ;
-
- private native String fixColonImpl(String path)
- /*-{ return path.replace(/%3A/g, ":"); }-*/ ;
-
- private native String fixDoubleQuote(String path)
- /*-{ return path.replace(/%22/g, '"'); }-*/ ;
- });
-
- initHostname();
- Window.setTitle(M.windowTitle1(myHost));
-
- RpcStatus.INSTANCE = new RpcStatus();
- CallbackGroup cbg = new CallbackGroup();
- getDocIndex(
- cbg.add(
- new GerritCallback<DocInfo>() {
- @Override
- public void onSuccess(DocInfo indexInfo) {
- hasDocumentation = indexInfo != null;
- docUrl = selfRedirect("/Documentation/");
- }
- }));
- ConfigServerApi.serverInfo(
- cbg.add(
- new GerritCallback<ServerInfo>() {
- @Override
- public void onSuccess(ServerInfo info) {
- myServerInfo = info;
- urlAliasMatcher = new UrlAliasMatcher(info.urlAliases());
- String du = info.gerrit().docUrl();
- if (du != null && !du.isEmpty()) {
- hasDocumentation = true;
- docUrl = du;
- }
- docSearch = info.gerrit().docSearch();
- }
- }));
- HostPageDataService hpd = GWT.create(HostPageDataService.class);
- hpd.load(
- cbg.addFinal(
- new GerritCallback<HostPageData>() {
- @Override
- public void onSuccess(HostPageData result) {
- Document.get().getElementById("gerrit_hostpagedata").removeFromParent();
- myTheme = result.theme;
- isNoteDbEnabled = result.isNoteDbEnabled;
- if (result.accountDiffPref != null) {
- myAccountDiffPref = result.accountDiffPref;
- }
- if (result.accountDiffPref != null) {
- // TODO: Support options on the GetDetail REST endpoint so that it can
- // also return the preferences. Then we can fetch everything with a
- // single request and we don't need the callback group anymore.
- CallbackGroup cbg = new CallbackGroup();
- AccountApi.self()
- .view("detail")
- .get(
- cbg.add(
- new GerritCallback<AccountInfo>() {
- @Override
- public void onSuccess(AccountInfo result) {
- myAccount = result;
- }
- }));
- AccountApi.self()
- .view("preferences")
- .get(
- cbg.add(
- new GerritCallback<GeneralPreferences>() {
- @Override
- public void onSuccess(GeneralPreferences prefs) {
- myPrefs = prefs;
- onModuleLoad2(result);
- }
- }));
- AccountApi.getEditPreferences(
- cbg.addFinal(
- new GerritCallback<EditPreferences>() {
- @Override
- public void onSuccess(EditPreferences prefs) {
- EditPreferencesInfo prefsInfo = new EditPreferencesInfo();
- prefs.copyTo(prefsInfo);
- editPrefs = prefsInfo;
- }
- }));
- } else {
- myAccount = AccountInfo.create(0, null, null, null);
- myPrefs = GeneralPreferences.createDefault();
- editPrefs = null;
- onModuleLoad2(result);
- }
- }
- }));
- }
-
- private native boolean canLoadInIFrame() /*-{
- return $wnd.gerrit_hostpagedata.canLoadInIFrame || false;
- }-*/;
-
- private static void initHostname() {
- myHost = Location.getHostName();
- final int d1 = myHost.indexOf('.');
- if (d1 < 0) {
- return;
- }
- final int d2 = myHost.indexOf('.', d1 + 1);
- if (d2 >= 0) {
- myHost = myHost.substring(0, d2);
- }
- }
-
- private static void dispatchHistoryHooks(String token) {
- ApiGlue.fireEvent("history", token);
- }
-
- private static String getUiSwitcherUrl(String token) {
- UrlBuilder builder = new UrlBuilder();
- builder.setProtocol(Location.getProtocol());
- builder.setHost(Location.getHost());
- String port = Location.getPort();
- if (port != null && !port.isEmpty()) {
- builder.setPort(Integer.parseInt(port));
- }
- String[] tokens = token.split("@", 2);
- if (Location.getPath().endsWith("/") && tokens[0].startsWith("/")) {
- tokens[0] = tokens[0].substring(1);
- }
- if (tokens[0].startsWith("projects/") && tokens[0].contains(",dashboards/")) {
- // Rewrite project dashboard URIs to a new format, because otherwise
- // "/projects/..." would be served as an API request.
- tokens[0] = "p/" + tokens[0].substring("projects/".length());
- tokens[0] = tokens[0].replace(",dashboards/", "/+/dashboard/");
- }
- builder.setPath(Location.getPath() + tokens[0]);
- if (tokens.length == 2) {
- builder.setHash(tokens[1]);
- }
- builder.setParameter("polygerrit", "1");
- return builder.buildString();
- }
-
- private static void populateBottomMenu(RootPanel btmmenu, HostPageData hpd) {
- String vs = hpd.version;
- if (vs == null || vs.isEmpty()) {
- vs = "dev";
- }
-
- btmmenu.add(new InlineHTML(M.poweredBy(vs)));
-
- btmmenu.add(new InlineLabel(" | "));
- uiSwitcherLink = new Anchor(C.newUi(), getUiSwitcherUrl(History.getToken()));
- uiSwitcherLink.setStyleName("");
- btmmenu.add(uiSwitcherLink);
-
- String reportBugUrl = info().gerrit().reportBugUrl();
- if (reportBugUrl != null) {
- String reportBugText = info().gerrit().reportBugText();
- Anchor a = new Anchor(reportBugText == null ? C.reportBug() : reportBugText, reportBugUrl);
- a.setTarget("_blank");
- a.setStyleName("");
- btmmenu.add(new InlineLabel(" | "));
- btmmenu.add(a);
- }
- btmmenu.add(new InlineLabel(" | "));
- btmmenu.add(new InlineLabel(C.keyHelp()));
- }
-
- private static void updateUiLink(String token) {
- if (uiSwitcherLink != null) {
- uiSwitcherLink.setHref(getUiSwitcherUrl(token));
- }
- }
-
- private void onModuleLoad2(HostPageData hpd) {
- RESOURCES.gwt_override().ensureInjected();
- RESOURCES.css().ensureInjected();
-
- topMenu = RootPanel.get("gerrit_topmenu");
- final RootPanel gStarting = RootPanel.get("gerrit_startinggerrit");
- final RootPanel gBody = RootPanel.get("gerrit_body");
- bottomMenu = RootPanel.get("gerrit_btmmenu");
-
- topMenu.setStyleName(RESOURCES.css().gerritTopMenu());
- gBody.setStyleName(RESOURCES.css().gerritBody());
-
- final Grid menuLine = new Grid(1, 3);
- menuLeft = new MorphingTabPanel();
- menuRight = new LinkMenuBar();
- searchPanel = new SearchPanel();
- menuLeft.setStyleName(RESOURCES.css().topmenuMenuLeft());
- menuLine.setStyleName(RESOURCES.css().topmenu());
- topMenu.add(menuLine);
- final FlowPanel menuRightPanel = new FlowPanel();
- menuRightPanel.setStyleName(RESOURCES.css().topmenuMenuRight());
- menuRightPanel.add(searchPanel);
- menuRightPanel.add(menuRight);
- menuLine.setWidget(0, 0, menuLeft);
- menuLine.setWidget(0, 1, new FlowPanel());
- menuLine.setWidget(0, 2, menuRightPanel);
- final CellFormatter fmt = menuLine.getCellFormatter();
- fmt.setStyleName(0, 0, RESOURCES.css().topmenuTDmenu());
- fmt.setStyleName(0, 1, RESOURCES.css().topmenuTDglue());
- fmt.setStyleName(0, 2, RESOURCES.css().topmenuTDmenu());
-
- siteHeader = RootPanel.get("gerrit_header");
- siteFooter = RootPanel.get("gerrit_footer");
-
- body =
- new ViewSite<Screen>() {
- @Override
- protected void onShowView(Screen view) {
- String token = view.getToken();
- History.newItem(token, false);
- dispatchHistoryHooks(token);
-
- if (view instanceof ChangeListScreen) {
- lastChangeListToken = token;
- }
-
- super.onShowView(view);
- view.onShowView();
- lastViewToken = token;
- }
- };
- gBody.add(body);
-
- JsonUtil.addRpcStartHandler(RpcStatus.INSTANCE);
- JsonUtil.addRpcCompleteHandler(RpcStatus.INSTANCE);
-
- gStarting.getElement().getParentElement().removeChild(gStarting.getElement());
- RootPanel.detachNow(gStarting);
- ApiGlue.init();
-
- applyUserPreferences();
- populateBottomMenu(bottomMenu, hpd);
- refreshMenuBar();
-
- History.addValueChangeHandler(
- new ValueChangeHandler<String>() {
- @Override
- public void onValueChange(ValueChangeEvent<String> event) {
- display(event.getValue());
- }
- });
- JumpKeys.register(body);
-
- saveDefaultTheme();
- if (hpd.messages != null) {
- new MessageOfTheDayBar(hpd.messages).show();
- }
- PluginLoader.load(
- hpd.plugins,
- hpd.pluginsLoadTimeout,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- String token = History.getToken();
- if (token.isEmpty()) {
- token = isSignedIn() ? PageLinks.MINE : PageLinks.toChangeQuery("status:open");
- }
- display(token);
- }
- });
- }
-
- private void saveDefaultTheme() {
- THEMER.init(
- Document.get().getElementById("gerrit_sitecss"),
- Document.get().getElementById("gerrit_header"),
- Document.get().getElementById("gerrit_footer"));
- }
-
- public static void refreshMenuBar() {
- menuLeft.clear();
- menuRight.clear();
-
- menuBars = new HashMap<>();
-
- boolean signedIn = isSignedIn();
- AuthInfo authInfo = info().auth();
- LinkMenuBar m;
-
- m = new LinkMenuBar();
- menuBars.put(GerritTopMenu.ALL.menuName, m);
- addLink(m, C.menuAllOpen(), PageLinks.toChangeQuery("status:open"));
- addLink(m, C.menuAllMerged(), PageLinks.toChangeQuery("status:merged"));
- addLink(m, C.menuAllAbandoned(), PageLinks.toChangeQuery("status:abandoned"));
- menuLeft.add(m, C.menuAll());
-
- if (signedIn) {
- LinkMenuBar myBar = new LinkMenuBar();
- menuBars.put(GerritTopMenu.MY.menuName, myBar);
-
- if (myPrefs.my() != null) {
- myBar.clear();
- String url = null;
- List<TopMenuItem> myMenuItems = Natives.asList(myPrefs.my());
- if (!myMenuItems.isEmpty()) {
- if (myMenuItems.get(0).getUrl().startsWith("#")) {
- url = myMenuItems.get(0).getUrl().substring(1);
- }
- for (TopMenuItem item : myMenuItems) {
- addExtensionLink(myBar, item);
- }
- }
- defaultScreenToken = url;
- }
-
- menuLeft.add(myBar, C.menuMine());
- menuLeft.selectTab(1);
- } else {
- menuLeft.selectTab(0);
- }
-
- final LinkMenuBar projectsBar = new LinkMenuBar();
- menuBars.put(GerritTopMenu.PROJECTS.menuName, projectsBar);
- addLink(projectsBar, C.menuProjectsList(), PageLinks.ADMIN_PROJECTS);
- projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsInfo(), ProjectScreen.INFO));
- projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsBranches(), ProjectScreen.BRANCHES));
- projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsTags(), ProjectScreen.TAGS));
- projectsBar.addItem(new ProjectLinkMenuItem(C.menuProjectsAccess(), ProjectScreen.ACCESS));
- final LinkMenuItem dashboardsMenuItem =
- new ProjectLinkMenuItem(C.menuProjectsDashboards(), ProjectScreen.DASHBOARDS) {
- @Override
- protected boolean match(String token) {
- return super.match(token)
- || (!getTargetHistoryToken().isEmpty()
- && ("/admin" + token).startsWith(getTargetHistoryToken()));
- }
- };
- projectsBar.addItem(dashboardsMenuItem);
- menuLeft.add(projectsBar, C.menuProjects());
-
- if (signedIn) {
- final LinkMenuBar peopleBar = new LinkMenuBar();
- menuBars.put(GerritTopMenu.PEOPLE.menuName, peopleBar);
- final LinkMenuItem groupsListMenuItem =
- addLink(peopleBar, C.menuPeopleGroupsList(), PageLinks.ADMIN_GROUPS);
- menuLeft.add(peopleBar, C.menuPeople());
-
- final LinkMenuBar pluginsBar = new LinkMenuBar();
- menuBars.put(GerritTopMenu.PLUGINS.menuName, pluginsBar);
- AccountCapabilities.all(
- new GerritCallback<AccountCapabilities>() {
- @Override
- public void onSuccess(AccountCapabilities result) {
- if (result.canPerform(CREATE_PROJECT)) {
- insertLink(
- projectsBar,
- C.menuProjectsCreate(),
- PageLinks.ADMIN_CREATE_PROJECT,
- projectsBar.getWidgetIndex(dashboardsMenuItem) + 1);
- }
- if (result.canPerform(CREATE_GROUP)) {
- insertLink(
- peopleBar,
- C.menuPeopleGroupsCreate(),
- PageLinks.ADMIN_CREATE_GROUP,
- peopleBar.getWidgetIndex(groupsListMenuItem) + 1);
- }
- if (result.canPerform(VIEW_PLUGINS)) {
- insertLink(pluginsBar, C.menuPluginsInstalled(), PageLinks.ADMIN_PLUGINS, 0);
- menuLeft.insert(
- pluginsBar, C.menuPlugins(), menuLeft.getWidgetIndex(peopleBar) + 1);
- }
- }
- },
- CREATE_PROJECT,
- CREATE_GROUP,
- VIEW_PLUGINS);
- }
-
- if (hasDocumentation) {
- m = new LinkMenuBar();
- menuBars.put(GerritTopMenu.DOCUMENTATION.menuName, m);
- addDocLink(m, C.menuDocumentationTOC(), "index.html");
- addDocLink(m, C.menuDocumentationSearch(), "user-search.html");
- addDocLink(m, C.menuDocumentationUpload(), "user-upload.html");
- addDocLink(m, C.menuDocumentationAccess(), "access-control.html");
- addDocLink(m, C.menuDocumentationAPI(), "rest-api.html");
- addDocLink(m, C.menuDocumentationProjectOwnerGuide(), "intro-project-owner.html");
- menuLeft.add(m, C.menuDocumentation());
- }
-
- if (signedIn) {
- whoAmI(!authInfo.isClientSslCertLdap());
- } else {
- switch (authInfo.authType()) {
- case CLIENT_SSL_CERT_LDAP:
- break;
-
- case OPENID:
- menuRight.addItem(
- C.menuRegister(),
- new Command() {
- @Override
- public void execute() {
- String t = History.getToken();
- if (t == null) {
- t = "";
- }
- doSignIn(PageLinks.REGISTER + t);
- }
- });
- menuRight.addItem(
- C.menuSignIn(),
- new Command() {
- @Override
- public void execute() {
- doSignIn(History.getToken());
- }
- });
- break;
-
- case OAUTH:
- menuRight.addItem(
- C.menuSignIn(),
- new Command() {
- @Override
- public void execute() {
- doSignIn(History.getToken());
- }
- });
- break;
-
- case OPENID_SSO:
- menuRight.addItem(
- C.menuSignIn(),
- new Command() {
- @Override
- public void execute() {
- doSignIn(History.getToken());
- }
- });
- break;
-
- case HTTP:
- case HTTP_LDAP:
- if (authInfo.loginUrl() != null) {
- String signinText =
- authInfo.loginText() == null ? C.menuSignIn() : authInfo.loginText();
- menuRight.add(anchor(signinText, authInfo.loginUrl()));
- }
- break;
-
- case LDAP:
- case LDAP_BIND:
- case CUSTOM_EXTENSION:
- if (authInfo.registerUrl() != null) {
- String registerText =
- authInfo.registerText() == null ? C.menuRegister() : authInfo.registerText();
- menuRight.add(anchor(registerText, authInfo.registerUrl()));
- }
- menuRight.addItem(
- C.menuSignIn(),
- new Command() {
- @Override
- public void execute() {
- doSignIn(History.getToken());
- }
- });
- break;
-
- case DEVELOPMENT_BECOME_ANY_ACCOUNT:
- menuRight.add(anchor("Become", loginRedirect("")));
- break;
- }
- }
- ConfigServerApi.topMenus(
- new GerritCallback<TopMenuList>() {
- @Override
- public void onSuccess(TopMenuList result) {
- List<TopMenu> topMenuExtensions = Natives.asList(result);
- for (TopMenu menu : topMenuExtensions) {
- String name = menu.getName();
- LinkMenuBar existingBar = menuBars.get(name);
- LinkMenuBar bar = existingBar != null ? existingBar : new LinkMenuBar();
- for (TopMenuItem item : Natives.asList(menu.getItems())) {
- addMenuLink(bar, item);
- }
- if (existingBar == null) {
- menuBars.put(name, bar);
- menuLeft.add(bar, name);
- }
- }
- }
- });
- }
-
- public static void refreshUserPreferences() {
- if (isSignedIn()) {
- AccountApi.self()
- .view("preferences")
- .get(
- new GerritCallback<GeneralPreferences>() {
- @Override
- public void onSuccess(GeneralPreferences prefs) {
- setUserPreferences(prefs);
- }
- });
- } else {
- setUserPreferences(GeneralPreferences.createDefault());
- }
- }
-
- public static void setUserPreferences(GeneralPreferences prefs) {
- myPrefs = prefs;
- applyUserPreferences();
- refreshMenuBar();
- }
-
- private static void applyUserPreferences() {
- CopyableLabel.setFlashEnabled(myPrefs.useFlashClipboard());
- if (siteHeader != null) {
- siteHeader.setVisible(myPrefs.showSiteHeader());
- }
- if (siteFooter != null) {
- siteFooter.setVisible(myPrefs.showSiteHeader());
- }
- FormatUtil.setPreferences(myPrefs);
- urlAliasMatcher.updateUserAliases(myPrefs.urlAliases());
- }
-
- public static boolean hasDocSearch() {
- return docSearch;
- }
-
- private static void getDocIndex(AsyncCallback<DocInfo> cb) {
- RequestBuilder req = new RequestBuilder(RequestBuilder.HEAD, GWT.getHostPageBaseURL() + INDEX);
- req.setCallback(
- new RequestCallback() {
- @Override
- public void onResponseReceived(Request req, Response resp) {
- switch (resp.getStatusCode()) {
- case Response.SC_OK:
- case Response.SC_MOVED_PERMANENTLY:
- case Response.SC_MOVED_TEMPORARILY:
- cb.onSuccess(DocInfo.create());
- break;
- default:
- cb.onSuccess(null);
- break;
- }
- }
-
- @Override
- public void onError(Request request, Throwable e) {
- cb.onFailure(e);
- }
- });
- try {
- req.send();
- } catch (RequestException e) {
- cb.onFailure(e);
- }
- }
-
- private static void whoAmI(boolean canLogOut) {
- AccountInfo account = getUserAccount();
- final UserPopupPanel userPopup = new UserPopupPanel(account, canLogOut, true);
- final FlowPanel userSummaryPanel = new FlowPanel();
- class PopupHandler implements KeyDownHandler, ClickHandler {
- private void showHidePopup() {
- if (userPopup.isShowing() && userPopup.isVisible()) {
- userPopup.hide();
- } else {
- userPopup.showRelativeTo(userSummaryPanel);
- }
- }
-
- @Override
- public void onClick(ClickEvent event) {
- showHidePopup();
- }
-
- @Override
- public void onKeyDown(KeyDownEvent event) {
- if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
- showHidePopup();
- event.preventDefault();
- }
- }
- }
- final PopupHandler popupHandler = new PopupHandler();
- final InlineLabel l = new InlineLabel(FormatUtil.name(account));
- l.setStyleName(RESOURCES.css().menuBarUserName());
- final AvatarImage avatar = new AvatarImage(account, 26, false);
- avatar.setStyleName(RESOURCES.css().menuBarUserNameAvatar());
- userSummaryPanel.setStyleName(RESOURCES.css().menuBarUserNamePanel());
- userSummaryPanel.add(l);
- userSummaryPanel.add(avatar);
- // "BLACK DOWN-POINTING SMALL TRIANGLE"
- userSummaryPanel.add(new InlineLabel(" \u25be"));
- userPopup.addAutoHidePartner(userSummaryPanel.getElement());
- FocusPanel fp = new FocusPanel(userSummaryPanel);
- fp.setStyleName(RESOURCES.css().menuBarUserNameFocusPanel());
- fp.addKeyDownHandler(popupHandler);
- fp.addClickHandler(popupHandler);
- menuRight.add(fp);
- }
-
- private static Anchor anchor(String text, String to) {
- final Anchor a = new Anchor(text, to);
- a.setStyleName(RESOURCES.css().menuItem());
- Roles.getMenuitemRole().set(a.getElement());
- return a;
- }
-
- private static LinkMenuItem addLink(final LinkMenuBar m, String text, String historyToken) {
- LinkMenuItem i = new LinkMenuItem(text, historyToken);
- m.addItem(i);
- return i;
- }
-
- private static void insertLink(
- final LinkMenuBar m, String text, String historyToken, int beforeIndex) {
- m.insertItem(new LinkMenuItem(text, historyToken), beforeIndex);
- }
-
- private static LinkMenuItem addProjectLink(LinkMenuBar m, TopMenuItem item) {
- LinkMenuItem i =
- new ProjectLinkMenuItem(item.getName(), item.getUrl()) {
- @Override
- protected void onScreenLoad(Project.NameKey project) {
- String p = panel.replace(PROJECT_NAME_MENU_VAR, URL.encodeQueryString(project.get()));
- if (!panel.startsWith("/x/") && !isAbsolute(panel)) {
- UrlBuilder builder = new UrlBuilder();
- builder.setProtocol(Location.getProtocol());
- builder.setHost(Location.getHost());
- String port = Location.getPort();
- if (port != null && !port.isEmpty()) {
- builder.setPort(Integer.parseInt(port));
- }
- builder.setPath(Location.getPath());
- p = builder.buildString() + p;
- }
- getElement().setPropertyString("href", p);
- }
-
- @Override
- public void go() {
- String href = getElement().getPropertyString("href");
- if (href.startsWith("#")) {
- super.go();
- } else {
- Window.open(href, getElement().getPropertyString("target"), "");
- }
- }
- };
- if (item.getTarget() != null && !item.getTarget().isEmpty()) {
- i.getElement().setAttribute("target", item.getTarget());
- }
- if (item.getId() != null) {
- i.getElement().setAttribute("id", item.getId());
- }
- m.addItem(i);
- return i;
- }
-
- private static void addDocLink(LinkMenuBar m, String text, String href) {
- final Anchor atag = anchor(text, docUrl + href);
- atag.setTarget("_blank");
- m.add(atag);
- }
-
- private static void addMenuLink(LinkMenuBar m, TopMenuItem item) {
- if (item.getUrl().contains(PROJECT_NAME_MENU_VAR)) {
- addProjectLink(m, item);
- } else {
- addExtensionLink(m, item);
- }
- }
-
- private static void addExtensionLink(LinkMenuBar m, TopMenuItem item) {
- if (item.getUrl().startsWith("#") && (item.getTarget() == null || item.getTarget().isEmpty())) {
- LinkMenuItem a = new LinkMenuItem(item.getName(), item.getUrl().substring(1));
- if (item.getId() != null) {
- a.getElement().setAttribute("id", item.getId());
- }
- m.addItem(a);
- } else {
- Anchor atag =
- anchor(
- item.getName(),
- isAbsolute(item.getUrl()) ? item.getUrl() : selfRedirect(item.getUrl()));
- if (item.getTarget() != null && !item.getTarget().isEmpty()) {
- atag.setTarget(item.getTarget());
- }
- if (item.getId() != null) {
- atag.getElement().setAttribute("id", item.getId());
- }
- m.add(atag);
- }
- }
-
- private static boolean isAbsolute(String url) {
- return url.matches("^https?://.*");
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
deleted file mode 100644
index eae3431a6f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
+++ /dev/null
@@ -1,199 +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.client;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface GerritConstants extends Constants {
- String menuSignIn();
-
- String menuRegister();
-
- String reportBug();
-
- String loadingPlugins();
-
- String signInDialogTitle();
-
- String signInDialogGoAnonymous();
-
- String linkIdentityDialogTitle();
-
- String registerDialogTitle();
-
- String loginTypeUnsupported();
-
- String errorTitle();
-
- String errorDialogContinue();
-
- String warnTitle();
-
- String confirmationDialogOk();
-
- String confirmationDialogCancel();
-
- String branchCreationDialogTitle();
-
- String branchCreationConfirmationMessage();
-
- String tagCreationDialogTitle();
-
- String tagCreationConfirmationMessage();
-
- String branchDeletionDialogTitle();
-
- String branchDeletionConfirmationMessage();
-
- String tagDeletionDialogTitle();
-
- String tagDeletionConfirmationMessage();
-
- String newUi();
-
- String notSignedInTitle();
-
- String notSignedInBody();
-
- String notFoundTitle();
-
- String notFoundBody();
-
- String noSuchAccountTitle();
-
- String noSuchGroupTitle();
-
- String inactiveAccountBody();
-
- String labelNotApplicable();
-
- String menuAll();
-
- String menuAllOpen();
-
- String menuAllMerged();
-
- String menuAllAbandoned();
-
- String menuMine();
-
- String menuMyChanges();
-
- String menuMyWatchedChanges();
-
- String menuMyStarredChanges();
-
- String menuMyDraftComments();
-
- String menuDiff();
-
- String menuDiffCommit();
-
- String menuDiffPreferences();
-
- String menuDiffPatchSets();
-
- String menuDiffFiles();
-
- String menuProjects();
-
- String menuProjectsList();
-
- String menuProjectsInfo();
-
- String menuProjectsBranches();
-
- String menuProjectsTags();
-
- String menuProjectsAccess();
-
- String menuProjectsDashboards();
-
- String menuProjectsCreate();
-
- String menuPeople();
-
- String menuPeopleGroupsList();
-
- String menuPeopleGroupsCreate();
-
- String menuPlugins();
-
- String menuPluginsInstalled();
-
- String menuDocumentation();
-
- String menuDocumentationTOC();
-
- String menuDocumentationSearch();
-
- String menuDocumentationUpload();
-
- String menuDocumentationAccess();
-
- String menuDocumentationAPI();
-
- String menuDocumentationProjectOwnerGuide();
-
- String searchHint();
-
- String searchButton();
-
- String rpcStatusWorking();
-
- String sectionNavigation();
-
- String sectionActions();
-
- String keySearch();
-
- String keyEditor();
-
- String keyHelp();
-
- String sectionJumping();
-
- String jumpAllOpen();
-
- String jumpAllMerged();
-
- String jumpAllAbandoned();
-
- String jumpMine();
-
- String jumpMineWatched();
-
- String jumpMineStarred();
-
- String jumpMineDraftComments();
-
- String projectAccessError();
-
- String projectAccessProposeForReviewHint();
-
- String userCannotVoteToolTip();
-
- String stringListPanelAdd();
-
- String stringListPanelDelete();
-
- String stringListPanelUp();
-
- String stringListPanelDown();
-
- String searchDropdownChanges();
-
- String searchDropdownDoc();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
deleted file mode 100644
index 2819d22e44..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ /dev/null
@@ -1,121 +0,0 @@
-menuSignIn = Sign In
-menuRegister = Register
-reportBug = Report Bug
-loadingPlugins = Loading plugins ...
-
-signInDialogTitle = Code Review - Sign In
-signInDialogGoAnonymous = Go Anonymous
-
-linkIdentityDialogTitle = Code Review - Link Identity
-registerDialogTitle = Code Review - Register New Account
-loginTypeUnsupported = Sign in is not available.
-
-errorTitle = Code Review - Error
-errorDialogContinue = Continue
-warnTitle = Code Review - Warning
-
-confirmationDialogOk = OK
-confirmationDialogCancel = Cancel
-
-branchCreationDialogTitle = Branch Creation
-branchCreationConfirmationMessage = The following branch was successfully created:
-
-tagCreationDialogTitle = Tag Creation
-tagCreationConfirmationMessage = The following tag was successfully created:
-
-branchDeletionDialogTitle = Branch Deletion
-branchDeletionConfirmationMessage = Do you really want to delete the following branches?
-
-tagDeletionDialogTitle = Tag Deletion
-tagDeletionConfirmationMessage = Do you really want to delete the following tags?
-
-newUi = Switch to New UI
-
-notSignedInTitle = Code Review - Session Expired
-notSignedInBody = <b>Session Expired</b>\
-<p>You are no longer signed in to Gerrit Code Review.</p>\
-<p>To continue, please sign-in again.</p>
-
-notFoundTitle = Not Found
-notFoundBody = The page you requested was not found, or you do not have permission to view this page.
-noSuchAccountTitle = Code Review - Unknown User
-
-noSuchGroupTitle = Code Review - Unknown Group
-
-inactiveAccountBody = This user is currently inactive.
-
-labelNotApplicable = Label not applicable
-
-menuAll = All
-menuAllOpen = Open
-menuAllMerged = Merged
-menuAllAbandoned = Abandoned
-
-menuMine = My
-menuMyChanges = Changes
-menuMyStarredChanges = Starred Changes
-menuMyWatchedChanges = Watched Changes
-menuMyDraftComments = Draft Comments
-
-menuDiff = Differences
-menuDiffCommit = Commit Message
-menuDiffPreferences = Preferences
-menuDiffPatchSets = Patch Sets
-menuDiffFiles = Files
-
-menuProjects = Projects
-menuProjectsList = List
-menuProjectsInfo = General
-menuProjectsBranches = Branches
-menuProjectsTags = Tags
-menuProjectsAccess = Access
-menuProjectsDashboards = Dashboards
-menuProjectsCreate = Create New Project
-
-menuPeople = People
-menuPeopleGroupsList = List Groups
-menuPeopleGroupsCreate = Create New Group
-
-menuPlugins = Plugins
-menuPluginsInstalled = Installed
-
-menuDocumentation = Documentation
-menuDocumentationTOC = Table of Contents
-menuDocumentationSearch = Searching
-menuDocumentationUpload = Uploading
-menuDocumentationAccess = Access Controls
-menuDocumentationAPI = REST API
-menuDocumentationProjectOwnerGuide = Project Owner Guide
-
-searchHint = Search term
-searchButton = Search
-
-rpcStatusWorking = Working ...
-
-sectionNavigation = Navigation
-sectionActions = Actions
-keySearch = Search
-keyEditor = Open Inline Editor
-keyHelp = Press '?' to view keyboard shortcuts
-
-sectionJumping = Jumping
-jumpAllOpen = Go to all open changes
-jumpAllMerged = Go to all merged changes
-jumpAllAbandoned = Go to all abandoned changes
-jumpMine = Go to my dashboard
-jumpMineWatched = Go to watched changes
-jumpMineStarred = Go to starred changes
-jumpMineDraftComments = Go to draft comments
-
-projectAccessError = You don't have permissions to modify the access rights for the following refs:
-projectAccessProposeForReviewHint = You may propose these modifications to the project owners by clicking on 'Save for Review'.
-
-userCannotVoteToolTip = User cannot vote in this category
-
-stringListPanelAdd = Add
-stringListPanelDelete = Delete
-stringListPanelUp = Up
-stringListPanelDown = Down
-
-searchDropdownChanges = Changes
-searchDropdownDoc = Docs
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
deleted file mode 100644
index 968104c557..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ /dev/null
@@ -1,299 +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.client;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface GerritCss extends CssResource {
- String accountDashboard();
-
- String accountInfoBlock();
-
- String accountLinkPanel();
-
- String accountPassword();
-
- String accountUsername();
-
- String activeRow();
-
- String addBranch();
-
- String addMemberTextBox();
-
- String addSshKeyPanel();
-
- String addWatchPanel();
-
- String avatarInfoPanel();
-
- String bottomheader();
-
- String branchTableDeleteButton();
-
- String branchTablePrevNextLinks();
-
- String cAPPROVAL();
-
- String cASSIGNEE();
-
- String cASSIGNEDTOME();
-
- String cLastUpdate();
-
- String cOWNER();
-
- String cSIZE();
-
- String cSUBJECT();
-
- String cSTATUS();
-
- String changeSize();
-
- String changeTable();
-
- String changeTablePrevNextLinks();
-
- String commentedActionDialog();
-
- String commentedActionMessage();
-
- String contributorAgreementAlreadySubmitted();
-
- String contributorAgreementButton();
-
- String contributorAgreementLegal();
-
- String contributorAgreementShortDescription();
-
- String createProjectPanel();
-
- String dataCell();
-
- String dataCellHidden();
-
- String dataHeader();
-
- String dataHeaderHidden();
-
- String downloadBox();
-
- String downloadBoxTable();
-
- String downloadBoxTableCommandColumn();
-
- String downloadBoxSpacer();
-
- String downloadBoxScheme();
-
- String downloadBoxCopyLabel();
-
- String downloadLink();
-
- String downloadLinkCopyLabel();
-
- String downloadLinkHeader();
-
- String downloadLinkHeaderGap();
-
- String downloadLinkList();
-
- String downloadLink_Active();
-
- String editHeadButton();
-
- String emptySection();
-
- String errorDialog();
-
- String errorDialogButtons();
-
- String errorDialogErrorType();
-
- String errorDialogGlass();
-
- String errorDialogTitle();
-
- String extensionPanel();
-
- String loadingPluginsDialog();
-
- String gerritBody();
-
- String gerritTopMenu();
-
- String greenCheckClass();
-
- String groupDescriptionPanel();
-
- String groupIncludesTable();
-
- String groupMembersTable();
-
- String groupName();
-
- String groupNamePanel();
-
- String groupNameTextBox();
-
- String groupOptionsPanel();
-
- String groupOwnerPanel();
-
- String groupOwnerTextBox();
-
- String groupUUIDPanel();
-
- String header();
-
- String iconCell();
-
- String iconHeader();
-
- String identityUntrustedExternalId();
-
- String infoBlock();
-
- String inputFieldTypeHint();
-
- String labelNotApplicable();
-
- String leftMostCell();
-
- String link();
-
- String linkMenuBar();
-
- String linkMenuItemNotLast();
-
- String maxObjectSizeLimitEffectiveLabel();
-
- String menuBarUserName();
-
- String menuBarUserNameAvatar();
-
- String menuBarUserNameFocusPanel();
-
- String menuBarUserNamePanel();
-
- String menuItem();
-
- String menuScreenMenuBar();
-
- String needsReview();
-
- String negscore();
-
- String oauthExpires();
-
- String oauthInfoBlock();
-
- String oauthPanel();
-
- String oauthPanelCookieEntry();
-
- String oauthPanelCookieHeading();
-
- String oauthPanelNetRCEntry();
-
- String oauthPanelNetRCHeading();
-
- String oauthToken();
-
- String pagingLink();
-
- String patchSetActions();
-
- String pluginProjectConfigInheritedValue();
-
- String pluginsTable();
-
- String posscore();
-
- String projectActions();
-
- String projectFilterLabel();
-
- String projectFilterPanel();
-
- String projectNameColumn();
-
- String queryIcon();
-
- String rebaseContentPanel();
-
- String rebaseSuggestBox();
-
- String registerScreenExplain();
-
- String registerScreenNextLinks();
-
- String registerScreenSection();
-
- String rpcStatus();
-
- String screen();
-
- String screenHeader();
-
- String searchPanel();
-
- String suggestBoxPopup();
-
- String sectionHeader();
-
- String singleLine();
-
- String smallHeading();
-
- String specialBranchDataCell();
-
- String specialBranchIconCell();
-
- String sshHostKeyPanel();
-
- String sshHostKeyPanelFingerprintData();
-
- String sshHostKeyPanelHeading();
-
- String sshHostKeyPanelKnownHostEntry();
-
- String sshKeyPanelEncodedKey();
-
- String sshKeyPanelInvalid();
-
- String sshKeyTable();
-
- String stringListPanelButtons();
-
- String topmenu();
-
- String topmenuMenuLeft();
-
- String topmenuMenuRight();
-
- String topmenuTDglue();
-
- String topmenuTDmenu();
-
- String topmost();
-
- String userInfoPopup();
-
- String usernameField();
-
- String watchedProjectFilter();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
deleted file mode 100644
index 980529a3d7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
+++ /dev/null
@@ -1,51 +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.client;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface GerritMessages extends Messages {
- String windowTitle1(String hostname);
-
- String windowTitle2(String section, String hostname);
-
- String poweredBy(String version);
-
- String noSuchAccountMessage(String who);
-
- String noSuchGroupMessage(String who);
-
- String nameAlreadyUsedBody(String alreadyUsedName);
-
- String branchCreationFailed(String branchName, String error);
-
- String invalidBranchName(String branchName);
-
- String invalidRevision(String revision);
-
- String branchCreationNotAllowedUnderRefnamePrefix(String refnamePrefix);
-
- String branchAlreadyExists(String branchName);
-
- String branchCreationConflict(String branchName, String existingBranchName);
-
- String pluginFailed(String scriptPath);
-
- String cannotDownloadPlugin(String scriptPath);
-
- String parentUpdateFailed(String message);
-
- String fileCount(int fileNumber, int fileCount);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
deleted file mode 100644
index b2d67b8201..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-windowTitle1 = {0} Code Review
-windowTitle2 = {0} | {1} Code Review
-poweredBy = Powered by <a href="https://www.gerritcodereview.com/" target="_blank">Gerrit Code Review</a> ({0})
-
-noSuchAccountMessage = {0} is not a registered user.
-noSuchGroupMessage = Group {0} does not exist or is not visible to you.
-nameAlreadyUsedBody = The name {0} is already in use.
-
-branchCreationFailed = Creating branch {0} failed. Error: {1}
-invalidBranchName = The branch name {0} is not valid.
-invalidRevision = The revision {0} is not valid.
-branchCreationNotAllowedUnderRefnamePrefix = Branch creation is not allowed under {0}.
-branchAlreadyExists = A branch with the name {0} already exists.
-branchCreationConflict = Cannot create branch {0} since it conflicts with branch {1}.
-
-pluginFailed = Plugin "{0}" failed to load
-cannotDownloadPlugin = Cannot load plugin from {0}
-
-parentUpdateFailed = Setting parent project failed: {0}
-
-fileCount = File {0} of {1}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
deleted file mode 100644
index 01be2f2381..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritResources.java
+++ /dev/null
@@ -1,25 +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.client;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface GerritResources extends Resources {
- @Source("gerrit.css")
- GerritCss css();
-
- @Source("gwt_override.css")
- CssResource gwt_override();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java
deleted file mode 100644
index bd3d483aa3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java
+++ /dev/null
@@ -1,28 +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.client;
-
-import com.google.gerrit.common.data.HostPageData;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.HostPageCache;
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.gwtjsonrpc.common.RpcImpl;
-import com.google.gwtjsonrpc.common.RpcImpl.Version;
-
-@RpcImpl(version = Version.V2_0)
-interface HostPageDataService extends RemoteJsonService {
- @HostPageCache(name = "gerrit_hostpagedata", once = true)
- void load(AsyncCallback<HostPageData> callback);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java
deleted file mode 100644
index a4879caae7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java
+++ /dev/null
@@ -1,103 +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.client;
-
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.CompoundKeyCommand;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-
-public class JumpKeys {
- private static HandlerRegistration activeHandler;
- private static KeyCommandSet keys;
- private static Widget bodyWidget;
-
- public static void enable(boolean enable) {
- if (enable && activeHandler == null) {
- activeHandler = GlobalKey.add(bodyWidget, keys);
- } else if (!enable && activeHandler != null) {
- activeHandler.removeHandler();
- activeHandler = null;
- }
- }
-
- static void register(Widget body) {
- final KeyCommandSet jumps = new KeyCommandSet();
-
- jumps.add(
- new KeyCommand(0, 'o', Gerrit.C.jumpAllOpen()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChangeQuery("status:open"));
- }
- });
- jumps.add(
- new KeyCommand(0, 'm', Gerrit.C.jumpAllMerged()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChangeQuery("status:merged"));
- }
- });
- jumps.add(
- new KeyCommand(0, 'a', Gerrit.C.jumpAllAbandoned()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChangeQuery("status:abandoned"));
- }
- });
-
- if (Gerrit.isSignedIn()) {
- jumps.add(
- new KeyCommand(0, 'i', Gerrit.C.jumpMine()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.MINE);
- }
- });
- jumps.add(
- new KeyCommand(0, 'c', Gerrit.C.jumpMineDraftComments()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChangeQuery("has:draft"));
- }
- });
- jumps.add(
- new KeyCommand(0, 'w', Gerrit.C.jumpMineWatched()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChangeQuery("is:watched status:open"));
- }
- });
- jumps.add(
- new KeyCommand(0, 's', Gerrit.C.jumpMineStarred()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChangeQuery("is:starred"));
- }
- });
- }
-
- keys = new KeyCommandSet(Gerrit.C.sectionJumping());
- keys.add(new CompoundKeyCommand(0, 'g', "", jumps));
- bodyWidget = body;
- activeHandler = GlobalKey.add(body, keys);
- }
-
- private JumpKeys() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.java
deleted file mode 100644
index 36fa6e8279..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2014 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.client;
-
-import com.google.gerrit.common.data.HostPageData;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Cookies;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Displays pending messages from the server. */
-class MessageOfTheDayBar extends Composite {
- interface Binder extends UiBinder<HTMLPanel, MessageOfTheDayBar> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private final List<HostPageData.Message> motd;
- @UiField HTML message;
- @UiField Anchor dismiss;
-
- MessageOfTheDayBar(List<HostPageData.Message> motd) {
- this.motd = filter(motd);
- initWidget(uiBinder.createAndBindUi(this));
-
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- if (this.motd.size() == 1) {
- b.append(SafeHtml.asis(this.motd.get(0).html));
- } else {
- for (HostPageData.Message m : this.motd) {
- b.openDiv();
- b.append(SafeHtml.asis(m.html));
- b.openElement("hr");
- b.closeSelf();
- b.closeDiv();
- }
- }
- message.setHTML(b);
- }
-
- void show() {
- if (!motd.isEmpty()) {
- RootPanel.get().add(this);
- }
- }
-
- @UiHandler("dismiss")
- void onDismiss(@SuppressWarnings("unused") ClickEvent e) {
- removeFromParent();
-
- for (HostPageData.Message m : motd) {
- Cookies.setCookie(cookieName(m), "1", m.redisplay);
- }
- }
-
- private static List<HostPageData.Message> filter(List<HostPageData.Message> in) {
- List<HostPageData.Message> show = new ArrayList<>();
- for (HostPageData.Message m : in) {
- if (Cookies.getCookie(cookieName(m)) == null) {
- show.add(m);
- }
- }
- return show;
- }
-
- private static String cookieName(HostPageData.Message m) {
- return "msg-" + m.id;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml
deleted file mode 100644
index 36d08b1d57..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/MessageOfTheDayBar.ui.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false'>
- .popup {
- position: fixed;
- top: 5px;
- left: 50%;
- margin-left: -200px;
- z-index: 201;
- padding-top: 5px;
- padding-bottom: 5px;
- padding-left: 12px;
- padding-right: 12px;
- background: #FFF1A8;
- border-radius: 10px;
- }
-
- @if user.agent safari {
- .popup {
- \-webkit-border-radius: 10px;
- }
- }
- @if user.agent gecko1_8 {
- .popup {
- \-moz-border-radius: 10px;
- }
- }
-
- .message {
- display: inline;
- }
- .message a {
- color: #222;
- text-decoration: underline;
- }
- a.action {
- color: #222;
- text-decoration: underline;
- display: inline-block;
- margin-left: 0.5em;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.popup}'>
- <g:HTML ui:field='message' styleName='{style.message}'/>
- <g:Anchor ui:field='dismiss'
- styleName='{style.action}'
- href='javascript:;'
- title='Hide this message'>
- <ui:attribute name='title'/>
- Dismiss
- </g:Anchor>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java
deleted file mode 100644
index 29bbf85c8d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java
+++ /dev/null
@@ -1,34 +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.client;
-
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.user.client.ui.Label;
-
-/** Displays an error message letting the user know the page doesn't exist. */
-public class NotFoundScreen extends Screen {
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Gerrit.C.notFoundTitle());
- add(new Label(Gerrit.C.notFoundBody()));
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- display();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
deleted file mode 100644
index cd5197a069..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
+++ /dev/null
@@ -1,99 +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.client;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-/** A dialog box telling the user they are not signed in. */
-public class NotSignedInDialog extends PopupPanel implements CloseHandler<PopupPanel> {
- private Button signin;
- private boolean buttonClicked;
-
- public NotSignedInDialog() {
- super(/* auto hide */ false, /* modal */ true);
- setGlassEnabled(true);
- getGlassElement().addClassName(Gerrit.RESOURCES.css().errorDialogGlass());
- addStyleName(Gerrit.RESOURCES.css().errorDialog());
-
- final FlowPanel buttons = new FlowPanel();
- signin = new Button();
- signin.setText(Gerrit.C.menuSignIn());
- signin.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- buttonClicked = true;
- hide();
- Gerrit.doSignIn(History.getToken());
- }
- });
- buttons.add(signin);
-
- final Button close = new Button();
- close.getElement().getStyle().setProperty("marginLeft", "200px");
- close.setText(Gerrit.C.signInDialogGoAnonymous());
- close.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- buttonClicked = true;
- Gerrit.deleteSessionCookie();
- hide();
- }
- });
- buttons.add(close);
-
- Label title = new Label(Gerrit.C.notSignedInTitle());
- title.setStyleName(Gerrit.RESOURCES.css().errorDialogTitle());
-
- FlowPanel center = new FlowPanel();
- center.add(title);
- center.add(new HTML(Gerrit.C.notSignedInBody()));
- center.add(buttons);
- add(center);
-
- int l = Window.getScrollLeft() + 20;
- int t = Window.getScrollTop() + 20;
- setPopupPosition(l, t);
- addCloseHandler(this);
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (!buttonClicked) {
- // the dialog was closed without one of the buttons being pressed
- // e.g. the user pressed ESC to close the dialog
- Gerrit.deleteSessionCookie();
- }
- }
-
- @Override
- public void center() {
- show();
- GlobalKey.dialog(this);
- signin.setFocus(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RangeInfo.java
deleted file mode 100644
index 42454a32a7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RangeInfo.java
+++ /dev/null
@@ -1,25 +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.client;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class RangeInfo extends JavaScriptObject {
- public final native int start() /*-{ return this.start; }-*/;
-
- public final native int end() /*-{ return this.end; }-*/;
-
- protected RangeInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
deleted file mode 100644
index 41534398f9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
+++ /dev/null
@@ -1,74 +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.client;
-
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwtjsonrpc.client.event.RpcCompleteEvent;
-import com.google.gwtjsonrpc.client.event.RpcCompleteHandler;
-import com.google.gwtjsonrpc.client.event.RpcStartEvent;
-import com.google.gwtjsonrpc.client.event.RpcStartHandler;
-
-public class RpcStatus implements RpcStartHandler, RpcCompleteHandler {
- public static RpcStatus INSTANCE;
-
- private static int hideDepth;
-
- /** Execute code, hiding the RPCs they execute from being shown visually. */
- public static void hide(Runnable run) {
- try {
- hideDepth++;
- run.run();
- } finally {
- hideDepth--;
- }
- }
-
- private final Label loading;
- private int activeCalls;
-
- RpcStatus() {
- loading = new InlineLabel();
- loading.setText(Gerrit.C.rpcStatusWorking());
- loading.setStyleName(Gerrit.RESOURCES.css().rpcStatus());
- loading.setVisible(false);
- RootPanel.get().add(loading);
- }
-
- @Override
- public void onRpcStart(RpcStartEvent event) {
- onRpcStart();
- }
-
- public void onRpcStart() {
- if (++activeCalls == 1) {
- if (hideDepth == 0) {
- loading.setVisible(true);
- }
- }
- }
-
- @Override
- public void onRpcComplete(RpcCompleteEvent event) {
- onRpcComplete();
- }
-
- public void onRpcComplete() {
- if (--activeCalls == 0) {
- loading.setVisible(false);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
deleted file mode 100644
index 406ab4ef18..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
+++ /dev/null
@@ -1,163 +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.client;
-
-import com.google.gerrit.client.changes.QueryScreen;
-import com.google.gerrit.client.ui.HintTextBox;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-
-class SearchPanel extends Composite {
- private final HintTextBox searchBox;
- private final ListBox dropdown;
- private HandlerRegistration regFocus;
-
- SearchPanel() {
- final FlowPanel body = new FlowPanel();
- initWidget(body);
- setStyleName(Gerrit.RESOURCES.css().searchPanel());
-
- searchBox = new HintTextBox();
- final MySuggestionDisplay suggestionDisplay = new MySuggestionDisplay();
- searchBox.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- if (!suggestionDisplay.isSuggestionSelected) {
- doSearch();
- }
- }
- }
- });
-
- if (Gerrit.hasDocSearch()) {
- dropdown = new ListBox();
- dropdown.setStyleName("searchDropdown");
- dropdown.addItem(Gerrit.C.searchDropdownChanges());
- dropdown.addItem(Gerrit.C.searchDropdownDoc());
- dropdown.setVisibleItemCount(1);
- dropdown.setSelectedIndex(0);
- } else {
- // Doc search is NOT available.
- dropdown = null;
- }
-
- final SuggestBox suggestBox =
- new SuggestBox(new SearchSuggestOracle(), searchBox, suggestionDisplay);
- searchBox.setStyleName("searchTextBox");
- searchBox.setVisibleLength(70);
- searchBox.setHintText(Gerrit.C.searchHint());
-
- final Button searchButton = new Button(Gerrit.C.searchButton());
- searchButton.setStyleName("searchButton");
- searchButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSearch();
- }
- });
-
- body.add(suggestBox);
- if (dropdown != null) {
- body.add(dropdown);
- }
- body.add(searchButton);
- }
-
- void setText(String query) {
- searchBox.setText(query);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- if (regFocus == null) {
- regFocus =
- GlobalKey.addApplication(
- this,
- new KeyCommand(0, '/', Gerrit.C.keySearch()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- event.preventDefault();
- searchBox.setFocus(true);
- searchBox.selectAll();
- }
- });
- }
- }
-
- @Override
- protected void onUnload() {
- if (regFocus != null) {
- regFocus.removeHandler();
- regFocus = null;
- }
- }
-
- private void doSearch() {
- final String query = searchBox.getText().trim();
- if ("".equals(query)) {
- return;
- }
-
- searchBox.setFocus(false);
-
- if (dropdown != null && dropdown.getSelectedValue().equals(Gerrit.C.searchDropdownDoc())) {
- // doc
- Gerrit.display(PageLinks.toDocumentationQuery(query));
- } else {
- // changes
- if (query.matches("^[1-9][0-9]*$")) {
- // Query is a change number. Project can't be supplied.
- Gerrit.display(PageLinks.toChange(null, Change.Id.parse(query)));
- } else {
- Gerrit.display(PageLinks.toChangeQuery(query), QueryScreen.forQuery(query));
- }
- }
- }
-
- private static class MySuggestionDisplay extends SuggestBox.DefaultSuggestionDisplay {
- private boolean isSuggestionSelected;
-
- private MySuggestionDisplay() {
- super();
-
- getPopupPanel().addStyleName(Gerrit.RESOURCES.css().suggestBoxPopup());
- }
-
- @Override
- protected Suggestion getCurrentSelection() {
- Suggestion currentSelection = super.getCurrentSelection();
- isSuggestionSelected = currentSelection != null;
- return currentSelection;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
deleted file mode 100644
index e74ed715e5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright (C) 2012 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.client;
-
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.AccountSuggestOracle;
-import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.TreeSet;
-
-public class SearchSuggestOracle extends HighlightSuggestOracle {
- private static final List<ParamSuggester> paramSuggester =
- Arrays.asList(
- new ParamSuggester(
- Arrays.asList("project:", "p:", "parentproject:"), new ProjectNameSuggestOracle()),
- new ParamSuggester(
- Arrays.asList(
- "owner:",
- "o:",
- "reviewer:",
- "r:",
- "commentby:",
- "reviewedby:",
- "author:",
- "committer:",
- "from:",
- "assignee:",
- "cc:"),
- new AccountSuggestOracle() {
- @Override
- public void onRequestSuggestions(Request request, Callback done) {
- super.onRequestSuggestions(
- request,
- new Callback() {
- @Override
- public void onSuggestionsReady(final Request request, Response response) {
- if ("self".startsWith(request.getQuery())) {
- final ArrayList<SuggestOracle.Suggestion> r =
- new ArrayList<>(response.getSuggestions().size() + 1);
- r.add(
- new SuggestOracle.Suggestion() {
- @Override
- public String getDisplayString() {
- return getReplacementString();
- }
-
- @Override
- public String getReplacementString() {
- return "self";
- }
- });
- r.addAll(response.getSuggestions());
- response.setSuggestions(r);
- }
- done.onSuggestionsReady(request, response);
- }
- });
- }
- }),
- new ParamSuggester(
- Arrays.asList("ownerin:", "reviewerin:"), new AccountGroupSuggestOracle()));
-
- private static final TreeSet<String> suggestions = new TreeSet<>();
-
- static {
- suggestions.add("age:");
- suggestions.add("age:1week"); // Give an example age
-
- suggestions.add("change:");
-
- suggestions.add("owner:");
- suggestions.add("owner:self");
- suggestions.add("ownerin:");
- suggestions.add("author:");
- suggestions.add("committer:");
- suggestions.add("assignee:");
-
- suggestions.add("reviewer:");
- suggestions.add("reviewer:self");
- suggestions.add("reviewerin:");
- suggestions.add("reviewedby:");
-
- suggestions.add("commit:");
- suggestions.add("comment:");
- suggestions.add("message:");
- suggestions.add("commentby:");
- suggestions.add("from:");
- suggestions.add("file:");
- suggestions.add("conflicts:");
- suggestions.add("project:");
- suggestions.add("projects:");
- suggestions.add("parentproject:");
- suggestions.add("branch:");
- suggestions.add("topic:");
- suggestions.add("intopic:");
- suggestions.add("ref:");
- suggestions.add("tr:");
- suggestions.add("bug:");
- suggestions.add("label:");
- suggestions.add("query:");
- suggestions.add("has:");
- suggestions.add("has:draft");
- suggestions.add("has:edit");
- suggestions.add("has:star");
- suggestions.add("has:stars");
- suggestions.add("has:unresolved");
- suggestions.add("star:");
-
- suggestions.add("is:");
- suggestions.add("is:starred");
- suggestions.add("is:watched");
- suggestions.add("is:reviewed");
- suggestions.add("is:owner");
- suggestions.add("is:reviewer");
- suggestions.add("is:open");
- suggestions.add("is:pending");
- suggestions.add("is:private");
- suggestions.add("is:closed");
- suggestions.add("is:merged");
- suggestions.add("is:abandoned");
- suggestions.add("is:mergeable");
- suggestions.add("is:ignored");
- suggestions.add("is:wip");
- suggestions.add("is:assigned");
- suggestions.add("is:submittable");
-
- suggestions.add("status:");
- suggestions.add("status:open");
- suggestions.add("status:pending");
- suggestions.add("status:reviewed");
- suggestions.add("status:closed");
- suggestions.add("status:merged");
- suggestions.add("status:abandoned");
-
- suggestions.add("added:");
- suggestions.add("deleted:");
- suggestions.add("delta:");
- suggestions.add("size:");
-
- suggestions.add("unresolved:");
-
- suggestions.add("revertof:");
-
- if (Gerrit.isNoteDbEnabled()) {
- suggestions.add("cc:");
- suggestions.add("hashtag:");
- }
-
- suggestions.add("is:assigned");
- suggestions.add("is:unassigned");
- suggestions.add("assignee:");
-
- suggestions.add("AND");
- suggestions.add("OR");
- suggestions.add("NOT");
- }
-
- @Override
- public void requestDefaultSuggestions(Request request, Callback done) {
- final ArrayList<SearchSuggestion> r = new ArrayList<>();
- // No text - show some default suggestions.
- r.add(new SearchSuggestion("status:open", "status:open"));
- r.add(new SearchSuggestion("age:1week", "age:1week"));
- if (Gerrit.isSignedIn()) {
- r.add(new SearchSuggestion("owner:self", "owner:self"));
- }
- done.onSuggestionsReady(request, new Response(r));
- }
-
- @Override
- protected void onRequestSuggestions(Request request, Callback done) {
- final String query = request.getQuery();
-
- final String lastWord = getLastWord(query);
- if (lastWord == null) {
- // Starting a new word - don't show suggestions yet.
- done.onSuggestionsReady(request, null);
- return;
- }
-
- for (ParamSuggester ps : paramSuggester) {
- if (ps.applicable(lastWord)) {
- ps.suggest(lastWord, request, done);
- return;
- }
- }
-
- final ArrayList<SearchSuggestion> r = new ArrayList<>();
- for (String suggestion : suggestions.tailSet(lastWord)) {
- if ((lastWord.length() < suggestion.length()) && suggestion.startsWith(lastWord)) {
- if (suggestion.contains("self") && !Gerrit.isSignedIn()) {
- continue;
- }
- r.add(new SearchSuggestion(suggestion, query + suggestion.substring(lastWord.length())));
- }
- }
- done.onSuggestionsReady(request, new Response(r));
- }
-
- private String getLastWord(String query) {
- final int lastSpace = query.lastIndexOf(' ');
- if (lastSpace == query.length() - 1) {
- return null;
- }
- if (lastSpace == -1) {
- return query;
- }
- return query.substring(lastSpace + 1);
- }
-
- @Override
- protected String getQueryPattern(String query) {
- return super.getQueryPattern(getLastWord(query));
- }
-
- @Override
- protected boolean isHTML() {
- return true;
- }
-
- private static class SearchSuggestion implements SuggestOracle.Suggestion {
- private final String suggestion;
- private final String fullQuery;
-
- SearchSuggestion(String suggestion, String fullQuery) {
- this.suggestion = suggestion;
- // Add a space to the query if it is a complete operation (e.g.
- // "status:open") so the user can keep on typing.
- this.fullQuery = fullQuery.endsWith(":") ? fullQuery : fullQuery + " ";
- }
-
- @Override
- public String getDisplayString() {
- return suggestion;
- }
-
- @Override
- public String getReplacementString() {
- return fullQuery;
- }
- }
-
- private static class ParamSuggester {
- private final List<String> operators;
- private final SuggestOracle parameterSuggestionOracle;
-
- ParamSuggester(List<String> operators, SuggestOracle parameterSuggestionOracle) {
- this.operators = operators;
- this.parameterSuggestionOracle = parameterSuggestionOracle;
- }
-
- boolean applicable(String query) {
- final String operator = getApplicableOperator(query, operators);
- return operator != null && query.length() > operator.length();
- }
-
- private String getApplicableOperator(String lastWord, List<String> operators) {
- for (String operator : operators) {
- if (lastWord.startsWith(operator)) {
- return operator;
- }
- }
- return null;
- }
-
- void suggest(String lastWord, Request request, Callback done) {
- final String operator = getApplicableOperator(lastWord, operators);
- parameterSuggestionOracle.requestSuggestions(
- new Request(lastWord.substring(operator.length()), request.getLimit()),
- new Callback() {
- @Override
- public void onSuggestionsReady(Request req, Response response) {
- final String query = request.getQuery();
- final List<SearchSuggestOracle.Suggestion> r =
- new ArrayList<>(response.getSuggestions().size());
- for (SearchSuggestOracle.Suggestion s : response.getSuggestions()) {
- r.add(
- new SearchSuggestion(
- s.getDisplayString(),
- query.substring(0, query.length() - lastWord.length())
- + operator
- + quoteIfNeeded(s.getReplacementString())));
- }
- done.onSuggestionsReady(request, new Response(r));
- }
-
- private String quoteIfNeeded(String s) {
- if (!s.matches("^\\S*$")) {
- return "\"" + s + "\"";
- }
- return s;
- }
- });
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/StringListPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/StringListPanel.java
deleted file mode 100644
index f771fee435..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/StringListPanel.java
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright (C) 2014 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.client;
-
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FocusWidget;
-import com.google.gwt.user.client.ui.HasEnabled;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import java.util.ArrayList;
-import java.util.List;
-
-public class StringListPanel extends FlowPanel implements HasEnabled {
- private final StringListTable t;
- private HorizontalPanel titlePanel;
- protected final HorizontalPanel buttonPanel;
- private final Button deleteButton;
- private Image info;
- protected FocusWidget widget;
-
- public StringListPanel(String title, List<String> fieldNames, FocusWidget w, boolean autoSort) {
- widget = w;
- if (title != null) {
- titlePanel = new HorizontalPanel();
- SmallHeading titleLabel = new SmallHeading(title);
- titlePanel.add(titleLabel);
- add(titlePanel);
- }
-
- t = new StringListTable(fieldNames, autoSort);
- add(t);
-
- buttonPanel = new HorizontalPanel();
- buttonPanel.setStyleName(Gerrit.RESOURCES.css().stringListPanelButtons());
- deleteButton = new Button(Gerrit.C.stringListPanelDelete());
- deleteButton.setEnabled(false);
- deleteButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- widget.setEnabled(true);
- t.deleteChecked();
- }
- });
- buttonPanel.add(deleteButton);
- add(buttonPanel);
- }
-
- public void display(List<List<String>> values) {
- t.display(values);
- }
-
- public void setInfo(String msg) {
- if (info == null && titlePanel != null) {
- info = new Image(Gerrit.RESOURCES.info());
- titlePanel.add(info);
- }
- if (info != null) {
- info.setTitle(msg);
- }
- }
-
- public List<List<String>> getValues() {
- return t.getValues();
- }
-
- public List<String> getValues(int i) {
- List<List<String>> allValuesList = getValues();
- List<String> singleValueList = new ArrayList<>(allValuesList.size());
- for (List<String> values : allValuesList) {
- singleValueList.add(values.get(i));
- }
- return singleValueList;
- }
-
- private class StringListTable extends NavigationTable<List<String>> {
- private final Button addButton;
- private final List<NpTextBox> inputs;
- private final boolean autoSort;
-
- StringListTable(List<String> names, boolean autoSort) {
- this.autoSort = autoSort;
-
- addButton = new Button(new ImageResourceRenderer().render(Gerrit.RESOURCES.listAdd()));
- addButton.setTitle(Gerrit.C.stringListPanelAdd());
- OnEditEnabler e = new OnEditEnabler(addButton);
- inputs = new ArrayList<>();
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().leftMostCell());
- for (int i = 0; i < names.size(); i++) {
- fmt.addStyleName(0, i + 1, Gerrit.RESOURCES.css().dataHeader());
- table.setText(0, i + 1, names.get(i));
-
- NpTextBox input = new NpTextBox();
- input.setVisibleLength(35);
- input.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- widget.setEnabled(true);
- add();
- }
- }
- });
- inputs.add(input);
- fmt.addStyleName(1, i + 1, Gerrit.RESOURCES.css().dataHeader());
- table.setWidget(1, i + 1, input);
- e.listenTo(input);
- }
- addButton.setEnabled(false);
-
- addButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- widget.setEnabled(true);
- add();
- }
- });
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().leftMostCell());
- table.setWidget(1, 0, addButton);
-
- if (!autoSort) {
- fmt.addStyleName(0, names.size() + 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, names.size() + 2, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(1, names.size() + 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(1, names.size() + 2, Gerrit.RESOURCES.css().iconHeader());
- }
- }
-
- void display(List<List<String>> values) {
- for (int row = 2; row < table.getRowCount(); row++) {
- table.removeRow(row--);
- }
- int row = 2;
- for (List<String> v : values) {
- populate(row, v);
- row++;
- }
- updateNavigationLinks();
- }
-
- List<List<String>> getValues() {
- List<List<String>> values = new ArrayList<>();
- for (int row = 2; row < table.getRowCount(); row++) {
- values.add(getRowItem(row));
- }
- return values;
- }
-
- @Override
- protected List<String> getRowItem(int row) {
- List<String> v = new ArrayList<>();
- for (int i = 0; i < inputs.size(); i++) {
- v.add(table.getText(row, i + 1));
- }
- return v;
- }
-
- private void populate(int row, List<String> values) {
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().leftMostCell());
- CheckBox checkBox = new CheckBox();
- checkBox.addValueChangeHandler(
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- enableDelete();
- }
- });
- table.setWidget(row, 0, checkBox);
- for (int i = 0; i < values.size(); i++) {
- fmt.addStyleName(row, i + 1, Gerrit.RESOURCES.css().dataCell());
- table.setText(row, i + 1, values.get(i));
- }
- if (!autoSort) {
- fmt.addStyleName(row, values.size() + 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, values.size() + 2, Gerrit.RESOURCES.css().dataCell());
-
- Image down = new Image(Gerrit.RESOURCES.arrowDown());
- down.setTitle(Gerrit.C.stringListPanelDown());
- down.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- moveDown(row);
- }
- });
- table.setWidget(row, values.size() + 1, down);
-
- Image up = new Image(Gerrit.RESOURCES.arrowUp());
- up.setTitle(Gerrit.C.stringListPanelUp());
- up.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- moveUp(row);
- }
- });
- table.setWidget(row, values.size() + 2, up);
- }
- }
-
- @Override
- protected void onCellSingleClick(Event event, int row, int column) {
- if (column == inputs.size() + 1 && row >= 2 && row < table.getRowCount() - 2) {
- moveDown(row);
- } else if (column == inputs.size() + 2 && row > 2) {
- moveUp(row);
- }
- }
-
- void moveDown(int row) {
- if (row < table.getRowCount() - 1) {
- swap(row, row + 1);
- }
- }
-
- void moveUp(int row) {
- if (row > 2) {
- swap(row - 1, row);
- }
- }
-
- void swap(int row1, int row2) {
- List<String> value = getRowItem(row1);
- List<String> nextValue = getRowItem(row2);
- populate(row1, nextValue);
- populate(row2, value);
- updateNavigationLinks();
- widget.setEnabled(true);
- }
-
- private void updateNavigationLinks() {
- if (!autoSort) {
- for (int row = 2; row < table.getRowCount(); row++) {
- table.getWidget(row, inputs.size() + 1).setVisible(row < table.getRowCount() - 1);
- table.getWidget(row, inputs.size() + 2).setVisible(row > 2);
- }
- }
- }
-
- void add() {
- List<String> values = new ArrayList<>();
- for (NpTextBox input : inputs) {
- values.add(input.getValue().trim());
- input.setValue("");
- }
- insert(values);
- }
-
- void insert(List<String> v) {
- int insertPos = table.getRowCount();
- if (autoSort) {
- for (int row = 1; row < table.getRowCount(); row++) {
- int compareResult = v.get(0).compareTo(table.getText(row, 1));
- if (compareResult < 0) {
- insertPos = row;
- break;
- } else if (compareResult == 0) {
- return;
- }
- }
- }
- table.insertRow(insertPos);
- populate(insertPos, v);
- updateNavigationLinks();
- }
-
- void enableDelete() {
- for (int row = 2; row < table.getRowCount(); row++) {
- if (((CheckBox) table.getWidget(row, 0)).getValue()) {
- deleteButton.setEnabled(true);
- return;
- }
- }
- deleteButton.setEnabled(false);
- }
-
- void deleteChecked() {
- deleteButton.setEnabled(false);
- for (int row = 2; row < table.getRowCount(); row++) {
- if (((CheckBox) table.getWidget(row, 0)).getValue()) {
- table.removeRow(row--);
- }
- }
- updateNavigationLinks();
- }
-
- @Override
- protected void onOpenRow(int row) {}
-
- @Override
- protected Object getRowItemKey(List<String> item) {
- return item.get(0);
- }
-
- void setEnabled(boolean enabled) {
- addButton.setVisible(enabled);
- for (NpTextBox input : inputs) {
- input.setEnabled(enabled);
- }
- for (int row = 2; row < table.getRowCount(); row++) {
- table.getWidget(row, 0).setVisible(enabled);
- if (!autoSort) {
- table.getWidget(row, inputs.size() + 1).setVisible(enabled);
- table.getWidget(row, inputs.size() + 2).setVisible(enabled);
- }
- }
- if (enabled) {
- updateNavigationLinks();
- }
- }
- }
-
- @Override
- public boolean isEnabled() {
- return deleteButton.isVisible();
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- t.setEnabled(enabled);
- deleteButton.setVisible(enabled);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Themer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Themer.java
deleted file mode 100644
index d87c0b8646..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Themer.java
+++ /dev/null
@@ -1,83 +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.client;
-
-import com.google.gerrit.client.projects.ThemeInfo;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.StyleElement;
-
-public class Themer {
- public static class ThemerIE extends Themer {
- protected ThemerIE() {}
-
- @Override
- protected String getCssText(StyleElement el) {
- return el.getCssText();
- }
-
- @Override
- protected void setCssText(StyleElement el, String css) {
- el.setCssText(css);
- }
- }
-
- protected StyleElement cssElement;
- protected Element headerElement;
- protected Element footerElement;
- protected String cssText;
- protected String headerHtml;
- protected String footerHtml;
-
- protected Themer() {}
-
- public void set(ThemeInfo theme) {
- if (theme != null) {
- set(
- theme.css() != null ? theme.css() : cssText,
- theme.header() != null ? theme.header() : headerHtml,
- theme.footer() != null ? theme.footer() : footerHtml);
- } else {
- set(cssText, headerHtml, footerHtml);
- }
- }
-
- public void clear() {
- set(null);
- }
-
- void init(Element css, Element header, Element footer) {
- cssElement = StyleElement.as(css);
- headerElement = header;
- footerElement = footer;
-
- cssText = getCssText(this.cssElement);
- headerHtml = header.getInnerHTML();
- footerHtml = footer.getInnerHTML();
- }
-
- protected String getCssText(StyleElement el) {
- return el.getInnerHTML();
- }
-
- protected void setCssText(StyleElement el, String css) {
- el.setInnerHTML(css);
- }
-
- private void set(String css, String header, String footer) {
- setCssText(cssElement, css);
- headerElement.setInnerHTML(header);
- footerElement.setInnerHTML(footer);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java
deleted file mode 100644
index acaaf46c04..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-import com.google.gwt.regexp.shared.RegExp;
-import java.util.HashMap;
-import java.util.Map;
-
-public class UrlAliasMatcher {
- private final Map<RegExp, String> userUrlAliases;
- private final Map<RegExp, String> globalUrlAliases;
-
- UrlAliasMatcher(Map<String, String> globalUrlAliases) {
- this.globalUrlAliases = compile(globalUrlAliases);
- this.userUrlAliases = new HashMap<>();
- }
-
- private static Map<RegExp, String> compile(Map<String, String> urlAliases) {
- Map<RegExp, String> compiledUrlAliases = new HashMap<>();
- if (urlAliases != null) {
- for (Map.Entry<String, String> e : urlAliases.entrySet()) {
- compiledUrlAliases.put(RegExp.compile(e.getKey()), e.getValue());
- }
- }
- return compiledUrlAliases;
- }
-
- void clearUserAliases() {
- this.userUrlAliases.clear();
- }
-
- void updateUserAliases(Map<String, String> userUrlAliases) {
- clearUserAliases();
- this.userUrlAliases.putAll(compile(userUrlAliases));
- }
-
- public String replace(String token) {
- for (Map.Entry<RegExp, String> e : userUrlAliases.entrySet()) {
- RegExp pat = e.getKey();
- if (pat.exec(token) != null) {
- return pat.replace(token, e.getValue());
- }
- }
-
- for (Map.Entry<RegExp, String> e : globalUrlAliases.entrySet()) {
- RegExp pat = e.getKey();
- if (pat.exec(token) != null) {
- return pat.replace(token, e.getValue());
- }
- }
- return token;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
deleted file mode 100644
index cb529f4518..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2012 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.client;
-
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.AnchorElement;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-public class UserPopupPanel extends PopupPanel {
- interface Binder extends UiBinder<Widget, UserPopupPanel> {}
-
- private static final Binder binder = GWT.create(Binder.class);
-
- @UiField(provided = true)
- AvatarImage avatar;
-
- @UiField Label userName;
- @UiField Label userEmail;
- @UiField Element userLinks;
- @UiField AnchorElement switchAccount;
- @UiField AnchorElement logout;
- @UiField InlineHyperlink settings;
-
- public UserPopupPanel(AccountInfo account, boolean canLogOut, boolean showSettingsLink) {
- super(/* auto hide */ true, /* modal */ false);
- avatar = new AvatarImage(account, 100, false);
- setWidget(binder.createAndBindUi(this));
- setStyleName(Gerrit.RESOURCES.css().userInfoPopup());
- if (account.name() != null) {
- userName.setText(account.name());
- }
- if (account.email() != null) {
- userEmail.setText(account.email());
- }
- if (showSettingsLink) {
- String switchAccountUrl = Gerrit.info().auth().switchAccountUrl();
- if (switchAccountUrl != null) {
- switchAccount.setHref(switchAccountUrl.replace("${path}", "/"));
- } else if (Gerrit.info().auth().isDev() || Gerrit.info().auth().isOpenId()) {
- switchAccount.setHref(Gerrit.selfRedirect("/login"));
- } else {
- switchAccount.removeFromParent();
- switchAccount = null;
- }
- if (canLogOut) {
- logout.setHref(Gerrit.selfRedirect("/logout"));
- } else {
- logout.removeFromParent();
- logout = null;
- }
-
- } else {
- settings.removeFromParent();
- settings = null;
- userLinks.removeFromParent();
- userLinks = null;
- logout = null;
- }
-
- // We must show and then hide this popup so that it is part of the DOM.
- // Otherwise the image does not get any events. Calling hide() would
- // remove it from the DOM so we use setVisible(false) instead.
- show();
- setVisible(false);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml
deleted file mode 100644
index 8f5073d74c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2012 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.
--->
-<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:gerrit='urn:import:com.google.gerrit.client'
- xmlns:u='urn:import:com.google.gerrit.client.ui'>
- <ui:style gss='false'>
- .panel {
- padding: 8px;
- }
- .avatar {
- padding-right: 4px;
- width: 100px;
- height: 100px;
- }
- .infoCell {
- vertical-align: top;
- }
- .userName {
- font-weight: bold;
- }
- .email {
- padding-bottom: 6px;
- }
- .userLinks {
- min-width: 250px;
- }
- .userLinksRight {
- float: right;
- }
- .switchAccount {
- border-right: 1px solid black;
- padding-right: 0.5em;
- margin-right: 0.5em;
- }
- </ui:style>
-
- <g:HTMLPanel styleName='{style.panel}'>
- <table><tr><td>
- <gerrit:AvatarImage ui:field='avatar' styleName='{style.avatar}' />
- </td><td class='{style.infoCell}'>
- <g:Label ui:field='userName' styleName="{style.userName}" />
- <g:Label ui:field='userEmail' styleName="{style.email}" />
- </td></tr></table>
- <div ui:field='userLinks' class='{style.userLinks}'>
- <u:InlineHyperlink ui:field='settings' targetHistoryToken='/settings/'>
- <ui:msg>Settings</ui:msg>
- </u:InlineHyperlink>
- <span class='{style.userLinksRight}'>
- <a ui:field='switchAccount' class='{style.switchAccount}'><ui:msg>Switch Account</ui:msg></a
- ><a ui:field='logout'><ui:msg>Sign Out</ui:msg></a>
- </span>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/VoidResult.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/VoidResult.java
deleted file mode 100644
index 810ebe79c3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/VoidResult.java
+++ /dev/null
@@ -1,25 +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.client;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public final class VoidResult extends JavaScriptObject {
- protected VoidResult() {}
-
- public static VoidResult create() {
- return createObject().cast();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/AccessMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/AccessMap.java
deleted file mode 100644
index a0060d5a36..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/AccessMap.java
+++ /dev/null
@@ -1,51 +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.client.access;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.Collections;
-import java.util.Set;
-
-/** Access rights available from {@code /access/}. */
-public class AccessMap extends NativeMap<ProjectAccessInfo> {
- public static void get(Set<Project.NameKey> projects, AsyncCallback<AccessMap> callback) {
- RestApi api = new RestApi("/access/");
- for (Project.NameKey p : projects) {
- api.addParameter("project", p.get());
- }
- api.get(NativeMap.copyKeysIntoChildren(callback));
- }
-
- public static void get(Project.NameKey project, AsyncCallback<ProjectAccessInfo> cb) {
- get(
- Collections.singleton(project),
- new AsyncCallback<AccessMap>() {
- @Override
- public void onSuccess(AccessMap result) {
- cb.onSuccess(result.get(project.get()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- }
-
- protected AccessMap() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java
deleted file mode 100644
index 88635df8c4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java
+++ /dev/null
@@ -1,29 +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.client.access;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ProjectAccessInfo extends JavaScriptObject {
- public final native boolean canAddRefs() /*-{ return this.can_add ? true : false; }-*/;
-
- public final native boolean canAddTagRefs() /*-{ return this.can_add_tags ? true : false; }-*/;
-
- public final native boolean isOwner() /*-{ return this.is_owner ? true : false; }-*/;
-
- public final native boolean configVisible() /*-{ return this.config_visible ? true : false; }-*/;
-
- protected ProjectAccessInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
deleted file mode 100644
index 7f4522f961..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
+++ /dev/null
@@ -1,287 +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.client.account;
-
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.AgreementInfo;
-import com.google.gerrit.client.info.GpgKeyInfo;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.KeyUtil;
-import java.util.HashSet;
-import java.util.Set;
-
-/** A collection of static methods which work on the Gerrit REST API for specific accounts. */
-public class AccountApi {
- public static RestApi self() {
- return new RestApi("/accounts/").view("self");
- }
-
- /** Retrieve the account edit preferences */
- public static void getEditPreferences(AsyncCallback<EditPreferences> cb) {
- self().view("preferences.edit").get(cb);
- }
-
- /** Put the account edit preferences */
- public static void putEditPreferences(EditPreferences in, AsyncCallback<EditPreferences> cb) {
- self().view("preferences.edit").put(in, cb);
- }
-
- public static void suggest(String query, int limit, AsyncCallback<JsArray<AccountInfo>> cb) {
- new RestApi("/accounts/")
- .addParameterTrue("suggest")
- .addParameterRaw("q", KeyUtil.encode(query))
- .addParameter("n", limit)
- .background()
- .get(cb);
- }
-
- public static void putDiffPreferences(DiffPreferences in, AsyncCallback<DiffPreferences> cb) {
- self().view("preferences.diff").put(in, cb);
- }
-
- /** Retrieve the username */
- public static void getUsername(String account, AsyncCallback<NativeString> cb) {
- new RestApi("/accounts/").id(account).view("username").get(cb);
- }
-
- /** Set the username */
- public static void setUsername(String account, String username, AsyncCallback<NativeString> cb) {
- UsernameInput input = UsernameInput.create();
- input.username(username);
- new RestApi("/accounts/").id(account).view("username").put(input, cb);
- }
-
- /** Retrieve the account name */
- public static void getName(String account, AsyncCallback<NativeString> cb) {
- new RestApi("/accounts/").id(account).view("name").get(cb);
- }
-
- /** Set the account name */
- public static void setName(String account, String name, AsyncCallback<NativeString> cb) {
- AccountNameInput input = AccountNameInput.create();
- input.name(name);
- new RestApi("/accounts/").id(account).view("name").put(input, cb);
- }
-
- /** Retrieve email addresses */
- public static void getEmails(String account, AsyncCallback<JsArray<EmailInfo>> cb) {
- new RestApi("/accounts/").id(account).view("emails").get(cb);
- }
-
- /** Register a new email address */
- public static void registerEmail(String account, String email, AsyncCallback<EmailInfo> cb) {
- JavaScriptObject in = JavaScriptObject.createObject();
- new RestApi("/accounts/").id(account).view("emails").id(email).ifNoneMatch().put(in, cb);
- }
-
- /** Set preferred email address */
- public static void setPreferredEmail(
- String account, String email, AsyncCallback<NativeString> cb) {
- new RestApi("/accounts/").id(account).view("emails").id(email).view("preferred").put(cb);
- }
-
- /** Retrieve SSH keys */
- public static void getSshKeys(String account, AsyncCallback<JsArray<SshKeyInfo>> cb) {
- new RestApi("/accounts/").id(account).view("sshkeys").get(cb);
- }
-
- /** Add a new SSH keys */
- public static void addSshKey(String account, String sshPublicKey, AsyncCallback<SshKeyInfo> cb) {
- new RestApi("/accounts/").id(account).view("sshkeys").post(sshPublicKey, cb);
- }
-
- /** Retrieve Watched Projects */
- public static void getWatchedProjects(
- String account, AsyncCallback<JsArray<ProjectWatchInfo>> cb) {
- new RestApi("/accounts/").id(account).view("watched.projects").get(cb);
- }
-
- /** Create/Update Watched Project */
- public static void updateWatchedProject(
- String account,
- ProjectWatchInfo watchedProjectInfo,
- AsyncCallback<JsArray<ProjectWatchInfo>> cb) {
- Set<ProjectWatchInfo> watchedProjectInfos = new HashSet<>();
- watchedProjectInfos.add(watchedProjectInfo);
- updateWatchedProjects(account, watchedProjectInfos, cb);
- }
-
- /** Create/Update Watched Projects */
- public static void updateWatchedProjects(
- String account,
- Set<ProjectWatchInfo> watchedProjectInfos,
- AsyncCallback<JsArray<ProjectWatchInfo>> cb) {
- new RestApi("/accounts/")
- .id(account)
- .view("watched.projects")
- .post(projectWatchArrayFromSet(watchedProjectInfos), cb);
- }
-
- /** Delete Watched Project */
- public static void deleteWatchedProject(
- String account,
- ProjectWatchInfo watchedProjectInfo,
- AsyncCallback<JsArray<ProjectWatchInfo>> cb) {
- Set<ProjectWatchInfo> watchedProjectInfos = new HashSet<>();
- watchedProjectInfos.add(watchedProjectInfo);
- deleteWatchedProjects(account, watchedProjectInfos, cb);
- }
-
- /** Delete Watched Projects */
- public static void deleteWatchedProjects(
- String account,
- Set<ProjectWatchInfo> watchedProjectInfos,
- AsyncCallback<JsArray<ProjectWatchInfo>> cb) {
- new RestApi("/accounts/")
- .id(account)
- .view("watched.projects:delete")
- .post(projectWatchArrayFromSet(watchedProjectInfos), cb);
- }
-
- /**
- * Delete SSH keys. For each key to be deleted a separate DELETE request is fired to the server.
- * The {@code onSuccess} method of the provided callback is invoked once after all requests
- * succeeded. If any request fails the callbacks' {@code onFailure} method is invoked. In a
- * failure case it can be that still some of the keys were successfully deleted.
- */
- public static void deleteSshKeys(
- String account, Set<Integer> sequenceNumbers, AsyncCallback<VoidResult> cb) {
- CallbackGroup group = new CallbackGroup();
- for (int seq : sequenceNumbers) {
- new RestApi("/accounts/").id(account).view("sshkeys").id(seq).delete(group.add(cb));
- cb = CallbackGroup.emptyCallback();
- }
- group.done();
- }
-
- /** Generate a new HTTP password */
- public static void generateHttpPassword(String account, AsyncCallback<NativeString> cb) {
- HttpPasswordInput in = HttpPasswordInput.create();
- in.generate(true);
- new RestApi("/accounts/").id(account).view("password.http").put(in, cb);
- }
-
- /** Retrieve account external ids */
- public static void getExternalIds(AsyncCallback<JsArray<ExternalIdInfo>> cb) {
- self().view("external.ids").get(cb);
- }
-
- /** Delete account external ids */
- public static void deleteExternalIds(Set<String> ids, AsyncCallback<VoidResult> cb) {
- self().view("external.ids:delete").post(Natives.arrayOf(ids), cb);
- }
-
- /** Enter a contributor agreement */
- public static void enterAgreement(String account, String name, AsyncCallback<NativeString> cb) {
- AgreementInput in = AgreementInput.create();
- in.name(name);
- new RestApi("/accounts/").id(account).view("agreements").put(in, cb);
- }
-
- private static JsArray<ProjectWatchInfo> projectWatchArrayFromSet(Set<ProjectWatchInfo> set) {
- JsArray<ProjectWatchInfo> jsArray = JsArray.createArray().cast();
- for (ProjectWatchInfo p : set) {
- jsArray.push(p);
- }
- return jsArray;
- }
-
- private static class AgreementInput extends JavaScriptObject {
- final native void name(String n) /*-{ if(n)this.name=n; }-*/;
-
- static AgreementInput create() {
- return createObject().cast();
- }
-
- protected AgreementInput() {}
- }
-
- private static class HttpPasswordInput extends JavaScriptObject {
- final native void generate(boolean g) /*-{ if(g)this.generate=g; }-*/;
-
- static HttpPasswordInput create() {
- return createObject().cast();
- }
-
- protected HttpPasswordInput() {}
- }
-
- private static class UsernameInput extends JavaScriptObject {
- final native void username(String u) /*-{ if(u)this.username=u; }-*/;
-
- static UsernameInput create() {
- return createObject().cast();
- }
-
- protected UsernameInput() {}
- }
-
- private static class AccountNameInput extends JavaScriptObject {
- final native void name(String n) /*-{ if(n)this.name=n; }-*/;
-
- static AccountNameInput create() {
- return createObject().cast();
- }
-
- protected AccountNameInput() {}
- }
-
- public static void addGpgKey(
- String account, String armored, AsyncCallback<NativeMap<GpgKeyInfo>> cb) {
- new RestApi("/accounts/").id(account).view("gpgkeys").post(GpgKeysInput.add(armored), cb);
- }
-
- public static void deleteGpgKeys(
- String account, Iterable<String> fingerprints, AsyncCallback<NativeMap<GpgKeyInfo>> cb) {
- new RestApi("/accounts/")
- .id(account)
- .view("gpgkeys")
- .post(GpgKeysInput.delete(fingerprints), cb);
- }
-
- /** List contributor agreements */
- public static void getAgreements(String account, AsyncCallback<JsArray<AgreementInfo>> cb) {
- new RestApi("/accounts/").id(account).view("agreements").get(cb);
- }
-
- private static class GpgKeysInput extends JavaScriptObject {
- static GpgKeysInput add(String key) {
- return createWithAdd(Natives.arrayOf(key));
- }
-
- static GpgKeysInput delete(Iterable<String> fingerprints) {
- return createWithDelete(Natives.arrayOf(fingerprints));
- }
-
- private static native GpgKeysInput createWithAdd(JsArrayString keys) /*-{
- return {'add': keys};
- }-*/;
-
- private static native GpgKeysInput createWithDelete(JsArrayString fingerprints) /*-{
- return {'delete': fingerprints};
- }-*/;
-
- protected GpgKeysInput() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java
deleted file mode 100644
index d317881c3d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountCapabilities.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2012 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.client.account;
-
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Capabilities the caller has from {@code /accounts/self/capabilities}. */
-public class AccountCapabilities extends JavaScriptObject {
- public static void all(AsyncCallback<AccountCapabilities> cb, String... filter) {
- new RestApi("/accounts/self/capabilities").addParameter("q", filter).get(cb);
- }
-
- protected AccountCapabilities() {}
-
- public final native boolean canPerform(String name) /*-{ return this[name] ? true : false; }-*/;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
deleted file mode 100644
index 3e21619256..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ /dev/null
@@ -1,311 +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.client.account;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface AccountConstants extends Constants {
- String settingsHeading();
-
- String changeAvatar();
-
- String fullName();
-
- String preferredEmail();
-
- String registeredOn();
-
- String accountId();
-
- String diffViewLabel();
-
- String maximumPageSizeFieldLabel();
-
- String dateFormatLabel();
-
- String contextWholeFile();
-
- String showSiteHeader();
-
- String useFlashClipboard();
-
- String reviewCategoryLabel();
-
- String messageShowInReviewCategoryNone();
-
- String messageShowInReviewCategoryName();
-
- String messageShowInReviewCategoryEmail();
-
- String messageShowInReviewCategoryUsername();
-
- String messageShowInReviewCategoryAbbrev();
-
- String buttonSaveChanges();
-
- String highlightAssigneeInChangeTable();
-
- String showRelativeDateInChangeTable();
-
- String showSizeBarInChangeTable();
-
- String showLegacycidInChangeTable();
-
- String muteCommonPathPrefixes();
-
- String signedOffBy();
-
- String publishCommentsOnPush();
-
- String workInProgressByDefault();
-
- String myMenu();
-
- String myMenuInfo();
-
- String myMenuName();
-
- String myMenuUrl();
-
- String myMenuReset();
-
- String tabAccountSummary();
-
- String tabAgreements();
-
- String tabContactInformation();
-
- String tabDiffPreferences();
-
- String tabEditPreferences();
-
- String tabGpgKeys();
-
- String tabHttpAccess();
-
- String tabMyGroups();
-
- String tabOAuthToken();
-
- String tabPreferences();
-
- String tabSshKeys();
-
- String tabWatchedProjects();
-
- String tabWebIdentities();
-
- String buttonShowAddSshKey();
-
- String buttonCloseAddSshKey();
-
- String buttonDeleteSshKey();
-
- String buttonClearSshKeyInput();
-
- String buttonAddSshKey();
-
- String userName();
-
- String password();
-
- String buttonSetUserName();
-
- String confirmSetUserNameTitle();
-
- String confirmSetUserName();
-
- String buttonClearPassword();
-
- String buttonGeneratePassword();
-
- String revokePassword();
-
- String linkObtainPassword();
-
- String linkEditFullName();
-
- String linkReloadContact();
-
- String invalidUserName();
-
- String invalidUserEmail();
-
- String labelOAuthToken();
-
- String labelOAuthExpires();
-
- String labelOAuthNetRCEntry();
-
- String labelOAuthGitCookie();
-
- String labelOAuthExpired();
-
- String sshKeyInvalid();
-
- String sshKeyAlgorithm();
-
- String sshKeyKey();
-
- String sshKeyComment();
-
- String sshKeyStatus();
-
- String addSshKeyPanelHeader();
-
- String addSshKeyHelpTitle();
-
- String addSshKeyHelp();
-
- String sshJavaAppletNotAvailable();
-
- String invalidSshKeyError();
-
- String sshHostKeyTitle();
-
- String sshHostKeyFingerprint();
-
- String sshHostKeyKnownHostEntry();
-
- String gpgKeyId();
-
- String gpgKeyFingerprint();
-
- String gpgKeyUserIds();
-
- String webIdStatus();
-
- String webIdEmail();
-
- String webIdIdentity();
-
- String untrustedProvider();
-
- String buttonDeleteIdentity();
-
- String buttonLinkIdentity();
-
- String buttonWatchProject();
-
- String defaultProjectName();
-
- String defaultFilter();
-
- String buttonBrowseProjects();
-
- String projects();
-
- String projectsClose();
-
- String projectListOpen();
-
- String watchedProjectName();
-
- String watchedProjectFilter();
-
- String watchedProjectColumnEmailNotifications();
-
- String watchedProjectColumnNewChanges();
-
- String watchedProjectColumnNewPatchSets();
-
- String watchedProjectColumnAllComments();
-
- String watchedProjectColumnSubmittedChanges();
-
- String watchedProjectColumnAbandonedChanges();
-
- String contactFieldFullName();
-
- String contactFieldEmail();
-
- String buttonOpenRegisterNewEmail();
-
- String buttonSendRegisterNewEmail();
-
- String buttonCancel();
-
- String titleRegisterNewEmail();
-
- String descRegisterNewEmail();
-
- String errorDialogTitleRegisterNewEmail();
-
- String emailFilterHelpTitle();
-
- String emailFilterHelp();
-
- String newAgreement();
-
- String agreementName();
-
- String agreementDescription();
-
- String newAgreementSelectTypeHeading();
-
- String newAgreementNoneAvailable();
-
- String newAgreementReviewLegalHeading();
-
- String newAgreementReviewContactHeading();
-
- String newAgreementCompleteHeading();
-
- String newAgreementIAGREE();
-
- String newAgreementAlreadySubmitted();
-
- String buttonSubmitNewAgreement();
-
- String welcomeToGerritCodeReview();
-
- String welcomeReviewContact();
-
- String welcomeContactFrom();
-
- String welcomeUsernameHeading();
-
- String welcomeSshKeyHeading();
-
- String welcomeSshKeyText();
-
- String welcomeAgreementHeading();
-
- String welcomeAgreementText();
-
- String welcomeAgreementLater();
-
- String welcomeContinue();
-
- String messageEnabled();
-
- String messageCCMeOnMyComments();
-
- String messageDisabled();
-
- String emailFieldLabel();
-
- String emailFormatFieldLabel();
-
- String messagePlaintextOnly();
-
- String messageHtmlPlaintext();
-
- String defaultBaseForMerges();
-
- String autoMerge();
-
- String firstParent();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
deleted file mode 100644
index c32efedaea..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ /dev/null
@@ -1,274 +0,0 @@
-settingsHeading = Settings
-
-changeAvatar = Change Avatar
-fullName = Full Name
-preferredEmail = Email Address
-registeredOn = Registered
-accountId = Account ID
-showSiteHeader = Show Site Header / Footer
-useFlashClipboard = Use Flash Clipboard Widget
-reviewCategoryLabel = Display In Review Category
-messageShowInReviewCategoryNone = None (default)
-messageShowInReviewCategoryName = Show Name
-messageShowInReviewCategoryEmail = Show Email
-messageShowInReviewCategoryUsername = Show Username
-messageShowInReviewCategoryAbbrev = Show Abbreviated Name
-
-emailFieldLabel = Email Notifications:
-messageCCMeOnMyComments = Every Comment
-messageEnabled = Only Comments Left By Others
-messageDisabled = None
-
-emailFormatFieldLabel = Email Format:
-messagePlaintextOnly = Plaintext Only
-messageHtmlPlaintext = HTML and Plaintext
-
-defaultBaseForMerges = Default Base For Merges:
-autoMerge = Auto Merge
-firstParent = First Parent
-
-maximumPageSizeFieldLabel = Maximum Page Size:
-diffViewLabel = Diff View:
-dateFormatLabel = Date/Time Format:
-contextWholeFile = Whole File
-buttonSaveChanges = Save Changes
-highlightAssigneeInChangeTable = Highlight Changes Assigned To Me In Changes Table
-showRelativeDateInChangeTable = Show Relative Dates In Changes Table
-showSizeBarInChangeTable = Show Change Sizes As Colored Bars
-showLegacycidInChangeTable = Show Change Number In Changes Table
-muteCommonPathPrefixes = Mute Common Path Prefixes In File List
-signedOffBy = Insert Signed-off-by Footer For Inline Edit Changes
-publishCommentsOnPush = Publish Comments On Push
-workInProgressByDefault = Set all new changes work-in-progress by default
-myMenu = My Menu
-myMenuInfo = \
- Menu items for the 'My' top level menu. \
- The first menu item defines the default screen.
-myMenuName = Name
-myMenuUrl = URL
-myMenuReset = Reset
-
-tabAccountSummary = Profile
-tabAgreements = Agreements
-tabContactInformation = Contact Information
-tabDiffPreferences = Diff Preferences
-tabEditPreferences = Edit Preferences
-tabGpgKeys = GPG Public Keys
-tabHttpAccess = HTTP Password
-tabOAuthToken = OAuth Token
-tabMyGroups = Groups
-tabPreferences = Preferences
-tabSshKeys = SSH Public Keys
-tabWatchedProjects = Watched Projects
-tabWebIdentities = Identities
-
-buttonShowAddSshKey = Add Key ...
-buttonCloseAddSshKey = Close
-buttonDeleteSshKey = Delete
-buttonClearSshKeyInput = Clear
-buttonAddSshKey = Add
-
-userName = Username
-password = Password
-buttonSetUserName = Select Username
-confirmSetUserNameTitle = Confirm Setting the Username
-confirmSetUserName = Setting the Username is permanent. Are you sure?
-buttonClearPassword = Clear Password
-buttonGeneratePassword = Generate Password
-revokePassword = (click 'Generate Password' to revoke an old password)
-linkObtainPassword = Obtain Password
-linkEditFullName = Edit
-linkReloadContact = Reload
-invalidUserName = Username must contain only letters, numbers, _, - or .
-invalidUserEmail = Email format is wrong.
-
-labelOAuthToken = Access Token
-labelOAuthExpires = Expires
-labelOAuthNetRCEntry = Entry for ~/.netrc
-labelOAuthGitCookie = Entry for ~/.gitcookies
-labelOAuthExpired = To obtain an access token please sign out and sign in again.
-
-sshKeyInvalid = Invalid Key
-sshKeyAlgorithm = Algorithm
-sshKeyKey = Key
-sshKeyComment = Comment
-sshKeyStatus = Status
-
-sshHostKeyTitle = Server Host Key
-sshHostKeyFingerprint = Fingerprint:
-sshHostKeyKnownHostEntry = Entry for <code>~/.ssh/known_hosts</code>:
-
-gpgKeyId = ID
-gpgKeyFingerprint = Fingerprint
-gpgKeyUserIds = User IDs
-
-webIdStatus = Status
-webIdEmail = Email Address
-webIdIdentity = Identity
-untrustedProvider = Untrusted
-buttonDeleteIdentity = Delete
-buttonLinkIdentity = Link Another Identity
-
-addSshKeyPanelHeader = Add SSH Public Key
-addSshKeyHelpTitle = How to Generate an SSH Key
-addSshKeyHelp = \
- <ol>\
- <li>\
- From the Terminal or Git Bash, run <em>ssh-keygen</em>\
- </li>\
- <li>\
- Confirm the default path <em>.ssh/id_rsa</em>\
- </li>\
- <li>\
- Enter a passphrase (recommended) or leave it blank.<br />\
- Remember this passphrase, as you will need it to unlock the<br />\
- key whenever you use it.\
- </li>\
- <li>\
- Open <em>~/.ssh/id_rsa.pub</em> and copy & paste the contents into<br />\
- the box below, then click on "Add".<br />\
- Note that <em>id_rsa.pub</em> is your public key and can be shared,<br />\
- while <em>id_rsa</em> is your private key and should be kept secret.\
- </li>\
- <\ol>
-invalidSshKeyError = Invalid SSH Key
-sshJavaAppletNotAvailable = Open Key Unavailable: Java not enabled
-
-buttonWatchProject = Watch
-defaultProjectName = Project Name
-defaultFilter = branch:name, or other search expression
-projects = All Watchable Projects
-projectsClose = Close
-buttonBrowseProjects = Browse
-projectListOpen = Watch Selected project
-watchedProjectName = Project Name
-watchedProjectFilter = Only If
-watchedProjectColumnEmailNotifications = Email Notifications
-watchedProjectColumnNewChanges = New Changes
-watchedProjectColumnNewPatchSets = New Patch Sets
-watchedProjectColumnAllComments = All Comments
-watchedProjectColumnSubmittedChanges = Submitted Changes
-watchedProjectColumnAbandonedChanges = Abandoned Changes
-
-contactFieldFullName = Full Name
-contactFieldEmail = Preferred Email
-buttonOpenRegisterNewEmail = Register New Email ...
-buttonSendRegisterNewEmail = Register
-buttonCancel = Cancel
-titleRegisterNewEmail = Register Email Address
-descRegisterNewEmail = \
- <p>A confirmation link will be sent by email to this address.</p>\
- <p>You must click on the link to complete the registration and make the address available for selection.</p>
-errorDialogTitleRegisterNewEmail = Email Registration Failed
-emailFilterHelpTitle = Mail Filters
-emailFilterHelp = \
- <p>\
- Gerrit emails include metadata about the change to support \
- writing mail filters.\
- </p>\
- <p>\
- Here are some example Gmail queries that can be used for filters or \
- for searching through archived messages. View the \
- <a href="https://gerrit-review.googlesource.com/Documentation/user-notify.html"\
- target="_blank" rel="nofollow">Gerrit documentation</a> for \
- the complete set of footers.\
- </p>\
- <table>\
- <tbody>\
- <tr><th>Name</th><th>Query</th></tr>\
- <tr>\
- <td>Changes requesting my review</td>\
- <td>\
- <code>\
- "Gerrit-Reviewer: <em>Your Name</em>\
- &lt;<em>your.email@example.com</em>&gt;"\
- </code>\
- </td>\
- </tr>\
- <tr>\
- <td>Changes from a specific owner</td>\
- <td>\
- <code>\
- "Gerrit-Owner: <em>Owner name</em>\
- &lt;<em>owner.email@example.com</em>&gt;"\
- </code>\
- </td>\
- </tr>\
- <tr>\
- <td>Changes targeting a specific branch</td>\
- <td>\
- <code>\
- "Gerrit-Branch: <em>branch-name</em>"\
- </code>\
- </td>\
- </tr>\
- <tr>\
- <td>Changes in a specific project</td>\
- <td>\
- <code>\
- "Gerrit-Project: <em>project-name</em>"\
- </code>\
- </td>\
- </tr>\
- <tr>\
- <td>Messages related to a specific Change ID</td>\
- <td>\
- <code>\
- "Gerrit-Change-Id: <em>Change ID</em>"\
- </code>\
- </td>\
- </tr>\
- <tr>\
- <td>Messages related to a specific change number</td>\
- <td>\
- <code>\
- "Gerrit-Change-Number: <em>change number</em>"\
- </code>\
- </td>\
- </tr>\
- </tbody>\
- </table>
-
-newAgreement = New Contributor Agreement
-agreementName = Name
-agreementDescription = Description
-
-newAgreementSelectTypeHeading = Select an agreement type:
-newAgreementNoneAvailable = No contributor agreements are configured.
-newAgreementReviewLegalHeading = Review the agreement:
-newAgreementReviewContactHeading = Review your contact information:
-newAgreementCompleteHeading = Complete the agreement:
-newAgreementIAGREE = I AGREE
-newAgreementAlreadySubmitted = Agreement already submitted.
-buttonSubmitNewAgreement = Submit Agreement
-
-welcomeToGerritCodeReview = Welcome to Gerrit Code Review
-welcomeReviewContact = Please review your contact information:
-welcomeContactFrom = \
- <p>The following contact information was automatically obtained when \
- you signed-in to the site. This information is used to display who \
- you are to others, and to send updates to code reviews you have either \
- started or subscribed to.</p>
-
-welcomeUsernameHeading = Select a unique username:
-
-welcomeSshKeyHeading = Register an SSH public key:
-welcomeSshKeyText = \
- <p>Gerrit Code Review uses \
- <a href="http://en.wikipedia.org/wiki/Public-key_cryptography" target="_blank">public-key cryptography</a> \
- and \
- <a href="http://en.wikipedia.org/wiki/Secure_Shell" target="_blank">SSH</a> \
- to authenticate \
- you during git's push and pull commands to hosted projects. Registering \
- your public key allows Gerrit to identify you whenever you connect through \
- SSH.</p>\
- <p>This step can also be completed at a later time.</p>
-
-welcomeAgreementHeading = Complete a contributor agreement:
-welcomeAgreementText = \
- <p>If you will be contributing code or documentation changes to projects \
- hosted here, please consider taking a minute to review and complete \
- a contributor agreement.</p>\
- <p>This step can also be completed at a later time.</p>
-welcomeAgreementLater = Continue Without Agreement
-welcomeContinue = Continue
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
deleted file mode 100644
index 4cff1e2e32..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
+++ /dev/null
@@ -1,27 +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.client.account;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface AccountMessages extends Messages {
- String lines(short cnt);
-
- String rowsPerPage(int cnt);
-
- String changeScreenServerDefault(String d);
-
- String enterIAGREE(String iagree);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
deleted file mode 100644
index a8d61cf1c0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-lines = {0} lines
-rowsPerPage = {0} rows per page
-changeScreenServerDefault = Server Default ({0})
-enterIAGREE = (enter {0} in the box to the left)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
deleted file mode 100644
index a53706325a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ /dev/null
@@ -1,484 +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.client.account;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.ComplexDisclosurePanel;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-
-class ContactPanelShort extends Composite {
- protected final FlowPanel body;
- protected int labelIdx;
- protected int fieldIdx;
- protected Button save;
-
- private String currentEmail;
- protected boolean haveAccount;
- private boolean haveEmails;
-
- NpTextBox nameTxt;
- private ListBox emailPick;
- private Button registerNewEmail;
- private OnEditEnabler onEditEnabler;
-
- ContactPanelShort() {
- body = new FlowPanel();
- initWidget(body);
- }
-
- protected void onInitUI() {
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- }
-
- nameTxt = new NpTextBox();
- nameTxt.setVisibleLength(60);
- nameTxt.setReadOnly(!canEditFullName());
-
- emailPick = new ListBox();
-
- final Grid infoPlainText = new Grid(2, 2);
- infoPlainText.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- infoPlainText.addStyleName(Gerrit.RESOURCES.css().accountInfoBlock());
-
- body.add(infoPlainText);
-
- registerNewEmail = new Button(Util.C.buttonOpenRegisterNewEmail());
- registerNewEmail.setEnabled(false);
- registerNewEmail.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doRegisterNewEmail();
- }
- });
- final FlowPanel emailLine = new FlowPanel();
- emailLine.add(emailPick);
- if (canRegisterNewEmail()) {
- emailLine.add(registerNewEmail);
- }
-
- int row = 0;
- if (!Gerrit.info().auth().canEdit(AccountFieldName.USER_NAME)
- && Gerrit.info().auth().siteHasUsernames()) {
- infoPlainText.resizeRows(infoPlainText.getRowCount() + 1);
- row(infoPlainText, row++, Util.C.userName(), new UsernameField());
- }
-
- if (!canEditFullName()) {
- FlowPanel nameLine = new FlowPanel();
- nameLine.add(nameTxt);
- if (Gerrit.info().auth().editFullNameUrl() != null) {
- Button edit = new Button(Util.C.linkEditFullName());
- edit.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- Window.open(Gerrit.info().auth().editFullNameUrl(), "_blank", null);
- }
- });
- nameLine.add(edit);
- }
- Button reload = new Button(Util.C.linkReloadContact());
- reload.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- Window.Location.replace(Gerrit.loginRedirect(PageLinks.SETTINGS_CONTACT));
- }
- });
- nameLine.add(reload);
- row(infoPlainText, row++, Util.C.contactFieldFullName(), nameLine);
- } else {
- row(infoPlainText, row++, Util.C.contactFieldFullName(), nameTxt);
- }
- row(infoPlainText, row++, Util.C.contactFieldEmail(), emailLine);
-
- infoPlainText.getCellFormatter().addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
- infoPlainText.getCellFormatter().addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
- infoPlainText
- .getCellFormatter()
- .addStyleName(row - 1, 0, Gerrit.RESOURCES.css().bottomheader());
-
- save = new Button(Util.C.buttonSaveChanges());
- save.setEnabled(false);
- save.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSave();
- }
- });
-
- final ComplexDisclosurePanel mailFilterHelp =
- new ComplexDisclosurePanel(Util.C.emailFilterHelpTitle(), false);
- mailFilterHelp.setContent(new HTML(Util.C.emailFilterHelp()));
- body.add(mailFilterHelp);
-
- emailPick.addChangeHandler(
- new ChangeHandler() {
- @Override
- public void onChange(ChangeEvent event) {
- final int idx = emailPick.getSelectedIndex();
- final String v = 0 <= idx ? emailPick.getValue(idx) : null;
- if (Util.C.buttonOpenRegisterNewEmail().equals(v)) {
- for (int i = 0; i < emailPick.getItemCount(); i++) {
- if (currentEmail.equals(emailPick.getValue(i))) {
- emailPick.setSelectedIndex(i);
- break;
- }
- }
- doRegisterNewEmail();
- } else {
- save.setEnabled(true);
- }
- }
- });
-
- onEditEnabler = new OnEditEnabler(save, nameTxt);
- }
-
- private boolean canEditFullName() {
- return Gerrit.info().auth().canEdit(AccountFieldName.FULL_NAME);
- }
-
- private boolean canRegisterNewEmail() {
- return Gerrit.info().auth().canEdit(AccountFieldName.REGISTER_NEW_EMAIL);
- }
-
- void hideSaveButton() {
- save.setVisible(false);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- onInitUI();
- body.add(save);
- display(Gerrit.getUserAccount());
-
- emailPick.clear();
- emailPick.setEnabled(false);
- registerNewEmail.setEnabled(false);
-
- haveAccount = false;
- haveEmails = false;
-
- CallbackGroup group = new CallbackGroup();
- AccountApi.getName(
- "self",
- group.add(
- new GerritCallback<NativeString>() {
-
- @Override
- public void onSuccess(NativeString result) {
- nameTxt.setText(result.asString());
- haveAccount = true;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
-
- AccountApi.getEmails(
- "self",
- group.addFinal(
- new GerritCallback<JsArray<EmailInfo>>() {
- @Override
- public void onSuccess(JsArray<EmailInfo> result) {
- for (EmailInfo i : Natives.asList(result)) {
- emailPick.addItem(i.email());
- if (i.isPreferred()) {
- currentEmail = i.email();
- }
- }
- haveEmails = true;
- postLoad();
- }
- }));
- }
-
- private void postLoad() {
- if (haveAccount && haveEmails) {
- updateEmailList();
- registerNewEmail.setEnabled(true);
- save.setEnabled(false);
- onEditEnabler.updateOriginalValue(nameTxt);
- }
- display();
- }
-
- void display() {}
-
- protected void row(Grid info, int row, String name, Widget field) {
- info.setText(row, labelIdx, name);
- info.setWidget(row, fieldIdx, field);
- info.getCellFormatter().addStyleName(row, 0, Gerrit.RESOURCES.css().header());
- }
-
- protected void display(AccountInfo account) {
- currentEmail = account.email();
- nameTxt.setText(account.name());
- save.setEnabled(false);
- onEditEnabler.updateOriginalValue(nameTxt);
- }
-
- private void doRegisterNewEmail() {
- if (!canRegisterNewEmail()) {
- return;
- }
-
- final AutoCenterDialogBox box = new AutoCenterDialogBox(true, true);
- final VerticalPanel body = new VerticalPanel();
-
- final NpTextBox inEmail = new NpTextBox();
- inEmail.setVisibleLength(60);
-
- final Button register = new Button(Util.C.buttonSendRegisterNewEmail());
- final Button cancel = new Button(Util.C.buttonCancel());
- final FormPanel form = new FormPanel();
- form.addSubmitHandler(
- new FormPanel.SubmitHandler() {
- @Override
- public void onSubmit(SubmitEvent event) {
- event.cancel();
- final String addr = inEmail.getText().trim();
- if (!addr.contains("@")) {
- new ErrorDialog(Util.C.invalidUserEmail()).center();
- return;
- }
-
- inEmail.setEnabled(false);
- register.setEnabled(false);
- AccountApi.registerEmail(
- "self",
- addr,
- new GerritCallback<EmailInfo>() {
- @Override
- public void onSuccess(EmailInfo result) {
- box.hide();
- if (Gerrit.info().auth().isDev()) {
- currentEmail = addr;
- if (emailPick.getItemCount() == 0) {
- AccountInfo me = Gerrit.getUserAccount();
- me.email(addr);
- onSaveSuccess(me);
- } else {
- save.setEnabled(true);
- }
- updateEmailList();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- inEmail.setEnabled(true);
- register.setEnabled(true);
- if (caught.getMessage().startsWith(EmailException.MESSAGE)) {
- final ErrorDialog d =
- new ErrorDialog(
- caught.getMessage().substring(EmailException.MESSAGE.length()));
- d.setText(Util.C.errorDialogTitleRegisterNewEmail());
- d.center();
- } else {
- super.onFailure(caught);
- }
- }
- });
- }
- });
- form.setWidget(body);
-
- register.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- form.submit();
- }
- });
- cancel.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- box.hide();
- }
- });
-
- final FlowPanel buttons = new FlowPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().patchSetActions());
- buttons.add(register);
- buttons.add(cancel);
-
- if (!Gerrit.info().auth().isDev()) {
- body.add(new HTML(Util.C.descRegisterNewEmail()));
- }
- body.add(inEmail);
- body.add(buttons);
-
- box.setText(Util.C.titleRegisterNewEmail());
- box.setWidget(form);
- box.center();
- inEmail.setFocus(true);
- }
-
- void doSave() {
- final String newName;
- String name = canEditFullName() ? nameTxt.getText() : null;
- if (name != null && name.trim().isEmpty()) {
- newName = null;
- } else {
- newName = name;
- }
-
- final String newEmail;
- if (emailPick.isEnabled() && emailPick.getSelectedIndex() >= 0) {
- final String v = emailPick.getValue(emailPick.getSelectedIndex());
- if (Util.C.buttonOpenRegisterNewEmail().equals(v)) {
- newEmail = currentEmail;
- } else {
- newEmail = v;
- }
- } else {
- newEmail = currentEmail;
- }
-
- save.setEnabled(false);
- registerNewEmail.setEnabled(false);
-
- CallbackGroup group = new CallbackGroup();
- if (currentEmail != null && !newEmail.equals(currentEmail)) {
- AccountApi.setPreferredEmail(
- "self",
- newEmail,
- group.add(
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {}
- }));
- }
- AccountApi.setName(
- "self",
- newName,
- group.add(
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {}
-
- @Override
- public void onFailure(Throwable caught) {
- save.setEnabled(true);
- registerNewEmail.setEnabled(true);
- super.onFailure(caught);
- }
- }));
- group.done();
- group.addListener(
- new GerritCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- currentEmail = newEmail;
- AccountInfo me = Gerrit.getUserAccount();
- me.email(currentEmail);
- me.name(newName);
- onSaveSuccess(me);
- registerNewEmail.setEnabled(true);
- }
- });
- }
-
- void onSaveSuccess(AccountInfo result) {
- AccountInfo me = Gerrit.getUserAccount();
- me.name(result.name());
- me.email(result.email());
- Gerrit.refreshMenuBar();
- display(me);
- }
-
- private int emailListIndexOf(String value) {
- for (int i = 0; i < emailPick.getItemCount(); i++) {
- if (value.equalsIgnoreCase(emailPick.getValue(i))) {
- return i;
- }
- }
- return -1;
- }
-
- private void updateEmailList() {
- if (currentEmail != null) {
- int index = emailListIndexOf(currentEmail);
- if (index == -1) {
- emailPick.addItem(currentEmail);
- emailPick.setSelectedIndex(emailPick.getItemCount() - 1);
- } else {
- emailPick.setSelectedIndex(index);
- }
- }
- if (emailPick.getItemCount() > 0) {
- if (currentEmail == null) {
- int index = emailListIndexOf("");
- if (index != -1) {
- emailPick.removeItem(index);
- }
- emailPick.insertItem("", 0);
- emailPick.setSelectedIndex(0);
- }
- emailPick.setVisible(true);
- emailPick.setEnabled(true);
- if (canRegisterNewEmail()) {
- final String t = Util.C.buttonOpenRegisterNewEmail();
- int index = emailListIndexOf(t);
- if (index != -1) {
- emailPick.removeItem(index);
- }
- emailPick.addItem("... " + t + " ", t);
- }
- } else {
- emailPick.setVisible(false);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
deleted file mode 100644
index 286d29a0c5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
+++ /dev/null
@@ -1,226 +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.client.account;
-
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class DiffPreferences extends JavaScriptObject {
- public static DiffPreferences create(DiffPreferencesInfo in) {
- if (in == null) {
- in = DiffPreferencesInfo.defaults();
- }
- DiffPreferences p = createObject().cast();
- p.ignoreWhitespace(in.ignoreWhitespace);
- p.tabSize(in.tabSize);
- p.lineLength(in.lineLength);
- p.cursorBlinkRate(in.cursorBlinkRate);
- p.context(in.context);
- p.intralineDifference(in.intralineDifference);
- p.showLineEndings(in.showLineEndings);
- p.showTabs(in.showTabs);
- p.showWhitespaceErrors(in.showWhitespaceErrors);
- p.syntaxHighlighting(in.syntaxHighlighting);
- p.hideTopMenu(in.hideTopMenu);
- p.autoHideDiffTableHeader(in.autoHideDiffTableHeader);
- p.hideLineNumbers(in.hideLineNumbers);
- p.expandAllComments(in.expandAllComments);
- p.manualReview(in.manualReview);
- p.renderEntireFile(in.renderEntireFile);
- p.theme(in.theme);
- p.hideEmptyPane(in.hideEmptyPane);
- p.retainHeader(in.retainHeader);
- p.skipUnchanged(in.skipUnchanged);
- p.skipUncommented(in.skipUncommented);
- p.skipDeleted(in.skipDeleted);
- p.matchBrackets(in.matchBrackets);
- p.lineWrapping(in.lineWrapping);
- return p;
- }
-
- public final void copyTo(DiffPreferencesInfo p) {
- p.context = context();
- p.tabSize = tabSize();
- p.lineLength = lineLength();
- p.cursorBlinkRate = cursorBlinkRate();
- p.expandAllComments = expandAllComments();
- p.intralineDifference = intralineDifference();
- p.manualReview = manualReview();
- p.retainHeader = retainHeader();
- p.showLineEndings = showLineEndings();
- p.showTabs = showTabs();
- p.showWhitespaceErrors = showWhitespaceErrors();
- p.skipDeleted = skipDeleted();
- p.skipUnchanged = skipUnchanged();
- p.skipUncommented = skipUncommented();
- p.syntaxHighlighting = syntaxHighlighting();
- p.hideTopMenu = hideTopMenu();
- p.autoHideDiffTableHeader = autoHideDiffTableHeader();
- p.hideLineNumbers = hideLineNumbers();
- p.renderEntireFile = renderEntireFile();
- p.hideEmptyPane = hideEmptyPane();
- p.matchBrackets = matchBrackets();
- p.lineWrapping = lineWrapping();
- p.theme = theme();
- p.ignoreWhitespace = ignoreWhitespace();
- }
-
- public final void ignoreWhitespace(Whitespace i) {
- setIgnoreWhitespaceRaw(i.toString());
- }
-
- public final void theme(Theme i) {
- setThemeRaw(i != null ? i.toString() : Theme.DEFAULT.toString());
- }
-
- public final void showLineNumbers(boolean s) {
- hideLineNumbers(!s);
- }
-
- public final Whitespace ignoreWhitespace() {
- String s = ignoreWhitespaceRaw();
- return s != null ? Whitespace.valueOf(s) : Whitespace.IGNORE_NONE;
- }
-
- public final Theme theme() {
- String s = themeRaw();
- return s != null ? Theme.valueOf(s) : Theme.DEFAULT;
- }
-
- public final int tabSize() {
- return get("tab_size", 8);
- }
-
- public final int context() {
- return get("context", 10);
- }
-
- public final int lineLength() {
- return get("line_length", 100);
- }
-
- public final int cursorBlinkRate() {
- return get("cursor_blink_rate", 0);
- }
-
- public final boolean showLineNumbers() {
- return !hideLineNumbers();
- }
-
- public final boolean autoReview() {
- return !manualReview();
- }
-
- public final native void tabSize(int t) /*-{ this.tab_size = t }-*/;
-
- public final native void lineLength(int c) /*-{ this.line_length = c }-*/;
-
- public final native void context(int c) /*-{ this.context = c }-*/;
-
- public final native void cursorBlinkRate(int r) /*-{ this.cursor_blink_rate = r }-*/;
-
- public final native void intralineDifference(Boolean i) /*-{ this.intraline_difference = i }-*/;
-
- public final native void showLineEndings(Boolean s) /*-{ this.show_line_endings = s }-*/;
-
- public final native void showTabs(Boolean s) /*-{ this.show_tabs = s }-*/;
-
- public final native void showWhitespaceErrors(
- Boolean s) /*-{ this.show_whitespace_errors = s }-*/;
-
- public final native void syntaxHighlighting(Boolean s) /*-{ this.syntax_highlighting = s }-*/;
-
- public final native void hideTopMenu(Boolean s) /*-{ this.hide_top_menu = s }-*/;
-
- public final native void autoHideDiffTableHeader(
- Boolean s) /*-{ this.auto_hide_diff_table_header = s }-*/;
-
- public final native void hideLineNumbers(Boolean s) /*-{ this.hide_line_numbers = s }-*/;
-
- public final native void expandAllComments(Boolean e) /*-{ this.expand_all_comments = e }-*/;
-
- public final native void manualReview(Boolean r) /*-{ this.manual_review = r }-*/;
-
- public final native void renderEntireFile(Boolean r) /*-{ this.render_entire_file = r }-*/;
-
- public final native void retainHeader(Boolean r) /*-{ this.retain_header = r }-*/;
-
- public final native void hideEmptyPane(Boolean s) /*-{ this.hide_empty_pane = s }-*/;
-
- public final native void skipUnchanged(Boolean s) /*-{ this.skip_unchanged = s }-*/;
-
- public final native void skipUncommented(Boolean s) /*-{ this.skip_uncommented = s }-*/;
-
- public final native void skipDeleted(Boolean s) /*-{ this.skip_deleted = s }-*/;
-
- public final native void matchBrackets(Boolean m) /*-{ this.match_brackets = m }-*/;
-
- public final native void lineWrapping(Boolean w) /*-{ this.line_wrapping = w }-*/;
-
- public final native boolean
- intralineDifference() /*-{ return this.intraline_difference || false }-*/;
-
- public final native boolean showLineEndings() /*-{ return this.show_line_endings || false }-*/;
-
- public final native boolean showTabs() /*-{ return this.show_tabs || false }-*/;
-
- public final native boolean
- showWhitespaceErrors() /*-{ return this.show_whitespace_errors || false }-*/;
-
- public final native boolean
- syntaxHighlighting() /*-{ return this.syntax_highlighting || false }-*/;
-
- public final native boolean hideTopMenu() /*-{ return this.hide_top_menu || false }-*/;
-
- public final native boolean
- autoHideDiffTableHeader() /*-{ return this.auto_hide_diff_table_header || false }-*/;
-
- public final native boolean hideLineNumbers() /*-{ return this.hide_line_numbers || false }-*/;
-
- public final native boolean
- expandAllComments() /*-{ return this.expand_all_comments || false }-*/;
-
- public final native boolean manualReview() /*-{ return this.manual_review || false }-*/;
-
- public final native boolean renderEntireFile() /*-{ return this.render_entire_file || false }-*/;
-
- public final native boolean hideEmptyPane() /*-{ return this.hide_empty_pane || false }-*/;
-
- public final native boolean retainHeader() /*-{ return this.retain_header || false }-*/;
-
- public final native boolean skipUnchanged() /*-{ return this.skip_unchanged || false }-*/;
-
- public final native boolean skipUncommented() /*-{ return this.skip_uncommented || false }-*/;
-
- public final native boolean skipDeleted() /*-{ return this.skip_deleted || false }-*/;
-
- public final native boolean matchBrackets() /*-{ return this.match_brackets || false }-*/;
-
- public final native boolean lineWrapping() /*-{ return this.line_wrapping || false }-*/;
-
- private native void setThemeRaw(String i) /*-{ this.theme = i }-*/;
-
- private native void setIgnoreWhitespaceRaw(String i) /*-{ this.ignore_whitespace = i }-*/;
-
- private native String ignoreWhitespaceRaw() /*-{ return this.ignore_whitespace }-*/;
-
- private native String themeRaw() /*-{ return this.theme }-*/;
-
- private native int get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/;
-
- protected DiffPreferences() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
deleted file mode 100644
index 9cd2f17dd4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (C) 2014 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.client.account;
-
-import com.google.gerrit.extensions.client.EditPreferencesInfo;
-import com.google.gerrit.extensions.client.KeyMapType;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class EditPreferences extends JavaScriptObject {
- public static EditPreferences create(EditPreferencesInfo in) {
- EditPreferences p = createObject().cast();
- p.tabSize(in.tabSize);
- p.lineLength(in.lineLength);
- p.indentUnit(in.indentUnit);
- p.cursorBlinkRate(in.cursorBlinkRate);
- p.hideTopMenu(in.hideTopMenu);
- p.showTabs(in.showTabs);
- p.showWhitespaceErrors(in.showWhitespaceErrors);
- p.syntaxHighlighting(in.syntaxHighlighting);
- p.hideLineNumbers(in.hideLineNumbers);
- p.matchBrackets(in.matchBrackets);
- p.lineWrapping(in.lineWrapping);
- p.indentWithTabs(in.indentWithTabs);
- p.autoCloseBrackets(in.autoCloseBrackets);
- p.showBase(in.showBase);
- p.theme(in.theme);
- p.keyMapType(in.keyMapType);
- return p;
- }
-
- public final EditPreferencesInfo copyTo(EditPreferencesInfo p) {
- p.tabSize = tabSize();
- p.lineLength = lineLength();
- p.indentUnit = indentUnit();
- p.cursorBlinkRate = cursorBlinkRate();
- p.hideTopMenu = hideTopMenu();
- p.showTabs = showTabs();
- p.showWhitespaceErrors = showWhitespaceErrors();
- p.syntaxHighlighting = syntaxHighlighting();
- p.hideLineNumbers = hideLineNumbers();
- p.matchBrackets = matchBrackets();
- p.lineWrapping = lineWrapping();
- p.indentWithTabs = indentWithTabs();
- p.autoCloseBrackets = autoCloseBrackets();
- p.showBase = showBase();
- p.theme = theme();
- p.keyMapType = keyMapType();
- return p;
- }
-
- public final void theme(Theme i) {
- setThemeRaw(i != null ? i.toString() : Theme.DEFAULT.toString());
- }
-
- private native void setThemeRaw(String i) /*-{ this.theme = i }-*/;
-
- public final void keyMapType(KeyMapType i) {
- setkeyMapTypeRaw(i != null ? i.toString() : KeyMapType.DEFAULT.toString());
- }
-
- private native void setkeyMapTypeRaw(String i) /*-{ this.key_map_type = i }-*/;
-
- public final native void tabSize(int t) /*-{ this.tab_size = t }-*/;
-
- public final native void lineLength(int c) /*-{ this.line_length = c }-*/;
-
- public final native void indentUnit(int c) /*-{ this.indent_unit = c }-*/;
-
- public final native void cursorBlinkRate(int r) /*-{ this.cursor_blink_rate = r }-*/;
-
- public final native void hideTopMenu(boolean s) /*-{ this.hide_top_menu = s }-*/;
-
- public final native void showTabs(boolean s) /*-{ this.show_tabs = s }-*/;
-
- public final native void showWhitespaceErrors(
- boolean s) /*-{ this.show_whitespace_errors = s }-*/;
-
- public final native void syntaxHighlighting(boolean s) /*-{ this.syntax_highlighting = s }-*/;
-
- public final native void hideLineNumbers(boolean s) /*-{ this.hide_line_numbers = s }-*/;
-
- public final native void matchBrackets(boolean m) /*-{ this.match_brackets = m }-*/;
-
- public final native void lineWrapping(boolean w) /*-{ this.line_wrapping = w }-*/;
-
- public final native void indentWithTabs(boolean w) /*-{ this.indent_with_tabs = w }-*/;
-
- public final native void autoCloseBrackets(boolean c) /*-{ this.auto_close_brackets = c }-*/;
-
- public final native void showBase(boolean s) /*-{ this.show_base = s }-*/;
-
- public final Theme theme() {
- String s = themeRaw();
- return s != null ? Theme.valueOf(s) : Theme.DEFAULT;
- }
-
- private native String themeRaw() /*-{ return this.theme }-*/;
-
- public final KeyMapType keyMapType() {
- String s = keyMapTypeRaw();
- return s != null ? KeyMapType.valueOf(s) : KeyMapType.DEFAULT;
- }
-
- private native String keyMapTypeRaw() /*-{ return this.key_map_type }-*/;
-
- public final int tabSize() {
- return get("tab_size", 8);
- }
-
- public final int lineLength() {
- return get("line_length", 100);
- }
-
- public final int indentUnit() {
- return get("indent_unit", 2);
- }
-
- public final int cursorBlinkRate() {
- return get("cursor_blink_rate", 0);
- }
-
- public final native boolean hideTopMenu() /*-{ return this.hide_top_menu || false }-*/;
-
- public final native boolean showTabs() /*-{ return this.show_tabs || false }-*/;
-
- public final native boolean
- showWhitespaceErrors() /*-{ return this.show_whitespace_errors || false }-*/;
-
- public final native boolean
- syntaxHighlighting() /*-{ return this.syntax_highlighting || false }-*/;
-
- public final native boolean hideLineNumbers() /*-{ return this.hide_line_numbers || false }-*/;
-
- public final native boolean matchBrackets() /*-{ return this.match_brackets || false }-*/;
-
- public final native boolean lineWrapping() /*-{ return this.line_wrapping || false }-*/;
-
- public final native boolean indentWithTabs() /*-{ return this.indent_with_tabs || false }-*/;
-
- public final native boolean
- autoCloseBrackets() /*-{ return this.auto_close_brackets || false }-*/;
-
- public final native boolean showBase() /*-{ return this.show_base || false }-*/;
-
- private native int get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/;
-
- protected EditPreferences() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EmailInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EmailInfo.java
deleted file mode 100644
index 9c324be2e4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EmailInfo.java
+++ /dev/null
@@ -1,28 +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.client.account;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class EmailInfo extends JavaScriptObject {
- public final native String email() /*-{ return this.email; }-*/;
-
- public final native boolean isPreferred() /*-{ return this['preferred'] ? true : false; }-*/;
-
- public final native boolean
- isConfirmationPending() /*-{ return this['pending_confirmation'] ? true : false; }-*/;
-
- protected EmailInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdInfo.java
deleted file mode 100644
index 4ac071644d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdInfo.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// 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.client.account;
-
-import com.google.gerrit.client.auth.openid.OpenIdUtil;
-import com.google.gerrit.common.auth.openid.OpenIdUrls;
-import com.google.gerrit.extensions.client.AuthType;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ExternalIdInfo extends JavaScriptObject implements Comparable<ExternalIdInfo> {
- /**
- * Scheme used for {@link AuthType#LDAP}, {@link AuthType#CLIENT_SSL_CERT_LDAP}, {@link
- * AuthType#HTTP_LDAP}, and {@link AuthType#LDAP_BIND} usernames.
- *
- * <p>The name {@code gerrit:} was a very poor choice.
- */
- private static final String SCHEME_GERRIT = "gerrit:";
-
- /** Scheme used to represent only an email address. */
- private static final String SCHEME_MAILTO = "mailto:";
-
- /** Scheme for the username used to authenticate an account, e.g. over SSH. */
- private static final String SCHEME_USERNAME = "username:";
-
- public final native String identity() /*-{ return this.identity; }-*/;
-
- public final native String emailAddress() /*-{ return this.email_address; }-*/;
-
- public final native boolean isTrusted() /*-{ return this['trusted'] ? true : false; }-*/;
-
- public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;
-
- public final boolean isUsername() {
- return isScheme(SCHEME_USERNAME);
- }
-
- public final String describe() {
- if (isScheme(SCHEME_GERRIT)) {
- // A local user identity should just be itself.
- return getSchemeRest();
- } else if (isScheme(SCHEME_USERNAME)) {
- // A local user identity should just be itself.
- return getSchemeRest();
- } else if (isScheme(SCHEME_MAILTO)) {
- // Describe a mailto address as just its email address,
- // which is already shown in the email address field.
- return "";
- } else if (isScheme(OpenIdUrls.URL_LAUNCHPAD)) {
- return OpenIdUtil.C.nameLaunchpad();
- } else if (isScheme(OpenIdUrls.URL_YAHOO)) {
- return OpenIdUtil.C.nameYahoo();
- }
-
- return identity();
- }
-
- @Override
- public final int compareTo(ExternalIdInfo a) {
- return emailOf(this).compareTo(emailOf(a));
- }
-
- private boolean isScheme(String scheme) {
- return identity() != null && identity().startsWith(scheme);
- }
-
- private String getSchemeRest() {
- int colonIdx = identity().indexOf(':');
- String scheme = (colonIdx > 0) ? identity().substring(0, colonIdx) : null;
- return scheme != null ? identity().substring(scheme.length() + 1) : null;
- }
-
- private String emailOf(ExternalIdInfo a) {
- return a.emailAddress() != null ? a.emailAddress() : "";
- }
-
- protected ExternalIdInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java
deleted file mode 100644
index 4592b62954..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyAgreementsScreen.java
+++ /dev/null
@@ -1,97 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AgreementInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import java.util.List;
-
-public class MyAgreementsScreen extends SettingsScreen {
- private AgreementTable agreements;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- agreements = new AgreementTable();
- add(agreements);
- add(new Hyperlink(Util.C.newAgreement(), PageLinks.SETTINGS_NEW_AGREEMENT));
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- AccountApi.getAgreements(
- "self",
- new ScreenLoadCallback<JsArray<AgreementInfo>>(this) {
- @Override
- public void preDisplay(JsArray<AgreementInfo> result) {
- agreements.display(Natives.asList(result));
- }
- });
- }
-
- private static class AgreementTable extends FancyFlexTable<ContributorAgreement> {
- AgreementTable() {
- table.setWidth("");
- table.setText(0, 1, Util.C.agreementName());
- table.setText(0, 2, Util.C.agreementDescription());
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- for (int c = 1; c < 3; c++) {
- fmt.addStyleName(0, c, Gerrit.RESOURCES.css().dataHeader());
- }
- }
-
- void display(List<AgreementInfo> result) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (AgreementInfo info : result) {
- addOne(info);
- }
- }
-
- void addOne(AgreementInfo info) {
- int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- String url = info.url();
- if (url != null && url.length() > 0) {
- Anchor a = new Anchor(info.name(), url);
- a.setTarget("_blank");
- table.setWidget(row, 1, a);
- } else {
- table.setText(row, 1, info.name());
- }
- table.setText(row, 2, info.description());
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- for (int c = 1; c < 3; c++) {
- fmt.addStyleName(row, c, Gerrit.RESOURCES.css().dataCell());
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java
deleted file mode 100644
index d5884f4c8d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2010 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.client.account;
-
-public class MyContactInformationScreen extends SettingsScreen {
- private ContactPanelShort panel;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- panel =
- new ContactPanelShort() {
- @Override
- void display() {
- MyContactInformationScreen.this.display();
- }
- };
- add(panel);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java
deleted file mode 100644
index a721441095..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyDiffPreferencesScreen.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2015 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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.diff.PreferencesBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-
-public class MyDiffPreferencesScreen extends SettingsScreen {
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- PreferencesBox pb = new PreferencesBox(null);
- pb.set(DiffPreferences.create(Gerrit.getDiffPreferences()));
- FlowPanel p = new FlowPanel();
- p.setStyleName(pb.getStyle().dialog());
- p.add(pb);
- add(p);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- display();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyEditPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyEditPreferencesScreen.java
deleted file mode 100644
index 424b5d57eb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyEditPreferencesScreen.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2015 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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.editor.EditPreferencesBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-
-public class MyEditPreferencesScreen extends SettingsScreen {
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- EditPreferencesBox pb = new EditPreferencesBox(null);
- pb.set(EditPreferences.create(Gerrit.getEditPreferences()));
- FlowPanel p = new FlowPanel();
- p.setStyleName(pb.getStyle().dialog());
- p.add(pb);
- add(p);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- display();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
deleted file mode 100644
index 1a0090a01f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright (C) 2015 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.client.account;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.GpgKeyInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import java.util.ArrayList;
-import java.util.List;
-
-public class MyGpgKeysScreen extends SettingsScreen {
- interface Binder extends UiBinder<HTMLPanel, MyGpgKeysScreen> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField(provided = true)
- GpgKeyTable keys;
-
- @UiField Button deleteKey;
- @UiField Button addKey;
-
- @UiField VerticalPanel addKeyBlock;
- @UiField NpTextArea keyText;
-
- @UiField VerticalPanel errorPanel;
- @UiField Label errorText;
-
- @UiField Button clearButton;
- @UiField Button addButton;
- @UiField Button closeButton;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- keys = new GpgKeyTable();
- add(uiBinder.createAndBindUi(this));
- keys.updateDeleteButton();
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- refreshKeys();
- }
-
- @UiHandler("deleteKey")
- void onDeleteKey(@SuppressWarnings("unused") ClickEvent e) {
- keys.deleteChecked();
- }
-
- @UiHandler("addKey")
- void onAddKey(@SuppressWarnings("unused") ClickEvent e) {
- showAddKeyBlock(true);
- }
-
- @UiHandler("clearButton")
- void onClearButton(@SuppressWarnings("unused") ClickEvent e) {
- keyText.setText("");
- keyText.setFocus(true);
- errorPanel.setVisible(false);
- }
-
- @UiHandler("closeButton")
- void onCloseButton(@SuppressWarnings("unused") ClickEvent e) {
- showAddKeyBlock(false);
- }
-
- @UiHandler("addButton")
- void onAddButton(@SuppressWarnings("unused") ClickEvent e) {
- doAddKey();
- }
-
- private void refreshKeys() {
- AccountApi.self()
- .view("gpgkeys")
- .get(
- NativeMap.copyKeysIntoChildren(
- "id",
- new GerritCallback<NativeMap<GpgKeyInfo>>() {
- @Override
- public void onSuccess(NativeMap<GpgKeyInfo> result) {
- List<GpgKeyInfo> list = Natives.asList(result.values());
- // TODO(dborowitz): Sort on something more meaningful, like
- // created date?
- list.sort(comparing(GpgKeyInfo::id));
- keys.clear();
- keyText.setText("");
- errorPanel.setVisible(false);
- addButton.setEnabled(true);
- if (!list.isEmpty()) {
- keys.setVisible(true);
- for (GpgKeyInfo k : list) {
- keys.addOneKey(k);
- }
- showKeyTable(true);
- showAddKeyBlock(false);
- } else {
- keys.setVisible(false);
- showAddKeyBlock(true);
- showKeyTable(false);
- }
-
- display();
- }
- }));
- }
-
- private void showAddKeyBlock(boolean show) {
- addKey.setVisible(!show);
- addKeyBlock.setVisible(show);
- }
-
- private void showKeyTable(boolean show) {
- keys.setVisible(show);
- deleteKey.setVisible(show);
- addKey.setVisible(show);
- }
-
- private void doAddKey() {
- if (keyText.getText().isEmpty()) {
- return;
- }
- addButton.setEnabled(false);
- keyText.setEnabled(false);
- AccountApi.addGpgKey(
- "self",
- keyText.getText(),
- new AsyncCallback<NativeMap<GpgKeyInfo>>() {
- @Override
- public void onSuccess(NativeMap<GpgKeyInfo> result) {
- keyText.setEnabled(true);
- refreshKeys();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- keyText.setEnabled(true);
- addButton.setEnabled(true);
- if (caught instanceof StatusCodeException) {
- StatusCodeException sce = (StatusCodeException) caught;
- if (sce.getStatusCode() == Response.SC_CONFLICT
- || sce.getStatusCode() == Response.SC_BAD_REQUEST) {
- errorText.setText(sce.getEncodedResponse());
- } else {
- errorText.setText(sce.getMessage());
- }
- } else {
- errorText.setText("Unexpected error saving key: " + caught.getMessage());
- }
- errorPanel.setVisible(true);
- }
- });
- }
-
- private class GpgKeyTable extends FancyFlexTable<GpgKeyInfo> {
- private final ValueChangeHandler<Boolean> updateDeleteHandler;
-
- GpgKeyTable() {
- table.setWidth("");
- table.setText(0, 1, Util.C.gpgKeyId());
- table.setText(0, 2, Util.C.gpgKeyFingerprint());
- table.setText(0, 3, Util.C.gpgKeyUserIds());
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
-
- updateDeleteHandler =
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- updateDeleteButton();
- }
- };
- }
-
- private void addOneKey(GpgKeyInfo k) {
- int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- CheckBox sel = new CheckBox();
- sel.addValueChangeHandler(updateDeleteHandler);
- table.setWidget(row, 0, sel);
- table.setWidget(row, 1, new CopyableLabel(k.id()));
- table.setText(row, 2, k.fingerprint());
-
- VerticalPanel userIds = new VerticalPanel();
- for (int i = 0; i < k.userIds().length(); i++) {
- userIds.add(new InlineLabel(k.userIds().get(i)));
- }
- table.setWidget(row, 3, userIds);
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, k);
- }
-
- private void updateDeleteButton() {
- for (int row = 1; row < table.getRowCount(); row++) {
- if (isChecked(row)) {
- deleteKey.setEnabled(true);
- return;
- }
- }
- deleteKey.setEnabled(false);
- }
-
- private void deleteChecked() {
- deleteKey.setEnabled(false);
- List<String> toDelete = new ArrayList<>(table.getRowCount());
- for (int row = 1; row < table.getRowCount(); row++) {
- if (isChecked(row)) {
- toDelete.add(getRowItem(row).fingerprint());
- }
- }
- AccountApi.deleteGpgKeys(
- "self",
- toDelete,
- new GerritCallback<NativeMap<GpgKeyInfo>>() {
- @Override
- public void onSuccess(NativeMap<GpgKeyInfo> result) {
- refreshKeys();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- deleteKey.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private boolean isChecked(int row) {
- return ((CheckBox) table.getWidget(row, 0)).getValue();
- }
-
- private void clear() {
- while (table.getRowCount() > 1) {
- table.removeRow(1);
- }
- for (int i = table.getRowCount() - 1; i >= 1; i++) {
- table.removeRow(i);
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.ui.xml
deleted file mode 100644
index dc7373606b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.ui.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2015 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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:expui='urn:import:com.google.gwtexpui.globalkey.client'>
- <ui:with field='res' type='com.google.gerrit.client.GerritResources'/>
-
- <ui:style gss='false'>
- .errorHeader {
- font-weight: bold;
- }
- .errorText {
- white-space: pre-wrap;
- padding-bottom: 6px;
- }
- </ui:style>
-
- <g:HTMLPanel>
- <g:Widget ui:field='keys' addStyleNames='{res.css.sshKeyTable}'/>
- <g:FlowPanel>
- <g:Button ui:field='deleteKey'>
- <div><ui:msg>Delete</ui:msg></div>
- </g:Button>
- <g:Button ui:field='addKey'>
- <div><ui:msg>Add Key ...</ui:msg></div>
- </g:Button>
- </g:FlowPanel>
- <g:VerticalPanel ui:field='addKeyBlock'
- styleName='{res.css.addSshKeyPanel}'
- visible='false'>
- <g:Label>Add GPG Public Key</g:Label>
- <g:DisclosurePanel>
- <g:header>How to generate a GPG key</g:header>
- <g:HTMLPanel>
- <ol>
- <li>
- From the Terminal or Git Bash, run <em>gpg --gen-key</em> and
- follow the prompts to create the key.
- </li>
- <li>
- Use the default kind. Use the default (or higher) keysize. Choose
- any value for your expiration.
- </li>
- <li>
- The user ID should contain one of your registered email addresses.
- </li>
- <li>Setting a passphrase is strongly recommended.</li>
- <li>Note the ID of your new key.</li>
- <li>
- To export your key, run the following and paste the full output
- into the text box:
- <br/>
- <code>gpg --export -a &lt;key ID&gt;</code>
- </li>
- </ol>
- </g:HTMLPanel>
- </g:DisclosurePanel>
- <expui:NpTextArea
- visibleLines='12'
- characterWidth='80'
- spellCheck='false'
- ui:field='keyText'/>
- <g:VerticalPanel ui:field='errorPanel' visible='false'>
- <g:Label styleName='{style.errorHeader}'>Error adding GPG key:</g:Label>
- <g:Label styleName='{style.errorText}' ui:field='errorText'/>
- </g:VerticalPanel>
- <g:FlowPanel>
- <g:Button ui:field='clearButton'>
- <div><ui:msg>Clear</ui:msg></div>
- </g:Button>
- <g:Button ui:field='addButton'>
- <div><ui:msg>Add</ui:msg></div>
- </g:Button>
- <g:Button ui:field='closeButton'>
- <div><ui:msg>Close</ui:msg></div>
- </g:Button>
- </g:FlowPanel>
- </g:VerticalPanel>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java
deleted file mode 100644
index e9112de1bb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsScreen.java
+++ /dev/null
@@ -1,43 +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.client.account;
-
-import com.google.gerrit.client.admin.GroupTable;
-import com.google.gerrit.client.groups.GroupList;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-
-public class MyGroupsScreen extends SettingsScreen {
- private GroupTable groups;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- groups = new GroupTable();
- add(groups);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- GroupList.my(
- new ScreenLoadCallback<GroupList>(this) {
- @Override
- protected void preDisplay(GroupList result) {
- groups.display(result);
- groups.finishDisplay();
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
deleted file mode 100644
index 730d98e185..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
+++ /dev/null
@@ -1,224 +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.client.account;
-
-import static java.util.Comparator.naturalOrder;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import java.util.HashSet;
-import java.util.List;
-
-public class MyIdentitiesScreen extends SettingsScreen {
- private IdTable identites;
- private Button deleteIdentity;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- identites = new IdTable();
- add(identites);
-
- deleteIdentity = new Button(Util.C.buttonDeleteIdentity());
- deleteIdentity.setEnabled(false);
- deleteIdentity.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- identites.deleteChecked();
- }
- });
- add(deleteIdentity);
-
- if (Gerrit.info().auth().isOpenId() || Gerrit.info().auth().isOAuth()) {
- Button linkIdentity = new Button(Util.C.buttonLinkIdentity());
- linkIdentity.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- Location.assign(Gerrit.loginRedirect(History.getToken()) + "?link");
- }
- });
- add(linkIdentity);
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- AccountApi.getExternalIds(
- new GerritCallback<JsArray<ExternalIdInfo>>() {
- @Override
- public void onSuccess(JsArray<ExternalIdInfo> results) {
- identites.display(results);
- display();
- }
- });
- }
-
- private class IdTable extends FancyFlexTable<ExternalIdInfo> {
- private ValueChangeHandler<Boolean> updateDeleteHandler;
-
- IdTable() {
- table.setWidth("");
- table.setText(0, 2, Util.C.webIdStatus());
- table.setText(0, 3, Util.C.webIdEmail());
- table.setText(0, 4, Util.C.webIdIdentity());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
-
- updateDeleteHandler =
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- updateDeleteButton();
- }
- };
- }
-
- void deleteChecked() {
- final HashSet<String> keys = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final ExternalIdInfo k = getRowItem(row);
- if (k == null) {
- continue;
- }
- final CheckBox cb = (CheckBox) table.getWidget(row, 1);
- if (cb == null) {
- continue;
- }
- if (cb.getValue()) {
- keys.add(k.identity());
- }
- }
- if (keys.isEmpty()) {
- updateDeleteButton();
- } else {
- deleteIdentity.setEnabled(false);
- AccountApi.deleteExternalIds(
- keys,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- for (int row = 1; row < table.getRowCount(); ) {
- final ExternalIdInfo k = getRowItem(row);
- if (k != null && keys.contains(k.identity())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- updateDeleteButton();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- updateDeleteButton();
- super.onFailure(caught);
- }
- });
- }
- }
-
- void updateDeleteButton() {
- int off = 0;
- boolean on = false;
- for (int row = 1; row < table.getRowCount(); row++) {
- if (table.getWidget(row, 1) == null) {
- off++;
- } else {
- CheckBox sel = (CheckBox) table.getWidget(row, 1);
- if (sel.getValue()) {
- on = true;
- break;
- }
- }
- }
- deleteIdentity.setVisible(off < table.getRowCount() - 1);
- deleteIdentity.setEnabled(on);
- }
-
- void display(JsArray<ExternalIdInfo> results) {
- List<ExternalIdInfo> idList = Natives.asList(results);
- idList.sort(naturalOrder());
-
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (ExternalIdInfo k : idList) {
- addOneId(k);
- }
- updateDeleteButton();
- }
-
- void addOneId(ExternalIdInfo k) {
- if (k.isUsername()) {
- // Don't display the username as an identity here.
- return;
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- if (k.canDelete()) {
- final CheckBox sel = new CheckBox();
- sel.addValueChangeHandler(updateDeleteHandler);
- table.setWidget(row, 1, sel);
- } else {
- table.setText(row, 1, "");
- }
- if (k.isTrusted()) {
- table.setText(row, 2, "");
- } else {
- table.setText(row, 2, Util.C.untrustedProvider());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().identityUntrustedExternalId());
- }
- if (k.emailAddress() != null && k.emailAddress().length() > 0) {
- table.setText(row, 3, k.emailAddress());
- } else {
- table.setText(row, 3, "");
- }
- table.setText(row, 4, k.describe());
-
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, k);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java
deleted file mode 100644
index 173dba6291..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java
+++ /dev/null
@@ -1,198 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.info.OAuthTokenInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import java.util.Date;
-
-public class MyOAuthTokenScreen extends SettingsScreen {
- private CopyableLabel tokenLabel;
- private Label expiresLabel;
- private Label expiredNote;
- private CopyableLabel netrcValue;
- private CopyableLabel cookieValue;
- private FlowPanel flow;
- private Grid grid;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- tokenLabel = new CopyableLabel("");
- tokenLabel.addStyleName(Gerrit.RESOURCES.css().oauthToken());
-
- expiresLabel = new Label("");
- expiresLabel.addStyleName(Gerrit.RESOURCES.css().oauthExpires());
-
- grid = new Grid(2, 2);
- grid.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- grid.addStyleName(Gerrit.RESOURCES.css().oauthInfoBlock());
- add(grid);
-
- expiredNote = new Label(Util.C.labelOAuthExpired());
- expiredNote.setVisible(false);
- add(expiredNote);
-
- row(grid, 0, Util.C.labelOAuthToken(), tokenLabel);
- row(grid, 1, Util.C.labelOAuthExpires(), expiresLabel);
-
- CellFormatter fmt = grid.getCellFormatter();
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().bottomheader());
-
- flow = new FlowPanel();
- flow.setStyleName(Gerrit.RESOURCES.css().oauthPanel());
- add(flow);
-
- Label netrcLabel = new Label(Util.C.labelOAuthNetRCEntry());
- netrcLabel.setStyleName(Gerrit.RESOURCES.css().oauthPanelNetRCHeading());
- flow.add(netrcLabel);
- netrcValue = new CopyableLabel("");
- netrcValue.setStyleName(Gerrit.RESOURCES.css().oauthPanelNetRCEntry());
- flow.add(netrcValue);
-
- Label cookieLabel = new Label(Util.C.labelOAuthGitCookie());
- cookieLabel.setStyleName(Gerrit.RESOURCES.css().oauthPanelCookieHeading());
- flow.add(cookieLabel);
- cookieValue = new CopyableLabel("");
- cookieValue.setStyleName(Gerrit.RESOURCES.css().oauthPanelCookieEntry());
- flow.add(cookieValue);
- }
-
- private void row(Grid grid, int row, String name, Widget field) {
- final CellFormatter fmt = grid.getCellFormatter();
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- grid.setText(row, 1, name);
- grid.setWidget(row, 0, field);
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().header());
- } else {
- grid.setText(row, 0, name);
- grid.setWidget(row, 1, field);
- fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().header());
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- AccountApi.self()
- .view("preferences")
- .get(
- new ScreenLoadCallback<GeneralPreferences>(this) {
- @Override
- protected void preDisplay(GeneralPreferences prefs) {
- display(prefs);
- }
- });
- }
-
- private void display(GeneralPreferences prefs) {
- AccountApi.self()
- .view("oauthtoken")
- .get(
- new GerritCallback<OAuthTokenInfo>() {
- @Override
- public void onSuccess(OAuthTokenInfo tokenInfo) {
- tokenLabel.setText(tokenInfo.accessToken());
- expiresLabel.setText(getExpiresAt(tokenInfo, prefs));
- netrcValue.setText(getNetRC(tokenInfo));
- cookieValue.setText(getCookie(tokenInfo));
- flow.setVisible(true);
- expiredNote.setVisible(false);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (isNoSuchEntity(caught) || isSigninFailure(caught)) {
- tokenLabel.setText("");
- expiresLabel.setText("");
- netrcValue.setText("");
- cookieValue.setText("");
- flow.setVisible(false);
- expiredNote.setVisible(true);
- } else {
- showFailure(caught);
- }
- }
- });
- }
-
- private static long getExpiresAt(OAuthTokenInfo tokenInfo) {
- if (tokenInfo.expiresAt() == null) {
- return Long.MAX_VALUE;
- }
- long expiresAt;
- try {
- expiresAt = Long.parseLong(tokenInfo.expiresAt());
- } catch (NumberFormatException e) {
- return Long.MAX_VALUE;
- }
- return expiresAt;
- }
-
- private static long getExpiresAtSeconds(OAuthTokenInfo tokenInfo) {
- return getExpiresAt(tokenInfo) / 1000L;
- }
-
- private static String getExpiresAt(OAuthTokenInfo tokenInfo, GeneralPreferences prefs) {
- long expiresAt = getExpiresAt(tokenInfo);
- if (expiresAt == Long.MAX_VALUE) {
- return "";
- }
- String dateFormat = prefs.dateFormat().getLongFormat();
- String timeFormat = prefs.timeFormat().getFormat();
- DateTimeFormat formatter = DateTimeFormat.getFormat(dateFormat + " " + timeFormat);
- return formatter.format(new Date(expiresAt));
- }
-
- private static String getNetRC(OAuthTokenInfo accessTokenInfo) {
- StringBuilder sb = new StringBuilder();
- sb.append("machine ");
- sb.append(accessTokenInfo.resourceHost());
- sb.append(" login ");
- sb.append(accessTokenInfo.username());
- sb.append(" password ");
- sb.append(accessTokenInfo.accessToken());
- return sb.toString();
- }
-
- private static String getCookie(OAuthTokenInfo accessTokenInfo) {
- StringBuilder sb = new StringBuilder();
- sb.append(accessTokenInfo.resourceHost());
- sb.append("\tFALSE\t/\tTRUE\t");
- sb.append(getExpiresAtSeconds(accessTokenInfo));
- sb.append("\tgit-");
- sb.append(accessTokenInfo.username());
- sb.append('\t');
- sb.append(accessTokenInfo.accessToken());
- if (accessTokenInfo.providerId() != null) {
- sb.append('@').append(accessTokenInfo.providerId());
- }
- return sb.toString();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPasswordScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPasswordScreen.java
deleted file mode 100644
index 5dd7530e44..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPasswordScreen.java
+++ /dev/null
@@ -1,161 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.api.ExtensionPanel;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-
-public class MyPasswordScreen extends SettingsScreen {
- private CopyableLabel password;
- private Button generatePassword;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- String url = Gerrit.info().auth().httpPasswordUrl();
- if (url != null) {
- Anchor link = new Anchor();
- link.setText(Util.C.linkObtainPassword());
- link.setHref(url);
- link.setTarget("_blank");
- add(link);
- return;
- }
-
- password = new CopyableLabel(Util.C.revokePassword());
- password.addStyleName(Gerrit.RESOURCES.css().accountPassword());
-
- generatePassword = new Button(Util.C.buttonGeneratePassword());
- generatePassword.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doGeneratePassword();
- }
- });
-
- final Grid userInfo = new Grid(2, 2);
- final CellFormatter fmt = userInfo.getCellFormatter();
- userInfo.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- userInfo.addStyleName(Gerrit.RESOURCES.css().accountInfoBlock());
- add(userInfo);
-
- row(userInfo, 0, Util.C.userName(), new UsernameField());
- row(userInfo, 1, Util.C.password(), password);
-
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().bottomheader());
-
- final FlowPanel buttons = new FlowPanel();
- buttons.add(generatePassword);
- add(buttons);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- ExtensionPanel extensionPanel =
- createExtensionPoint(GerritUiExtensionPoint.PASSWORD_SCREEN_BOTTOM);
- extensionPanel.addStyleName(Gerrit.RESOURCES.css().extensionPanel());
- add(extensionPanel);
-
- if (password == null) {
- display();
- return;
- }
-
- enableUI(false);
- AccountApi.getUsername(
- "self",
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString user) {
- Gerrit.getUserAccount().username(user.asString());
- enableUI(true);
- display();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (RestApi.isNotFound(caught)) {
- Gerrit.getUserAccount().username(null);
- display();
- } else {
- super.onFailure(caught);
- }
- }
- });
- }
-
- private void display(String pass) {
- password.setText(pass != null ? pass : "");
- password.setVisible(pass != null);
- enableUI(true);
- }
-
- private void row(Grid info, int row, String name, Widget field) {
- final CellFormatter fmt = info.getCellFormatter();
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- info.setText(row, 1, name);
- info.setWidget(row, 0, field);
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().header());
- } else {
- info.setText(row, 0, name);
- info.setWidget(row, 1, field);
- fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().header());
- }
- }
-
- private void doGeneratePassword() {
- if (Gerrit.getUserAccount().username() != null) {
- enableUI(false);
- AccountApi.generateHttpPassword(
- "self",
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString newPassword) {
- display(newPassword.asString());
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableUI(true);
- }
- });
- }
- }
-
- private void enableUI(boolean on) {
- on &= Gerrit.getUserAccount().username() != null;
-
- generatePassword.setEnabled(on);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
deleted file mode 100644
index afb87188d4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyPreferencesScreen.java
+++ /dev/null
@@ -1,515 +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.client.account;
-
-import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.DEFAULT_PAGESIZE;
-import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.PAGESIZE_CHOICES;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.StringListPanel;
-import com.google.gerrit.client.api.ExtensionPanel;
-import com.google.gerrit.client.config.ConfigServerApi;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.info.TopMenuItem;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.ReviewCategoryStrategy;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwtexpui.user.client.UserAgent;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-public class MyPreferencesScreen extends SettingsScreen {
- private CheckBox showSiteHeader;
- private CheckBox useFlashClipboard;
- private CheckBox highlightAssigneeInChangeTable;
- private CheckBox relativeDateInChangeTable;
- private CheckBox sizeBarInChangeTable;
- private CheckBox legacycidInChangeTable;
- private CheckBox muteCommonPathPrefixes;
- private CheckBox signedOffBy;
- private CheckBox publishCommentsOnPush;
- private CheckBox workInProgressByDefault;
- private ListBox maximumPageSize;
- private ListBox dateFormat;
- private ListBox timeFormat;
- private ListBox reviewCategoryStrategy;
- private ListBox diffView;
- private ListBox emailStrategy;
- private ListBox emailFormat;
- private ListBox defaultBaseForMerges;
- private StringListPanel myMenus;
- private Button save;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- showSiteHeader = new CheckBox(Util.C.showSiteHeader());
- useFlashClipboard = new CheckBox(Util.C.useFlashClipboard());
- maximumPageSize = new ListBox();
- for (int v : PAGESIZE_CHOICES) {
- maximumPageSize.addItem(Util.M.rowsPerPage(v), String.valueOf(v));
- }
-
- reviewCategoryStrategy = new ListBox();
- reviewCategoryStrategy.addItem(
- Util.C.messageShowInReviewCategoryNone(),
- GeneralPreferencesInfo.ReviewCategoryStrategy.NONE.name());
- reviewCategoryStrategy.addItem(
- Util.C.messageShowInReviewCategoryName(),
- GeneralPreferencesInfo.ReviewCategoryStrategy.NAME.name());
- reviewCategoryStrategy.addItem(
- Util.C.messageShowInReviewCategoryEmail(),
- GeneralPreferencesInfo.ReviewCategoryStrategy.EMAIL.name());
- reviewCategoryStrategy.addItem(
- Util.C.messageShowInReviewCategoryUsername(),
- GeneralPreferencesInfo.ReviewCategoryStrategy.USERNAME.name());
- reviewCategoryStrategy.addItem(
- Util.C.messageShowInReviewCategoryAbbrev(),
- GeneralPreferencesInfo.ReviewCategoryStrategy.ABBREV.name());
-
- emailStrategy = new ListBox();
- emailStrategy.addItem(
- Util.C.messageCCMeOnMyComments(),
- GeneralPreferencesInfo.EmailStrategy.CC_ON_OWN_COMMENTS.name());
- emailStrategy.addItem(
- Util.C.messageEnabled(), GeneralPreferencesInfo.EmailStrategy.ENABLED.name());
- emailStrategy.addItem(
- Util.C.messageDisabled(), GeneralPreferencesInfo.EmailStrategy.DISABLED.name());
-
- emailFormat = new ListBox();
- emailFormat.addItem(
- Util.C.messagePlaintextOnly(), GeneralPreferencesInfo.EmailFormat.PLAINTEXT.name());
- emailFormat.addItem(
- Util.C.messageHtmlPlaintext(), GeneralPreferencesInfo.EmailFormat.HTML_PLAINTEXT.name());
-
- defaultBaseForMerges = new ListBox();
- defaultBaseForMerges.addItem(
- Util.C.autoMerge(), GeneralPreferencesInfo.DefaultBase.AUTO_MERGE.name());
- defaultBaseForMerges.addItem(
- Util.C.firstParent(), GeneralPreferencesInfo.DefaultBase.FIRST_PARENT.name());
-
- diffView = new ListBox();
- diffView.addItem(
- com.google.gerrit.client.changes.Util.C.sideBySide(),
- GeneralPreferencesInfo.DiffView.SIDE_BY_SIDE.name());
- diffView.addItem(
- com.google.gerrit.client.changes.Util.C.unifiedDiff(),
- GeneralPreferencesInfo.DiffView.UNIFIED_DIFF.name());
-
- Date now = new Date();
- dateFormat = new ListBox();
- for (GeneralPreferencesInfo.DateFormat fmt : GeneralPreferencesInfo.DateFormat.values()) {
- StringBuilder r = new StringBuilder();
- r.append(DateTimeFormat.getFormat(fmt.getShortFormat()).format(now));
- r.append(" ; ");
- r.append(DateTimeFormat.getFormat(fmt.getLongFormat()).format(now));
- dateFormat.addItem(r.toString(), fmt.name());
- }
-
- timeFormat = new ListBox();
- for (GeneralPreferencesInfo.TimeFormat fmt : GeneralPreferencesInfo.TimeFormat.values()) {
- StringBuilder r = new StringBuilder();
- r.append(DateTimeFormat.getFormat(fmt.getFormat()).format(now));
- timeFormat.addItem(r.toString(), fmt.name());
- }
-
- FlowPanel dateTimePanel = new FlowPanel();
-
- final int labelIdx;
- final int fieldIdx;
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- dateTimePanel.add(timeFormat);
- dateTimePanel.add(dateFormat);
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- dateTimePanel.add(dateFormat);
- dateTimePanel.add(timeFormat);
- }
- highlightAssigneeInChangeTable = new CheckBox(Util.C.highlightAssigneeInChangeTable());
- relativeDateInChangeTable = new CheckBox(Util.C.showRelativeDateInChangeTable());
- sizeBarInChangeTable = new CheckBox(Util.C.showSizeBarInChangeTable());
- legacycidInChangeTable = new CheckBox(Util.C.showLegacycidInChangeTable());
- muteCommonPathPrefixes = new CheckBox(Util.C.muteCommonPathPrefixes());
- signedOffBy = new CheckBox(Util.C.signedOffBy());
- publishCommentsOnPush = new CheckBox(Util.C.publishCommentsOnPush());
- workInProgressByDefault = new CheckBox(Util.C.workInProgressByDefault());
-
- boolean flashClippy = !UserAgent.hasJavaScriptClipboard() && UserAgent.Flash.isInstalled();
- final Grid formGrid = new Grid(16 + (flashClippy ? 1 : 0), 2);
-
- int row = 0;
-
- formGrid.setText(row, labelIdx, Util.C.reviewCategoryLabel());
- formGrid.setWidget(row, fieldIdx, reviewCategoryStrategy);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.maximumPageSizeFieldLabel());
- formGrid.setWidget(row, fieldIdx, maximumPageSize);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.dateFormatLabel());
- formGrid.setWidget(row, fieldIdx, dateTimePanel);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.emailFieldLabel());
- formGrid.setWidget(row, fieldIdx, emailStrategy);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.emailFormatFieldLabel());
- formGrid.setWidget(row, fieldIdx, emailFormat);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.defaultBaseForMerges());
- formGrid.setWidget(row, fieldIdx, defaultBaseForMerges);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.diffViewLabel());
- formGrid.setWidget(row, fieldIdx, diffView);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, showSiteHeader);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, highlightAssigneeInChangeTable);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, relativeDateInChangeTable);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, sizeBarInChangeTable);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, legacycidInChangeTable);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, muteCommonPathPrefixes);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, signedOffBy);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, publishCommentsOnPush);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, workInProgressByDefault);
- row++;
-
- if (flashClippy) {
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, useFlashClipboard);
- }
-
- add(formGrid);
-
- save = new Button(Util.C.buttonSaveChanges());
- save.setEnabled(false);
- save.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSave();
- }
- });
-
- myMenus = new MyMenuPanel(save);
- add(myMenus);
-
- add(save);
-
- final OnEditEnabler e = new OnEditEnabler(save);
- e.listenTo(showSiteHeader);
- e.listenTo(useFlashClipboard);
- e.listenTo(maximumPageSize);
- e.listenTo(dateFormat);
- e.listenTo(timeFormat);
- e.listenTo(highlightAssigneeInChangeTable);
- e.listenTo(relativeDateInChangeTable);
- e.listenTo(sizeBarInChangeTable);
- e.listenTo(legacycidInChangeTable);
- e.listenTo(muteCommonPathPrefixes);
- e.listenTo(signedOffBy);
- e.listenTo(publishCommentsOnPush);
- e.listenTo(workInProgressByDefault);
- e.listenTo(diffView);
- e.listenTo(reviewCategoryStrategy);
- e.listenTo(emailStrategy);
- e.listenTo(emailFormat);
- e.listenTo(defaultBaseForMerges);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- ExtensionPanel extensionPanel =
- createExtensionPoint(GerritUiExtensionPoint.PREFERENCES_SCREEN_BOTTOM);
- extensionPanel.addStyleName(Gerrit.RESOURCES.css().extensionPanel());
- add(extensionPanel);
-
- AccountApi.self()
- .view("preferences")
- .get(
- new ScreenLoadCallback<GeneralPreferences>(this) {
- @Override
- public void preDisplay(GeneralPreferences prefs) {
- display(prefs);
- }
- });
- }
-
- private void enable(boolean on) {
- showSiteHeader.setEnabled(on);
- useFlashClipboard.setEnabled(on);
- maximumPageSize.setEnabled(on);
- dateFormat.setEnabled(on);
- timeFormat.setEnabled(on);
- highlightAssigneeInChangeTable.setEnabled(on);
- relativeDateInChangeTable.setEnabled(on);
- sizeBarInChangeTable.setEnabled(on);
- legacycidInChangeTable.setEnabled(on);
- muteCommonPathPrefixes.setEnabled(on);
- signedOffBy.setEnabled(on);
- publishCommentsOnPush.setEnabled(on);
- workInProgressByDefault.setEnabled(on);
- reviewCategoryStrategy.setEnabled(on);
- diffView.setEnabled(on);
- emailStrategy.setEnabled(on);
- emailFormat.setEnabled(on);
- defaultBaseForMerges.setEnabled(on);
- }
-
- private void display(GeneralPreferences p) {
- showSiteHeader.setValue(p.showSiteHeader());
- useFlashClipboard.setValue(p.useFlashClipboard());
- setListBox(maximumPageSize, DEFAULT_PAGESIZE, p.changesPerPage());
- setListBox(
- dateFormat,
- GeneralPreferencesInfo.DateFormat.STD, //
- p.dateFormat());
- setListBox(
- timeFormat,
- GeneralPreferencesInfo.TimeFormat.HHMM_12, //
- p.timeFormat());
- highlightAssigneeInChangeTable.setValue(p.highlightAssigneeInChangeTable());
- relativeDateInChangeTable.setValue(p.relativeDateInChangeTable());
- sizeBarInChangeTable.setValue(p.sizeBarInChangeTable());
- legacycidInChangeTable.setValue(p.legacycidInChangeTable());
- muteCommonPathPrefixes.setValue(p.muteCommonPathPrefixes());
- signedOffBy.setValue(p.signedOffBy());
- publishCommentsOnPush.setValue(p.publishCommentsOnPush());
- workInProgressByDefault.setValue(p.workInProgressByDefault());
- setListBox(
- reviewCategoryStrategy,
- GeneralPreferencesInfo.ReviewCategoryStrategy.NONE,
- p.reviewCategoryStrategy());
- setListBox(diffView, GeneralPreferencesInfo.DiffView.SIDE_BY_SIDE, p.diffView());
- setListBox(emailStrategy, GeneralPreferencesInfo.EmailStrategy.ENABLED, p.emailStrategy());
- setListBox(emailFormat, GeneralPreferencesInfo.EmailFormat.HTML_PLAINTEXT, p.emailFormat());
- setListBox(
- defaultBaseForMerges,
- GeneralPreferencesInfo.DefaultBase.FIRST_PARENT,
- p.defaultBaseForMerges());
- display(p.my());
- }
-
- private void display(JsArray<TopMenuItem> items) {
- List<List<String>> values = new ArrayList<>();
- for (TopMenuItem item : Natives.asList(items)) {
- values.add(Arrays.asList(item.getName(), item.getUrl()));
- }
- myMenus.display(values);
- }
-
- private void setListBox(ListBox f, int defaultValue, int currentValue) {
- setListBox(f, String.valueOf(defaultValue), String.valueOf(currentValue));
- }
-
- private <T extends Enum<?>> void setListBox(final ListBox f, T defaultValue, T currentValue) {
- setListBox(
- f,
- defaultValue != null ? defaultValue.name() : "",
- currentValue != null ? currentValue.name() : "");
- }
-
- private void setListBox(ListBox f, String defaultValue, String currentValue) {
- final int n = f.getItemCount();
- for (int i = 0; i < n; i++) {
- if (f.getValue(i).equals(currentValue)) {
- f.setSelectedIndex(i);
- return;
- }
- }
- if (!currentValue.equals(defaultValue)) {
- setListBox(f, defaultValue, defaultValue);
- }
- }
-
- private int getListBox(ListBox f, int defaultValue) {
- final int idx = f.getSelectedIndex();
- if (0 <= idx) {
- return Short.parseShort(f.getValue(idx));
- }
- return defaultValue;
- }
-
- private <T extends Enum<?>> T getListBox(ListBox f, T defaultValue, T[] all) {
- final int idx = f.getSelectedIndex();
- if (0 <= idx) {
- String v = f.getValue(idx);
- if ("".equals(v)) {
- return defaultValue;
- }
- for (T t : all) {
- if (t.name().equals(v)) {
- return t;
- }
- }
- }
- return defaultValue;
- }
-
- private void doSave() {
- GeneralPreferences p = GeneralPreferences.create();
- p.showSiteHeader(showSiteHeader.getValue());
- p.useFlashClipboard(useFlashClipboard.getValue());
- p.changesPerPage(getListBox(maximumPageSize, DEFAULT_PAGESIZE));
- p.dateFormat(
- getListBox(
- dateFormat,
- GeneralPreferencesInfo.DateFormat.STD,
- GeneralPreferencesInfo.DateFormat.values()));
- p.timeFormat(
- getListBox(
- timeFormat,
- GeneralPreferencesInfo.TimeFormat.HHMM_12,
- GeneralPreferencesInfo.TimeFormat.values()));
- p.highlightAssigneeInChangeTable(highlightAssigneeInChangeTable.getValue());
- p.relativeDateInChangeTable(relativeDateInChangeTable.getValue());
- p.sizeBarInChangeTable(sizeBarInChangeTable.getValue());
- p.legacycidInChangeTable(legacycidInChangeTable.getValue());
- p.muteCommonPathPrefixes(muteCommonPathPrefixes.getValue());
- p.signedOffBy(signedOffBy.getValue());
- p.publishCommentsOnPush(publishCommentsOnPush.getValue());
- p.workInProgressByDefault(workInProgressByDefault.getValue());
- p.reviewCategoryStrategy(
- getListBox(
- reviewCategoryStrategy, ReviewCategoryStrategy.NONE, ReviewCategoryStrategy.values()));
- p.diffView(
- getListBox(
- diffView,
- GeneralPreferencesInfo.DiffView.SIDE_BY_SIDE,
- GeneralPreferencesInfo.DiffView.values()));
-
- p.emailStrategy(
- getListBox(
- emailStrategy,
- GeneralPreferencesInfo.EmailStrategy.ENABLED,
- GeneralPreferencesInfo.EmailStrategy.values()));
-
- p.emailFormat(
- getListBox(
- emailFormat,
- GeneralPreferencesInfo.EmailFormat.HTML_PLAINTEXT,
- GeneralPreferencesInfo.EmailFormat.values()));
-
- p.defaultBaseForMerges(
- getListBox(
- defaultBaseForMerges,
- GeneralPreferencesInfo.DefaultBase.FIRST_PARENT,
- GeneralPreferencesInfo.DefaultBase.values()));
-
- List<TopMenuItem> items = new ArrayList<>();
- for (List<String> v : myMenus.getValues()) {
- items.add(TopMenuItem.create(v.get(0), v.get(1)));
- }
- p.setMyMenus(items);
-
- enable(false);
- save.setEnabled(false);
-
- AccountApi.self()
- .view("preferences")
- .put(
- p,
- new GerritCallback<GeneralPreferences>() {
- @Override
- public void onSuccess(GeneralPreferences prefs) {
- Gerrit.setUserPreferences(prefs);
- enable(true);
- display(prefs);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enable(true);
- save.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private class MyMenuPanel extends StringListPanel {
- MyMenuPanel(Button save) {
- super(Util.C.myMenu(), Arrays.asList(Util.C.myMenuName(), Util.C.myMenuUrl()), save, false);
-
- setInfo(Util.C.myMenuInfo());
-
- Button resetButton = new Button(Util.C.myMenuReset());
- resetButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- ConfigServerApi.defaultPreferences(
- new GerritCallback<GeneralPreferences>() {
- @Override
- public void onSuccess(GeneralPreferences p) {
- MyPreferencesScreen.this.display(p.my());
- widget.setEnabled(true);
- }
- });
- }
- });
- buttonPanel.add(resetButton);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java
deleted file mode 100644
index 02759482a7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyProfileScreen.java
+++ /dev/null
@@ -1,127 +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.client.account;
-
-import static com.google.gerrit.client.FormatUtil.mediumFormat;
-
-import com.google.gerrit.client.AvatarImage;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-
-public class MyProfileScreen extends SettingsScreen {
- private AvatarImage avatar;
- private Anchor changeAvatar;
- private int labelIdx;
- private int fieldIdx;
- private Grid info;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- HorizontalPanel h = new HorizontalPanel();
- add(h);
-
- if (Gerrit.info().plugin().hasAvatars()) {
- VerticalPanel v = new VerticalPanel();
- v.addStyleName(Gerrit.RESOURCES.css().avatarInfoPanel());
- h.add(v);
- avatar = new AvatarImage();
- v.add(avatar);
- changeAvatar = new Anchor(Util.C.changeAvatar(), "", "_blank");
- changeAvatar.setVisible(false);
- v.add(changeAvatar);
- }
-
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- }
-
- info = new Grid((Gerrit.info().auth().siteHasUsernames() ? 1 : 0) + 4, 2);
- info.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- info.addStyleName(Gerrit.RESOURCES.css().accountInfoBlock());
- h.add(info);
-
- int row = 0;
- if (Gerrit.info().auth().siteHasUsernames()) {
- infoRow(row++, Util.C.userName());
- }
- infoRow(row++, Util.C.fullName());
- infoRow(row++, Util.C.preferredEmail());
- infoRow(row++, Util.C.registeredOn());
- infoRow(row++, Util.C.accountId());
-
- final CellFormatter fmt = info.getCellFormatter();
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(row - 1, 0, Gerrit.RESOURCES.css().bottomheader());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- add(createExtensionPoint(GerritUiExtensionPoint.PROFILE_SCREEN_BOTTOM));
- display(Gerrit.getUserAccount());
- display();
- }
-
- private void infoRow(int row, String name) {
- info.setText(row, labelIdx, name);
- info.getCellFormatter().addStyleName(row, 0, Gerrit.RESOURCES.css().header());
- }
-
- void display(AccountInfo account) {
- if (Gerrit.info().plugin().hasAvatars()) {
- avatar.setAccount(account, 93, false);
- new RestApi("/accounts/")
- .id("self")
- .view("avatar.change.url")
- .get(
- new AsyncCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString changeUrl) {
- changeAvatar.setHref(changeUrl.asString());
- changeAvatar.setVisible(true);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- int row = 0;
- if (Gerrit.info().auth().siteHasUsernames()) {
- info.setWidget(row++, fieldIdx, new UsernameField());
- }
- info.setText(row++, fieldIdx, account.name());
- info.setText(row++, fieldIdx, account.email());
- info.setText(row++, fieldIdx, mediumFormat(account.registeredOn()));
- info.setText(row, fieldIdx, Integer.toString(account._accountId()));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MySshKeysScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MySshKeysScreen.java
deleted file mode 100644
index 6ba63aa044..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MySshKeysScreen.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2010 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.client.account;
-
-public class MySshKeysScreen extends SettingsScreen {
- private SshPanel panel;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- panel =
- new SshPanel() {
- @Override
- void display() {
- MySshKeysScreen.this.display();
- }
- };
- add(panel);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchedProjectsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchedProjectsScreen.java
deleted file mode 100644
index f29b573b95..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchedProjectsScreen.java
+++ /dev/null
@@ -1,228 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.HintTextBox;
-import com.google.gerrit.client.ui.ProjectListPopup;
-import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-
-public class MyWatchedProjectsScreen extends SettingsScreen {
- private Button addNew;
- private RemoteSuggestBox nameBox;
- private HintTextBox filterTxt;
- private MyWatchesTable watchesTab;
- private Button browse;
- private Button delSel;
- private Grid grid;
- private ProjectListPopup projectsPopup;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- createWidgets();
-
- /* top table */
- grid = new Grid(2, 2);
- grid.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- grid.setText(0, 0, Util.C.watchedProjectName());
- final HorizontalPanel hp = new HorizontalPanel();
- hp.add(nameBox);
- hp.add(browse);
- grid.setWidget(0, 1, hp);
-
- grid.setText(1, 0, Util.C.watchedProjectFilter());
- grid.setWidget(1, 1, filterTxt);
-
- final CellFormatter fmt = grid.getCellFormatter();
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().header());
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().header());
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().bottomheader());
-
- final FlowPanel fp = new FlowPanel();
- fp.setStyleName(Gerrit.RESOURCES.css().addWatchPanel());
- fp.add(grid);
- fp.add(addNew);
- add(fp);
-
- /* bottom table */
- add(watchesTab);
- add(delSel);
-
- /* popup */
- projectsPopup =
- new ProjectListPopup() {
- @Override
- protected void onMovePointerTo(String projectName) {
- // prevent user input from being overwritten by simply poping up
- if (!projectsPopup.isPoppingUp() || "".equals(nameBox.getText())) {
- nameBox.setText(projectName);
- }
- }
-
- @Override
- protected void openRow(String projectName) {
- nameBox.setText(projectName);
- doAddNew();
- }
- };
- projectsPopup.initPopup(Util.C.projects(), PageLinks.SETTINGS_PROJECTS);
- }
-
- protected void createWidgets() {
- nameBox = new RemoteSuggestBox(new ProjectNameSuggestOracle());
- nameBox.setVisibleLength(50);
- nameBox.setHintText(Util.C.defaultProjectName());
- nameBox.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- doAddNew();
- }
- });
-
- filterTxt = new HintTextBox();
- filterTxt.setVisibleLength(50);
- filterTxt.setHintText(Util.C.defaultFilter());
- filterTxt.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doAddNew();
- }
- }
- });
-
- addNew = new Button(Util.C.buttonWatchProject());
- addNew.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doAddNew();
- }
- });
-
- browse = new Button(Util.C.buttonBrowseProjects());
- browse.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- int top = grid.getAbsoluteTop() - 50; // under page header
- // Try to place it to the right of everything else, but not
- // right justified
- int left =
- 5
- + Math.max(
- grid.getAbsoluteLeft() + grid.getOffsetWidth(),
- watchesTab.getAbsoluteLeft() + watchesTab.getOffsetWidth());
- projectsPopup.setPreferredCoordinates(top, left);
- projectsPopup.displayPopup();
- }
- });
-
- watchesTab = new MyWatchesTable();
-
- delSel = new Button(Util.C.buttonDeleteSshKey());
- delSel.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- watchesTab.deleteChecked();
- }
- });
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- populateWatches();
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- projectsPopup.closePopup();
- }
-
- protected void doAddNew() {
- final String projectName = nameBox.getText().trim();
- if ("".equals(projectName)) {
- return;
- }
-
- addNew.setEnabled(false);
- nameBox.setEnabled(false);
- filterTxt.setEnabled(false);
-
- final ProjectWatchInfo projectWatchInfo = JavaScriptObject.createObject().cast();
- projectWatchInfo.project(projectName);
- projectWatchInfo.filter(filterTxt.getText());
-
- AccountApi.updateWatchedProject(
- "self",
- projectWatchInfo,
- new GerritCallback<JsArray<ProjectWatchInfo>>() {
- @Override
- public void onSuccess(JsArray<ProjectWatchInfo> watchedProjects) {
- addNew.setEnabled(true);
- nameBox.setEnabled(true);
- filterTxt.setEnabled(true);
-
- nameBox.setText("");
- watchesTab.insertWatch(projectWatchInfo);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addNew.setEnabled(true);
- nameBox.setEnabled(true);
- filterTxt.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- protected void populateWatches() {
- AccountApi.getWatchedProjects(
- "self",
- new GerritCallback<JsArray<ProjectWatchInfo>>() {
- @Override
- public void onSuccess(JsArray<ProjectWatchInfo> watchedProjects) {
- watchesTab.display(watchedProjects);
- display();
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java
deleted file mode 100644
index 0a61b2d7d6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyWatchesTable.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (C) 2010 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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.ProjectLink;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Label;
-import java.util.HashSet;
-import java.util.Set;
-
-public class MyWatchesTable extends FancyFlexTable<ProjectWatchInfo> {
-
- public MyWatchesTable() {
- table.setWidth("");
- table.insertRow(1);
- table.setText(0, 2, Util.C.watchedProjectName());
- table.setText(0, 3, Util.C.watchedProjectColumnEmailNotifications());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.setRowSpan(0, 0, 2);
- fmt.setRowSpan(0, 1, 2);
- fmt.setRowSpan(0, 2, 2);
- fmt.getElement(0, 3).setPropertyString("align", "center");
-
- fmt.setColSpan(0, 3, 5);
- table.setText(1, 0, Util.C.watchedProjectColumnNewChanges());
- table.setText(1, 1, Util.C.watchedProjectColumnNewPatchSets());
- table.setText(1, 2, Util.C.watchedProjectColumnAllComments());
- table.setText(1, 3, Util.C.watchedProjectColumnSubmittedChanges());
- table.setText(1, 4, Util.C.watchedProjectColumnAbandonedChanges());
- fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(1, 1, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(1, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(1, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(1, 4, Gerrit.RESOURCES.css().dataHeader());
- }
-
- public void deleteChecked() {
- final Set<ProjectWatchInfo> infos = getCheckedProjectWatchInfos();
- if (!infos.isEmpty()) {
- AccountApi.deleteWatchedProjects(
- "self",
- infos,
- new GerritCallback<JsArray<ProjectWatchInfo>>() {
- @Override
- public void onSuccess(JsArray<ProjectWatchInfo> watchedProjects) {
- remove(infos);
- }
- });
- }
- }
-
- protected void remove(Set<ProjectWatchInfo> infos) {
- for (int row = 1; row < table.getRowCount(); ) {
- final ProjectWatchInfo k = getRowItem(row);
- if (k != null && infos.contains(k)) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
-
- protected Set<ProjectWatchInfo> getCheckedProjectWatchInfos() {
- final Set<ProjectWatchInfo> infos = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final ProjectWatchInfo k = getRowItem(row);
- if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- infos.add(k);
- }
- }
- return infos;
- }
-
- public void insertWatch(ProjectWatchInfo k) {
- final String newName = k.project();
- int row = 1;
- for (; row < table.getRowCount(); row++) {
- final ProjectWatchInfo i = getRowItem(row);
- if (i != null && i.project().compareTo(newName) >= 0) {
- break;
- }
- }
-
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
-
- public void display(JsArray<ProjectWatchInfo> result) {
- while (2 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (ProjectWatchInfo info : Natives.asList(result)) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, info);
- }
- }
-
- protected void populate(int row, ProjectWatchInfo info) {
- final FlowPanel fp = new FlowPanel();
- fp.add(new ProjectLink(info.project(), new Project.NameKey(info.project())));
- if (info.filter() != null) {
- Label filter = new Label(info.filter());
- filter.setStyleName(Gerrit.RESOURCES.css().watchedProjectFilter());
- fp.add(filter);
- }
-
- table.setWidget(row, 1, new CheckBox());
- table.setWidget(row, 2, fp);
-
- addNotifyButton(ProjectWatchInfo.Type.NEW_CHANGES, info, row, 3);
- addNotifyButton(ProjectWatchInfo.Type.NEW_PATCHSETS, info, row, 4);
- addNotifyButton(ProjectWatchInfo.Type.ALL_COMMENTS, info, row, 5);
- addNotifyButton(ProjectWatchInfo.Type.SUBMITTED_CHANGES, info, row, 6);
- addNotifyButton(ProjectWatchInfo.Type.ABANDONED_CHANGES, info, row, 7);
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 5, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 6, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 7, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, info);
- }
-
- protected void addNotifyButton(
- final ProjectWatchInfo.Type type, ProjectWatchInfo info, int row, int col) {
- final CheckBox cbox = new CheckBox();
-
- cbox.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final Boolean oldVal = info.notify(type);
- info.notify(type, cbox.getValue());
- cbox.setEnabled(false);
-
- AccountApi.updateWatchedProject(
- "self",
- info,
- new GerritCallback<JsArray<ProjectWatchInfo>>() {
- @Override
- public void onSuccess(JsArray<ProjectWatchInfo> watchedProjects) {
- cbox.setEnabled(true);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cbox.setEnabled(true);
- info.notify(type, oldVal);
- cbox.setValue(oldVal);
- super.onFailure(caught);
- }
- });
- }
- });
-
- cbox.setValue(info.notify(type));
- table.setWidget(row, col, cbox);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
deleted file mode 100644
index 7c90884c6c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
+++ /dev/null
@@ -1,258 +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.client.account;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AgreementInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.http.client.Request;
-import com.google.gwt.http.client.RequestBuilder;
-import com.google.gwt.http.client.RequestCallback;
-import com.google.gwt.http.client.RequestException;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.RadioButton;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class NewAgreementScreen extends AccountScreen {
- private final String nextToken;
- private Set<String> mySigned;
- private List<AgreementInfo> available;
- private AgreementInfo current;
-
- private VerticalPanel radios;
-
- private Panel agreementGroup;
- private HTML agreementHtml;
-
- private Panel finalGroup;
- private NpTextBox yesIAgreeBox;
- private Button submit;
-
- public NewAgreementScreen() {
- this(null);
- }
-
- public NewAgreementScreen(String token) {
- nextToken = token != null ? token : PageLinks.SETTINGS_AGREEMENTS;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- AccountApi.getAgreements(
- "self",
- new GerritCallback<JsArray<AgreementInfo>>() {
- @Override
- public void onSuccess(JsArray<AgreementInfo> result) {
- if (isAttached()) {
- mySigned = new HashSet<>();
- for (AgreementInfo info : Natives.asList(result)) {
- mySigned.add(info.name());
- }
- postRPC();
- }
- }
- });
-
- available = Gerrit.info().auth().contributorAgreements();
- postRPC();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.newAgreement());
-
- final FlowPanel formBody = new FlowPanel();
- radios = new VerticalPanel();
- formBody.add(radios);
-
- agreementGroup = new FlowPanel();
- agreementGroup.add(new SmallHeading(Util.C.newAgreementReviewLegalHeading()));
-
- agreementHtml = new HTML();
- agreementHtml.setStyleName(Gerrit.RESOURCES.css().contributorAgreementLegal());
- agreementGroup.add(agreementHtml);
- formBody.add(agreementGroup);
-
- finalGroup = new VerticalPanel();
- finalGroup.add(new SmallHeading(Util.C.newAgreementCompleteHeading()));
- final FlowPanel fp = new FlowPanel();
- yesIAgreeBox = new NpTextBox();
- yesIAgreeBox.setVisibleLength(Util.C.newAgreementIAGREE().length() + 8);
- yesIAgreeBox.setMaxLength(Util.C.newAgreementIAGREE().length());
- fp.add(yesIAgreeBox);
- fp.add(new InlineLabel(Util.M.enterIAGREE(Util.C.newAgreementIAGREE())));
- finalGroup.add(fp);
- submit = new Button(Util.C.buttonSubmitNewAgreement());
- submit.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSign();
- }
- });
- finalGroup.add(submit);
- formBody.add(finalGroup);
- new OnEditEnabler(submit, yesIAgreeBox);
-
- final FormPanel form = new FormPanel();
- form.add(formBody);
- add(form);
- }
-
- private void postRPC() {
- if (mySigned != null && available != null) {
- renderSelf();
- display();
- }
- }
-
- private void renderSelf() {
- current = null;
- agreementGroup.setVisible(false);
- finalGroup.setVisible(false);
- radios.clear();
-
- final SmallHeading hdr = new SmallHeading();
- if (available.isEmpty()) {
- hdr.setText(Util.C.newAgreementNoneAvailable());
- } else {
- hdr.setText(Util.C.newAgreementSelectTypeHeading());
- }
- radios.add(hdr);
-
- for (AgreementInfo cla : available) {
- final RadioButton r = new RadioButton("cla_id", cla.name());
- r.addStyleName(Gerrit.RESOURCES.css().contributorAgreementButton());
- radios.add(r);
-
- if (mySigned.contains(cla.name())) {
- r.setEnabled(false);
- final Label l = new Label(Util.C.newAgreementAlreadySubmitted());
- l.setStyleName(Gerrit.RESOURCES.css().contributorAgreementAlreadySubmitted());
- radios.add(l);
- } else {
- r.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- showCLA(cla);
- }
- });
- }
-
- if (cla.description() != null && !cla.description().equals("")) {
- final Label l = new Label(cla.description());
- l.setStyleName(Gerrit.RESOURCES.css().contributorAgreementShortDescription());
- radios.add(l);
- }
- }
- }
-
- private void doSign() {
- submit.setEnabled(false);
-
- if (current == null || !Util.C.newAgreementIAGREE().equalsIgnoreCase(yesIAgreeBox.getText())) {
- yesIAgreeBox.setText("");
- yesIAgreeBox.setFocus(true);
- return;
- }
- doEnterAgreement();
- }
-
- private void doEnterAgreement() {
- AccountApi.enterAgreement(
- "self",
- current.name(),
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {
- Gerrit.display(nextToken);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- yesIAgreeBox.setText("");
- super.onFailure(caught);
- }
- });
- }
-
- private void showCLA(AgreementInfo cla) {
- current = cla;
- String url = cla.url();
- if (url != null && url.length() > 0) {
- agreementGroup.setVisible(true);
- agreementHtml.setText(Gerrit.C.rpcStatusWorking());
- if (!url.startsWith("http:") && !url.startsWith("https:")) {
- url = GWT.getHostPageBaseURL() + url;
- }
- final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
- rb.setCallback(
- new RequestCallback() {
- @Override
- public void onError(Request request, Throwable exception) {
- new ErrorDialog(exception).center();
- }
-
- @Override
- public void onResponseReceived(Request request, Response response) {
- final String ct = response.getHeader("Content-Type");
- if (response.getStatusCode() == 200
- && ct != null
- && (ct.equals("text/html") || ct.startsWith("text/html;"))) {
- agreementHtml.setHTML(response.getText());
- } else {
- new ErrorDialog(response.getStatusText()).center();
- }
- }
- });
- try {
- rb.send();
- } catch (RequestException e) {
- new ErrorDialog(e).show();
- }
- } else {
- agreementGroup.setVisible(false);
- }
-
- finalGroup.setVisible(cla.autoVerifyGroup() != null);
- yesIAgreeBox.setText("");
- submit.setEnabled(false);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchInfo.java
deleted file mode 100644
index fab25ae1c4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchInfo.java
+++ /dev/null
@@ -1,97 +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.client.account;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ProjectWatchInfo extends JavaScriptObject {
-
- public enum Type {
- NEW_CHANGES,
- NEW_PATCHSETS,
- ALL_COMMENTS,
- SUBMITTED_CHANGES,
- ABANDONED_CHANGES
- }
-
- public final native String project() /*-{ return this.project; }-*/;
-
- public final native String filter() /*-{ return this.filter; }-*/;
-
- public final native void project(String s) /*-{ this.project = s; }-*/;
-
- public final native void filter(String s) /*-{ this.filter = s; }-*/;
-
- public final void notify(ProjectWatchInfo.Type t, Boolean b) {
- if (t == ProjectWatchInfo.Type.NEW_CHANGES) {
- notifyNewChanges(b.booleanValue());
- } else if (t == Type.NEW_PATCHSETS) {
- notifyNewPatchSets(b.booleanValue());
- } else if (t == Type.ALL_COMMENTS) {
- notifyAllComments(b.booleanValue());
- } else if (t == Type.SUBMITTED_CHANGES) {
- notifySubmittedChanges(b.booleanValue());
- } else if (t == Type.ABANDONED_CHANGES) {
- notifyAbandonedChanges(b.booleanValue());
- }
- }
-
- public final Boolean notify(ProjectWatchInfo.Type t) {
- boolean b = false;
- if (t == ProjectWatchInfo.Type.NEW_CHANGES) {
- b = notifyNewChanges();
- } else if (t == Type.NEW_PATCHSETS) {
- b = notifyNewPatchSets();
- } else if (t == Type.ALL_COMMENTS) {
- b = notifyAllComments();
- } else if (t == Type.SUBMITTED_CHANGES) {
- b = notifySubmittedChanges();
- } else if (t == Type.ABANDONED_CHANGES) {
- b = notifyAbandonedChanges();
- }
- return Boolean.valueOf(b);
- }
-
- private native boolean
- notifyNewChanges() /*-{ return this['notify_new_changes'] ? true : false; }-*/;
-
- private native boolean
- notifyNewPatchSets() /*-{ return this['notify_new_patch_sets'] ? true : false; }-*/;
-
- private native boolean
- notifyAllComments() /*-{ return this['notify_all_comments'] ? true : false; }-*/;
-
- private native boolean
- notifySubmittedChanges() /*-{ return this['notify_submitted_changes'] ? true : false; }-*/;
-
- private native boolean
- notifyAbandonedChanges() /*-{ return this['notify_abandoned_changes'] ? true : false; }-*/;
-
- private native void notifyNewChanges(
- boolean b) /*-{ this['notify_new_changes'] = b ? true : null; }-*/;
-
- private native void notifyNewPatchSets(
- boolean b) /*-{ this['notify_new_patch_sets'] = b ? true : null; }-*/;
-
- private native void notifyAllComments(
- boolean b) /*-{ this['notify_all_comments'] = b ? true : null; }-*/;
-
- private native void notifySubmittedChanges(
- boolean b) /*-{ this['notify_submitted_changes'] = b ? true : null; }-*/;
-
- private native void notifyAbandonedChanges(
- boolean b) /*-{ this['notify_abandoned_changes'] = b ? true : null; }-*/;
-
- protected ProjectWatchInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
deleted file mode 100644
index 29de14a9e8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
+++ /dev/null
@@ -1,141 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-
-public class RegisterScreen extends AccountScreen {
- private final String nextToken;
-
- public RegisterScreen(String next) {
- nextToken = next;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- display();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.welcomeToGerritCodeReview());
-
- final FlowPanel formBody = new FlowPanel();
-
- final FlowPanel contactGroup = new FlowPanel();
- contactGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
- contactGroup.add(new SmallHeading(Util.C.welcomeReviewContact()));
- final HTML whereFrom = new HTML(Util.C.welcomeContactFrom());
- whereFrom.setStyleName(Gerrit.RESOURCES.css().registerScreenExplain());
- contactGroup.add(whereFrom);
- contactGroup.add(
- new ContactPanelShort() {
- @Override
- protected void display(AccountInfo account) {
- super.display(account);
-
- if ("".equals(nameTxt.getText())) {
- // No name? Encourage the user to provide us something.
- //
- nameTxt.setFocus(true);
- save.setEnabled(true);
- }
- }
- });
- formBody.add(contactGroup);
-
- if (Gerrit.getUserAccount().username() == null
- && Gerrit.info().auth().canEdit(AccountFieldName.USER_NAME)) {
- final FlowPanel fp = new FlowPanel();
- fp.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
- fp.add(new SmallHeading(Util.C.welcomeUsernameHeading()));
-
- final Grid userInfo = new Grid(1, 2);
- final CellFormatter fmt = userInfo.getCellFormatter();
- userInfo.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- userInfo.addStyleName(Gerrit.RESOURCES.css().accountInfoBlock());
- fp.add(userInfo);
-
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().bottomheader());
-
- UsernameField field = new UsernameField();
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- userInfo.setText(0, 1, Util.C.userName());
- userInfo.setWidget(0, 0, field);
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().header());
- } else {
- userInfo.setText(0, 0, Util.C.userName());
- userInfo.setWidget(0, 1, field);
- fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().header());
- }
-
- formBody.add(fp);
- }
-
- if (Gerrit.info().hasSshd()) {
- final FlowPanel sshKeyGroup = new FlowPanel();
- sshKeyGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
- sshKeyGroup.add(new SmallHeading(Util.C.welcomeSshKeyHeading()));
- final HTML whySshKey = new HTML(Util.C.welcomeSshKeyText());
- whySshKey.setStyleName(Gerrit.RESOURCES.css().registerScreenExplain());
- sshKeyGroup.add(whySshKey);
- sshKeyGroup.add(
- new SshPanel() {
- {
- setKeyTableVisible(false);
- }
- });
- formBody.add(sshKeyGroup);
- }
-
- final FlowPanel choices = new FlowPanel();
- choices.setStyleName(Gerrit.RESOURCES.css().registerScreenNextLinks());
- if (Gerrit.info().auth().useContributorAgreements()) {
- final FlowPanel agreementGroup = new FlowPanel();
- agreementGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
- agreementGroup.add(new SmallHeading(Util.C.welcomeAgreementHeading()));
- final HTML whyAgreement = new HTML(Util.C.welcomeAgreementText());
- whyAgreement.setStyleName(Gerrit.RESOURCES.css().registerScreenExplain());
- agreementGroup.add(whyAgreement);
-
- choices.add(new InlineHyperlink(Util.C.newAgreement(), PageLinks.SETTINGS_NEW_AGREEMENT));
- choices.add(new InlineHyperlink(Util.C.welcomeAgreementLater(), nextToken));
- formBody.add(agreementGroup);
- } else {
- choices.add(new InlineHyperlink(Util.C.welcomeContinue(), nextToken));
- }
- formBody.add(choices);
-
- final FormPanel form = new FormPanel();
- form.add(formBody);
- add(form);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
deleted file mode 100644
index a9485958c6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (C) 2010 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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.api.ExtensionPanel;
-import com.google.gerrit.client.api.ExtensionSettingsScreen;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.MenuScreen;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
-import java.util.HashSet;
-import java.util.Set;
-
-public abstract class SettingsScreen extends MenuScreen {
- private final Set<String> allMenuNames;
- private final Set<String> ambiguousMenuNames;
-
- public SettingsScreen() {
- setRequiresSignIn(true);
-
- allMenuNames = new HashSet<>();
- ambiguousMenuNames = new HashSet<>();
-
- linkByGerrit(Util.C.tabAccountSummary(), PageLinks.SETTINGS);
- linkByGerrit(Util.C.tabPreferences(), PageLinks.SETTINGS_PREFERENCES);
- linkByGerrit(Util.C.tabDiffPreferences(), PageLinks.SETTINGS_DIFF_PREFERENCES);
- linkByGerrit(Util.C.tabEditPreferences(), PageLinks.SETTINGS_EDIT_PREFERENCES);
- linkByGerrit(Util.C.tabWatchedProjects(), PageLinks.SETTINGS_PROJECTS);
- linkByGerrit(Util.C.tabContactInformation(), PageLinks.SETTINGS_CONTACT);
- if (Gerrit.info().hasSshd()) {
- linkByGerrit(Util.C.tabSshKeys(), PageLinks.SETTINGS_SSHKEYS);
- }
- if (Gerrit.info().auth().isHttpPasswordSettingsEnabled()) {
- linkByGerrit(Util.C.tabHttpAccess(), PageLinks.SETTINGS_HTTP_PASSWORD);
- }
- if (Gerrit.info().auth().isOAuth()
- && Gerrit.info().auth().gitBasicAuthPolicy() == GitBasicAuthPolicy.OAUTH) {
- linkByGerrit(Util.C.tabOAuthToken(), PageLinks.SETTINGS_OAUTH_TOKEN);
- }
- if (Gerrit.info().gerrit().editGpgKeys()) {
- linkByGerrit(Util.C.tabGpgKeys(), PageLinks.SETTINGS_GPGKEYS);
- }
- linkByGerrit(Util.C.tabWebIdentities(), PageLinks.SETTINGS_WEBIDENT);
- linkByGerrit(Util.C.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS);
- if (Gerrit.info().auth().useContributorAgreements()) {
- linkByGerrit(Util.C.tabAgreements(), PageLinks.SETTINGS_AGREEMENTS);
- }
-
- for (String pluginName : ExtensionSettingsScreen.Definition.plugins()) {
- for (ExtensionSettingsScreen.Definition def :
- Natives.asList(ExtensionSettingsScreen.Definition.get(pluginName))) {
- if (!allMenuNames.add(def.getMenu())) {
- ambiguousMenuNames.add(def.getMenu());
- }
- }
- }
-
- for (String pluginName : ExtensionSettingsScreen.Definition.plugins()) {
- for (ExtensionSettingsScreen.Definition def :
- Natives.asList(ExtensionSettingsScreen.Definition.get(pluginName))) {
- linkByPlugin(pluginName, def.getMenu(), PageLinks.toSettings(pluginName, def.getPath()));
- }
- }
- }
-
- private void linkByGerrit(String text, String target) {
- allMenuNames.add(text);
- link(text, target);
- }
-
- private void linkByPlugin(String pluginName, String text, String target) {
- if (ambiguousMenuNames.contains(text)) {
- text += " (" + pluginName + ")";
- }
- link(text, target);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.settingsHeading());
- }
-
- protected ExtensionPanel createExtensionPoint(GerritUiExtensionPoint extensionPoint) {
- ExtensionPanel extensionPanel = new ExtensionPanel(extensionPoint);
- extensionPanel.putObject(GerritUiExtensionPoint.Key.ACCOUNT_INFO, Gerrit.getUserAccount());
- return extensionPanel;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java
deleted file mode 100644
index 2dfc2eddbf..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java
+++ /dev/null
@@ -1,52 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.data.SshHostKey;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-
-class SshHostKeyPanel extends Composite {
- SshHostKeyPanel(SshHostKey info) {
- final FlowPanel body = new FlowPanel();
- body.setStyleName(Gerrit.RESOURCES.css().sshHostKeyPanel());
- body.add(new SmallHeading(Util.C.sshHostKeyTitle()));
- {
- final Label fpLbl = new Label(Util.C.sshHostKeyFingerprint());
- fpLbl.setStyleName(Gerrit.RESOURCES.css().sshHostKeyPanelHeading());
- body.add(fpLbl);
- final Label fpVal = new Label(info.getFingerprint());
- fpVal.setStyleName(Gerrit.RESOURCES.css().sshHostKeyPanelFingerprintData());
- body.add(fpVal);
- }
- {
- final HTML hdr = new HTML(Util.C.sshHostKeyKnownHostEntry());
- hdr.setStyleName(Gerrit.RESOURCES.css().sshHostKeyPanelHeading());
- body.add(hdr);
-
- final CopyableLabel lbl;
- lbl = new CopyableLabel(info.getHostIdent() + " " + info.getHostKey());
- lbl.setPreviewText(SshPanel.elide(lbl.getText(), 80));
- lbl.addStyleName(Gerrit.RESOURCES.css().sshHostKeyPanelKnownHostEntry());
- body.add(lbl);
- }
- initWidget(body);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshKeyInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshKeyInfo.java
deleted file mode 100644
index 23b3d2d9a2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshKeyInfo.java
+++ /dev/null
@@ -1,33 +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.client.account;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class SshKeyInfo extends JavaScriptObject {
- public final native int seq() /*-{ return this.seq || 0; }-*/;
-
- public final native String sshPublicKey() /*-{ return this.ssh_public_key; }-*/;
-
- public final native String encodedKey() /*-{ return this.encoded_key; }-*/;
-
- public final native String algorithm() /*-{ return this.algorithm; }-*/;
-
- public final native String comment() /*-{ return this.comment; }-*/;
-
- public final native boolean isValid() /*-{ return this['valid'] ? true : false; }-*/;
-
- protected SshKeyInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java
deleted file mode 100644
index 6a8b44d7e0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java
+++ /dev/null
@@ -1,387 +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.client.account;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.ComplexDisclosurePanel;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.data.SshHostKey;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-
-class SshPanel extends Composite {
- private SshKeyTable keys;
-
- private Button showAddKeyBlock;
- private Panel addKeyBlock;
- private Button closeAddKeyBlock;
- private Button clearNew;
- private Button addNew;
- private NpTextArea addTxt;
- private Button deleteKey;
-
- private Panel serverKeys;
-
- private int loadCount;
-
- SshPanel() {
- final FlowPanel body = new FlowPanel();
-
- showAddKeyBlock = new Button(Util.C.buttonShowAddSshKey());
- showAddKeyBlock.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- showAddKeyBlock(true);
- }
- });
-
- keys = new SshKeyTable();
- body.add(keys);
- {
- final FlowPanel fp = new FlowPanel();
- deleteKey = new Button(Util.C.buttonDeleteSshKey());
- deleteKey.setEnabled(false);
- deleteKey.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- keys.deleteChecked();
- }
- });
- fp.add(deleteKey);
- fp.add(showAddKeyBlock);
- body.add(fp);
- }
-
- addKeyBlock = new VerticalPanel();
- addKeyBlock.setVisible(false);
- addKeyBlock.setStyleName(Gerrit.RESOURCES.css().addSshKeyPanel());
- addKeyBlock.add(new SmallHeading(Util.C.addSshKeyPanelHeader()));
-
- final ComplexDisclosurePanel addSshKeyHelp =
- new ComplexDisclosurePanel(Util.C.addSshKeyHelpTitle(), false);
- addSshKeyHelp.setContent(new HTML(Util.C.addSshKeyHelp()));
- addKeyBlock.add(addSshKeyHelp);
-
- addTxt = new NpTextArea();
- addTxt.setVisibleLines(12);
- addTxt.setCharacterWidth(80);
- addTxt.setSpellCheck(false);
- addKeyBlock.add(addTxt);
-
- final HorizontalPanel buttons = new HorizontalPanel();
- addKeyBlock.add(buttons);
-
- clearNew = new Button(Util.C.buttonClearSshKeyInput());
- clearNew.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- addTxt.setText("");
- addTxt.setFocus(true);
- }
- });
- buttons.add(clearNew);
-
- addNew = new Button(Util.C.buttonAddSshKey());
- addNew.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doAddNew();
- }
- });
- buttons.add(addNew);
-
- closeAddKeyBlock = new Button(Util.C.buttonCloseAddSshKey());
- closeAddKeyBlock.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- showAddKeyBlock(false);
- }
- });
- buttons.add(closeAddKeyBlock);
- buttons.setCellWidth(closeAddKeyBlock, "100%");
- buttons.setCellHorizontalAlignment(closeAddKeyBlock, HasHorizontalAlignment.ALIGN_RIGHT);
-
- body.add(addKeyBlock);
-
- serverKeys = new FlowPanel();
- body.add(serverKeys);
-
- initWidget(body);
- }
-
- void setKeyTableVisible(boolean on) {
- keys.setVisible(on);
- deleteKey.setVisible(on);
- closeAddKeyBlock.setVisible(on);
- }
-
- void doAddNew() {
- final String txt = addTxt.getText();
- if (txt != null && txt.length() > 0) {
- addNew.setEnabled(false);
- AccountApi.addSshKey(
- "self",
- txt,
- new GerritCallback<SshKeyInfo>() {
- @Override
- public void onSuccess(SshKeyInfo k) {
- addNew.setEnabled(true);
- addTxt.setText("");
- keys.addOneKey(k);
- if (!keys.isVisible()) {
- showAddKeyBlock(false);
- setKeyTableVisible(true);
- keys.updateDeleteButton();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addNew.setEnabled(true);
-
- if (isInvalidSshKey(caught)) {
- new ErrorDialog(Util.C.invalidSshKeyError()).center();
-
- } else {
- super.onFailure(caught);
- }
- }
-
- private boolean isInvalidSshKey(Throwable caught) {
- if (caught instanceof InvalidSshKeyException) {
- return true;
- }
- return caught instanceof RemoteJsonException
- && InvalidSshKeyException.MESSAGE.equals(caught.getMessage());
- }
- });
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- refreshSshKeys();
- Gerrit.SYSTEM_SVC.daemonHostKeys(
- new GerritCallback<List<SshHostKey>>() {
- @Override
- public void onSuccess(List<SshHostKey> result) {
- serverKeys.clear();
- for (SshHostKey keyInfo : result) {
- serverKeys.add(new SshHostKeyPanel(keyInfo));
- }
- if (++loadCount == 2) {
- display();
- }
- }
- });
- }
-
- private void refreshSshKeys() {
- AccountApi.getSshKeys(
- "self",
- new GerritCallback<JsArray<SshKeyInfo>>() {
- @Override
- public void onSuccess(JsArray<SshKeyInfo> result) {
- keys.display(Natives.asList(result));
- if (result.length() == 0 && keys.isVisible()) {
- showAddKeyBlock(true);
- }
- if (++loadCount == 2) {
- display();
- }
- }
- });
- }
-
- void display() {}
-
- private void showAddKeyBlock(boolean show) {
- showAddKeyBlock.setVisible(!show);
- addKeyBlock.setVisible(show);
- }
-
- private class SshKeyTable extends FancyFlexTable<SshKeyInfo> {
- private ValueChangeHandler<Boolean> updateDeleteHandler;
-
- SshKeyTable() {
- table.setWidth("");
- table.setText(0, 2, Util.C.sshKeyStatus());
- table.setText(0, 3, Util.C.sshKeyAlgorithm());
- table.setText(0, 4, Util.C.sshKeyKey());
- table.setText(0, 5, Util.C.sshKeyComment());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 5, Gerrit.RESOURCES.css().dataHeader());
-
- updateDeleteHandler =
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- updateDeleteButton();
- }
- };
- }
-
- void deleteChecked() {
- final HashSet<Integer> sequenceNumbers = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final SshKeyInfo k = getRowItem(row);
- if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- sequenceNumbers.add(k.seq());
- }
- }
- if (sequenceNumbers.isEmpty()) {
- updateDeleteButton();
- } else {
- deleteKey.setEnabled(false);
- AccountApi.deleteSshKeys(
- "self",
- sequenceNumbers,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- for (int row = 1; row < table.getRowCount(); ) {
- final SshKeyInfo k = getRowItem(row);
- if (k != null && sequenceNumbers.contains(k.seq())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- if (table.getRowCount() == 1) {
- display(Collections.<SshKeyInfo>emptyList());
- } else {
- updateDeleteButton();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- refreshSshKeys();
- updateDeleteButton();
- super.onFailure(caught);
- }
- });
- }
- }
-
- void display(List<SshKeyInfo> result) {
- if (result.isEmpty()) {
- setKeyTableVisible(false);
- showAddKeyBlock(true);
- } else {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
- for (SshKeyInfo k : result) {
- addOneKey(k);
- }
- setKeyTableVisible(true);
- deleteKey.setEnabled(false);
- }
- }
-
- void addOneKey(SshKeyInfo k) {
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- final CheckBox sel = new CheckBox();
- sel.addValueChangeHandler(updateDeleteHandler);
-
- table.setWidget(row, 1, sel);
- if (k.isValid()) {
- table.setText(row, 2, "");
- fmt.removeStyleName(
- row,
- 2, //
- Gerrit.RESOURCES.css().sshKeyPanelInvalid());
- } else {
- table.setText(row, 2, Util.C.sshKeyInvalid());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().sshKeyPanelInvalid());
- }
- table.setText(row, 3, k.algorithm());
-
- CopyableLabel keyLabel = new CopyableLabel(k.sshPublicKey());
- keyLabel.setPreviewText(elide(k.encodedKey(), 40));
- table.setWidget(row, 4, keyLabel);
-
- table.setText(row, 5, k.comment());
-
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().sshKeyPanelEncodedKey());
- for (int c = 2; c <= 5; c++) {
- fmt.addStyleName(row, c, Gerrit.RESOURCES.css().dataCell());
- }
-
- setRowItem(row, k);
- }
-
- void updateDeleteButton() {
- boolean on = false;
- for (int row = 1; row < table.getRowCount(); row++) {
- CheckBox sel = (CheckBox) table.getWidget(row, 1);
- if (sel.getValue()) {
- on = true;
- break;
- }
- }
- deleteKey.setEnabled(on);
- }
- }
-
- static String elide(String s, int len) {
- if (s == null || s.length() < len || len <= 10) {
- return s;
- }
- return s.substring(0, len - 10) + "..." + s.substring(s.length() - 10);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
deleted file mode 100644
index 4fdd06722e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
+++ /dev/null
@@ -1,204 +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.client.account;
-
-import com.google.gerrit.client.ConfirmationCallback;
-import com.google.gerrit.client.ConfirmationDialog;
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.extensions.client.AccountFieldName;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-class UsernameField extends Composite {
- // If these regular expressions are modified the same modifications should be done to the
- // corresponding regular expressions in the
- // com.google.gerrit.server.account.externalids.ExternalId class.
- private static final String USER_NAME_PATTERN_FIRST_REGEX = "[a-zA-Z0-9]";
- private static final String USER_NAME_PATTERN_REST_REGEX = "[a-zA-Z0-9.!#$%&’*+=?^_`\\{|\\}~@-]";
-
- private CopyableLabel userNameLbl;
- private NpTextBox userNameTxt;
- private Button setUserName;
-
- UsernameField() {
- String user = Gerrit.getUserAccount().username();
- userNameLbl = new CopyableLabel(user != null ? user : "");
- userNameLbl.setStyleName(Gerrit.RESOURCES.css().accountUsername());
-
- if (user != null || !canEditUserName()) {
- initWidget(userNameLbl);
-
- } else {
- final FlowPanel body = new FlowPanel();
- initWidget(body);
- setStyleName(Gerrit.RESOURCES.css().usernameField());
-
- userNameTxt = new NpTextBox();
- userNameTxt.addKeyPressHandler(new UserNameValidator());
- userNameTxt.addStyleName(Gerrit.RESOURCES.css().accountUsername());
- userNameTxt.setVisibleLength(16);
- userNameTxt.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- confirmSetUserName();
- }
- }
- });
-
- setUserName = new Button(Util.C.buttonSetUserName());
- setUserName.setVisible(canEditUserName());
- setUserName.setEnabled(false);
- setUserName.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- confirmSetUserName();
- }
- });
- new OnEditEnabler(setUserName, userNameTxt);
-
- userNameLbl.setVisible(false);
- body.add(userNameLbl);
- body.add(userNameTxt);
- body.add(setUserName);
- }
- }
-
- private boolean canEditUserName() {
- return Gerrit.info().auth().canEdit(AccountFieldName.USER_NAME);
- }
-
- private void confirmSetUserName() {
- new ConfirmationDialog(
- Util.C.confirmSetUserNameTitle(),
- new SafeHtmlBuilder().append(Util.C.confirmSetUserName()),
- new ConfirmationCallback() {
- @Override
- public void onOk() {
- doSetUserName();
- }
- })
- .center();
- }
-
- private void doSetUserName() {
- if (!canEditUserName()) {
- return;
- }
-
- enableUI(false);
-
- String newName = userNameTxt.getText();
- if ("".equals(newName)) {
- newName = null;
- }
- final String newUserName = newName;
-
- AccountApi.setUsername(
- "self",
- newUserName,
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {
- Gerrit.getUserAccount().username(newUserName);
- userNameLbl.setText(newUserName);
- userNameLbl.setVisible(true);
- userNameTxt.setVisible(false);
- setUserName.setVisible(false);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableUI(true);
- if (RestApi.isExpected(422 /* Unprocessable Entity */)) {
- new ErrorDialog(Util.C.invalidUserName()).center();
- } else {
- super.onFailure(caught);
- }
- }
- });
- }
-
- private void enableUI(boolean on) {
- userNameTxt.setEnabled(on);
- setUserName.setEnabled(on);
- }
-
- private static final class UserNameValidator implements KeyPressHandler {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- final char code = event.getCharCode();
- final int nativeCode = event.getNativeEvent().getKeyCode();
- switch (nativeCode) {
- case KeyCodes.KEY_ALT:
- case KeyCodes.KEY_BACKSPACE:
- case KeyCodes.KEY_CTRL:
- case KeyCodes.KEY_DELETE:
- case KeyCodes.KEY_DOWN:
- case KeyCodes.KEY_END:
- case KeyCodes.KEY_ENTER:
- case KeyCodes.KEY_ESCAPE:
- case KeyCodes.KEY_HOME:
- case KeyCodes.KEY_LEFT:
- case KeyCodes.KEY_PAGEDOWN:
- case KeyCodes.KEY_PAGEUP:
- case KeyCodes.KEY_RIGHT:
- case KeyCodes.KEY_SHIFT:
- case KeyCodes.KEY_TAB:
- case KeyCodes.KEY_UP:
- // Allow these, even if one of their assigned codes is
- // identical to an ASCII character we do not want to
- // allow in the box.
- //
- // We still want to let the user move around the input box
- // with their arrow keys, or to move between fields using tab.
- // Invalid characters introduced will be caught through the
- // server's own validation of the input data.
- //
- break;
-
- default:
- final TextBox box = (TextBox) event.getSource();
- final String re;
- if (box.getCursorPos() == 0) {
- re = USER_NAME_PATTERN_FIRST_REGEX;
- } else {
- re = USER_NAME_PATTERN_REST_REGEX;
- }
- if (!String.valueOf(code).matches("^" + re + "$")) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java
deleted file mode 100644
index 1c4870c52f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java
+++ /dev/null
@@ -1,30 +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.client.account;
-
-import com.google.gerrit.common.data.ProjectAdminService;
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class Util {
- public static final AccountConstants C = GWT.create(AccountConstants.class);
- public static final AccountMessages M = GWT.create(AccountMessages.class);
- public static final ProjectAdminService PROJECT_SVC;
-
- static {
- PROJECT_SVC = GWT.create(ProjectAdminService.class);
- JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
deleted file mode 100644
index b66f108e1f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
+++ /dev/null
@@ -1,52 +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.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.config.ConfigServerApi;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.common.PageLinks;
-
-public class ValidateEmailScreen extends AccountScreen {
- private final String magicToken;
-
- public ValidateEmailScreen(String magicToken) {
- this.magicToken = magicToken;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.settingsHeading());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- ConfigServerApi.confirmEmail(
- magicToken,
- new ScreenLoadCallback<VoidResult>(this) {
- @Override
- protected void preDisplay(VoidResult result) {}
-
- @Override
- protected void postDisplay() {
- Gerrit.display(PageLinks.SETTINGS_CONTACT);
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java
deleted file mode 100644
index 85937dbece..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/actions/ActionButton.java
+++ /dev/null
@@ -1,118 +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.client.actions;
-
-import com.google.gerrit.client.api.ActionContext;
-import com.google.gerrit.client.api.ChangeGlue;
-import com.google.gerrit.client.api.EditGlue;
-import com.google.gerrit.client.api.ProjectGlue;
-import com.google.gerrit.client.api.RevisionGlue;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-public class ActionButton extends Button implements ClickHandler {
- private final Project.NameKey project;
- private final BranchInfo branch;
- private final ChangeInfo change;
- private final EditInfo edit;
- private final RevisionInfo revision;
- private final ActionInfo action;
- private ActionContext ctx;
-
- public ActionButton(Project.NameKey project, ActionInfo action) {
- this(project, null, null, null, null, action);
- }
-
- public ActionButton(Project.NameKey project, BranchInfo branch, ActionInfo action) {
- this(project, branch, null, null, null, action);
- }
-
- public ActionButton(ChangeInfo change, ActionInfo action) {
- this(null, null, change, null, null, action);
- }
-
- public ActionButton(ChangeInfo change, RevisionInfo revision, ActionInfo action) {
- this(null, null, change, null, revision, action);
- }
-
- private ActionButton(
- Project.NameKey project,
- BranchInfo branch,
- ChangeInfo change,
- EditInfo edit,
- RevisionInfo revision,
- ActionInfo action) {
- super(new SafeHtmlBuilder().openDiv().append(action.label()).closeDiv());
- setStyleName("");
- setTitle(action.title());
- setEnabled(action.enabled());
- addClickHandler(this);
-
- this.project = project;
- this.branch = branch;
- this.change = change;
- this.edit = edit;
- this.revision = revision;
- this.action = action;
- }
-
- @Override
- public void onClick(ClickEvent event) {
- if (ctx != null && ctx.has_popup()) {
- ctx.hide();
- ctx = null;
- return;
- }
-
- if (revision != null) {
- RevisionGlue.onAction(change, revision, action, this);
- } else if (edit != null) {
- EditGlue.onAction(change, edit, action, this);
- } else if (change != null) {
- ChangeGlue.onAction(change, action, this);
- } else if (branch != null) {
- ProjectGlue.onAction(project, branch, action, this);
- } else if (project != null) {
- ProjectGlue.onAction(project, action, this);
- }
- }
-
- @Override
- public void onUnload() {
- if (ctx != null) {
- if (ctx.has_popup()) {
- ctx.hide();
- }
- ctx = null;
- }
- super.onUnload();
- }
-
- public void link(ActionContext ctx) {
- this.ctx = ctx;
- }
-
- public void unlink() {
- ctx = null;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
deleted file mode 100644
index 7bd8b825d4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
+++ /dev/null
@@ -1,295 +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.client.admin;
-
-import static java.util.stream.Collectors.toCollection;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.ProjectAccess;
-import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.SpanElement;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.editor.client.Editor;
-import com.google.gwt.editor.client.EditorDelegate;
-import com.google.gwt.editor.client.ValueAwareEditor;
-import com.google.gwt.editor.client.adapters.EditorSource;
-import com.google.gwt.editor.client.adapters.ListEditor;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.ValueListBox;
-import java.util.ArrayList;
-import java.util.List;
-
-public class AccessSectionEditor extends Composite
- implements Editor<AccessSection>, ValueAwareEditor<AccessSection> {
- interface Binder extends UiBinder<HTMLPanel, AccessSectionEditor> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField ValueEditor<String> name;
-
- @UiField FlowPanel permissionContainer;
- ListEditor<Permission, PermissionEditor> permissions;
-
- @UiField DivElement addContainer;
-
- @UiField(provided = true)
- @Editor.Ignore
- ValueListBox<String> permissionSelector;
-
- @UiField SpanElement deletedName;
-
- @UiField Anchor deleteSection;
-
- @UiField DivElement normal;
- @UiField DivElement deleted;
-
- @UiField SpanElement sectionType;
- @UiField SpanElement sectionName;
-
- private final ProjectAccess projectAccess;
- private AccessSection value;
- private boolean editing;
- private boolean readOnly;
- private boolean isDeleted;
-
- public AccessSectionEditor(ProjectAccess access) {
- projectAccess = access;
- permissionSelector = new ValueListBox<>(new PermissionNameRenderer(access.getCapabilities()));
- permissionSelector.addValueChangeHandler(
- new ValueChangeHandler<String>() {
- @Override
- public void onValueChange(ValueChangeEvent<String> event) {
- if (!AdminConstants.I.addPermission().equals(event.getValue())) {
- onAddPermission(event.getValue());
- }
- }
- });
-
- initWidget(uiBinder.createAndBindUi(this));
- permissions = ListEditor.of(new PermissionEditorSource());
- }
-
- @UiHandler("deleteSection")
- void onDeleteHover(@SuppressWarnings("unused") MouseOverEvent event) {
- normal.addClassName(AdminResources.I.css().deleteSectionHover());
- }
-
- @UiHandler("deleteSection")
- void onDeleteNonHover(@SuppressWarnings("unused") MouseOutEvent event) {
- normal.removeClassName(AdminResources.I.css().deleteSectionHover());
- }
-
- @UiHandler("deleteSection")
- void onDeleteSection(@SuppressWarnings("unused") ClickEvent event) {
- isDeleted = true;
-
- if (name.isVisible() && RefConfigSection.isValid(name.getValue())) {
- deletedName.setInnerText(AdminMessages.I.deletedReference(name.getValue()));
- } else {
- String name = AdminConstants.I.sectionNames().get(value.getName());
- if (name == null) {
- name = value.getName();
- }
- deletedName.setInnerText(AdminMessages.I.deletedSection(name));
- }
-
- normal.getStyle().setDisplay(Display.NONE);
- deleted.getStyle().setDisplay(Display.BLOCK);
- }
-
- @UiHandler("undoDelete")
- void onUndoDelete(@SuppressWarnings("unused") ClickEvent event) {
- isDeleted = false;
- deleted.getStyle().setDisplay(Display.NONE);
- normal.getStyle().setDisplay(Display.BLOCK);
- }
-
- void onAddPermission(String varName) {
- int idx = permissions.getList().size();
-
- Permission p = value.getPermission(varName, true);
- permissions.getList().add(p);
-
- PermissionEditor e = permissions.getEditors().get(idx);
- e.beginAddRule();
-
- rebuildPermissionSelector();
- }
-
- void editRefPattern() {
- name.edit();
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- name.setFocus(true);
- }
- });
- }
-
- void enableEditing() {
- readOnly = false;
- addContainer.getStyle().setDisplay(Display.BLOCK);
- rebuildPermissionSelector();
- }
-
- boolean isDeleted() {
- return isDeleted;
- }
-
- @Override
- public void setValue(AccessSection value) {
- sortPermissions(value);
-
- this.value = value;
- this.readOnly = !editing || !(projectAccess.isOwnerOf(value) || projectAccess.canUpload());
-
- name.setEnabled(!readOnly);
- deleteSection.setVisible(!readOnly);
-
- if (RefConfigSection.isValid(value.getName())) {
- name.setVisible(true);
- name.setIgnoreEditorValue(false);
- sectionType.setInnerText(AdminConstants.I.sectionTypeReference());
-
- } else {
- name.setVisible(false);
- name.setIgnoreEditorValue(true);
-
- String name = AdminConstants.I.sectionNames().get(value.getName());
- if (name != null) {
- sectionType.setInnerText(name);
- sectionName.getStyle().setDisplay(Display.NONE);
- } else {
- sectionType.setInnerText(AdminConstants.I.sectionTypeSection());
- sectionName.setInnerText(value.getName());
- sectionName.getStyle().clearDisplay();
- }
- }
-
- if (readOnly) {
- addContainer.getStyle().setDisplay(Display.NONE);
- } else {
- enableEditing();
- }
- }
-
- private void sortPermissions(AccessSection accessSection) {
- accessSection.setPermissions(
- accessSection.getPermissions().stream().sorted().collect(toCollection(ArrayList::new)));
- }
-
- void setEditing(boolean editing) {
- this.editing = editing;
- }
-
- private void rebuildPermissionSelector() {
- List<String> perms = new ArrayList<>();
-
- if (AccessSection.GLOBAL_CAPABILITIES.equals(value.getName())) {
- for (String varName : projectAccess.getCapabilities().keySet()) {
- addPermission(varName, perms);
- }
- } else if (RefConfigSection.isValid(value.getName())) {
- for (LabelType t : projectAccess.getLabelTypes().getLabelTypes()) {
- addPermission(Permission.forLabel(t.getName()), perms);
- }
- for (LabelType t : projectAccess.getLabelTypes().getLabelTypes()) {
- addPermission(Permission.forLabelAs(t.getName()), perms);
- }
- for (String varName : AdminConstants.I.permissionNames().keySet()) {
- addPermission(varName, perms);
- }
- }
- if (perms.isEmpty()) {
- addContainer.getStyle().setDisplay(Display.NONE);
- } else {
- addContainer.getStyle().setDisplay(Display.BLOCK);
- perms.add(0, AdminConstants.I.addPermission());
- permissionSelector.setValue(AdminConstants.I.addPermission());
- permissionSelector.setAcceptableValues(perms);
- }
- }
-
- private void addPermission(String permissionName, List<String> permissionList) {
- if (value.getPermission(permissionName) != null) {
- return;
- }
- if (Gerrit.info().gerrit().isAllProjects(projectAccess.getProjectName())
- && !Permission.canBeOnAllProjects(value.getName(), permissionName)) {
- return;
- }
- permissionList.add(permissionName);
- }
-
- @Override
- public void flush() {
- List<Permission> src = permissions.getList();
- List<Permission> keep = new ArrayList<>(src.size());
-
- for (int i = 0; i < src.size(); i++) {
- PermissionEditor e = (PermissionEditor) permissionContainer.getWidget(i);
- if (!e.isDeleted()) {
- keep.add(src.get(i));
- }
- }
- value.setPermissions(keep);
- }
-
- @Override
- public void onPropertyChange(String... paths) {}
-
- @Override
- public void setDelegate(EditorDelegate<AccessSection> delegate) {}
-
- private class PermissionEditorSource extends EditorSource<PermissionEditor> {
- @Override
- public PermissionEditor create(int index) {
- PermissionEditor subEditor =
- new PermissionEditor(projectAccess, readOnly, value, projectAccess.getLabelTypes());
- permissionContainer.insert(subEditor, index);
- return subEditor;
- }
-
- @Override
- public void dispose(PermissionEditor subEditor) {
- subEditor.removeFromParent();
- }
-
- @Override
- public void setIndex(PermissionEditor subEditor, int index) {
- permissionContainer.insert(subEditor, index);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml
deleted file mode 100644
index 3710265781..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:e='urn:import:com.google.gwt.editor.ui.client'
- xmlns:my='urn:import:com.google.gerrit.client.admin'
- ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
- ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
- ui:generateLocales='default,en'
- >
-<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
-<ui:style gss='false'>
- @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
- @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-
- .panel {
- width: 50em;
- position: relative;
- }
-
- .content {
- margin-top: 4px;
- margin-bottom: 4px;
- padding-bottom: 2px;
- }
-
- .normal {
- background-color: trimColor;
- }
-
- .deleted {
- padding-left: 7px;
- padding-bottom: 2px;
- }
-
- .header {
- padding-left: 5px;
- padding-right: 5px;
- }
- .headerText {
- vertical-align: top;
- white-space: nowrap;
- font-weight: bold;
- }
- .headerTable {
- border: 0;
- width: 100%;
- padding-right: 40px;
- }
-
- .header:hover {
- background-color: selectionColor;
- }
-
- .name {
- width: 100%;
- }
- .nameEdit {
- width: 100%;
- }
-
- .permissionList {
- margin-left: 5px;
- margin-right: 5px;
- margin-bottom: 5px;
- }
-
- .addContainer {
- padding-left: 16px;
- padding-right: 16px;
- font-size: 80%;
- }
- .addContainer:hover {
- background-color: selectionColor;
- }
-
- .deleteIcon {
- position: absolute;
- top: 5px;
- right: 17px;
- }
-
- .undoIcon {
- position: absolute;
- top: 2px;
- right: 17px;
- }
-</ui:style>
-
-<g:HTMLPanel styleName='{style.panel}'>
-<div ui:field='normal' class='{style.normal} {style.content}'>
- <div class='{style.header}'>
- <table class='{style.headerTable}'><tr>
- <td class='{style.headerText}'>
- <span ui:field='sectionType'/>
- </td>
- <td width='100%'>
- <my:ValueEditor
- ui:field='name'
- addStyleNames='{style.name}'
- editTitle='Edit reference pattern'>
- <ui:attribute name='editTitle'/>
- <my:editor>
- <my:RefPatternBox styleName='{style.nameEdit}'/>
- </my:editor>
- </my:ValueEditor>
- <span ui:field='sectionName' class='{style.name}'/>
- </td>
- </tr></table>
-
- <g:Anchor
- ui:field='deleteSection'
- href='javascript:void'
- styleName='{style.deleteIcon} {res.css.deleteIcon}'
- title='Delete this section (and nested rules)'>
- <ui:attribute name='title'/>
- </g:Anchor>
- </div>
-
- <g:FlowPanel
- ui:field='permissionContainer'
- styleName='{style.permissionList}'/>
- <div ui:field='addContainer' class='{style.addContainer}'>
- <g:ValueListBox ui:field='permissionSelector'/>
- </div>
-</div>
-
-<div
- ui:field='deleted'
- class='{style.deleted} {res.css.deleted}'
- style='display: none'>
- <span ui:field='deletedName'/>
- <g:Anchor
- ui:field='undoDelete'
- href='javascript:void'
- styleName='{style.undoIcon} {res.css.undoIcon}'
- title='Undo deletion'>
- <ui:attribute name='title'/>
- </g:Anchor>
-</div>
-</g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java
deleted file mode 100644
index 5e38a1414f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2015 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.client.admin;
-
-import static com.google.gerrit.client.FormatUtil.mediumFormat;
-import static com.google.gerrit.client.FormatUtil.name;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.groups.GroupApi;
-import com.google.gerrit.client.groups.GroupAuditEventInfo;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import java.util.List;
-
-public class AccountGroupAuditLogScreen extends AccountGroupScreen {
- private AuditEventTable auditEventTable;
-
- public AccountGroupAuditLogScreen(GroupInfo toShow, String token) {
- super(toShow, token);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- add(new SmallHeading(AdminConstants.I.headingAuditLog()));
- auditEventTable = new AuditEventTable();
- add(auditEventTable);
- }
-
- @Override
- protected void display(GroupInfo group, boolean canModify) {
- GroupApi.getAuditLog(
- group.getGroupUUID(),
- new GerritCallback<JsArray<GroupAuditEventInfo>>() {
- @Override
- public void onSuccess(JsArray<GroupAuditEventInfo> result) {
- auditEventTable.display(Natives.asList(result));
- }
- });
- }
-
- private static class AuditEventTable extends FancyFlexTable<GroupAuditEventInfo> {
- AuditEventTable() {
- table.setText(0, 1, AdminConstants.I.columnDate());
- table.setText(0, 2, AdminConstants.I.columnType());
- table.setText(0, 3, AdminConstants.I.columnMember());
- table.setText(0, 4, AdminConstants.I.columnByUser());
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
- }
-
- void display(List<GroupAuditEventInfo> auditEvents) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (GroupAuditEventInfo auditEvent : auditEvents) {
- int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, auditEvent);
- }
- }
-
- void populate(int row, GroupAuditEventInfo auditEvent) {
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- table.setText(row, 1, mediumFormat(auditEvent.date()));
-
- switch (auditEvent.type()) {
- case ADD_USER:
- case ADD_GROUP:
- table.setText(row, 2, AdminConstants.I.typeAdded());
- break;
- case REMOVE_USER:
- case REMOVE_GROUP:
- table.setText(row, 2, AdminConstants.I.typeRemoved());
- break;
- }
-
- switch (auditEvent.type()) {
- case ADD_USER:
- case REMOVE_USER:
- table.setText(row, 3, formatAccount(auditEvent.memberAsUser()));
- break;
- case ADD_GROUP:
- case REMOVE_GROUP:
- GroupInfo member = auditEvent.memberAsGroup();
- if (AccountGroup.isInternalGroup(member.getGroupUUID())) {
- table.setWidget(
- row,
- 3,
- new Hyperlink(formatGroup(member), Dispatcher.toGroup(member.getGroupUUID())));
- fmt.getElement(row, 3).setTitle(null);
- } else if (member.url() != null) {
- Anchor a = new Anchor();
- a.setText(formatGroup(member));
- a.setHref(member.url());
- a.setTitle("UUID " + member.getGroupUUID().get());
- table.setWidget(row, 3, a);
- fmt.getElement(row, 3).setTitle(null);
- } else {
- table.setText(row, 3, formatGroup(member));
- fmt.getElement(row, 3).setTitle("UUID " + member.getGroupUUID().get());
- }
- break;
- }
-
- table.setText(row, 4, formatAccount(auditEvent.user()));
-
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, auditEvent);
- }
- }
-
- private static String formatAccount(AccountInfo account) {
- StringBuilder b = new StringBuilder();
- b.append(name(account));
- b.append(" (");
- b.append(account._accountId());
- b.append(")");
- return b.toString();
- }
-
- private static String formatGroup(GroupInfo group) {
- return group.name() != null && !group.name().isEmpty()
- ? group.name()
- : group.getGroupUUID().get();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
deleted file mode 100644
index 34a1ac95dd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
+++ /dev/null
@@ -1,243 +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.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.groups.GroupApi;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-public class AccountGroupInfoScreen extends AccountGroupScreen {
- private CopyableLabel groupUUIDLabel;
-
- private NpTextBox groupNameTxt;
- private Button saveName;
-
- private RemoteSuggestBox ownerTxt;
- private Button saveOwner;
-
- private NpTextArea descTxt;
- private Button saveDesc;
-
- private CheckBox visibleToAllCheckBox;
- private Button saveGroupOptions;
-
- public AccountGroupInfoScreen(GroupInfo toShow, String token) {
- super(toShow, token);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- initUUID();
- initName();
- initOwner();
- initDescription();
- initGroupOptions();
- }
-
- private void enableForm(boolean canModify) {
- groupNameTxt.setEnabled(canModify);
- ownerTxt.setEnabled(canModify);
- descTxt.setEnabled(canModify);
- visibleToAllCheckBox.setEnabled(canModify);
- }
-
- private void initUUID() {
- final VerticalPanel groupUUIDPanel = new VerticalPanel();
- groupUUIDPanel.setStyleName(Gerrit.RESOURCES.css().groupUUIDPanel());
- groupUUIDPanel.add(new SmallHeading(AdminConstants.I.headingGroupUUID()));
- groupUUIDLabel = new CopyableLabel("");
- groupUUIDPanel.add(groupUUIDLabel);
- add(groupUUIDPanel);
- }
-
- private void initName() {
- final VerticalPanel groupNamePanel = new VerticalPanel();
- groupNamePanel.setStyleName(Gerrit.RESOURCES.css().groupNamePanel());
- groupNameTxt = new NpTextBox();
- groupNameTxt.setStyleName(Gerrit.RESOURCES.css().groupNameTextBox());
- groupNameTxt.setVisibleLength(60);
- groupNamePanel.add(groupNameTxt);
-
- saveName = new Button(AdminConstants.I.buttonRenameGroup());
- saveName.setEnabled(false);
- saveName.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final String newName = groupNameTxt.getText().trim();
- GroupApi.renameGroup(
- getGroupUUID(),
- newName,
- new GerritCallback<com.google.gerrit.client.VoidResult>() {
- @Override
- public void onSuccess(com.google.gerrit.client.VoidResult result) {
- saveName.setEnabled(false);
- setPageTitle(AdminMessages.I.group(newName));
- groupNameTxt.setText(newName);
- if (getGroupUUID().equals(getOwnerGroupUUID())) {
- ownerTxt.setText(newName);
- }
- }
- });
- }
- });
- groupNamePanel.add(saveName);
- add(groupNamePanel);
- }
-
- private void initOwner() {
- final VerticalPanel ownerPanel = new VerticalPanel();
- ownerPanel.setStyleName(Gerrit.RESOURCES.css().groupOwnerPanel());
- ownerPanel.add(new SmallHeading(AdminConstants.I.headingOwner()));
-
- final AccountGroupSuggestOracle accountGroupOracle = new AccountGroupSuggestOracle();
- ownerTxt = new RemoteSuggestBox(accountGroupOracle);
- ownerTxt.setStyleName(Gerrit.RESOURCES.css().groupOwnerTextBox());
- ownerTxt.setVisibleLength(60);
- ownerPanel.add(ownerTxt);
-
- saveOwner = new Button(AdminConstants.I.buttonChangeGroupOwner());
- saveOwner.setEnabled(false);
- saveOwner.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final String newOwner = ownerTxt.getText().trim();
- if (newOwner.length() > 0) {
- AccountGroup.UUID ownerUuid = accountGroupOracle.getUUID(newOwner);
- String ownerId = ownerUuid != null ? ownerUuid.get() : newOwner;
- GroupApi.setGroupOwner(
- getGroupUUID(),
- ownerId,
- new GerritCallback<GroupInfo>() {
- @Override
- public void onSuccess(GroupInfo result) {
- updateOwnerGroup(result);
- saveOwner.setEnabled(false);
- }
- });
- }
- }
- });
- ownerPanel.add(saveOwner);
- add(ownerPanel);
- }
-
- private void initDescription() {
- final VerticalPanel vp = new VerticalPanel();
- vp.setStyleName(Gerrit.RESOURCES.css().groupDescriptionPanel());
- vp.add(new SmallHeading(AdminConstants.I.headingDescription()));
-
- descTxt = new NpTextArea();
- descTxt.setVisibleLines(6);
- descTxt.setCharacterWidth(60);
- vp.add(descTxt);
-
- saveDesc = new Button(AdminConstants.I.buttonSaveDescription());
- saveDesc.setEnabled(false);
- saveDesc.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final String txt = descTxt.getText().trim();
- GroupApi.setGroupDescription(
- getGroupUUID(),
- txt,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- saveDesc.setEnabled(false);
- }
- });
- }
- });
- vp.add(saveDesc);
- add(vp);
- }
-
- private void initGroupOptions() {
- final VerticalPanel groupOptionsPanel = new VerticalPanel();
-
- final VerticalPanel vp = new VerticalPanel();
- vp.setStyleName(Gerrit.RESOURCES.css().groupOptionsPanel());
- vp.add(new SmallHeading(AdminConstants.I.headingGroupOptions()));
-
- visibleToAllCheckBox = new CheckBox(AdminConstants.I.isVisibleToAll());
- vp.add(visibleToAllCheckBox);
- groupOptionsPanel.add(vp);
-
- saveGroupOptions = new Button(AdminConstants.I.buttonSaveGroupOptions());
- saveGroupOptions.setEnabled(false);
- saveGroupOptions.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- GroupApi.setGroupOptions(
- getGroupUUID(),
- visibleToAllCheckBox.getValue(),
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- saveGroupOptions.setEnabled(false);
- }
- });
- }
- });
- groupOptionsPanel.add(saveGroupOptions);
-
- add(groupOptionsPanel);
-
- final OnEditEnabler enabler = new OnEditEnabler(saveGroupOptions);
- enabler.listenTo(visibleToAllCheckBox);
- }
-
- @Override
- protected void display(GroupInfo group, boolean canModify) {
- groupUUIDLabel.setText(group.getGroupUUID().get());
- groupNameTxt.setText(group.name());
- ownerTxt.setText(
- group.owner() != null
- ? group.owner()
- : AdminMessages.I.deletedReference(group.getOwnerUUID().get()));
- descTxt.setText(group.description());
- visibleToAllCheckBox.setValue(group.options().isVisibleToAll());
- setMembersTabVisible(AccountGroup.isInternalGroup(group.getGroupUUID()));
-
- enableForm(canModify);
- saveName.setVisible(canModify);
- saveOwner.setVisible(canModify);
- saveDesc.setVisible(canModify);
- saveGroupOptions.setVisible(canModify);
- new OnEditEnabler(saveDesc, descTxt);
- new OnEditEnabler(saveName, groupNameTxt);
- new OnEditEnabler(saveOwner, ownerTxt.getTextBox());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
deleted file mode 100644
index 6eaab5d6b4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
+++ /dev/null
@@ -1,436 +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.client.admin;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.groups.GroupApi;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.AccountLinkPanel;
-import com.google.gerrit.client.ui.AccountSuggestOracle;
-import com.google.gerrit.client.ui.AddMemberBox;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Panel;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-
-public class AccountGroupMembersScreen extends AccountGroupScreen {
-
- private MemberTable members;
- private IncludeTable includes;
-
- private Panel memberPanel;
- private AddMemberBox addMemberBox;
- private Button delMember;
-
- private Panel includePanel;
- private AddMemberBox addIncludeBox;
- private Button delInclude;
-
- private FlowPanel noMembersInfo;
- private AccountGroupSuggestOracle accountGroupSuggestOracle;
-
- public AccountGroupMembersScreen(GroupInfo toShow, String token) {
- super(toShow, token);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- initMemberList();
- initIncludeList();
- initNoMembersInfo();
- }
-
- private void enableForm(boolean canModify) {
- addMemberBox.setEnabled(canModify);
- members.setEnabled(canModify);
- addIncludeBox.setEnabled(canModify);
- includes.setEnabled(canModify);
- }
-
- private void initMemberList() {
- addMemberBox =
- new AddMemberBox(
- AdminConstants.I.buttonAddGroupMember(),
- AdminConstants.I.defaultAccountName(),
- new AccountSuggestOracle());
-
- addMemberBox.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doAddNewMember();
- }
- });
-
- members = new MemberTable();
- members.addStyleName(Gerrit.RESOURCES.css().groupMembersTable());
-
- delMember = new Button(AdminConstants.I.buttonDeleteGroupMembers());
- delMember.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- members.deleteChecked();
- }
- });
-
- memberPanel = new FlowPanel();
- memberPanel.add(new SmallHeading(AdminConstants.I.headingMembers()));
- memberPanel.add(addMemberBox);
- memberPanel.add(members);
- memberPanel.add(delMember);
- add(memberPanel);
- }
-
- private void initIncludeList() {
- accountGroupSuggestOracle = new AccountGroupSuggestOracle();
- addIncludeBox =
- new AddMemberBox(
- AdminConstants.I.buttonAddIncludedGroup(),
- AdminConstants.I.defaultAccountGroupName(),
- accountGroupSuggestOracle);
-
- addIncludeBox.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doAddNewInclude();
- }
- });
-
- includes = new IncludeTable();
- includes.addStyleName(Gerrit.RESOURCES.css().groupIncludesTable());
-
- delInclude = new Button(AdminConstants.I.buttonDeleteIncludedGroup());
- delInclude.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- includes.deleteChecked();
- }
- });
-
- includePanel = new FlowPanel();
- includePanel.add(new SmallHeading(AdminConstants.I.headingIncludedGroups()));
- includePanel.add(addIncludeBox);
- includePanel.add(includes);
- includePanel.add(delInclude);
- add(includePanel);
- }
-
- private void initNoMembersInfo() {
- noMembersInfo = new FlowPanel();
- noMembersInfo.setVisible(false);
- noMembersInfo.add(new SmallHeading(AdminConstants.I.noMembersInfo()));
- add(noMembersInfo);
- }
-
- @Override
- protected void display(GroupInfo group, boolean canModify) {
- if (AccountGroup.isInternalGroup(group.getGroupUUID())) {
- members.display(Natives.asList(group.members()));
- includes.display(Natives.asList(group.includes()));
- } else {
- memberPanel.setVisible(false);
- includePanel.setVisible(false);
- noMembersInfo.setVisible(true);
- }
-
- enableForm(canModify);
- delMember.setVisible(canModify);
- delInclude.setVisible(canModify);
- }
-
- void doAddNewMember() {
- final String nameEmail = addMemberBox.getText();
- if (nameEmail.length() == 0) {
- return;
- }
-
- addMemberBox.setEnabled(false);
- GroupApi.addMember(
- getGroupUUID(),
- nameEmail,
- new GerritCallback<AccountInfo>() {
- @Override
- public void onSuccess(AccountInfo memberInfo) {
- addMemberBox.setEnabled(true);
- addMemberBox.setText("");
- members.insert(memberInfo);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addMemberBox.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- void doAddNewInclude() {
- String groupName = addIncludeBox.getText();
- if (groupName.length() == 0) {
- return;
- }
-
- AccountGroup.UUID uuid = accountGroupSuggestOracle.getUUID(groupName);
- if (uuid == null) {
- return;
- }
-
- addIncludeBox.setEnabled(false);
- GroupApi.addIncludedGroup(
- getGroupUUID(),
- uuid.get(),
- new GerritCallback<GroupInfo>() {
- @Override
- public void onSuccess(GroupInfo result) {
- addIncludeBox.setEnabled(true);
- addIncludeBox.setText("");
- includes.insert(result);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addIncludeBox.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private class MemberTable extends FancyFlexTable<AccountInfo> {
- private boolean enabled = true;
-
- MemberTable() {
- table.setText(0, 2, AdminConstants.I.columnMember());
- table.setText(0, 3, AdminConstants.I.columnEmailAddress());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- }
-
- void setEnabled(boolean enabled) {
- this.enabled = enabled;
- for (int row = 1; row < table.getRowCount(); row++) {
- final AccountInfo i = getRowItem(row);
- if (i != null) {
- ((CheckBox) table.getWidget(row, 1)).setEnabled(enabled);
- }
- }
- }
-
- void deleteChecked() {
- final HashSet<Integer> ids = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final AccountInfo i = getRowItem(row);
- if (i != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(i._accountId());
- }
- }
- if (!ids.isEmpty()) {
- GroupApi.removeMembers(
- getGroupUUID(),
- ids,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- for (int row = 1; row < table.getRowCount(); ) {
- final AccountInfo i = getRowItem(row);
- if (i != null && ids.contains(i._accountId())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
- });
- }
- }
-
- void display(List<AccountInfo> result) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (AccountInfo i : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, i);
- }
- }
-
- void insert(AccountInfo info) {
- Comparator<AccountInfo> c =
- comparing((AccountInfo a) -> nullToEmpty(a.name()))
- .thenComparing(a -> nullToEmpty(a.email()))
- .thenComparing(AccountInfo::_accountId);
- int insertPos = getInsertRow(c, info);
- if (insertPos >= 0) {
- table.insertRow(insertPos);
- applyDataRowStyle(insertPos);
- populate(insertPos, info);
- }
- }
-
- void populate(int row, AccountInfo i) {
- CheckBox checkBox = new CheckBox();
- table.setWidget(row, 1, checkBox);
- checkBox.setEnabled(enabled);
- table.setWidget(row, 2, AccountLinkPanel.create(i));
- table.setText(row, 3, i.email());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, i);
- }
- }
-
- private class IncludeTable extends FancyFlexTable<GroupInfo> {
- private boolean enabled = true;
-
- IncludeTable() {
- table.setText(0, 2, AdminConstants.I.columnGroupName());
- table.setText(0, 3, AdminConstants.I.columnGroupDescription());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- }
-
- void setEnabled(boolean enabled) {
- this.enabled = enabled;
- for (int row = 1; row < table.getRowCount(); row++) {
- final GroupInfo i = getRowItem(row);
- if (i != null) {
- ((CheckBox) table.getWidget(row, 1)).setEnabled(enabled);
- }
- }
- }
-
- void deleteChecked() {
- final HashSet<AccountGroup.UUID> ids = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final GroupInfo i = getRowItem(row);
- if (i != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(i.getGroupUUID());
- }
- }
- if (!ids.isEmpty()) {
- GroupApi.removeIncludedGroups(
- getGroupUUID(),
- ids,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- for (int row = 1; row < table.getRowCount(); ) {
- final GroupInfo i = getRowItem(row);
- if (i != null && ids.contains(i.getGroupUUID())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
- });
- }
- }
-
- void display(List<GroupInfo> list) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (GroupInfo i : list) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, i);
- }
- }
-
- void insert(GroupInfo info) {
- Comparator<GroupInfo> c =
- comparing((GroupInfo g) -> nullToEmpty(g.name())).thenComparing(GroupInfo::getGroupUUID);
- int insertPos = getInsertRow(c, info);
- if (insertPos >= 0) {
- table.insertRow(insertPos);
- applyDataRowStyle(insertPos);
- populate(insertPos, info);
- }
- }
-
- void populate(int row, GroupInfo i) {
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
-
- AccountGroup.UUID uuid = i.getGroupUUID();
- CheckBox checkBox = new CheckBox();
- table.setWidget(row, 1, checkBox);
- checkBox.setEnabled(enabled);
- if (AccountGroup.isInternalGroup(uuid)) {
- table.setWidget(row, 2, new Hyperlink(i.name(), Dispatcher.toGroup(uuid)));
- fmt.getElement(row, 2).setTitle(null);
- table.setText(row, 3, i.description());
- } else if (i.url() != null) {
- Anchor a = new Anchor();
- a.setText(i.name());
- a.setHref(i.url());
- a.setTitle("UUID " + uuid.get());
- table.setWidget(row, 2, a);
- fmt.getElement(row, 2).setTitle(null);
- } else {
- table.setText(row, 2, i.name());
- fmt.getElement(row, 2).setTitle("UUID " + uuid.get());
- }
-
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, i);
- }
- }
-
- // Like Guava's Strings#nullToEmpty, which can't be used in GWT UI code.
- private static String nullToEmpty(String str) {
- return str == null ? "" : str;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
deleted file mode 100644
index b67213ba96..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
+++ /dev/null
@@ -1,97 +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.client.admin;
-
-import static com.google.gerrit.client.Dispatcher.toGroup;
-
-import com.google.gerrit.client.groups.GroupApi;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.MenuScreen;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-
-public abstract class AccountGroupScreen extends MenuScreen {
- public static final String INFO = "info";
- public static final String MEMBERS = "members";
- public static final String AUDIT_LOG = "audit-log";
-
- private final GroupInfo group;
- private final String token;
- private final String membersTabToken;
- private final String auditLogTabToken;
-
- public AccountGroupScreen(GroupInfo toShow, String token) {
- setRequiresSignIn(true);
-
- this.group = toShow;
- this.token = token;
- this.membersTabToken = getTabToken(token, MEMBERS);
- this.auditLogTabToken = getTabToken(token, AUDIT_LOG);
-
- link(AdminConstants.I.groupTabGeneral(), getTabToken(token, INFO));
- link(
- AdminConstants.I.groupTabMembers(),
- membersTabToken,
- AccountGroup.isInternalGroup(group.getGroupUUID()));
- }
-
- private String getTabToken(String token, String tab) {
- if (token.startsWith("/admin/groups/uuid-")) {
- return toGroup(group.getGroupUUID(), tab);
- }
- return toGroup(group.getGroupId(), tab);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- setPageTitle(AdminMessages.I.group(group.name()));
- display();
- GroupApi.isGroupOwner(
- group.name(),
- new GerritCallback<Boolean>() {
- @Override
- public void onSuccess(Boolean result) {
- if (result) {
- link(
- AdminConstants.I.groupTabAuditLog(),
- auditLogTabToken,
- AccountGroup.isInternalGroup(group.getGroupUUID()));
- setToken(token);
- }
- display(group, result);
- }
- });
- }
-
- protected abstract void display(GroupInfo group, boolean canModify);
-
- protected AccountGroup.UUID getGroupUUID() {
- return group.getGroupUUID();
- }
-
- protected void updateOwnerGroup(GroupInfo ownerGroup) {
- group.setOwnerUUID(ownerGroup.getGroupUUID());
- group.owner(ownerGroup.name());
- }
-
- protected AccountGroup.UUID getOwnerGroupUUID() {
- return group.getOwnerUUID();
- }
-
- protected void setMembersTabVisible(boolean visible) {
- setLinkVisible(membersTabToken, visible);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
deleted file mode 100644
index 9def3b3371..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ /dev/null
@@ -1,293 +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.client.admin;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Constants;
-import java.util.Map;
-
-public interface AdminConstants extends Constants {
- AdminConstants I = GWT.create(AdminConstants.class);
-
- String defaultAccountName();
-
- String defaultAccountGroupName();
-
- String defaultBranchName();
-
- String defaultTagName();
-
- String defaultRevisionSpec();
-
- String annotation();
-
- String buttonDeleteIncludedGroup();
-
- String buttonAddIncludedGroup();
-
- String buttonDeleteGroupMembers();
-
- String buttonAddGroupMember();
-
- String buttonSaveDescription();
-
- String buttonRenameGroup();
-
- String buttonCreateGroup();
-
- String buttonCreateProject();
-
- String buttonChangeGroupOwner();
-
- String buttonChangeGroupType();
-
- String buttonSelectGroup();
-
- String buttonSaveChanges();
-
- String checkBoxEmptyCommit();
-
- String checkBoxPermissionsOnly();
-
- String useContentMerge();
-
- String useContributorAgreements();
-
- String useSignedOffBy();
-
- String createNewChangeForAllNotInTarget();
-
- String enableSignedPush();
-
- String requireSignedPush();
-
- String requireChangeID();
-
- String rejectImplicitMerges();
-
- String privateByDefault();
-
- String workInProgressByDefault();
-
- String enableReviewerByEmail();
-
- String matchAuthorToCommitterDate();
-
- String headingMaxObjectSizeLimit();
-
- String headingGroupOptions();
-
- String isVisibleToAll();
-
- String buttonSaveGroupOptions();
-
- String suggestedGroupLabel();
-
- String parentSuggestions();
-
- String buttonBrowseProjects();
-
- String projects();
-
- String projectRepoBrowser();
-
- String headingGroupUUID();
-
- String headingOwner();
-
- String headingDescription();
-
- String headingProjectOptions();
-
- String headingProjectCommands();
-
- String headingCommands();
-
- String headingMembers();
-
- String headingIncludedGroups();
-
- String noMembersInfo();
-
- String headingExternalGroup();
-
- String headingCreateGroup();
-
- String headingParentProjectName();
-
- String columnProjectName();
-
- String headingAgreements();
-
- String headingAuditLog();
-
- String headingProjectSubmitType();
-
- String projectSubmitType_INHERIT();
-
- String projectSubmitType_FAST_FORWARD_ONLY();
-
- String projectSubmitType_MERGE_ALWAYS();
-
- String projectSubmitType_MERGE_IF_NECESSARY();
-
- String projectSubmitType_REBASE_IF_NECESSARY();
-
- String projectSubmitType_REBASE_ALWAYS();
-
- String projectSubmitType_CHERRY_PICK();
-
- String headingProjectState();
-
- String projectState_ACTIVE();
-
- String projectState_READ_ONLY();
-
- String projectState_HIDDEN();
-
- String columnMember();
-
- String columnEmailAddress();
-
- String columnGroupName();
-
- String columnGroupDescription();
-
- String columnGroupType();
-
- String columnGroupNotifications();
-
- String columnGroupVisibleToAll();
-
- String columnDate();
-
- String columnType();
-
- String columnByUser();
-
- String typeAdded();
-
- String typeRemoved();
-
- String columnBranchName();
-
- String columnBranchRevision();
-
- String columnTagName();
-
- String columnTagRevision();
-
- String columnTagAnnotation();
-
- String initialRevision();
-
- String revision();
-
- String buttonAddBranch();
-
- String buttonDeleteBranch();
-
- String buttonAddTag();
-
- String buttonDeleteTag();
-
- String saveHeadButton();
-
- String cancelHeadButton();
-
- String groupItemHelp();
-
- String groupListTitle();
-
- String groupFilter();
-
- String createGroupTitle();
-
- String groupTabGeneral();
-
- String groupTabMembers();
-
- String groupTabAuditLog();
-
- String projectListTitle();
-
- String projectFilter();
-
- String createProjectTitle();
-
- String projectListQueryLink();
-
- String plugins();
-
- String pluginEnabled();
-
- String pluginDisabled();
-
- String pluginSettingsToolTip();
-
- String columnPluginName();
-
- String columnPluginSettings();
-
- String columnPluginVersion();
-
- String columnPluginStatus();
-
- String noGroupSelected();
-
- String errorNoMatchingGroups();
-
- String errorNoGitRepository();
-
- String addPermission();
-
- Map<String, String> permissionNames();
-
- String refErrorEmpty();
-
- String refErrorBeginSlash();
-
- String refErrorDoubleSlash();
-
- String refErrorNoSpace();
-
- String refErrorPrintable();
-
- String errorsMustBeFixed();
-
- String sectionTypeReference();
-
- String sectionTypeSection();
-
- Map<String, String> sectionNames();
-
- String pagedListPrev();
-
- String pagedListNext();
-
- String buttonCreate();
-
- String buttonCreateDescription();
-
- String buttonCreateChange();
-
- String buttonCreateChangeDescription();
-
- String buttonEditConfig();
-
- String buttonEditConfigDescription();
-
- String editConfigMessage();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
deleted file mode 100644
index 7901f33dfc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ /dev/null
@@ -1,204 +0,0 @@
-defaultAccountName = Name or Email
-defaultAccountGroupName = Group Name
-defaultBranchName = Branch Name
-defaultTagName = Tag Name
-defaultRevisionSpec = Revision (Branch or SHA-1)
-annotation = Annotation (optional)
-
-buttonDeleteIncludedGroup = Delete
-buttonAddIncludedGroup = Add
-buttonDeleteGroupMembers = Delete
-buttonAddGroupMember = Add
-buttonRenameGroup = Rename Group
-buttonSaveDescription = Save Description
-buttonCreateGroup = Create Group
-buttonCreateProject = Create Project
-buttonChangeGroupOwner = Change Owner
-buttonChangeGroupType = Change Type
-buttonSelectGroup = Select
-buttonSaveChanges = Save Changes
-checkBoxEmptyCommit = Create initial empty commit
-checkBoxPermissionsOnly = Only serve as parent for other projects
-buttonBrowseProjects = Browse
-projects = All projects
-projectRepoBrowser = Repository Browser
-useContentMerge = Allow content merges
-useContributorAgreements = Require a valid contributor agreement to upload
-useSignedOffBy = Require <code>Signed-off-by</code> in commit message
-createNewChangeForAllNotInTarget = Create a new change for every commit not in the target branch
-enableSignedPush = Enable signed push
-requireSignedPush = Require signed push
-requireChangeID = Require <code>Change-Id</code> in commit message
-rejectImplicitMerges = Reject implicit merges when changes are pushed for review
-privateByDefault = Set all new changes private by default
-workInProgressByDefault = Set all new changes work-in-progress by default
-headingMaxObjectSizeLimit = Maximum Git object size limit
-headingGroupOptions = Group Options
-isVisibleToAll = Make group visible to all registered users.
-buttonSaveGroupOptions = Save Group Options
-suggestedGroupLabel = group
-headingParentProjectName = Rights Inherit From
-parentSuggestions = Parent Suggestion
-columnProjectName = Project Name
-enableReviewerByEmail = Enable adding unregistered users as reviewers and CCs on changes
-matchAuthorToCommitterDate = Match authored date with committer date upon submit
-
-headingGroupUUID = Group UUID
-headingOwner = Owners
-headingDescription = Description
-headingProjectOptions = Project Options
-headingProjectCommands = Project Commands
-headingCommands = Commands
-headingMembers = Members
-headingIncludedGroups = Included Groups
-noMembersInfo = Group Members can only be viewed for Gerrit internal groups. For external groups and Gerrit system groups the members cannot be displayed.
-headingExternalGroup = Selected External Group
-headingCreateGroup = Create New Group
-headingAgreements = Contributor Agreements
-headingAuditLog = Audit Log
-
-headingProjectSubmitType = Submit Type
-projectSubmitType_INHERIT = Inherit
-projectSubmitType_FAST_FORWARD_ONLY = Fast Forward Only
-projectSubmitType_MERGE_IF_NECESSARY = Merge if Necessary
-projectSubmitType_REBASE_ALWAYS = Rebase Always
-projectSubmitType_REBASE_IF_NECESSARY = Rebase if Necessary
-projectSubmitType_MERGE_ALWAYS = Always Merge
-projectSubmitType_CHERRY_PICK = Cherry Pick
-
-headingProjectState = State
-projectState_ACTIVE = Active
-projectState_READ_ONLY = Read Only
-projectState_HIDDEN = Hidden
-
-columnMember = Member
-columnEmailAddress = Email Address
-columnGroupName = Group Name
-columnGroupDescription = Description
-columnGroupType = Group Type
-columnGroupNotifications = Email Only Authors
-columnGroupVisibleToAll = Visible To All
-
-columnDate = Date
-columnType = Type
-columnByUser = By User
-
-typeAdded = Added
-typeRemoved = Removed
-
-columnBranchName = Branch Name
-columnBranchRevision = Revision
-columnTagName = Tag Name
-columnTagRevision = Revision
-columnTagAnnotation = Annotation
-initialRevision = Initial Revision
-revision = Revision
-buttonAddBranch = Create Branch
-buttonAddTag = Create Tag
-buttonDeleteBranch = Delete
-buttonDeleteTag = Delete
-saveHeadButton = Save
-cancelHeadButton = Cancel
-
-groupItemHelp = group
-
-groupListTitle = Groups
-groupFilter = Filter
-createGroupTitle = Create Group
-groupTabGeneral = General
-groupTabMembers = Members
-groupTabAuditLog = Audit Log
-projectListTitle = Projects
-projectFilter = Filter
-createProjectTitle = Create Project
-projectListQueryLink = Search for changes on this project
-
-plugins = Plugins
-pluginEnabled = Enabled
-pluginDisabled = Disabled
-pluginSettingsToolTip = Plugin Settings
-columnPluginName = Plugin Name
-columnPluginSettings = Settings
-columnPluginVersion = Version
-columnPluginStatus = Status
-
-noGroupSelected = (No group selected)
-errorNoMatchingGroups = No Matching Groups
-errorNoGitRepository = No Git Repository
-
-pagedListPrev = &#x21e6;Prev
-pagedListNext = Next&#x21e8;
-
-addPermission = Add Permission ...
-
-# Permission Names
-permissionNames = \
- abandon, \
- addPatchSet, \
- create, \
- createTag, \
- createSignedTag, \
- delete, \
- deleteChanges, \
- deleteOwnChanges, \
- editAssignee, \
- editHashtags, \
- editTopicName, \
- forgeAuthor, \
- forgeCommitter, \
- forgeServerAsCommitter, \
- owner, \
- push, \
- pushMerge, \
- read, \
- rebase, \
- removeReviewer, \
- submit, \
- submitAs, \
- viewPrivateChanges
-
-abandon = Abandon
-addPatchSet = Add Patch Set
-create = Create Reference
-createTag = Create Annotated Tag
-createSignedTag = Create Signed Tag
-delete = Delete Reference
-deleteChanges = Delete Changes
-deleteOwnChanges = Delete Own Changes
-editAssignee = Edit Assignee
-editHashtags = Edit Hashtags
-editTopicName = Edit Topic Name
-forgeAuthor = Forge Author Identity
-forgeCommitter = Forge Committer Identity
-forgeServerAsCommitter = Forge Server Identity
-owner = Owner
-push = Push
-pushMerge = Push Merge Commit
-read = Read
-rebase = Rebase
-removeReviewer = Remove Reviewer
-submit = Submit
-submitAs = Submit (On Behalf Of)
-viewPrivateChanges = View Private Changes
-
-refErrorEmpty = Reference must be supplied
-refErrorBeginSlash = Reference must not start with '/'
-refErrorDoubleSlash = References cannot contain '//'
-refErrorNoSpace = References cannot contain spaces
-refErrorPrintable = References may contain only printable characters
-errorsMustBeFixed = Errors must be fixed before committing changes.
-
-# Section Names
-sectionTypeReference = Reference:
-sectionTypeSection = Section:
-sectionNames = \
- GLOBAL_CAPABILITIES
-GLOBAL_CAPABILITIES = Global Capabilities
-
-buttonCreate = Create
-buttonCreateDescription = Insert the description of the change.
-buttonCreateChange = Create Change
-buttonCreateChangeDescription = Create change directly in the browser.
-buttonEditConfig = Edit Config
-buttonEditConfigDescription = Creates a change to edit the project configuration in the browser.
-editConfigMessage = Edit Project Config
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java
deleted file mode 100644
index 53b2e720cd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java
+++ /dev/null
@@ -1,29 +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.client.admin;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface AdminCss extends CssResource {
- String deleteIcon();
-
- String undoIcon();
-
- String deleted();
-
- String deletedBorder();
-
- String deleteSectionHover();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
deleted file mode 100644
index 7b18a392ed..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
+++ /dev/null
@@ -1,46 +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.client.admin;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Messages;
-
-public interface AdminMessages extends Messages {
- AdminMessages I = GWT.create(AdminMessages.class);
-
- String group(String name);
-
- String label(String name);
-
- String labelAs(String name);
-
- String project(String name);
-
- String deletedGroup(int id);
-
- String deletedReference(String name);
-
- String deletedSection(String name);
-
- String effectiveMaxObjectSizeLimit(String effectiveMaxObjectSizeLimit);
-
- String noMaxObjectSizeLimit();
-
- String pluginProjectOptionsTitle(String pluginName);
-
- String pluginProjectInheritedValue(String value);
-
- String pluginProjectInheritedListValue(String value);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
deleted file mode 100644
index c9aa987964..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-group = Group {0}
-label = Label {0}
-labelAs = Label {0} (On Behalf Of)
-project = Project {0}
-deletedGroup = Deleted Group {0}
-deletedReference = Reference {0} was deleted
-deletedSection = Section {0} was deleted
-effectiveMaxObjectSizeLimit = effective: {0} bytes
-noMaxObjectSizeLimit = No max object size limit is set.
-pluginProjectOptionsTitle = {0} Plugin Options
-pluginProjectOptionsTitle = {0} Plugin
-pluginProjectInheritedValue = inherited: {0}
-pluginProjectInheritedListValue = INHERIT ({0})
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java
deleted file mode 100644
index dfbfbb6f7d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java
+++ /dev/null
@@ -1,37 +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.client.admin;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.ImageResource;
-
-public interface AdminResources extends ClientBundle {
- AdminResources I = GWT.create(AdminResources.class);
-
- @Source("admin.css")
- AdminCss css();
-
- /** unknown origin TODO replace icons */
- @Source("deleteNormal.png")
- ImageResource deleteNormal();
-
- @Source("deleteHover.png")
- ImageResource deleteHover();
-
- /** silk icons (CC-BY3.0): http://famfamfam.com/lab/icons/silk/ */
- @Source("arrow_undo.png")
- ImageResource undoNormal();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
deleted file mode 100644
index 611db856f6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateChangeAction.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2014 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.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.CreateChangeDialog;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.PopupPanel;
-
-class CreateChangeAction {
- static void call(Button b, String project) {
- // TODO Replace CreateChangeDialog with a nicer looking display.
- b.setEnabled(false);
- new CreateChangeDialog(new Project.NameKey(project)) {
- {
- sendButton.setText(AdminConstants.I.buttonCreate());
- message.setText(AdminConstants.I.buttonCreateDescription());
- }
-
- @Override
- public void onSend() {
- ChangeApi.createChange(
- project,
- getDestinationBranch(),
- getDestinationTopic(),
- message.getText(),
- null,
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- sent = true;
- hide();
- Gerrit.display(PageLinks.toChange(result.projectNameKey(), result.legacyId()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableButtons(true);
- super.onFailure(caught);
- }
- });
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- super.onClose(event);
- b.setEnabled(true);
- }
- }.center();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateGroupScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateGroupScreen.java
deleted file mode 100644
index 6914ee9b91..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateGroupScreen.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (C) 2012 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.client.admin;
-
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotFoundScreen;
-import com.google.gerrit.client.account.AccountCapabilities;
-import com.google.gerrit.client.groups.GroupApi;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-public class CreateGroupScreen extends Screen {
-
- private NpTextBox addTxt;
- private Button addNew;
-
- public CreateGroupScreen() {
- super();
- setRequiresSignIn(true);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- AccountCapabilities.all(
- new GerritCallback<AccountCapabilities>() {
- @Override
- public void onSuccess(AccountCapabilities ac) {
- if (ac.canPerform(CREATE_GROUP)) {
- display();
- } else {
- Gerrit.display(PageLinks.ADMIN_CREATE_GROUP, new NotFoundScreen());
- }
- }
- },
- CREATE_GROUP);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(AdminConstants.I.createGroupTitle());
- addCreateGroupPanel();
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- if (addTxt != null) {
- addTxt.setFocus(true);
- }
- }
-
- private void addCreateGroupPanel() {
- VerticalPanel addPanel = new VerticalPanel();
- addPanel.setStyleName(Gerrit.RESOURCES.css().addSshKeyPanel());
- addPanel.add(new SmallHeading(AdminConstants.I.headingCreateGroup()));
-
- addTxt =
- new NpTextBox() {
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (event.getTypeInt() == Event.ONPASTE) {
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- if (addTxt.getValue().trim().length() != 0) {
- addNew.setEnabled(true);
- }
- }
- });
- }
- }
- };
- addTxt.sinkEvents(Event.ONPASTE);
-
- addTxt.setVisibleLength(60);
- addTxt.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doCreateGroup();
- }
- }
- });
- addPanel.add(addTxt);
-
- addNew = new Button(AdminConstants.I.buttonCreateGroup());
- addNew.setEnabled(false);
- addNew.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doCreateGroup();
- }
- });
- addPanel.add(addNew);
- add(addPanel);
-
- new OnEditEnabler(addNew, addTxt);
- }
-
- private void doCreateGroup() {
- final String newName = addTxt.getText();
- if (newName == null || newName.length() == 0) {
- return;
- }
-
- addNew.setEnabled(false);
- GroupApi.createGroup(
- newName,
- new GerritCallback<GroupInfo>() {
- @Override
- public void onSuccess(GroupInfo result) {
- History.newItem(Dispatcher.toGroup(result.getGroupId(), AccountGroupScreen.MEMBERS));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- super.onFailure(caught);
- addNew.setEnabled(true);
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
deleted file mode 100644
index 02b9169024..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/CreateProjectScreen.java
+++ /dev/null
@@ -1,306 +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.client.admin;
-
-import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotFoundScreen;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.account.AccountCapabilities;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.projects.ProjectInfo;
-import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.ProjectListPopup;
-import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
-import com.google.gerrit.client.ui.ProjectsTable;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.ProjectUtil;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-public class CreateProjectScreen extends Screen {
- private Grid grid;
- private NpTextBox project;
- private Button create;
- private Button browse;
- private RemoteSuggestBox parent;
- private CheckBox emptyCommit;
- private CheckBox permissionsOnly;
- private ProjectsTable suggestedParentsTab;
- private ProjectListPopup projectsPopup;
-
- public CreateProjectScreen() {
- super();
- setRequiresSignIn(true);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- AccountCapabilities.all(
- new GerritCallback<AccountCapabilities>() {
- @Override
- public void onSuccess(AccountCapabilities ac) {
- if (ac.canPerform(CREATE_PROJECT)) {
- display();
- } else {
- Gerrit.display(PageLinks.ADMIN_CREATE_PROJECT, new NotFoundScreen());
- }
- }
- },
- CREATE_PROJECT);
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- projectsPopup.closePopup();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(AdminConstants.I.createProjectTitle());
- addCreateProjectPanel();
-
- /* popup */
- projectsPopup =
- new ProjectListPopup() {
- @Override
- protected void onMovePointerTo(String projectName) {
- // prevent user input from being overwritten by simply poping up
- if (!projectsPopup.isPoppingUp() || "".equals(parent.getText())) {
- parent.setText(projectName);
- }
- }
- };
- projectsPopup.initPopup(AdminConstants.I.projects(), PageLinks.ADMIN_PROJECTS);
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- if (project != null) {
- project.setFocus(true);
- }
- }
-
- private void addCreateProjectPanel() {
- final VerticalPanel fp = new VerticalPanel();
- fp.setStyleName(Gerrit.RESOURCES.css().createProjectPanel());
-
- initCreateButton();
- initCreateTxt();
- initParentBox();
-
- addGrid(fp);
-
- emptyCommit = new CheckBox(AdminConstants.I.checkBoxEmptyCommit());
- emptyCommit.setValue(true);
- permissionsOnly = new CheckBox(AdminConstants.I.checkBoxPermissionsOnly());
- fp.add(emptyCommit);
- fp.add(permissionsOnly);
- fp.add(create);
- VerticalPanel vp = new VerticalPanel();
- vp.add(fp);
- initSuggestedParents();
- vp.add(suggestedParentsTab);
- add(vp);
- }
-
- private void initCreateTxt() {
- project =
- new NpTextBox() {
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- if (event.getTypeInt() == Event.ONPASTE) {
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- if (project.getValue().trim().length() != 0) {
- create.setEnabled(true);
- }
- }
- });
- }
- }
- };
- project.sinkEvents(Event.ONPASTE);
- project.setVisibleLength(50);
- project.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doCreateProject();
- }
- }
- });
- new OnEditEnabler(create, project);
- }
-
- private void initCreateButton() {
- create = new Button(AdminConstants.I.buttonCreateProject());
- create.setEnabled(false);
- create.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doCreateProject();
- }
- });
-
- browse = new Button(AdminConstants.I.buttonBrowseProjects());
- browse.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- int top = grid.getAbsoluteTop() - 50; // under page header
- // Try to place it to the right of everything else, but not
- // right justified
- int left =
- 5
- + Math.max(
- grid.getAbsoluteLeft() + grid.getOffsetWidth(),
- suggestedParentsTab.getAbsoluteLeft()
- + suggestedParentsTab.getOffsetWidth());
- projectsPopup.setPreferredCoordinates(top, left);
- projectsPopup.displayPopup();
- }
- });
- }
-
- private void initParentBox() {
- parent = new RemoteSuggestBox(new ProjectNameSuggestOracle());
- parent.setVisibleLength(50);
- }
-
- private void initSuggestedParents() {
- suggestedParentsTab =
- new ProjectsTable() {
- {
- table.setText(0, 1, AdminConstants.I.parentSuggestions());
- }
-
- @Override
- protected void populate(int row, ProjectInfo k) {
- populateState(row, k);
- final Anchor projectLink = new Anchor(k.name());
- projectLink.addClickHandler(
- new ClickHandler() {
-
- @Override
- public void onClick(ClickEvent event) {
- parent.setText(getRowItem(row).name());
- }
- });
-
- table.setWidget(row, ProjectsTable.C_NAME, projectLink);
- table.setText(row, ProjectsTable.C_DESCRIPTION, k.description());
-
- setRowItem(row, k);
- }
- };
- suggestedParentsTab.setVisible(false);
-
- ProjectMap.parentCandidates(
- new GerritCallback<ProjectMap>() {
- @Override
- public void onSuccess(ProjectMap list) {
- if (!list.isEmpty()) {
- suggestedParentsTab.setVisible(true);
- suggestedParentsTab.display(list);
- suggestedParentsTab.finishDisplay();
- }
- }
- });
- }
-
- private void addGrid(VerticalPanel fp) {
- grid = new Grid(2, 3);
- grid.setStyleName(Gerrit.RESOURCES.css().infoBlock());
- grid.setText(0, 0, AdminConstants.I.columnProjectName() + ":");
- grid.setWidget(0, 1, project);
- grid.setText(1, 0, AdminConstants.I.headingParentProjectName() + ":");
- grid.setWidget(1, 1, parent);
- grid.setWidget(1, 2, browse);
- fp.add(grid);
- }
-
- private void doCreateProject() {
- final String projectName = project.getText().trim();
- final String parentName = parent.getText().trim();
-
- if ("".equals(projectName)) {
- project.setFocus(true);
- return;
- }
-
- enableForm(false);
- ProjectApi.createProject(
- projectName,
- parentName,
- emptyCommit.getValue(),
- permissionsOnly.getValue(),
- new AsyncCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- String nameWithoutSuffix = ProjectUtil.stripGitSuffix(projectName);
- History.newItem(
- Dispatcher.toProjectAdmin(
- new Project.NameKey(nameWithoutSuffix), ProjectScreen.INFO));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- new ErrorDialog(caught.getMessage()).center();
- enableForm(true);
- }
- });
- }
-
- private void enableForm(boolean enabled) {
- project.setEnabled(enabled);
- create.setEnabled(enabled);
- parent.setEnabled(enabled);
- emptyCommit.setEnabled(enabled);
- permissionsOnly.setEnabled(enabled);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java
deleted file mode 100644
index cb2ca0f3d4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/EditConfigAction.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2015 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.client.admin;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gwt.user.client.ui.Button;
-
-public class EditConfigAction {
-
- static void call(Button b, Project.NameKey project) {
- b.setEnabled(false);
-
- ChangeApi.createChange(
- project.get(),
- RefNames.REFS_CONFIG,
- null,
- AdminConstants.I.editConfigMessage(),
- null,
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- Gerrit.display(
- Dispatcher.toEditScreen(
- project, new PatchSet.Id(result.legacyId(), 1), "project.config"));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- b.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
deleted file mode 100644
index b37a680f07..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
+++ /dev/null
@@ -1,232 +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.client.admin;
-
-import static com.google.gerrit.common.PageLinks.ADMIN_GROUPS;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.groups.GroupMap;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.PagingHyperlink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
-import com.google.gwt.http.client.URL;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-public class GroupListScreen extends Screen {
- private Hyperlink prev;
- private Hyperlink next;
- private GroupTable groups;
- private NpTextBox filterTxt;
- private int pageSize;
-
- private String match = "";
- private int start;
- private Query query;
-
- public GroupListScreen() {
- setRequiresSignIn(true);
- pageSize = Gerrit.getUserPreferences().changesPerPage();
- }
-
- public GroupListScreen(String params) {
- this();
- for (String kvPair : params.split("[,;&]")) {
- String[] kv = kvPair.split("=", 2);
- if (kv.length != 2 || kv[0].isEmpty()) {
- continue;
- }
-
- if ("filter".equals(kv[0])) {
- match = URL.decodeQueryString(kv[1]);
- }
-
- if ("skip".equals(kv[0]) && URL.decodeQueryString(kv[1]).matches("^[\\d]+")) {
- start = Integer.parseInt(URL.decodeQueryString(kv[1]));
- }
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- query = new Query(match).start(start).run();
- }
-
- private void setupNavigationLink(Hyperlink link, String filter, int skip) {
- link.setTargetHistoryToken(getTokenForScreen(filter, skip));
- link.setVisible(true);
- }
-
- private String getTokenForScreen(String filter, int skip) {
- String token = ADMIN_GROUPS;
- if (filter != null && !filter.isEmpty()) {
- token += "?filter=" + URL.encodeQueryString(filter);
- }
- if (skip > 0) {
- if (token.contains("?filter=")) {
- token += ",";
- } else {
- token += "?";
- }
- token += "skip=" + skip;
- }
- return token;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(AdminConstants.I.groupListTitle());
- initPageHeader();
-
- prev = PagingHyperlink.createPrev();
- prev.setVisible(false);
-
- next = PagingHyperlink.createNext();
- next.setVisible(false);
-
- groups = new GroupTable(PageLinks.ADMIN_GROUPS);
- add(groups);
-
- final HorizontalPanel buttons = new HorizontalPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().changeTablePrevNextLinks());
- buttons.add(prev);
- buttons.add(next);
- add(buttons);
- }
-
- private void initPageHeader() {
- final HorizontalPanel hp = new HorizontalPanel();
- hp.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
- final Label filterLabel = new Label(AdminConstants.I.projectFilter());
- filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
- hp.add(filterLabel);
- filterTxt = new NpTextBox();
- filterTxt.setValue(match);
- filterTxt.addKeyUpHandler(
- new KeyUpHandler() {
- @Override
- public void onKeyUp(KeyUpEvent event) {
- Query q =
- new Query(filterTxt.getValue())
- .open(event.getNativeKeyCode() == KeyCodes.KEY_ENTER);
- if (match.equals(q.qMatch)) {
- q.start(start);
- }
- if (q.open || !match.equals(q.qMatch)) {
- if (query == null) {
- q.run();
- }
- query = q;
- }
- }
- });
- hp.add(filterTxt);
- add(hp);
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- if (match != null) {
- filterTxt.setCursorPos(match.length());
- }
- filterTxt.setFocus(true);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- groups.setRegisterKeys(true);
- }
-
- private class Query {
- private final String qMatch;
- private int qStart;
- private boolean open;
-
- Query(String match) {
- this.qMatch = match;
- }
-
- Query start(int start) {
- this.qStart = start;
- return this;
- }
-
- Query open(boolean open) {
- this.open = open;
- return this;
- }
-
- Query run() {
- int limit = open ? 1 : pageSize + 1;
- GroupMap.match(
- qMatch,
- limit,
- qStart,
- new GerritCallback<GroupMap>() {
- @Override
- public void onSuccess(GroupMap result) {
- if (!isAttached()) {
- // View has been disposed.
- } else if (query == Query.this) {
- query = null;
- showMap(result);
- } else {
- query.run();
- }
- }
- });
- return this;
- }
-
- private void showMap(GroupMap result) {
- if (open && !result.isEmpty()) {
- Gerrit.display(PageLinks.toGroup(result.values().get(0).getGroupUUID()));
- return;
- }
-
- setToken(getTokenForScreen(qMatch, qStart));
- GroupListScreen.this.match = qMatch;
- GroupListScreen.this.start = qStart;
-
- if (result.size() <= pageSize) {
- groups.display(result, qMatch);
- next.setVisible(false);
- } else {
- groups.displaySubset(result, 0, result.size() - 1, qMatch);
- setupNavigationLink(next, qMatch, qStart + pageSize);
- }
-
- if (qStart > 0) {
- setupNavigationLink(prev, qMatch, qStart - pageSize);
- } else {
- prev.setVisible(false);
- }
-
- if (!isCurrentView()) {
- display();
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
deleted file mode 100644
index db138a9503..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
+++ /dev/null
@@ -1,116 +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.client.admin;
-
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.editor.client.LeafValueEditor;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.HasCloseHandlers;
-import com.google.gwt.event.logical.shared.HasSelectionHandlers;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Focusable;
-
-public class GroupReferenceBox extends Composite
- implements LeafValueEditor<GroupReference>,
- HasSelectionHandlers<GroupReference>,
- HasCloseHandlers<GroupReferenceBox>,
- Focusable {
- private final AccountGroupSuggestOracle oracle;
- private final RemoteSuggestBox suggestBox;
-
- public GroupReferenceBox() {
- oracle = new AccountGroupSuggestOracle();
- suggestBox = new RemoteSuggestBox(oracle);
- initWidget(suggestBox);
-
- suggestBox.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- SelectionEvent.fire(GroupReferenceBox.this, toValue(event.getSelectedItem()));
- }
- });
- suggestBox.addCloseHandler(
- new CloseHandler<RemoteSuggestBox>() {
- @Override
- public void onClose(CloseEvent<RemoteSuggestBox> event) {
- suggestBox.setText("");
- CloseEvent.fire(GroupReferenceBox.this, GroupReferenceBox.this);
- }
- });
- }
-
- public void setVisibleLength(int len) {
- suggestBox.setVisibleLength(len);
- }
-
- @Override
- public HandlerRegistration addSelectionHandler(SelectionHandler<GroupReference> handler) {
- return addHandler(handler, SelectionEvent.getType());
- }
-
- @Override
- public HandlerRegistration addCloseHandler(CloseHandler<GroupReferenceBox> handler) {
- return addHandler(handler, CloseEvent.getType());
- }
-
- @Override
- public GroupReference getValue() {
- return toValue(suggestBox.getText());
- }
-
- private GroupReference toValue(String name) {
- if (name != null && !name.isEmpty()) {
- return new GroupReference(oracle.getUUID(name), name);
- }
- return null;
- }
-
- @Override
- public void setValue(GroupReference value) {
- suggestBox.setText(value != null ? value.getName() : "");
- }
-
- @Override
- public int getTabIndex() {
- return suggestBox.getTabIndex();
- }
-
- @Override
- public void setTabIndex(int index) {
- suggestBox.setTabIndex(index);
- }
-
- @Override
- public void setFocus(boolean focused) {
- suggestBox.setFocus(focused);
- }
-
- @Override
- public void setAccessKey(char key) {
- suggestBox.setAccessKey(key);
- }
-
- public void setProject(Project.NameKey projectName) {
- oracle.setProject(projectName);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
deleted file mode 100644
index be0db41ac1..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
+++ /dev/null
@@ -1,151 +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.client.admin;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.groups.GroupList;
-import com.google.gerrit.client.groups.GroupMap;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.HighlightingInlineHyperlink;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.Util;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-import com.google.gwt.user.client.ui.Image;
-import java.util.List;
-
-public class GroupTable extends NavigationTable<GroupInfo> {
- private static final int NUM_COLS = 3;
-
- public GroupTable() {
- this(null);
- }
-
- public GroupTable(String pointerId) {
- super(AdminConstants.I.groupItemHelp());
- setSavePointerId(pointerId);
-
- table.setText(0, 1, AdminConstants.I.columnGroupName());
- table.setText(0, 2, AdminConstants.I.columnGroupDescription());
- table.setText(0, 3, AdminConstants.I.columnGroupVisibleToAll());
- table.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final Cell cell = table.getCellForEvent(event);
- if (cell != null
- && cell.getCellIndex() != 1
- && getRowItem(cell.getRowIndex()) != null) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- for (int i = 1; i <= NUM_COLS; i++) {
- fmt.addStyleName(0, i, Gerrit.RESOURCES.css().dataHeader());
- }
- }
-
- @Override
- protected Object getRowItemKey(GroupInfo item) {
- return item.getGroupId();
- }
-
- @Override
- protected void onOpenRow(int row) {
- GroupInfo groupInfo = getRowItem(row);
- if (isInteralGroup(groupInfo)) {
- History.newItem(Dispatcher.toGroup(groupInfo.getGroupId()));
- } else if (groupInfo.url() != null) {
- Window.open(groupInfo.url(), "_self", null);
- }
- }
-
- public void display(GroupMap groups, String toHighlight) {
- display(Natives.asList(groups.values()), toHighlight);
- }
-
- public void display(GroupList groups) {
- display(Natives.asList(groups), null);
- }
-
- public void display(List<GroupInfo> list, String toHighlight) {
- displaySubset(list, toHighlight, 0, list.size());
- }
-
- public void displaySubset(GroupMap groups, int fromIndex, int toIndex, String toHighlight) {
- displaySubset(Natives.asList(groups.values()), toHighlight, fromIndex, toIndex);
- }
-
- public void displaySubset(List<GroupInfo> list, String toHighlight, int fromIndex, int toIndex) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- list.sort(comparing(GroupInfo::name));
- for (GroupInfo group : list.subList(fromIndex, toIndex)) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, group, toHighlight);
- }
- }
-
- void populate(int row, GroupInfo k, String toHighlight) {
- if (k.url() != null) {
- if (isInteralGroup(k)) {
- table.setWidget(
- row,
- 1,
- new HighlightingInlineHyperlink(
- k.name(), Dispatcher.toGroup(k.getGroupId()), toHighlight));
- } else {
- Anchor link = new Anchor();
- link.setHTML(Util.highlight(k.name(), toHighlight));
- link.setHref(k.url());
- table.setWidget(row, 1, link);
- }
- } else {
- table.setHTML(row, 1, Util.highlight(k.name(), toHighlight));
- }
- table.setText(row, 2, k.description());
- if (k.options().isVisibleToAll()) {
- table.setWidget(row, 3, new Image(Gerrit.RESOURCES.greenCheck()));
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().groupName());
- for (int i = 1; i <= NUM_COLS; i++) {
- fmt.addStyleName(row, i, Gerrit.RESOURCES.css().dataCell());
- }
-
- setRowItem(row, k);
- }
-
- private boolean isInteralGroup(GroupInfo groupInfo) {
- return groupInfo != null && groupInfo.url().startsWith("#" + PageLinks.ADMIN_GROUPS);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PaginatedProjectScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PaginatedProjectScreen.java
deleted file mode 100644
index 75c3cb6898..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PaginatedProjectScreen.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2015 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.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.http.client.URL;
-
-abstract class PaginatedProjectScreen extends ProjectScreen {
- protected int pageSize;
- protected String match = "";
- protected int start;
-
- PaginatedProjectScreen(Project.NameKey toShow) {
- super(toShow);
- pageSize = Gerrit.getUserPreferences().changesPerPage();
- }
-
- protected void parseToken(String token) {
- for (String kvPair : token.split("[,;&/?]")) {
- String[] kv = kvPair.split("=", 2);
- if (kv.length != 2 || kv[0].isEmpty()) {
- continue;
- }
-
- if ("filter".equals(kv[0])) {
- match = URL.decodeQueryString(kv[1]);
- }
-
- if ("skip".equals(kv[0]) && URL.decodeQueryString(kv[1]).matches("^[\\d]+")) {
- start = Integer.parseInt(URL.decodeQueryString(kv[1]));
- }
- }
- }
-
- protected void parseToken() {
- parseToken(getToken());
- }
-
- protected String getTokenForScreen(String filter, int skip) {
- String token = getScreenToken();
- if (filter != null && !filter.isEmpty()) {
- token += "?filter=" + URL.encodeQueryString(filter);
- }
- if (skip > 0) {
- if (token.contains("?filter=")) {
- token += ",";
- } else {
- token += "?";
- }
- token += "skip=" + skip;
- }
- return token;
- }
-
- protected abstract String getScreenToken();
-
- protected void setupNavigationLink(Hyperlink link, String filter, int skip) {
- link.setTargetHistoryToken(getTokenForScreen(filter, skip));
- link.setVisible(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
deleted file mode 100644
index 39dadcc5b8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
+++ /dev/null
@@ -1,329 +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.client.admin;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.groups.GroupMap;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupInfo;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelTypes;
-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.ProjectAccess;
-import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.editor.client.Editor;
-import com.google.gwt.editor.client.EditorDelegate;
-import com.google.gwt.editor.client.ValueAwareEditor;
-import com.google.gwt.editor.client.adapters.EditorSource;
-import com.google.gwt.editor.client.adapters.ListEditor;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.ValueLabel;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class PermissionEditor extends Composite
- implements Editor<Permission>, ValueAwareEditor<Permission> {
- interface Binder extends UiBinder<HTMLPanel, PermissionEditor> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField(provided = true)
- @Path("name")
- ValueLabel<String> normalName;
-
- @UiField(provided = true)
- @Path("name")
- ValueLabel<String> deletedName;
-
- @UiField CheckBox exclusiveGroup;
-
- @UiField FlowPanel ruleContainer;
- ListEditor<PermissionRule, PermissionRuleEditor> rules;
-
- @UiField DivElement addContainer;
- @UiField DivElement addStage1;
- @UiField DivElement addStage2;
- @UiField Anchor beginAddRule;
- @UiField @Editor.Ignore GroupReferenceBox groupToAdd;
- @UiField Button addRule;
-
- @UiField Anchor deletePermission;
-
- @UiField DivElement normal;
- @UiField DivElement deleted;
-
- private final Project.NameKey projectName;
- private final Map<AccountGroup.UUID, GroupInfo> groupInfo;
- private final boolean readOnly;
- private final AccessSection section;
- private final LabelTypes labelTypes;
- private Permission value;
- private PermissionRange.WithDefaults validRange;
- private boolean isDeleted;
-
- public PermissionEditor(
- ProjectAccess projectAccess, boolean readOnly, AccessSection section, LabelTypes labelTypes) {
- this.readOnly = readOnly;
- this.section = section;
- this.projectName = projectAccess.getProjectName();
- this.groupInfo = projectAccess.getGroupInfo();
- this.labelTypes = labelTypes;
-
- PermissionNameRenderer nameRenderer =
- new PermissionNameRenderer(projectAccess.getCapabilities());
- normalName = new ValueLabel<>(nameRenderer);
- deletedName = new ValueLabel<>(nameRenderer);
-
- initWidget(uiBinder.createAndBindUi(this));
- groupToAdd.setProject(projectName);
- rules = ListEditor.of(new RuleEditorSource());
-
- exclusiveGroup.setEnabled(!readOnly);
- exclusiveGroup.setVisible(RefConfigSection.isValid(section.getName()));
-
- if (readOnly) {
- addContainer.removeFromParent();
- addContainer = null;
-
- deletePermission.removeFromParent();
- deletePermission = null;
- }
- }
-
- @UiHandler("deletePermission")
- void onDeleteHover(@SuppressWarnings("unused") MouseOverEvent event) {
- addStyleName(AdminResources.I.css().deleteSectionHover());
- }
-
- @UiHandler("deletePermission")
- void onDeleteNonHover(@SuppressWarnings("unused") MouseOutEvent event) {
- removeStyleName(AdminResources.I.css().deleteSectionHover());
- }
-
- @UiHandler("deletePermission")
- void onDeletePermission(@SuppressWarnings("unused") ClickEvent event) {
- isDeleted = true;
- normal.getStyle().setDisplay(Display.NONE);
- deleted.getStyle().setDisplay(Display.BLOCK);
- }
-
- @UiHandler("undoDelete")
- void onUndoDelete(@SuppressWarnings("unused") ClickEvent event) {
- isDeleted = false;
- deleted.getStyle().setDisplay(Display.NONE);
- normal.getStyle().setDisplay(Display.BLOCK);
- }
-
- @UiHandler("beginAddRule")
- void onBeginAddRule(@SuppressWarnings("unused") ClickEvent event) {
- beginAddRule();
- }
-
- void beginAddRule() {
- addStage1.getStyle().setDisplay(Display.NONE);
- addStage2.getStyle().setDisplay(Display.BLOCK);
-
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- groupToAdd.setFocus(true);
- }
- });
- }
-
- @UiHandler("addRule")
- void onAddGroupByClick(@SuppressWarnings("unused") ClickEvent event) {
- GroupReference ref = groupToAdd.getValue();
- if (ref != null) {
- addGroup(ref);
- } else {
- groupToAdd.setFocus(true);
- }
- }
-
- @UiHandler("groupToAdd")
- void onAddGroupByEnter(SelectionEvent<GroupReference> event) {
- GroupReference ref = event.getSelectedItem();
- if (ref != null) {
- addGroup(ref);
- }
- }
-
- @UiHandler("groupToAdd")
- void onAbortAddGroup(@SuppressWarnings("unused") CloseEvent<GroupReferenceBox> event) {
- hideAddGroup();
- }
-
- @UiHandler("hideAddGroup")
- void hideAddGroup(@SuppressWarnings("unused") ClickEvent event) {
- hideAddGroup();
- }
-
- private void hideAddGroup() {
- addStage1.getStyle().setDisplay(Display.BLOCK);
- addStage2.getStyle().setDisplay(Display.NONE);
- }
-
- private void addGroup(GroupReference ref) {
- if (ref.getUUID() != null) {
- if (value.getRule(ref) == null) {
- PermissionRule newRule = value.getRule(ref, true);
- if (validRange != null) {
- int min = validRange.getDefaultMin();
- int max = validRange.getDefaultMax();
- newRule.setRange(min, max);
-
- } else if (GlobalCapability.PRIORITY.equals(value.getName())) {
- newRule.setAction(PermissionRule.Action.BATCH);
- }
-
- rules.getList().add(newRule);
- }
- groupToAdd.setValue(null);
- groupToAdd.setFocus(true);
-
- } else {
- // If the oracle didn't get to complete a UUID, resolve it now.
- //
- addRule.setEnabled(false);
- GroupMap.suggestAccountGroupForProject(
- projectName.get(),
- ref.getName(),
- 1,
- new GerritCallback<GroupMap>() {
- @Override
- public void onSuccess(GroupMap result) {
- addRule.setEnabled(true);
- if (result.values().length() == 1) {
- addGroup(
- new GroupReference(
- result.values().get(0).getGroupUUID(), result.values().get(0).name()));
- } else {
- groupToAdd.setFocus(true);
- new ErrorDialog(Gerrit.M.noSuchGroupMessage(ref.getName())).center();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addRule.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
- }
-
- boolean isDeleted() {
- return isDeleted;
- }
-
- @Override
- public void setValue(Permission value) {
- this.value = value;
-
- if (Permission.hasRange(value.getName())) {
- LabelType lt = labelTypes.byLabel(value.getLabel());
- if (lt != null) {
- validRange =
- new PermissionRange.WithDefaults(
- value.getName(),
- lt.getMin().getValue(),
- lt.getMax().getValue(),
- lt.getMin().getValue(),
- lt.getMax().getValue());
- }
- } else if (GlobalCapability.isGlobalCapability(value.getName())) {
- validRange = GlobalCapability.getRange(value.getName());
-
- } else {
- validRange = null;
- }
-
- if (Permission.OWNER.equals(value.getName())) {
- exclusiveGroup.setEnabled(false);
- } else {
- exclusiveGroup.setEnabled(!readOnly);
- }
- }
-
- @Override
- public void flush() {
- List<PermissionRule> src = rules.getList();
- List<PermissionRule> keep = new ArrayList<>(src.size());
-
- for (int i = 0; i < src.size(); i++) {
- PermissionRuleEditor e = (PermissionRuleEditor) ruleContainer.getWidget(i);
- if (!e.isDeleted()) {
- keep.add(src.get(i));
- }
- }
- value.setRules(keep);
- }
-
- @Override
- public void onPropertyChange(String... paths) {}
-
- @Override
- public void setDelegate(EditorDelegate<Permission> delegate) {}
-
- private class RuleEditorSource extends EditorSource<PermissionRuleEditor> {
- @Override
- public PermissionRuleEditor create(int index) {
- PermissionRuleEditor subEditor =
- new PermissionRuleEditor(readOnly, groupInfo, section, value, validRange);
- ruleContainer.insert(subEditor, index);
- return subEditor;
- }
-
- @Override
- public void dispose(PermissionRuleEditor subEditor) {
- subEditor.removeFromParent();
- }
-
- @Override
- public void setIndex(PermissionRuleEditor subEditor, int index) {
- ruleContainer.insert(subEditor, index);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml
deleted file mode 100644
index 00c41dc09f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml
+++ /dev/null
@@ -1,146 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:e='urn:import:com.google.gwt.editor.ui.client'
- xmlns:my='urn:import:com.google.gerrit.client.admin'
- ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
- ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
- ui:generateLocales='default,en'
- >
-<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
-<ui:style gss='false'>
- @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
- @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
-
- .panel {
- position: relative;
- }
-
- .normal {
- border: 1px solid backgroundColor;
- margin-top: -1px;
- margin-bottom: -1px;
- }
-
- .header {
- position: relative;
- padding-left: 5px;
- padding-right: 5px;
- padding-bottom: 1px;
- white-space: nowrap;
- }
-
- .header:hover {
- background-color: selectionColor;
- }
-
- .name {
- font-style: italic;
- }
-
- .exclusiveGroup {
- position: absolute;
- top: 0;
- right: 36px;
- width: 7em;
- font-size: 80%;
- }
-
- .addContainer {
- padding-left: 10px;
- position: relative;
- }
- .addContainer:hover {
- background-color: selectionColor;
- }
- .addLink {
- font-size: 80%;
- }
-
- .deleteIcon {
- position: absolute;
- top: 1px;
- right: 12px;
- }
-</ui:style>
-
-<g:HTMLPanel stylePrimaryName='{style.panel}'>
-<div ui:field='normal' class='{style.normal}'>
- <div class='{style.header}'>
- <g:ValueLabel styleName='{style.name}' ui:field='normalName'/>
- <g:CheckBox
- ui:field='exclusiveGroup'
- addStyleNames='{style.exclusiveGroup}'
- text='Exclusive'>
- <ui:attribute name='text'/>
- </g:CheckBox>
- <g:Anchor
- ui:field='deletePermission'
- href='javascript:void'
- styleName='{style.deleteIcon} {res.css.deleteIcon}'
- title='Delete this permission (and nested rules)'>
- <ui:attribute name='title'/>
- </g:Anchor>
- </div>
- <g:FlowPanel ui:field='ruleContainer'/>
- <div ui:field='addContainer' class='{style.addContainer}'>
- <div ui:field='addStage1'>
- <g:Anchor
- ui:field='beginAddRule'
- styleName='{style.addLink}'
- href='javascript:void'
- text='Add Group'>
- <ui:attribute name='text'/>
- </g:Anchor>
- </div>
- <div ui:field='addStage2' style='display: none'>
- <ui:msg>Group Name: <my:GroupReferenceBox
- ui:field='groupToAdd'
- visibleLength='45'/></ui:msg>
- <g:Button
- ui:field='addRule'
- text='Add'>
- <ui:attribute name='text'/>
- </g:Button>
- <g:Anchor
- ui:field='hideAddGroup'
- href='javascript:void'
- styleName='{style.deleteIcon} {res.css.deleteIcon}'
- title='Cancel additional group'>
- <ui:attribute name='title'/>
- </g:Anchor>
- </div>
- </div>
-</div>
-
-<div
- ui:field='deleted'
- class='{res.css.deleted} {res.css.deletedBorder}'
- style='display: none'>
- <ui:msg>Permission <g:ValueLabel styleName='{style.name}' ui:field='deletedName'/> was deleted</ui:msg>
- <g:Anchor
- ui:field='undoDelete'
- href='javascript:void'
- styleName='{style.deleteIcon} {res.css.undoIcon}'
- title='Undo deletion'>
- <ui:attribute name='title'/>
- </g:Anchor>
-</div>
-</g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java
deleted file mode 100644
index ef02bd052a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java
+++ /dev/null
@@ -1,74 +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.client.admin;
-
-import com.google.gerrit.common.data.Permission;
-import com.google.gwt.text.shared.Renderer;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-class PermissionNameRenderer implements Renderer<String> {
- private static final Map<String, String> permissions;
-
- static {
- permissions = new HashMap<>();
- for (Map.Entry<String, String> e : AdminConstants.I.permissionNames().entrySet()) {
- permissions.put(e.getKey(), e.getValue());
- permissions.put(e.getKey().toLowerCase(), e.getValue());
- }
- }
-
- private final Map<String, String> fromServer;
-
- PermissionNameRenderer(Map<String, String> allFromOutside) {
- fromServer = allFromOutside;
- }
-
- @Override
- public String render(String varName) {
- if (Permission.isLabelAs(varName)) {
- return AdminMessages.I.labelAs(Permission.extractLabel(varName));
- } else if (Permission.isLabel(varName)) {
- return AdminMessages.I.label(Permission.extractLabel(varName));
- }
-
- String desc = permissions.get(varName);
- if (desc != null) {
- return desc;
- }
-
- desc = fromServer.get(varName);
- if (desc != null) {
- return desc;
- }
-
- desc = permissions.get(varName.toLowerCase());
- if (desc != null) {
- return desc;
- }
-
- desc = fromServer.get(varName.toLowerCase());
- if (desc != null) {
- return desc;
- }
- return varName;
- }
-
- @Override
- public void render(String object, Appendable appendable) throws IOException {
- appendable.append(render(object));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
deleted file mode 100644
index 16dd1679c3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
+++ /dev/null
@@ -1,258 +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.client.admin;
-
-import static com.google.gerrit.common.data.Permission.EDIT_TOPIC_NAME;
-import static com.google.gerrit.common.data.Permission.PUSH;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.GroupInfo;
-import com.google.gerrit.common.data.GroupReference;
-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.gwt.core.client.GWT;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.SpanElement;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.editor.client.Editor;
-import com.google.gwt.editor.client.EditorDelegate;
-import com.google.gwt.editor.client.ValueAwareEditor;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.text.shared.Renderer;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.ValueListBox;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-public class PermissionRuleEditor extends Composite
- implements Editor<PermissionRule>, ValueAwareEditor<PermissionRule> {
- interface Binder extends UiBinder<HTMLPanel, PermissionRuleEditor> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField(provided = true)
- ValueListBox<PermissionRule.Action> action;
-
- @UiField(provided = true)
- RangeBox min;
-
- @UiField(provided = true)
- RangeBox max;
-
- @UiField CheckBox force;
-
- @UiField Anchor groupNameLink;
- @UiField SpanElement groupNameSpan;
- @UiField SpanElement deletedGroupName;
-
- @UiField Anchor deleteRule;
-
- @UiField DivElement normal;
- @UiField DivElement deleted;
-
- @UiField SpanElement rangeEditor;
-
- private Map<AccountGroup.UUID, GroupInfo> groupInfo;
- private boolean isDeleted;
- private HandlerRegistration clickHandler;
-
- public PermissionRuleEditor(
- boolean readOnly,
- Map<AccountGroup.UUID, GroupInfo> groupInfo,
- AccessSection section,
- Permission permission,
- PermissionRange.WithDefaults validRange) {
- this.groupInfo = groupInfo;
- action = new ValueListBox<>(actionRenderer);
-
- if (validRange != null && 10 < validRange.getRangeSize()) {
- min = new RangeBox.Box();
- max = new RangeBox.Box();
-
- } else if (validRange != null) {
- RangeBox.List minList = new RangeBox.List();
- RangeBox.List maxList = new RangeBox.List();
- List<Integer> valueList = validRange.getValuesAsList();
-
- minList.list.setValue(validRange.getMin());
- maxList.list.setValue(validRange.getMax());
-
- minList.list.setAcceptableValues(valueList);
- maxList.list.setAcceptableValues(valueList);
-
- min = minList;
- max = maxList;
-
- action.setAcceptableValues(
- Arrays.asList(
- PermissionRule.Action.ALLOW,
- PermissionRule.Action.DENY,
- PermissionRule.Action.BLOCK));
-
- } else {
- min = new RangeBox.Box();
- max = new RangeBox.Box();
-
- if (GlobalCapability.PRIORITY.equals(permission.getName())) {
- action.setValue(PermissionRule.Action.INTERACTIVE);
- action.setAcceptableValues(
- Arrays.asList(PermissionRule.Action.INTERACTIVE, PermissionRule.Action.BATCH));
-
- } else {
- action.setValue(PermissionRule.Action.ALLOW);
- action.setAcceptableValues(
- Arrays.asList(
- PermissionRule.Action.ALLOW,
- PermissionRule.Action.DENY,
- PermissionRule.Action.BLOCK));
- }
- }
-
- initWidget(uiBinder.createAndBindUi(this));
-
- String name = permission.getName();
- boolean canForce = PUSH.equals(name);
- if (canForce) {
- String ref = section.getName();
- canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/");
- force.setText(PermissionRule.FORCE_PUSH);
- } else {
- canForce = EDIT_TOPIC_NAME.equals(name);
- force.setText(PermissionRule.FORCE_EDIT);
- }
- force.setVisible(canForce);
- force.setEnabled(!readOnly);
- action.getElement().setPropertyBoolean("disabled", readOnly);
-
- if (validRange != null) {
- min.setEnabled(!readOnly);
- max.setEnabled(!readOnly);
- } else {
- rangeEditor.getStyle().setDisplay(Display.NONE);
- }
-
- if (readOnly) {
- deleteRule.removeFromParent();
- deleteRule = null;
- }
-
- if (name.equals(GlobalCapability.BATCH_CHANGES_LIMIT)) {
- min.setEnabled(false);
- }
- }
-
- boolean isDeleted() {
- return isDeleted;
- }
-
- @UiHandler("deleteRule")
- void onDeleteRule(@SuppressWarnings("unused") ClickEvent event) {
- isDeleted = true;
- normal.getStyle().setDisplay(Display.NONE);
- deleted.getStyle().setDisplay(Display.BLOCK);
- }
-
- @UiHandler("undoDelete")
- void onUndoDelete(@SuppressWarnings("unused") ClickEvent event) {
- isDeleted = false;
- deleted.getStyle().setDisplay(Display.NONE);
- normal.getStyle().setDisplay(Display.BLOCK);
- }
-
- @Override
- public void setValue(PermissionRule value) {
- if (clickHandler != null) {
- clickHandler.removeHandler();
- clickHandler = null;
- }
-
- GroupReference ref = value.getGroup();
- GroupInfo info =
- groupInfo != null && ref.getUUID() != null ? groupInfo.get(ref.getUUID()) : null;
-
- boolean link;
- if (ref.getUUID() != null && AccountGroup.isInternalGroup(ref.getUUID())) {
- final String token = Dispatcher.toGroup(ref.getUUID());
- groupNameLink.setText(ref.getName());
- groupNameLink.setHref("#" + token);
- groupNameLink.setTitle(info != null ? info.getDescription() : null);
- groupNameLink.setTarget(null);
- clickHandler =
- groupNameLink.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- event.preventDefault();
- event.stopPropagation();
- Gerrit.display(token);
- }
- });
- link = true;
- } else if (info != null && info.getUrl() != null) {
- groupNameLink.setText(ref.getName());
- groupNameLink.setHref(info.getUrl());
- groupNameLink.setTitle(info.getDescription());
- groupNameLink.setTarget("_blank");
- link = true;
- } else {
- groupNameSpan.setInnerText(ref.getName());
- groupNameSpan.setTitle(ref.getUUID() != null ? ref.getUUID().get() : "");
- link = false;
- }
-
- deletedGroupName.setInnerText(ref.getName());
- groupNameLink.setVisible(link);
- UIObject.setVisible(groupNameSpan, !link);
- }
-
- @Override
- public void setDelegate(EditorDelegate<PermissionRule> delegate) {}
-
- @Override
- public void flush() {}
-
- @Override
- public void onPropertyChange(String... paths) {}
-
- private static class ActionRenderer implements Renderer<PermissionRule.Action> {
- @Override
- public String render(PermissionRule.Action object) {
- return object != null ? object.toString() : "";
- }
-
- @Override
- public void render(PermissionRule.Action object, Appendable appendable) throws IOException {
- appendable.append(render(object));
- }
- }
-
- private static final ActionRenderer actionRenderer = new ActionRenderer();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
deleted file mode 100644
index 644fef4349..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:e='urn:import:com.google.gwt.editor.ui.client'
- xmlns:my='urn:import:com.google.gerrit.client.admin'
- xmlns:q='urn:import:com.google.gerrit.client.ui'
- ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
- ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
- ui:generateLocales='default,en'
- >
-<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
-<ui:style gss='false'>
- @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-
- .panel {
- position: relative;
- height: 1.5em;
- }
-
- .panel:hover {
- background-color: selectionColor;
- }
-
- .normal {
- padding-left: 10px;
- white-space: nowrap;
- height: 100%;
- }
-
- .deleted {
- height: 100%;
- }
-
- .actionList, .minmax {
- font-size: 80%;
- }
-
- .forcePush {
- position: absolute;
- top: 0;
- right: 36px;
- width: 7em;
- font-size: 80%;
- }
-
- .deleteIcon {
- position: absolute;
- top: 2px;
- right: 11px;
- }
-
- .groupName {
- display: inline;
- }
-</ui:style>
-
-<g:HTMLPanel styleName='{style.panel}'>
-<div ui:field='normal' class='{style.normal}'>
- <g:ValueListBox ui:field='action' styleName='{style.actionList}'/>
- <span ui:field='rangeEditor'>
- <g:Widget ui:field='min' styleName='{style.minmax}'/>
- <g:Widget ui:field='max' styleName='{style.minmax}'/>
- </span>
-
- <g:Anchor ui:field='groupNameLink' styleName='{style.groupName}'/>
- <span ui:field='groupNameSpan' styleName='{style.groupName}'/>
- <g:CheckBox ui:field='force' addStyleNames='{style.forcePush}'/>
- <g:Anchor
- ui:field='deleteRule'
- href='javascript:void'
- styleName='{style.deleteIcon} {res.css.deleteIcon}'
- title='Delete this rule'>
- <ui:attribute name='title'/>
- </g:Anchor>
-</div>
-
-<div
- ui:field='deleted'
- class='{res.css.deleted} {style.deleted}'
- style='display: none'>
- <ui:msg>Group <span ui:field='deletedGroupName'/> was deleted</ui:msg>
- <g:Anchor
- ui:field='undoDelete'
- href='javascript:void'
- styleName='{style.deleteIcon} {res.css.undoIcon}'
- title='Undo deletion'>
- <ui:attribute name='title'/>
- </g:Anchor>
-</div>
-</g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java
deleted file mode 100644
index 381c64476f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (C) 2012 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.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.api.ExtensionScreen;
-import com.google.gerrit.client.plugins.PluginInfo;
-import com.google.gerrit.client.plugins.PluginMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwt.user.client.ui.Panel;
-
-public class PluginListScreen extends PluginScreen {
-
- private Panel pluginPanel;
- private PluginTable pluginTable;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- initPluginList();
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- PluginMap.all(
- new ScreenLoadCallback<PluginMap>(this) {
- @Override
- protected void preDisplay(PluginMap result) {
- pluginTable.display(result);
- }
- });
- }
-
- private void initPluginList() {
- pluginTable = new PluginTable();
- pluginTable.addStyleName(Gerrit.RESOURCES.css().pluginsTable());
-
- pluginPanel = new FlowPanel();
- pluginPanel.setWidth("500px");
- pluginPanel.add(pluginTable);
- add(pluginPanel);
- }
-
- private static class PluginTable extends FancyFlexTable<PluginInfo> {
- PluginTable() {
- table.setText(0, 1, AdminConstants.I.columnPluginName());
- table.setText(0, 2, AdminConstants.I.columnPluginSettings());
- table.setText(0, 3, AdminConstants.I.columnPluginVersion());
- table.setText(0, 4, AdminConstants.I.columnPluginStatus());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
- }
-
- void display(PluginMap plugins) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (PluginInfo p : Natives.asList(plugins.values())) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, p);
- }
- }
-
- void populate(int row, PluginInfo plugin) {
- if (plugin.disabled() || plugin.indexUrl() == null) {
- table.setText(row, 1, plugin.name());
- } else {
- table.setWidget(
- row, 1, new Anchor(plugin.name(), Gerrit.selfRedirect(plugin.indexUrl()), "_blank"));
-
- if (new ExtensionScreen(plugin.name() + "/settings").isFound()) {
- InlineHyperlink adminScreenLink = new InlineHyperlink();
- adminScreenLink.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.gear()));
- adminScreenLink.setTargetHistoryToken("/x/" + plugin.name() + "/settings");
- adminScreenLink.setTitle(AdminConstants.I.pluginSettingsToolTip());
- table.setWidget(row, 2, adminScreenLink);
- }
- }
-
- table.setText(row, 3, plugin.version());
- table.setText(
- row,
- 4,
- plugin.disabled() ? AdminConstants.I.pluginDisabled() : AdminConstants.I.pluginEnabled());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell());
-
- setRowItem(row, plugin);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
deleted file mode 100644
index 0909fe1f25..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2012 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.client.admin;
-
-import com.google.gerrit.client.ui.Screen;
-
-public abstract class PluginScreen extends Screen {
-
- public PluginScreen() {
- setRequiresSignIn(true);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- setPageTitle(AdminConstants.I.plugins());
- display();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
deleted file mode 100644
index a52ea6071d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
+++ /dev/null
@@ -1,196 +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.client.admin;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.ParentProjectBox;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.ProjectAccess;
-import com.google.gerrit.common.data.WebLinkInfoCommon;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.editor.client.Editor;
-import com.google.gwt.editor.client.EditorDelegate;
-import com.google.gwt.editor.client.ValueAwareEditor;
-import com.google.gwt.editor.client.adapters.EditorSource;
-import com.google.gwt.editor.client.adapters.ListEditor;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ProjectAccessEditor extends Composite
- implements Editor<ProjectAccess>, ValueAwareEditor<ProjectAccess> {
- interface Binder extends UiBinder<HTMLPanel, ProjectAccessEditor> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField DivElement inheritsFrom;
-
- @UiField Hyperlink parentProject;
-
- @UiField @Editor.Ignore ParentProjectBox parentProjectBox;
-
- @UiField DivElement history;
-
- @UiField FlowPanel webLinkPanel;
-
- @UiField FlowPanel localContainer;
- ListEditor<AccessSection, AccessSectionEditor> local;
-
- @UiField Anchor addSection;
-
- private ProjectAccess value;
-
- private boolean editing;
-
- public ProjectAccessEditor() {
- initWidget(uiBinder.createAndBindUi(this));
- local = ListEditor.of(new Source(localContainer));
- }
-
- @UiHandler("addSection")
- void onAddSection(@SuppressWarnings("unused") ClickEvent event) {
- int index = local.getList().size();
- local.getList().add(new AccessSection("refs/heads/*"));
-
- AccessSectionEditor editor = local.getEditors().get(index);
- editor.enableEditing();
- editor.editRefPattern();
- }
-
- @Override
- public void setValue(ProjectAccess value) {
- // If the owner can edit the Global Capabilities but they don't exist in this
- // project, create an empty one at the beginning of the list making it
- // possible to add permissions to it.
- if (editing
- && value.isOwnerOf(AccessSection.GLOBAL_CAPABILITIES)
- && value.getLocal(AccessSection.GLOBAL_CAPABILITIES) == null) {
- value.getLocal().add(0, new AccessSection(AccessSection.GLOBAL_CAPABILITIES));
- }
-
- this.value = value;
-
- Project.NameKey parent = value.getInheritsFrom();
- if (parent != null) {
- inheritsFrom.getStyle().setDisplay(Display.BLOCK);
- parentProject.setText(parent.get());
- parentProject.setTargetHistoryToken( //
- Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS));
-
- parentProjectBox.setVisible(editing);
- parentProjectBox.setProject(value.getProjectName());
- parentProjectBox.setParentProject(value.getInheritsFrom());
- parentProject.setVisible(!parentProjectBox.isVisible());
- } else {
- inheritsFrom.getStyle().setDisplay(Display.NONE);
- }
- setUpWebLinks();
-
- addSection.setVisible(editing && (!value.getOwnerOf().isEmpty() || value.canUpload()));
- }
-
- @Override
- public void flush() {
- List<AccessSection> src = local.getList();
- List<AccessSection> keep = new ArrayList<>(src.size());
-
- for (int i = 0; i < src.size(); i++) {
- AccessSectionEditor e = (AccessSectionEditor) localContainer.getWidget(i);
- if (!e.isDeleted() && !src.get(i).getPermissions().isEmpty()) {
- keep.add(src.get(i));
- }
- }
- value.setLocal(keep);
- value.setInheritsFrom(parentProjectBox.getParentProjectName());
- }
-
- @Override
- public void onPropertyChange(String... paths) {}
-
- @Override
- public void setDelegate(EditorDelegate<ProjectAccess> delegate) {}
-
- void setEditing(boolean editing) {
- this.editing = editing;
- addSection.setVisible(editing);
- }
-
- private void setUpWebLinks() {
- List<WebLinkInfoCommon> links = value.getFileHistoryLinks();
- if (!value.isConfigVisible() || links == null || links.isEmpty()) {
- history.getStyle().setDisplay(Display.NONE);
- return;
- }
- for (WebLinkInfoCommon link : links) {
- webLinkPanel.add(toAnchor(link));
- }
- }
-
- private static Anchor toAnchor(WebLinkInfoCommon info) {
- Anchor a = new Anchor();
- a.setHref(info.url);
- if (info.target != null && !info.target.isEmpty()) {
- a.setTarget(info.target);
- }
- if (info.imageUrl != null && !info.imageUrl.isEmpty()) {
- Image img = new Image();
- img.setAltText(info.name);
- img.setUrl(info.imageUrl);
- img.setTitle(info.name);
- a.getElement().appendChild(img.getElement());
- } else {
- a.setText("(" + info.name + ")");
- }
- return a;
- }
-
- private class Source extends EditorSource<AccessSectionEditor> {
- private final FlowPanel container;
-
- Source(FlowPanel container) {
- this.container = container;
- }
-
- @Override
- public AccessSectionEditor create(int index) {
- AccessSectionEditor subEditor = new AccessSectionEditor(value);
- subEditor.setEditing(editing);
- container.insert(subEditor, index);
- return subEditor;
- }
-
- @Override
- public void dispose(AccessSectionEditor subEditor) {
- subEditor.removeFromParent();
- }
-
- @Override
- public void setIndex(AccessSectionEditor subEditor, int index) {
- container.insert(subEditor, index);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
deleted file mode 100644
index 7fbf70d20f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:q='urn:import:com.google.gerrit.client.ui'
- ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
- ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
- ui:generateLocales='default,en'
- >
-<ui:style gss='false'>
- .inheritsFrom {
- margin-bottom: 0.5em;
- }
- .parentTitle {
- font-weight: bold;
- }
- .parentLink {
- display: inline;
- }
-
- .history {
- margin-bottom: 0.5em;
- }
- .historyTitle {
- font-weight: bold;
- }
- .webLinkPanel a {
- display: inline;
- }
- .webLinkPanel>a {
- margin-left:2px;
- }
-
- .addContainer {
- margin-top: 5px;
- }
- .addContainer:hover {
- background-color: selectionColor;
- }
-</ui:style>
-
-<g:HTMLPanel>
- <div ui:field='inheritsFrom' class='{style.inheritsFrom}'>
- <span class='{style.parentTitle}'><ui:msg>Rights Inherit From:</ui:msg></span>
- <q:Hyperlink ui:field='parentProject' styleName='{style.parentLink}'/>
- <q:ParentProjectBox
- ui:field='parentProjectBox'
- visible='false'/>
- </div>
- <div ui:field='history' class='{style.history}'>
- <span class='{style.historyTitle}'><ui:msg>History:</ui:msg></span>
- <td>
- <g:FlowPanel ui:field="webLinkPanel" styleName='{style.webLinkPanel}'/>
- </td>
- </div>
-
- <g:FlowPanel ui:field='localContainer'/>
- <div class='{style.addContainer}'>
- <g:Anchor
- ui:field='addSection'
- href='javascript:void'
- text='Add Reference'>
- <ui:attribute name='text'/>
- </g:Anchor>
- </div>
-</g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
deleted file mode 100644
index 47a50e9234..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
+++ /dev/null
@@ -1,312 +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.client.admin;
-
-import static com.google.gerrit.common.ProjectAccessUtil.mergeSections;
-import static com.google.gerrit.common.ProjectAccessUtil.removeEmptyPermissionsAndSections;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.config.CapabilityInfo;
-import com.google.gerrit.client.config.ConfigServerApi;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.ProjectAccess;
-import com.google.gerrit.common.errors.UpdateParentFailedException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.editor.client.SimpleBeanEditorDriver;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class ProjectAccessScreen extends ProjectScreen {
- interface Binder extends UiBinder<HTMLPanel, ProjectAccessScreen> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Driver
- extends SimpleBeanEditorDriver< //
- ProjectAccess, //
- ProjectAccessEditor> {}
-
- @UiField DivElement editTools;
-
- @UiField Button edit;
-
- @UiField Button cancel1;
-
- @UiField Button cancel2;
-
- @UiField VerticalPanel error;
-
- @UiField ProjectAccessEditor accessEditor;
-
- @UiField DivElement commitTools;
-
- @UiField NpTextArea commitMessage;
-
- @UiField Button commit;
-
- @UiField Button review;
-
- private Driver driver;
-
- private ProjectAccess access;
-
- private NativeMap<CapabilityInfo> capabilityMap;
-
- public ProjectAccessScreen(Project.NameKey toShow) {
- super(toShow);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- add(uiBinder.createAndBindUi(this));
-
- driver = GWT.create(Driver.class);
- accessEditor.setEditing(false);
- driver.initialize(accessEditor);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- CallbackGroup cbs = new CallbackGroup();
- ConfigServerApi.capabilities(
- cbs.add(
- new AsyncCallback<NativeMap<CapabilityInfo>>() {
- @Override
- public void onSuccess(NativeMap<CapabilityInfo> result) {
- capabilityMap = result;
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // Handled by ScreenLoadCallback.onFailure().
- }
- }));
- Util.PROJECT_SVC.projectAccess(
- getProjectKey(),
- cbs.addFinal(
- new ScreenLoadCallback<ProjectAccess>(this) {
- @Override
- public void preDisplay(ProjectAccess access) {
- displayReadOnly(access);
- }
- }));
- savedPanel = ACCESS;
- }
-
- private void displayReadOnly(ProjectAccess access) {
- this.access = access;
- Map<String, String> allCapabilities = new HashMap<>();
- for (CapabilityInfo c : Natives.asList(capabilityMap.values())) {
- allCapabilities.put(c.id(), c.name());
- }
- this.access.setCapabilities(allCapabilities);
- accessEditor.setEditing(false);
- UIObject.setVisible(editTools, !access.getOwnerOf().isEmpty() || access.canUpload());
- edit.setEnabled(!access.getOwnerOf().isEmpty() || access.canUpload());
- cancel1.setVisible(false);
- UIObject.setVisible(commitTools, false);
- driver.edit(access);
- }
-
- @UiHandler("edit")
- void onEdit(@SuppressWarnings("unused") ClickEvent event) {
- resetEditors();
-
- edit.setEnabled(false);
- cancel1.setVisible(true);
- UIObject.setVisible(commitTools, true);
- commit.setVisible(!access.getOwnerOf().isEmpty());
- review.setVisible(access.canUpload());
- accessEditor.setEditing(true);
- driver.edit(access);
- }
-
- private void resetEditors() {
- // Push an empty instance through the driver before pushing the real
- // data. This will force GWT to delete and recreate the editors, which
- // is required to build initialize them as editable vs. read-only.
- ProjectAccess mock = new ProjectAccess();
- mock.setProjectName(access.getProjectName());
- mock.setRevision(access.getRevision());
- mock.setLocal(Collections.<AccessSection>emptyList());
- mock.setOwnerOf(Collections.<String>emptySet());
- driver.edit(mock);
- }
-
- @UiHandler(value = {"cancel1", "cancel2"})
- void onCancel(@SuppressWarnings("unused") ClickEvent event) {
- Gerrit.display(PageLinks.toProjectAccess(getProjectKey()));
- }
-
- @UiHandler("commit")
- void onCommit(@SuppressWarnings("unused") ClickEvent event) {
- final ProjectAccess access = driver.flush();
-
- if (driver.hasErrors()) {
- Window.alert(AdminConstants.I.errorsMustBeFixed());
- return;
- }
-
- String message = commitMessage.getText().trim();
- if ("".equals(message)) {
- message = null;
- }
-
- enable(false);
- Util.PROJECT_SVC.changeProjectAccess( //
- getProjectKey(), //
- access.getRevision(), //
- message, //
- access.getLocal(), //
- access.getInheritsFrom(), //
- new GerritCallback<ProjectAccess>() {
- @Override
- public void onSuccess(ProjectAccess newAccess) {
- enable(true);
- commitMessage.setText("");
- error.clear();
- final Set<String> diffs = getDiffs(access, newAccess);
- if (diffs.isEmpty()) {
- displayReadOnly(newAccess);
- } else {
- error.add(new Label(Gerrit.C.projectAccessError()));
- for (String diff : diffs) {
- error.add(new Label(diff));
- }
- if (access.canUpload()) {
- error.add(new Label(Gerrit.C.projectAccessProposeForReviewHint()));
- }
- }
- }
-
- private Set<String> getDiffs(ProjectAccess wantedAccess, ProjectAccess newAccess) {
- List<AccessSection> wantedSections =
- mergeSections(removeEmptyPermissionsAndSections(wantedAccess.getLocal()));
- List<AccessSection> newSections =
- removeEmptyPermissionsAndSections(newAccess.getLocal());
- HashSet<AccessSection> same = new HashSet<>(wantedSections);
- HashSet<AccessSection> different =
- new HashSet<>(wantedSections.size() + newSections.size());
- different.addAll(wantedSections);
- different.addAll(newSections);
- same.retainAll(newSections);
- different.removeAll(same);
-
- Set<String> differentNames = new HashSet<>();
- for (AccessSection s : different) {
- differentNames.add(s.getName());
- }
- return differentNames;
- }
-
- @Override
- public void onFailure(Throwable caught) {
- error.clear();
- enable(true);
- if (caught instanceof RemoteJsonException
- && caught.getMessage().startsWith(UpdateParentFailedException.MESSAGE)) {
- new ErrorDialog(
- Gerrit.M.parentUpdateFailed(
- caught
- .getMessage()
- .substring(UpdateParentFailedException.MESSAGE.length() + 1)))
- .center();
- } else {
- super.onFailure(caught);
- }
- }
- });
- }
-
- @UiHandler("review")
- void onReview(@SuppressWarnings("unused") ClickEvent event) {
- final ProjectAccess access = driver.flush();
-
- if (driver.hasErrors()) {
- Window.alert(AdminConstants.I.errorsMustBeFixed());
- return;
- }
-
- String message = commitMessage.getText().trim();
- if ("".equals(message)) {
- message = null;
- }
-
- enable(false);
- Util.PROJECT_SVC.reviewProjectAccess( //
- getProjectKey(), //
- access.getRevision(), //
- message, //
- access.getLocal(), //
- access.getInheritsFrom(), //
- new GerritCallback<Change.Id>() {
- @Override
- public void onSuccess(Change.Id changeId) {
- enable(true);
- commitMessage.setText("");
- error.clear();
- if (changeId != null) {
- Gerrit.display(PageLinks.toChange(getProjectKey(), changeId));
- } else {
- displayReadOnly(access);
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- error.clear();
- enable(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void enable(boolean enabled) {
- commitMessage.setEnabled(enabled);
- commit.setEnabled(enabled && !access.getOwnerOf().isEmpty());
- review.setEnabled(enabled && access.canUpload());
- cancel1.setEnabled(enabled);
- cancel2.setEnabled(enabled);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml
deleted file mode 100644
index 724c7a1b71..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:my='urn:import:com.google.gerrit.client.admin'
- xmlns:expui='urn:import:com.google.gwtexpui.globalkey.client'
- ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
- ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
- ui:generateLocales='default,en'
- >
-<ui:style gss='false'>
- @external .gwt-TextArea;
-
- .commitMessage {
- margin-top: 2em;
- }
- .commitMessage .gwt-TextArea {
- margin: 5px 5px 5px 5px;
- }
- .errorMessage {
- margin-top: 5px;
- margin-bottom: 5px;
- color: red;
- }
-</ui:style>
-
-<g:HTMLPanel>
- <div ui:field='editTools'>
- <g:Button
- ui:field='edit'
- text='Edit'>
- <ui:attribute name='text'/>
- </g:Button>
- <g:Button
- ui:field='cancel1'
- text='Cancel'>
- <ui:attribute name='text'/>
- </g:Button>
- </div>
- <my:ProjectAccessEditor ui:field='accessEditor'/>
- <div ui:field='commitTools'>
- <div class='{style.commitMessage}'>
- <ui:msg>Commit Message (optional):</ui:msg><br/>
- <expui:NpTextArea
- ui:field='commitMessage'
- visibleLines='4'
- characterWidth='60'
- spellCheck='true'
- />
- </div>
- <g:VerticalPanel
- styleName='{style.errorMessage}'
- ui:field='error'>
- </g:VerticalPanel>
- <g:Button
- ui:field='commit'
- text='Save Changes'>
- <ui:attribute name='text'/>
- </g:Button>
- <g:Button
- ui:field='review'
- text='Save for Review'>
- <ui:attribute name='text'/>
- </g:Button>
- <g:Button
- ui:field='cancel2'
- text='Cancel'>
- <ui:attribute name='text'/>
- </g:Button>
- </div>
-</g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
deleted file mode 100644
index e9a6499388..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
+++ /dev/null
@@ -1,673 +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.client.admin;
-
-import static com.google.gerrit.client.ui.Util.highlight;
-
-import com.google.gerrit.client.ConfirmationCallback;
-import com.google.gerrit.client.ConfirmationDialog;
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.access.AccessMap;
-import com.google.gerrit.client.access.ProjectAccessInfo;
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.HintTextBox;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.PagingHyperlink;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.InlineHTML;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ProjectBranchesScreen extends PaginatedProjectScreen {
- private Hyperlink prev;
- private Hyperlink next;
- private BranchesTable branchTable;
- private Button delBranch;
- private Button addBranch;
- private HintTextBox nameTxtBox;
- private HintTextBox irevTxtBox;
- private FlowPanel addPanel;
- private NpTextBox filterTxt;
- private Query query;
-
- public ProjectBranchesScreen(Project.NameKey toShow) {
- super(toShow);
- }
-
- @Override
- public String getScreenToken() {
- return PageLinks.toProjectBranches(getProjectKey());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- addPanel.setVisible(false);
- AccessMap.get(
- getProjectKey(),
- new GerritCallback<ProjectAccessInfo>() {
- @Override
- public void onSuccess(ProjectAccessInfo result) {
- addPanel.setVisible(result.canAddRefs());
- }
- });
- query = new Query(match).start(start).run();
- savedPanel = BRANCHES;
- }
-
- private void updateForm() {
- branchTable.updateDeleteButton();
- addBranch.setEnabled(true);
- nameTxtBox.setEnabled(true);
- irevTxtBox.setEnabled(true);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- initPageHeader();
-
- prev = PagingHyperlink.createPrev();
- prev.setVisible(false);
-
- next = PagingHyperlink.createNext();
- next.setVisible(false);
-
- addPanel = new FlowPanel();
-
- final Grid addGrid = new Grid(2, 2);
- addGrid.setStyleName(Gerrit.RESOURCES.css().addBranch());
- final int texBoxLength = 50;
-
- nameTxtBox = new HintTextBox();
- nameTxtBox.setVisibleLength(texBoxLength);
- nameTxtBox.setHintText(AdminConstants.I.defaultBranchName());
- nameTxtBox.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doAddNewBranch();
- }
- }
- });
- addGrid.setText(0, 0, AdminConstants.I.columnBranchName() + ":");
- addGrid.setWidget(0, 1, nameTxtBox);
-
- irevTxtBox = new HintTextBox();
- irevTxtBox.setVisibleLength(texBoxLength);
- irevTxtBox.setHintText(AdminConstants.I.defaultRevisionSpec());
- irevTxtBox.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doAddNewBranch();
- }
- }
- });
- addGrid.setText(1, 0, AdminConstants.I.initialRevision() + ":");
- addGrid.setWidget(1, 1, irevTxtBox);
-
- addBranch = new Button(AdminConstants.I.buttonAddBranch());
- addBranch.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doAddNewBranch();
- }
- });
- addPanel.add(addGrid);
- addPanel.add(addBranch);
-
- branchTable = new BranchesTable();
-
- delBranch = new Button(AdminConstants.I.buttonDeleteBranch());
- delBranch.setStyleName(Gerrit.RESOURCES.css().branchTableDeleteButton());
- delBranch.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- branchTable.deleteChecked();
- }
- });
- HorizontalPanel buttons = new HorizontalPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().branchTablePrevNextLinks());
- buttons.add(delBranch);
- buttons.add(prev);
- buttons.add(next);
- add(branchTable);
- add(buttons);
- add(addPanel);
- }
-
- private void initPageHeader() {
- parseToken();
- HorizontalPanel hp = new HorizontalPanel();
- hp.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
- Label filterLabel = new Label(AdminConstants.I.projectFilter());
- filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
- hp.add(filterLabel);
- filterTxt = new NpTextBox();
- filterTxt.setValue(match);
- filterTxt.addKeyUpHandler(
- new KeyUpHandler() {
- @Override
- public void onKeyUp(KeyUpEvent event) {
- Query q = new Query(filterTxt.getValue());
- if (match.equals(q.qMatch)) {
- q.start(start);
- } else {
- if (query == null) {
- q.run();
- }
- query = q;
- }
- }
- });
- hp.add(filterTxt);
- add(hp);
- }
-
- private void doAddNewBranch() {
- final String branchName = nameTxtBox.getText().trim();
- if ("".equals(branchName)) {
- nameTxtBox.setFocus(true);
- return;
- }
-
- final String rev = irevTxtBox.getText().trim();
- if ("".equals(rev)) {
- irevTxtBox.setText("HEAD");
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- irevTxtBox.selectAll();
- irevTxtBox.setFocus(true);
- }
- });
- return;
- }
-
- addBranch.setEnabled(false);
- ProjectApi.createBranch(
- getProjectKey(),
- branchName,
- rev,
- new GerritCallback<BranchInfo>() {
- @Override
- public void onSuccess(BranchInfo branch) {
- showAddedBranch(branch);
- nameTxtBox.setText("");
- irevTxtBox.setText("");
- query = new Query(match).start(start).run();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addBranch.setEnabled(true);
- selectAllAndFocus(nameTxtBox);
- new ErrorDialog(caught.getMessage()).center();
- }
- });
- }
-
- void showAddedBranch(BranchInfo branch) {
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.openElement("b");
- b.append(Gerrit.C.branchCreationConfirmationMessage());
- b.closeElement("b");
-
- b.openElement("p");
- b.append(branch.ref());
- b.closeElement("p");
-
- ConfirmationDialog confirmationDialog =
- new ConfirmationDialog(
- Gerrit.C.branchCreationDialogTitle(),
- b.toSafeHtml(),
- new ConfirmationCallback() {
- @Override
- public void onOk() {
- // do nothing
- }
- });
- confirmationDialog.center();
- confirmationDialog.setCancelVisible(false);
- }
-
- private static void selectAllAndFocus(TextBox textBox) {
- textBox.selectAll();
- textBox.setFocus(true);
- }
-
- private class BranchesTable extends NavigationTable<BranchInfo> {
- private ValueChangeHandler<Boolean> updateDeleteHandler;
- boolean canDelete;
-
- BranchesTable() {
- table.setWidth("");
- table.setText(0, 2, AdminConstants.I.columnBranchName());
- table.setText(0, 3, AdminConstants.I.columnBranchRevision());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
-
- updateDeleteHandler =
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- updateDeleteButton();
- }
- };
- }
-
- Set<String> getCheckedRefs() {
- Set<String> refs = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final BranchInfo k = getRowItem(row);
- if (k != null
- && table.getWidget(row, 1) instanceof CheckBox
- && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- refs.add(k.ref());
- }
- }
- return refs;
- }
-
- void setChecked(Set<String> refs) {
- for (int row = 1; row < table.getRowCount(); row++) {
- final BranchInfo k = getRowItem(row);
- if (k != null && refs.contains(k.ref()) && table.getWidget(row, 1) instanceof CheckBox) {
- ((CheckBox) table.getWidget(row, 1)).setValue(true);
- }
- }
- }
-
- void deleteChecked() {
- final Set<String> refs = getCheckedRefs();
-
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.openElement("b");
- b.append(Gerrit.C.branchDeletionConfirmationMessage());
- b.closeElement("b");
-
- b.openElement("p");
- boolean first = true;
- for (String ref : refs) {
- if (!first) {
- b.append(",").br();
- }
- b.append(ref);
- first = false;
- }
- b.closeElement("p");
-
- if (refs.isEmpty()) {
- updateDeleteButton();
- return;
- }
-
- delBranch.setEnabled(false);
- ConfirmationDialog confirmationDialog =
- new ConfirmationDialog(
- Gerrit.C.branchDeletionDialogTitle(),
- b.toSafeHtml(),
- new ConfirmationCallback() {
- @Override
- public void onOk() {
- deleteBranches(refs);
- }
-
- @Override
- public void onCancel() {
- branchTable.updateDeleteButton();
- }
- });
- confirmationDialog.center();
- }
-
- private void deleteBranches(Set<String> branches) {
- ProjectApi.deleteBranches(
- getProjectKey(),
- branches,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- query = new Query(match).start(start).run();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- query = new Query(match).start(start).run();
- super.onFailure(caught);
- }
- });
- }
-
- void display(List<BranchInfo> branches) {
- displaySubset(branches, 0, branches.size());
- }
-
- void displaySubset(List<BranchInfo> branches, int fromIndex, int toIndex) {
- canDelete = false;
-
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (BranchInfo k : branches.subList(fromIndex, toIndex)) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(int row, BranchInfo k) {
- if (k.canDelete()) {
- CheckBox sel = new CheckBox();
- sel.addValueChangeHandler(updateDeleteHandler);
- table.setWidget(row, 1, sel);
- canDelete = true;
- } else {
- table.setText(row, 1, "");
- }
-
- table.setWidget(row, 2, new InlineHTML(highlight(k.getShortName(), match)));
-
- if (k.revision() != null) {
- if ("HEAD".equals(k.getShortName())) {
- setHeadRevision(row, 3, k.revision());
- } else {
- table.setText(row, 3, k.revision());
- }
- } else {
- table.setText(row, 3, "");
- }
-
- FlowPanel actionsPanel = new FlowPanel();
- if (k.webLinks() != null) {
- for (WebLinkInfo webLink : Natives.asList(k.webLinks())) {
- actionsPanel.add(webLink.toAnchor());
- }
- }
- if (k.actions() != null) {
- k.actions().copyKeysIntoChildren("id");
- for (ActionInfo a : Natives.asList(k.actions().values())) {
- actionsPanel.add(new ActionButton(getProjectKey(), k, a));
- }
- }
- table.setWidget(row, 4, actionsPanel);
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- String iconCellStyle = Gerrit.RESOURCES.css().iconCell();
- String dataCellStyle = Gerrit.RESOURCES.css().dataCell();
- if (RefNames.REFS_CONFIG.equals(k.getShortName()) || "HEAD".equals(k.getShortName())) {
- iconCellStyle = Gerrit.RESOURCES.css().specialBranchIconCell();
- dataCellStyle = Gerrit.RESOURCES.css().specialBranchDataCell();
- fmt.setStyleName(row, 0, iconCellStyle);
- }
- fmt.addStyleName(row, 1, iconCellStyle);
- fmt.addStyleName(row, 2, dataCellStyle);
- fmt.addStyleName(row, 3, dataCellStyle);
- fmt.addStyleName(row, 4, dataCellStyle);
-
- setRowItem(row, k);
- }
-
- private void setHeadRevision(int row, int column, String rev) {
- AccessMap.get(
- getProjectKey(),
- new GerritCallback<ProjectAccessInfo>() {
- @Override
- public void onSuccess(ProjectAccessInfo result) {
- if (result.isOwner()) {
- table.setWidget(row, column, getHeadRevisionWidget(rev));
- } else {
- table.setText(row, 3, rev);
- }
- }
- });
- }
-
- private Widget getHeadRevisionWidget(String headRevision) {
- FlowPanel p = new FlowPanel();
- final InlineLabel l = new InlineLabel(headRevision);
- final Image edit = new Image(Gerrit.RESOURCES.edit());
- edit.addStyleName(Gerrit.RESOURCES.css().editHeadButton());
-
- final NpTextBox input = new NpTextBox();
- input.setVisibleLength(35);
- input.setValue(headRevision);
- input.setVisible(false);
- final Button save = new Button();
- save.setText(AdminConstants.I.saveHeadButton());
- save.setVisible(false);
- save.setEnabled(false);
- final Button cancel = new Button();
- cancel.setText(AdminConstants.I.cancelHeadButton());
- cancel.setVisible(false);
-
- OnEditEnabler e = new OnEditEnabler(save);
- e.listenTo(input);
-
- edit.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- l.setVisible(false);
- edit.setVisible(false);
- input.setVisible(true);
- save.setVisible(true);
- cancel.setVisible(true);
- }
- });
- save.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- save.setEnabled(false);
- ProjectApi.setHead(
- getProjectKey(),
- input.getValue().trim(),
- new GerritCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {
- Gerrit.display(PageLinks.toProjectBranches(getProjectKey()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- super.onFailure(caught);
- save.setEnabled(true);
- }
- });
- }
- });
- cancel.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- l.setVisible(true);
- edit.setVisible(true);
- input.setVisible(false);
- input.setValue(headRevision);
- save.setVisible(false);
- save.setEnabled(false);
- cancel.setVisible(false);
- }
- });
-
- p.add(l);
- p.add(edit);
- p.add(input);
- p.add(save);
- p.add(cancel);
- return p;
- }
-
- boolean hasBranchCanDelete() {
- return canDelete;
- }
-
- void updateDeleteButton() {
- boolean on = false;
- for (int row = 1; row < table.getRowCount(); row++) {
- Widget w = table.getWidget(row, 1);
- if (w instanceof CheckBox) {
- CheckBox sel = (CheckBox) w;
- if (sel.getValue()) {
- on = true;
- break;
- }
- }
- }
- delBranch.setEnabled(on);
- }
-
- @Override
- protected void onOpenRow(int row) {
- if (row > 0) {
- movePointerTo(row);
- }
- }
-
- @Override
- protected Object getRowItemKey(BranchInfo item) {
- return item.ref();
- }
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- if (match != null) {
- filterTxt.setCursorPos(match.length());
- }
- filterTxt.setFocus(true);
- }
-
- private class Query {
- private String qMatch;
- private int qStart;
-
- Query(String match) {
- this.qMatch = match;
- }
-
- Query start(int start) {
- this.qStart = start;
- return this;
- }
-
- Query run() {
- // Retrieve one more branch than page size to determine if there are more
- // branches to display
- ProjectApi.getBranches(
- getProjectKey(),
- pageSize + 1,
- qStart,
- qMatch,
- new ScreenLoadCallback<JsArray<BranchInfo>>(ProjectBranchesScreen.this) {
- @Override
- public void preDisplay(JsArray<BranchInfo> result) {
- if (!isAttached()) {
- // View has been disposed.
- } else if (query == Query.this) {
- query = null;
- showList(result);
- } else {
- query.run();
- }
- }
- });
- return this;
- }
-
- void showList(JsArray<BranchInfo> result) {
- setToken(getTokenForScreen(qMatch, qStart));
- ProjectBranchesScreen.this.match = qMatch;
- ProjectBranchesScreen.this.start = qStart;
-
- if (result.length() <= pageSize) {
- branchTable.display(Natives.asList(result));
- next.setVisible(false);
- } else {
- branchTable.displaySubset(Natives.asList(result), 0, result.length() - 1);
- setupNavigationLink(next, qMatch, qStart + pageSize);
- }
- if (qStart > 0) {
- setupNavigationLink(prev, qMatch, qStart - pageSize);
- } else {
- prev.setVisible(false);
- }
-
- delBranch.setVisible(branchTable.hasBranchCanDelete());
- Set<String> checkedRefs = branchTable.getCheckedRefs();
- branchTable.setChecked(checkedRefs);
- updateForm();
-
- if (!isCurrentView()) {
- display();
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectDashboardsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectDashboardsScreen.java
deleted file mode 100644
index 7b5d04d08b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectDashboardsScreen.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2012 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.client.admin;
-
-import com.google.gerrit.client.dashboards.DashboardList;
-import com.google.gerrit.client.dashboards.DashboardsTable;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.FlowPanel;
-
-public class ProjectDashboardsScreen extends ProjectScreen {
- private DashboardsTable dashes;
- Project.NameKey project;
-
- public ProjectDashboardsScreen(Project.NameKey project) {
- super(project);
- this.project = project;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- DashboardList.all(
- getProjectKey(),
- new ScreenLoadCallback<JsArray<DashboardList>>(this) {
- @Override
- protected void preDisplay(JsArray<DashboardList> result) {
- dashes.display(result);
- }
- });
- savedPanel = DASHBOARDS;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- dashes = new DashboardsTable(project);
- FlowPanel fp = new FlowPanel();
- fp.add(dashes);
- add(fp);
- dashes.setSavePointerId("dashboards/project/" + getProjectKey().get());
- display();
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- dashes.setRegisterKeys(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
deleted file mode 100644
index d10a031b50..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ /dev/null
@@ -1,826 +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.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.StringListPanel;
-import com.google.gerrit.client.access.AccessMap;
-import com.google.gerrit.client.access.ProjectAccessInfo;
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.api.ExtensionPanel;
-import com.google.gerrit.client.change.Resources;
-import com.google.gerrit.client.download.DownloadPanel;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.DownloadInfo.DownloadCommandInfo;
-import com.google.gerrit.client.info.DownloadInfo.DownloadSchemeInfo;
-import com.google.gerrit.client.projects.ConfigInfo;
-import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterInfo;
-import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterValue;
-import com.google.gerrit.client.projects.ConfigInfo.InheritedBooleanInfo;
-import com.google.gerrit.client.projects.ConfigInfo.SubmitTypeInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.NpIntTextBox;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.client.ui.SmallHeading;
-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.reviewdb.client.Project;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HasEnabled;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-public class ProjectInfoScreen extends ProjectScreen {
- private boolean isOwner;
- private boolean configVisible;
-
- private LabeledWidgetsGrid grid;
- private Panel pluginOptionsPanel;
- private LabeledWidgetsGrid actionsGrid;
-
- // Section: Project Options
- private ListBox requireChangeID;
- private ListBox submitType;
- private ListBox state;
- private ListBox contentMerge;
- private ListBox newChangeForAllNotInTarget;
- private ListBox enableSignedPush;
- private ListBox requireSignedPush;
- private ListBox rejectImplicitMerges;
- private ListBox privateByDefault;
- private ListBox workInProgressByDefault;
- private ListBox enableReviewerByEmail;
- private ListBox matchAuthorToCommitterDate;
- private NpTextBox maxObjectSizeLimit;
- private Label effectiveMaxObjectSizeLimit;
- private Map<String, Map<String, HasEnabled>> pluginConfigWidgets;
-
- // Section: Contributor Agreements
- private ListBox contributorAgreements;
- private ListBox signedOffBy;
-
- private NpTextArea descTxt;
- private Button saveProject;
-
- private OnEditEnabler saveEnabler;
-
- public ProjectInfoScreen(Project.NameKey toShow) {
- super(toShow);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- Resources.I.style().ensureInjected();
- saveProject = new Button(AdminConstants.I.buttonSaveChanges());
- saveProject.setStyleName("");
- saveProject.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSave();
- }
- });
-
- ExtensionPanel extensionPanelTop =
- new ExtensionPanel(GerritUiExtensionPoint.PROJECT_INFO_SCREEN_TOP);
- extensionPanelTop.put(GerritUiExtensionPoint.Key.PROJECT_NAME, getProjectKey().get());
- add(extensionPanelTop);
-
- add(new ProjectDownloadPanel(getProjectKey().get(), true));
-
- initDescription();
- grid = new LabeledWidgetsGrid();
- pluginOptionsPanel = new FlowPanel();
- actionsGrid = new LabeledWidgetsGrid();
- initProjectOptions();
- initAgreements();
- add(grid);
- add(pluginOptionsPanel);
- add(saveProject);
- add(actionsGrid);
-
- ExtensionPanel extensionPanelBottom =
- new ExtensionPanel(GerritUiExtensionPoint.PROJECT_INFO_SCREEN_BOTTOM);
- extensionPanelBottom.put(GerritUiExtensionPoint.Key.PROJECT_NAME, getProjectKey().get());
- add(extensionPanelBottom);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- Project.NameKey project = getProjectKey();
- CallbackGroup cbg = new CallbackGroup();
- AccessMap.get(
- project,
- cbg.add(
- new GerritCallback<ProjectAccessInfo>() {
- @Override
- public void onSuccess(ProjectAccessInfo result) {
- isOwner = result.isOwner();
- configVisible = result.configVisible();
- enableForm();
- saveProject.setVisible(isOwner);
- }
- }));
- ProjectApi.getConfig(
- project,
- cbg.addFinal(
- new ScreenLoadCallback<ConfigInfo>(this) {
- @Override
- public void preDisplay(ConfigInfo result) {
- display(result);
- }
- }));
-
- savedPanel = INFO;
- }
-
- private void enableForm() {
- enableForm(isOwner);
- }
-
- private void enableForm(boolean isOwner) {
- state.setEnabled(isOwner);
- submitType.setEnabled(isOwner);
- setEnabledForUseContentMerge();
- newChangeForAllNotInTarget.setEnabled(isOwner);
- if (enableSignedPush != null) {
- enableSignedPush.setEnabled(isOwner);
- }
- if (requireSignedPush != null) {
- requireSignedPush.setEnabled(isOwner);
- }
- descTxt.setEnabled(isOwner);
- contributorAgreements.setEnabled(isOwner);
- signedOffBy.setEnabled(isOwner);
- requireChangeID.setEnabled(isOwner);
- rejectImplicitMerges.setEnabled(isOwner);
- privateByDefault.setEnabled(isOwner);
- workInProgressByDefault.setEnabled(isOwner);
- maxObjectSizeLimit.setEnabled(isOwner);
- enableReviewerByEmail.setEnabled(isOwner);
- matchAuthorToCommitterDate.setEnabled(isOwner);
-
- if (pluginConfigWidgets != null) {
- for (Map<String, HasEnabled> widgetMap : pluginConfigWidgets.values()) {
- for (HasEnabled widget : widgetMap.values()) {
- widget.setEnabled(isOwner);
- }
- }
- }
- }
-
- private void initDescription() {
- final VerticalPanel vp = new VerticalPanel();
- vp.add(new SmallHeading(AdminConstants.I.headingDescription()));
-
- descTxt = new NpTextArea();
- descTxt.setVisibleLines(6);
- descTxt.setCharacterWidth(60);
- vp.add(descTxt);
-
- add(vp);
- saveEnabler = new OnEditEnabler(saveProject);
- saveEnabler.listenTo(descTxt);
- }
-
- private void initProjectOptions() {
- grid.addHeader(new SmallHeading(AdminConstants.I.headingProjectOptions()));
-
- state = new ListBox();
- for (ProjectState stateValue : ProjectState.values()) {
- state.addItem(Util.toLongString(stateValue), stateValue.name());
- }
- saveEnabler.listenTo(state);
- grid.add(AdminConstants.I.headingProjectState(), state);
-
- submitType = new ListBox();
- for (SubmitType type : SubmitType.values()) {
- submitType.addItem(Util.toLongString(type), type.name());
- }
- submitType.addChangeHandler(
- new ChangeHandler() {
- @Override
- public void onChange(ChangeEvent event) {
- setEnabledForUseContentMerge();
- }
- });
- saveEnabler.listenTo(submitType);
- grid.add(AdminConstants.I.headingProjectSubmitType(), submitType);
-
- contentMerge = newInheritedBooleanBox();
- saveEnabler.listenTo(contentMerge);
- grid.add(AdminConstants.I.useContentMerge(), contentMerge);
-
- newChangeForAllNotInTarget = newInheritedBooleanBox();
- saveEnabler.listenTo(newChangeForAllNotInTarget);
- grid.add(AdminConstants.I.createNewChangeForAllNotInTarget(), newChangeForAllNotInTarget);
-
- requireChangeID = newInheritedBooleanBox();
- saveEnabler.listenTo(requireChangeID);
- grid.addHtml(AdminConstants.I.requireChangeID(), requireChangeID);
-
- if (Gerrit.info().receive().enableSignedPush()) {
- enableSignedPush = newInheritedBooleanBox();
- saveEnabler.listenTo(enableSignedPush);
- grid.add(AdminConstants.I.enableSignedPush(), enableSignedPush);
- requireSignedPush = newInheritedBooleanBox();
- saveEnabler.listenTo(requireSignedPush);
- grid.add(AdminConstants.I.requireSignedPush(), requireSignedPush);
- }
-
- rejectImplicitMerges = newInheritedBooleanBox();
- saveEnabler.listenTo(rejectImplicitMerges);
- grid.addHtml(AdminConstants.I.rejectImplicitMerges(), rejectImplicitMerges);
-
- privateByDefault = newInheritedBooleanBox();
- saveEnabler.listenTo(privateByDefault);
- grid.addHtml(AdminConstants.I.privateByDefault(), privateByDefault);
-
- workInProgressByDefault = newInheritedBooleanBox();
- saveEnabler.listenTo(workInProgressByDefault);
- grid.addHtml(AdminConstants.I.workInProgressByDefault(), workInProgressByDefault);
-
- enableReviewerByEmail = newInheritedBooleanBox();
- saveEnabler.listenTo(enableReviewerByEmail);
- grid.addHtml(AdminConstants.I.enableReviewerByEmail(), enableReviewerByEmail);
-
- matchAuthorToCommitterDate = newInheritedBooleanBox();
- saveEnabler.listenTo(matchAuthorToCommitterDate);
- grid.addHtml(AdminConstants.I.matchAuthorToCommitterDate(), matchAuthorToCommitterDate);
-
- maxObjectSizeLimit = new NpTextBox();
- saveEnabler.listenTo(maxObjectSizeLimit);
- effectiveMaxObjectSizeLimit = new Label();
- effectiveMaxObjectSizeLimit.setStyleName(
- Gerrit.RESOURCES.css().maxObjectSizeLimitEffectiveLabel());
- HorizontalPanel p = new HorizontalPanel();
- p.add(maxObjectSizeLimit);
- p.add(effectiveMaxObjectSizeLimit);
- grid.addHtml(AdminConstants.I.headingMaxObjectSizeLimit(), p);
- }
-
- private static ListBox newInheritedBooleanBox() {
- ListBox box = new ListBox();
- for (InheritableBoolean b : InheritableBoolean.values()) {
- box.addItem(b.name(), b.name());
- }
- return box;
- }
-
- /**
- * Enables the {@link #contentMerge} checkbox if the selected submit type allows the usage of
- * content merge. If the submit type (currently only 'Fast Forward Only') does not allow content
- * merge the useContentMerge checkbox gets disabled.
- */
- private void setEnabledForUseContentMerge() {
- if (SubmitType.FAST_FORWARD_ONLY.equals(
- SubmitType.valueOf(submitType.getValue(submitType.getSelectedIndex())))) {
- contentMerge.setEnabled(false);
- InheritedBooleanInfo b = InheritedBooleanInfo.create();
- b.setConfiguredValue(InheritableBoolean.FALSE);
- setBool(contentMerge, b);
- } else {
- contentMerge.setEnabled(submitType.isEnabled());
- }
- }
-
- private void initAgreements() {
- grid.addHeader(new SmallHeading(AdminConstants.I.headingAgreements()));
-
- contributorAgreements = newInheritedBooleanBox();
- if (Gerrit.info().auth().useContributorAgreements()) {
- saveEnabler.listenTo(contributorAgreements);
- grid.add(AdminConstants.I.useContributorAgreements(), contributorAgreements);
- }
-
- signedOffBy = newInheritedBooleanBox();
- saveEnabler.listenTo(signedOffBy);
- grid.addHtml(AdminConstants.I.useSignedOffBy(), signedOffBy);
- }
-
- private void setSubmitType(SubmitTypeInfo newSubmitType) {
- int index = -1;
- if (newSubmitType != null) {
- for (int i = 0; i < submitType.getItemCount(); i++) {
- if (submitType.getValue(i).equals(SubmitType.INHERIT.name())) {
- submitType.setItemText(i, getInheritString(newSubmitType));
- }
- if (newSubmitType.configuredValue().name().equals(submitType.getValue(i))) {
- index = i;
- }
- }
- submitType.setSelectedIndex(index);
- setEnabledForUseContentMerge();
- }
- }
-
- private static String getInheritString(SubmitTypeInfo submitType) {
- return Util.toLongString(SubmitType.INHERIT)
- + " ("
- + Util.toLongString(submitType.inheritedValue())
- + ")";
- }
-
- private void setState(ProjectState newState) {
- if (state != null) {
- for (int i = 0; i < state.getItemCount(); i++) {
- if (newState.name().equals(state.getValue(i))) {
- state.setSelectedIndex(i);
- break;
- }
- }
- }
- }
-
- private void setBool(ListBox box, InheritedBooleanInfo inheritedBoolean) {
- if (box == null) {
- return;
- }
- int inheritedIndex = -1;
- for (int i = 0; i < box.getItemCount(); i++) {
- if (box.getValue(i).startsWith(InheritableBoolean.INHERIT.name())) {
- inheritedIndex = i;
- }
- if (box.getValue(i).startsWith(inheritedBoolean.configuredValue().name())) {
- box.setSelectedIndex(i);
- }
- }
- if (inheritedIndex >= 0) {
- if (Gerrit.info().gerrit().isAllProjects(getProjectKey())) {
- if (box.getSelectedIndex() == inheritedIndex) {
- for (int i = 0; i < box.getItemCount(); i++) {
- if (box.getValue(i).equals(InheritableBoolean.FALSE.name())) {
- box.setSelectedIndex(i);
- break;
- }
- }
- }
- box.removeItem(inheritedIndex);
- } else {
- box.setItemText(
- inheritedIndex,
- InheritableBoolean.INHERIT.name() + " (" + inheritedBoolean.inheritedValue() + ")");
- }
- }
- }
-
- private static InheritableBoolean getBool(ListBox box) {
- int i = box.getSelectedIndex();
- if (i >= 0) {
- final String selectedValue = box.getValue(i);
- if (selectedValue.startsWith(InheritableBoolean.INHERIT.name())) {
- return InheritableBoolean.INHERIT;
- }
- return InheritableBoolean.valueOf(selectedValue);
- }
- return InheritableBoolean.INHERIT;
- }
-
- void display(ConfigInfo result) {
- descTxt.setText(result.description());
- setBool(contributorAgreements, result.useContributorAgreements());
- setBool(signedOffBy, result.useSignedOffBy());
- setBool(contentMerge, result.useContentMerge());
- setBool(newChangeForAllNotInTarget, result.createNewChangeForAllNotInTarget());
- setBool(requireChangeID, result.requireChangeId());
- if (Gerrit.info().receive().enableSignedPush()) {
- setBool(enableSignedPush, result.enableSignedPush());
- setBool(requireSignedPush, result.requireSignedPush());
- }
- setBool(rejectImplicitMerges, result.rejectImplicitMerges());
- setBool(privateByDefault, result.privateByDefault());
- setBool(workInProgressByDefault, result.workInProgressByDefault());
- setBool(enableReviewerByEmail, result.enableReviewerByEmail());
- setBool(matchAuthorToCommitterDate, result.matchAuthorToCommitterDate());
- setSubmitType(result.defaultSubmitType());
- setState(result.state());
- maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue());
- if (result.maxObjectSizeLimit().value() != null) {
- effectiveMaxObjectSizeLimit.setText(
- AdminMessages.I.effectiveMaxObjectSizeLimit(result.maxObjectSizeLimit().value()));
- if (result.maxObjectSizeLimit().summary() != null) {
- effectiveMaxObjectSizeLimit.setTitle(result.maxObjectSizeLimit().summary());
- }
- } else {
- effectiveMaxObjectSizeLimit.setText(AdminMessages.I.noMaxObjectSizeLimit());
- }
-
- saveProject.setEnabled(false);
- initPluginOptions(result);
- initProjectActions(result);
- }
-
- private void initPluginOptions(ConfigInfo info) {
- pluginOptionsPanel.clear();
- pluginConfigWidgets = new HashMap<>();
-
- for (String pluginName : info.pluginConfig().keySet()) {
- Map<String, HasEnabled> widgetMap = new HashMap<>();
- pluginConfigWidgets.put(pluginName, widgetMap);
- LabeledWidgetsGrid g = new LabeledWidgetsGrid();
- g.addHeader(new SmallHeading(AdminMessages.I.pluginProjectOptionsTitle(pluginName)));
- pluginOptionsPanel.add(g);
- NativeMap<ConfigParameterInfo> pluginConfig = info.pluginConfig(pluginName);
- pluginConfig.copyKeysIntoChildren("name");
- for (ConfigParameterInfo param : Natives.asList(pluginConfig.values())) {
- HasEnabled w;
- switch (param.type()) {
- case "STRING":
- case "INT":
- case "LONG":
- w = renderTextBox(g, param);
- break;
- case "BOOLEAN":
- w = renderCheckBox(g, param);
- break;
- case "LIST":
- w = renderListBox(g, param);
- break;
- case "ARRAY":
- w = renderStringListPanel(g, param);
- break;
- default:
- throw new UnsupportedOperationException("unsupported widget type");
- }
- if (param.editable()) {
- widgetMap.put(param.name(), w);
- } else {
- w.setEnabled(false);
- }
- }
- }
-
- enableForm();
- }
-
- private TextBox renderTextBox(LabeledWidgetsGrid g, ConfigParameterInfo param) {
- NpTextBox textBox = param.type().equals("STRING") ? new NpTextBox() : new NpIntTextBox();
- if (param.inheritable()) {
- textBox.setValue(param.configuredValue());
- Label inheritedLabel =
- new Label(AdminMessages.I.pluginProjectInheritedValue(param.inheritedValue()));
- inheritedLabel.setStyleName(Gerrit.RESOURCES.css().pluginProjectConfigInheritedValue());
- HorizontalPanel p = new HorizontalPanel();
- p.add(textBox);
- p.add(inheritedLabel);
- addWidget(g, p, param);
- } else {
- textBox.setValue(param.value());
- addWidget(g, textBox, param);
- }
- if (textBox.getValue().length() > textBox.getVisibleLength()) {
- textBox.setVisibleLength(textBox.getValue().length());
- }
- saveEnabler.listenTo(textBox);
- return textBox;
- }
-
- private CheckBox renderCheckBox(LabeledWidgetsGrid g, ConfigParameterInfo param) {
- CheckBox checkBox = new CheckBox(getDisplayName(param));
- checkBox.setValue(Boolean.parseBoolean(param.value()));
- HorizontalPanel p = new HorizontalPanel();
- p.add(checkBox);
- if (param.description() != null) {
- Image infoImg = new Image(Gerrit.RESOURCES.info());
- infoImg.setTitle(param.description());
- p.add(infoImg);
- }
- if (param.warning() != null) {
- Image warningImg = new Image(Gerrit.RESOURCES.warning());
- warningImg.setTitle(param.warning());
- p.add(warningImg);
- }
- g.add((String) null, p);
- saveEnabler.listenTo(checkBox);
- return checkBox;
- }
-
- private ListBox renderListBox(LabeledWidgetsGrid g, ConfigParameterInfo param) {
- if (param.permittedValues() == null) {
- return null;
- }
- ListBox listBox = new ListBox();
- if (param.inheritable()) {
- listBox.addItem(AdminMessages.I.pluginProjectInheritedListValue(param.inheritedValue()));
- if (param.configuredValue() == null) {
- listBox.setSelectedIndex(0);
- }
- for (int i = 0; i < param.permittedValues().length(); i++) {
- String pv = param.permittedValues().get(i);
- listBox.addItem(pv);
- if (pv.equals(param.configuredValue())) {
- listBox.setSelectedIndex(i + 1);
- }
- }
- } else {
- for (int i = 0; i < param.permittedValues().length(); i++) {
- String pv = param.permittedValues().get(i);
- listBox.addItem(pv);
- if (pv.equals(param.value())) {
- listBox.setSelectedIndex(i);
- }
- }
- }
-
- if (param.editable()) {
- saveEnabler.listenTo(listBox);
- addWidget(g, listBox, param);
- } else {
- listBox.setEnabled(false);
-
- if (param.inheritable() && listBox.getSelectedIndex() != 0) {
- // the inherited value is not selected,
- // since the listBox is disabled the inherited value cannot be
- // seen and we have to display it explicitly
- Label inheritedLabel =
- new Label(AdminMessages.I.pluginProjectInheritedValue(param.inheritedValue()));
- inheritedLabel.setStyleName(Gerrit.RESOURCES.css().pluginProjectConfigInheritedValue());
- HorizontalPanel p = new HorizontalPanel();
- p.add(listBox);
- p.add(inheritedLabel);
- addWidget(g, p, param);
- } else {
- addWidget(g, listBox, param);
- }
- }
-
- return listBox;
- }
-
- private StringListPanel renderStringListPanel(LabeledWidgetsGrid g, ConfigParameterInfo param) {
- StringListPanel p =
- new StringListPanel(null, Arrays.asList(getDisplayName(param)), saveProject, false);
- List<List<String>> values = new ArrayList<>();
- for (String v : Natives.asList(param.values())) {
- values.add(Arrays.asList(v));
- }
- p.display(values);
- if (!param.editable()) {
- p.setEnabled(false);
- }
- addWidget(g, p, param);
- return p;
- }
-
- private void addWidget(LabeledWidgetsGrid g, Widget w, ConfigParameterInfo param) {
- if (param.description() != null || param.warning() != null) {
- HorizontalPanel p = new HorizontalPanel();
- p.add(new Label(getDisplayName(param)));
- if (param.description() != null) {
- Image infoImg = new Image(Gerrit.RESOURCES.info());
- infoImg.setTitle(param.description());
- p.add(infoImg);
- }
- if (param.warning() != null) {
- Image warningImg = new Image(Gerrit.RESOURCES.warning());
- warningImg.setTitle(param.warning());
- p.add(warningImg);
- }
- p.add(new Label(":"));
- g.add(p, w);
- } else {
- g.add(getDisplayName(param), w);
- }
- }
-
- private String getDisplayName(ConfigParameterInfo param) {
- return param.displayName() != null ? param.displayName() : param.name();
- }
-
- private void initProjectActions(ConfigInfo info) {
- actionsGrid.clear(true);
- actionsGrid.removeAllRows();
- boolean showCreateChange = Gerrit.isSignedIn();
-
- NativeMap<ActionInfo> actions = info.actions();
- if (actions == null) {
- actions = NativeMap.create().cast();
- }
- if (actions.isEmpty() && !showCreateChange) {
- return;
- }
- actions.copyKeysIntoChildren("id");
- actionsGrid.addHeader(new SmallHeading(AdminConstants.I.headingProjectCommands()));
- FlowPanel actionsPanel = new FlowPanel();
- actionsPanel.setStyleName(Gerrit.RESOURCES.css().projectActions());
- actionsPanel.setVisible(true);
- actionsGrid.add(AdminConstants.I.headingCommands(), actionsPanel);
-
- for (String id : actions.keySet()) {
- actionsPanel.add(new ActionButton(getProjectKey(), actions.get(id)));
- }
-
- // TODO: The user should have create permission on the branch referred to by
- // HEAD. This would have to happen on the server side.
- if (showCreateChange) {
- actionsPanel.add(createChangeAction());
- }
-
- if (isOwner && configVisible) {
- actionsPanel.add(createEditConfigAction());
- }
- }
-
- private Button createChangeAction() {
- final Button createChange = new Button(AdminConstants.I.buttonCreateChange());
- createChange.setStyleName("");
- createChange.setTitle(AdminConstants.I.buttonCreateChangeDescription());
- createChange.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- CreateChangeAction.call(createChange, getProjectKey().get());
- }
- });
- return createChange;
- }
-
- private Button createEditConfigAction() {
- final Button editConfig = new Button(AdminConstants.I.buttonEditConfig());
- editConfig.setStyleName("");
- editConfig.setTitle(AdminConstants.I.buttonEditConfigDescription());
- editConfig.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- EditConfigAction.call(editConfig, getProjectKey());
- }
- });
- return editConfig;
- }
-
- private void doSave() {
- enableForm(false);
- saveProject.setEnabled(false);
- InheritableBoolean esp = enableSignedPush != null ? getBool(enableSignedPush) : null;
- InheritableBoolean rsp = requireSignedPush != null ? getBool(requireSignedPush) : null;
- ProjectApi.setConfig(
- getProjectKey(),
- descTxt.getText().trim(),
- getBool(contributorAgreements),
- getBool(contentMerge),
- getBool(signedOffBy),
- getBool(newChangeForAllNotInTarget),
- getBool(requireChangeID),
- esp,
- rsp,
- getBool(rejectImplicitMerges),
- getBool(privateByDefault),
- getBool(workInProgressByDefault),
- getBool(enableReviewerByEmail),
- getBool(matchAuthorToCommitterDate),
- maxObjectSizeLimit.getText().trim(),
- SubmitType.valueOf(submitType.getValue(submitType.getSelectedIndex())),
- ProjectState.valueOf(state.getValue(state.getSelectedIndex())),
- getPluginConfigValues(),
- new GerritCallback<ConfigInfo>() {
- @Override
- public void onSuccess(ConfigInfo result) {
- enableForm();
- display(result);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableForm();
- super.onFailure(caught);
- }
- });
- }
-
- private Map<String, Map<String, ConfigParameterValue>> getPluginConfigValues() {
- Map<String, Map<String, ConfigParameterValue>> pluginConfigValues =
- new HashMap<>(pluginConfigWidgets.size());
- for (Entry<String, Map<String, HasEnabled>> e : pluginConfigWidgets.entrySet()) {
- Map<String, ConfigParameterValue> values = new HashMap<>(e.getValue().size());
- pluginConfigValues.put(e.getKey(), values);
- for (Entry<String, HasEnabled> e2 : e.getValue().entrySet()) {
- HasEnabled widget = e2.getValue();
- if (widget instanceof TextBox) {
- values.put(
- e2.getKey(),
- ConfigParameterValue.create().value(((TextBox) widget).getValue().trim()));
- } else if (widget instanceof CheckBox) {
- values.put(
- e2.getKey(),
- ConfigParameterValue.create()
- .value(Boolean.toString(((CheckBox) widget).getValue())));
- } else if (widget instanceof ListBox) {
- ListBox listBox = (ListBox) widget;
- // the inherited value is at index 0,
- // if it is selected no value should be set on this project
- String value =
- listBox.getSelectedIndex() > 0 ? listBox.getValue(listBox.getSelectedIndex()) : null;
- values.put(e2.getKey(), ConfigParameterValue.create().value(value));
- } else if (widget instanceof StringListPanel) {
- values.put(
- e2.getKey(),
- ConfigParameterValue.create()
- .values(((StringListPanel) widget).getValues(0).toArray(new String[] {})));
- } else {
- throw new UnsupportedOperationException("unsupported widget type");
- }
- }
- }
- return pluginConfigValues;
- }
-
- public static class ProjectDownloadPanel extends DownloadPanel {
- public ProjectDownloadPanel(String project, boolean isAllowsAnonymous) {
- super(project, isAllowsAnonymous);
- }
-
- @Override
- protected List<DownloadCommandInfo> getCommands(DownloadSchemeInfo schemeInfo) {
- return schemeInfo.cloneCommands(project);
- }
- }
-
- private static class LabeledWidgetsGrid extends FlexTable {
- private String labelSuffix;
-
- LabeledWidgetsGrid() {
- super();
- labelSuffix = ":";
- }
-
- private void addHeader(Widget widget) {
- int row = getRowCount();
- insertRow(row);
- setWidget(row, 0, widget);
- getCellFormatter().getElement(row, 0).setAttribute("colSpan", "2");
- }
-
- private void add(String label, boolean labelIsHtml, Widget widget) {
- int row = getRowCount();
- insertRow(row);
- if (label != null) {
- if (labelIsHtml) {
- setHTML(row, 0, label + labelSuffix);
- } else {
- setText(row, 0, label + labelSuffix);
- }
- }
- setWidget(row, 1, widget);
- }
-
- public void add(String label, Widget widget) {
- add(label, false, widget);
- }
-
- public void addHtml(String label, Widget widget) {
- add(label, true, widget);
- }
-
- public void add(Widget label, Widget widget) {
- int row = getRowCount();
- insertRow(row);
- setWidget(row, 0, label);
- setWidget(row, 1, widget);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
deleted file mode 100644
index 2a0313629c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ /dev/null
@@ -1,259 +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.client.admin;
-
-import static com.google.gerrit.common.PageLinks.ADMIN_PROJECTS;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.projects.ProjectInfo;
-import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.HighlightingInlineHyperlink;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.PagingHyperlink;
-import com.google.gerrit.client.ui.ProjectSearchLink;
-import com.google.gerrit.client.ui.ProjectsTable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import java.util.List;
-
-public class ProjectListScreen extends PaginatedProjectScreen {
- private Hyperlink prev;
- private Hyperlink next;
- private ProjectsTable projects;
- private NpTextBox filterTxt;
-
- private Query query;
-
- public ProjectListScreen() {
- super(null);
- }
-
- public ProjectListScreen(String params) {
- this();
- parseToken(params);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- query = new Query(match).start(start).run();
- }
-
- @Override
- public String getScreenToken() {
- return ADMIN_PROJECTS;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(AdminConstants.I.projectListTitle());
- initPageHeader();
-
- prev = PagingHyperlink.createPrev();
- prev.setVisible(false);
-
- next = PagingHyperlink.createNext();
- next.setVisible(false);
-
- projects =
- new ProjectsTable() {
- @Override
- protected void initColumnHeaders() {
- super.initColumnHeaders();
- table.setText(0, ProjectsTable.C_REPO_BROWSER, AdminConstants.I.projectRepoBrowser());
- table
- .getFlexCellFormatter()
- .addStyleName(0, ProjectsTable.C_REPO_BROWSER, Gerrit.RESOURCES.css().dataHeader());
- }
-
- @Override
- protected void onOpenRow(int row) {
- History.newItem(link(getRowItem(row)));
- }
-
- private String link(ProjectInfo item) {
- return Dispatcher.toProject(item.name_key());
- }
-
- @Override
- protected void insert(int row, ProjectInfo k) {
- super.insert(row, k);
- table
- .getFlexCellFormatter()
- .addStyleName(row, ProjectsTable.C_REPO_BROWSER, Gerrit.RESOURCES.css().dataCell());
- }
-
- @Override
- protected void populate(int row, ProjectInfo k) {
- populateState(row, k);
- FlowPanel fp = new FlowPanel();
- fp.add(new ProjectSearchLink(k.name_key()));
- fp.add(new HighlightingInlineHyperlink(k.name(), link(k), match));
- table.setWidget(row, ProjectsTable.C_NAME, fp);
- table.setText(row, ProjectsTable.C_DESCRIPTION, k.description());
- addWebLinks(row, k);
-
- setRowItem(row, k);
- }
-
- private void addWebLinks(int row, ProjectInfo k) {
- List<WebLinkInfo> webLinks = Natives.asList(k.webLinks());
- if (webLinks != null && !webLinks.isEmpty()) {
- FlowPanel p = new FlowPanel();
- table.setWidget(row, ProjectsTable.C_REPO_BROWSER, p);
- for (WebLinkInfo weblink : webLinks) {
- p.add(weblink.toAnchor());
- }
- }
- }
- };
- projects.setSavePointerId(PageLinks.ADMIN_PROJECTS);
-
- add(projects);
- final HorizontalPanel buttons = new HorizontalPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().changeTablePrevNextLinks());
- buttons.add(prev);
- buttons.add(next);
- add(buttons);
- }
-
- private void initPageHeader() {
- final HorizontalPanel hp = new HorizontalPanel();
- hp.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
- final Label filterLabel = new Label(AdminConstants.I.projectFilter());
- filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
- hp.add(filterLabel);
- filterTxt = new NpTextBox();
- filterTxt.setValue(match);
- filterTxt.addKeyUpHandler(
- new KeyUpHandler() {
- @Override
- public void onKeyUp(KeyUpEvent event) {
- Query q =
- new Query(filterTxt.getValue())
- .open(event.getNativeKeyCode() == KeyCodes.KEY_ENTER);
- if (match.equals(q.qMatch)) {
- q.start(start);
- }
- if (q.open || !match.equals(q.qMatch)) {
- if (query == null) {
- q.run();
- }
- query = q;
- }
- }
- });
- hp.add(filterTxt);
- add(hp);
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- if (match != null) {
- filterTxt.setCursorPos(match.length());
- }
- filterTxt.setFocus(true);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- projects.setRegisterKeys(true);
- }
-
- private class Query {
- private final String qMatch;
- private int qStart;
- private boolean open;
-
- Query(String match) {
- this.qMatch = match;
- }
-
- Query start(int start) {
- this.qStart = start;
- return this;
- }
-
- Query open(boolean open) {
- this.open = open;
- return this;
- }
-
- Query run() {
- int limit = open ? 1 : pageSize + 1;
- ProjectMap.match(
- qMatch,
- limit,
- qStart,
- new GerritCallback<ProjectMap>() {
- @Override
- public void onSuccess(ProjectMap result) {
- if (!isAttached()) {
- // View has been disposed.
- } else if (query == Query.this) {
- query = null;
- showMap(result);
- } else {
- query.run();
- }
- }
- });
- return this;
- }
-
- private void showMap(ProjectMap result) {
- if (open && !result.isEmpty()) {
- Gerrit.display(PageLinks.toProject(result.values().get(0).name_key()));
- return;
- }
-
- setToken(getTokenForScreen(qMatch, qStart));
- ProjectListScreen.this.match = qMatch;
- ProjectListScreen.this.start = qStart;
-
- if (result.size() <= pageSize) {
- projects.display(result);
- next.setVisible(false);
- } else {
- projects.displaySubset(result, 0, result.size() - 1);
- setupNavigationLink(next, qMatch, qStart + pageSize);
- }
-
- if (qStart > 0) {
- setupNavigationLink(prev, qMatch, qStart - pageSize);
- } else {
- prev.setVisible(false);
- }
-
- if (!isCurrentView()) {
- display();
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectScreen.java
deleted file mode 100644
index dc964b8432..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectScreen.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2010 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.client.admin;
-
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.reviewdb.client.Project;
-
-public abstract class ProjectScreen extends Screen {
- public static final String INFO = "info";
- public static final String BRANCHES = "branches";
- public static final String ACCESS = "access";
- public static final String DASHBOARDS = "dashboards";
- public static final String TAGS = "tags";
-
- protected static String savedPanel;
- protected static Project.NameKey savedKey;
-
- public static String getSavedPanel() {
- return savedPanel;
- }
-
- public static Project.NameKey getSavedKey() {
- return savedKey;
- }
-
- private final Project.NameKey name;
-
- public ProjectScreen(Project.NameKey toShow) {
- name = toShow;
- }
-
- public Project.NameKey getProjectKey() {
- return name;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- if (name != null) {
- setPageTitle(AdminMessages.I.project(name.get()));
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- savedKey = name;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java
deleted file mode 100644
index 320f9564c4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java
+++ /dev/null
@@ -1,571 +0,0 @@
-// Copyright (C) 2015 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.client.admin;
-
-import static com.google.gerrit.client.ui.Util.highlight;
-
-import com.google.gerrit.client.ConfirmationCallback;
-import com.google.gerrit.client.ConfirmationDialog;
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.access.AccessMap;
-import com.google.gerrit.client.access.ProjectAccessInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.projects.TagInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.HintTextBox;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.PagingHyperlink;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.InlineHTML;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ProjectTagsScreen extends PaginatedProjectScreen {
- private Hyperlink prev;
- private Hyperlink next;
- private TagsTable tagTable;
- private Button delTag;
- private Button addTag;
- private HintTextBox nameTxtBox;
- private HintTextBox irevTxtBox;
- private HintTextBox annotationTxtBox;
- private FlowPanel addPanel;
- private NpTextBox filterTxt;
- private Query query;
-
- public ProjectTagsScreen(Project.NameKey toShow) {
- super(toShow);
- }
-
- @Override
- public String getScreenToken() {
- return PageLinks.toProjectTags(getProjectKey());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- addPanel.setVisible(false);
- AccessMap.get(
- getProjectKey(),
- new GerritCallback<ProjectAccessInfo>() {
- @Override
- public void onSuccess(ProjectAccessInfo result) {
- addPanel.setVisible(result.canAddTagRefs());
- }
- });
- query = new Query(match).start(start).run();
- savedPanel = TAGS;
- }
-
- private void updateForm() {
- tagTable.updateDeleteButton();
- addTag.setEnabled(true);
- nameTxtBox.setEnabled(true);
- irevTxtBox.setEnabled(true);
- annotationTxtBox.setEnabled(true);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- initPageHeader();
-
- prev = PagingHyperlink.createPrev();
- prev.setVisible(false);
-
- next = PagingHyperlink.createNext();
- next.setVisible(false);
-
- addPanel = new FlowPanel();
-
- Grid addGrid = new Grid(3, 2);
- addGrid.setStyleName(Gerrit.RESOURCES.css().addBranch());
- int texBoxLength = 50;
-
- KeyPressHandler onKeyPress =
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doAddNewTag();
- }
- }
- };
-
- nameTxtBox = new HintTextBox();
- nameTxtBox.setVisibleLength(texBoxLength);
- nameTxtBox.setHintText(AdminConstants.I.defaultTagName());
- nameTxtBox.addKeyPressHandler(onKeyPress);
- addGrid.setText(0, 0, AdminConstants.I.columnTagName() + ":");
- addGrid.setWidget(0, 1, nameTxtBox);
-
- irevTxtBox = new HintTextBox();
- irevTxtBox.setVisibleLength(texBoxLength);
- irevTxtBox.setHintText(AdminConstants.I.defaultRevisionSpec());
- irevTxtBox.addKeyPressHandler(onKeyPress);
- addGrid.setText(1, 0, AdminConstants.I.revision() + ":");
- addGrid.setWidget(1, 1, irevTxtBox);
-
- annotationTxtBox = new HintTextBox();
- annotationTxtBox.setVisibleLength(texBoxLength);
- annotationTxtBox.setHintText(AdminConstants.I.annotation());
- annotationTxtBox.addKeyPressHandler(onKeyPress);
- addGrid.setText(2, 0, AdminConstants.I.columnTagAnnotation() + ":");
- addGrid.setWidget(2, 1, annotationTxtBox);
-
- addTag = new Button(AdminConstants.I.buttonAddTag());
- addTag.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doAddNewTag();
- }
- });
- addPanel.add(addGrid);
- addPanel.add(addTag);
-
- tagTable = new TagsTable();
-
- delTag = new Button(AdminConstants.I.buttonDeleteTag());
- delTag.setStyleName(Gerrit.RESOURCES.css().branchTableDeleteButton());
- delTag.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- tagTable.deleteChecked();
- }
- });
-
- HorizontalPanel buttons = new HorizontalPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().branchTablePrevNextLinks());
- buttons.add(delTag);
- buttons.add(prev);
- buttons.add(next);
- add(tagTable);
- add(buttons);
- add(addPanel);
- }
-
- private void initPageHeader() {
- parseToken();
- HorizontalPanel hp = new HorizontalPanel();
- hp.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
- Label filterLabel = new Label(AdminConstants.I.projectFilter());
- filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
- hp.add(filterLabel);
- filterTxt = new NpTextBox();
- filterTxt.setValue(match);
- filterTxt.addKeyUpHandler(
- new KeyUpHandler() {
- @Override
- public void onKeyUp(KeyUpEvent event) {
- Query q = new Query(filterTxt.getValue());
- if (match.equals(q.qMatch)) {
- q.start(start);
- } else {
- if (query == null) {
- q.run();
- }
- query = q;
- }
- }
- });
- hp.add(filterTxt);
- add(hp);
- }
-
- private void doAddNewTag() {
- String tagName = nameTxtBox.getText().trim();
- if (tagName.isEmpty()) {
- nameTxtBox.setFocus(true);
- return;
- }
-
- String rev = irevTxtBox.getText().trim();
- if (rev.isEmpty()) {
- irevTxtBox.setText("HEAD");
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- irevTxtBox.selectAll();
- irevTxtBox.setFocus(true);
- }
- });
- return;
- }
-
- String annotation = annotationTxtBox.getText().trim();
- if (annotation.isEmpty()) {
- annotation = null;
- }
-
- addTag.setEnabled(false);
- ProjectApi.createTag(
- getProjectKey(),
- tagName,
- rev,
- annotation,
- new GerritCallback<TagInfo>() {
- @Override
- public void onSuccess(TagInfo tag) {
- showAddedTag(tag);
- nameTxtBox.setText("");
- irevTxtBox.setText("");
- annotationTxtBox.setText("");
- query = new Query(match).start(start).run();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- addTag.setEnabled(true);
- selectAllAndFocus(nameTxtBox);
- new ErrorDialog(caught.getMessage()).center();
- }
- });
- }
-
- void showAddedTag(TagInfo tag) {
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.openElement("b");
- b.append(Gerrit.C.tagCreationConfirmationMessage());
- b.closeElement("b");
-
- b.openElement("p");
- b.append(tag.ref());
- b.closeElement("p");
-
- ConfirmationDialog confirmationDialog =
- new ConfirmationDialog(
- Gerrit.C.tagCreationDialogTitle(),
- b.toSafeHtml(),
- new ConfirmationCallback() {
- @Override
- public void onOk() {
- // do nothing
- }
- });
- confirmationDialog.center();
- confirmationDialog.setCancelVisible(false);
- }
-
- private static void selectAllAndFocus(TextBox textBox) {
- textBox.selectAll();
- textBox.setFocus(true);
- }
-
- private class TagsTable extends NavigationTable<TagInfo> {
- private ValueChangeHandler<Boolean> updateDeleteHandler;
- boolean canDelete;
-
- TagsTable() {
- table.setWidth("");
- table.setText(0, 2, AdminConstants.I.columnTagName());
- table.setText(0, 3, AdminConstants.I.columnTagRevision());
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
-
- updateDeleteHandler =
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- updateDeleteButton();
- }
- };
- }
-
- Set<String> getCheckedRefs() {
- Set<String> refs = new HashSet<>();
- for (int row = 1; row < table.getRowCount(); row++) {
- TagInfo k = getRowItem(row);
- if (k != null
- && table.getWidget(row, 1) instanceof CheckBox
- && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- refs.add(k.ref());
- }
- }
- return refs;
- }
-
- void setChecked(Set<String> refs) {
- for (int row = 1; row < table.getRowCount(); row++) {
- TagInfo k = getRowItem(row);
- if (k != null && refs.contains(k.ref()) && table.getWidget(row, 1) instanceof CheckBox) {
- ((CheckBox) table.getWidget(row, 1)).setValue(true);
- }
- }
- }
-
- void deleteChecked() {
- final Set<String> refs = getCheckedRefs();
-
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.openElement("b");
- b.append(Gerrit.C.tagDeletionConfirmationMessage());
- b.closeElement("b");
-
- b.openElement("p");
- boolean first = true;
- for (String ref : refs) {
- if (!first) {
- b.append(",").br();
- }
- b.append(ref);
- first = false;
- }
- b.closeElement("p");
-
- if (refs.isEmpty()) {
- updateDeleteButton();
- return;
- }
-
- delTag.setEnabled(false);
- ConfirmationDialog confirmationDialog =
- new ConfirmationDialog(
- Gerrit.C.tagDeletionDialogTitle(),
- b.toSafeHtml(),
- new ConfirmationCallback() {
- @Override
- public void onOk() {
- deleteTags(refs);
- }
-
- @Override
- public void onCancel() {
- tagTable.updateDeleteButton();
- }
- });
- confirmationDialog.center();
- }
-
- private void deleteTags(Set<String> tags) {
- ProjectApi.deleteTags(
- getProjectKey(),
- tags,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- query = new Query(match).start(start).run();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- query = new Query(match).start(start).run();
- super.onFailure(caught);
- }
- });
- }
-
- void display(List<TagInfo> tags) {
- displaySubset(tags, 0, tags.size());
- }
-
- void displaySubset(List<TagInfo> tags, int fromIndex, int toIndex) {
- canDelete = false;
-
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- for (TagInfo k : tags.subList(fromIndex, toIndex)) {
- int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(int row, TagInfo k) {
- if (k.canDelete()) {
- CheckBox sel = new CheckBox();
- sel.addValueChangeHandler(updateDeleteHandler);
- table.setWidget(row, 1, sel);
- canDelete = true;
- } else {
- table.setText(row, 1, "");
- }
-
- table.setWidget(row, 2, new InlineHTML(highlight(k.getShortName(), match)));
-
- if (k.revision() != null) {
- table.setText(row, 3, k.revision());
- } else {
- table.setText(row, 3, "");
- }
-
- FlowPanel actionsPanel = new FlowPanel();
- if (k.webLinks() != null) {
- for (WebLinkInfo webLink : Natives.asList(k.webLinks())) {
- actionsPanel.add(webLink.toAnchor());
- }
- }
- table.setWidget(row, 4, actionsPanel);
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- String iconCellStyle = Gerrit.RESOURCES.css().iconCell();
- String dataCellStyle = Gerrit.RESOURCES.css().dataCell();
- fmt.addStyleName(row, 1, iconCellStyle);
- fmt.addStyleName(row, 2, dataCellStyle);
- fmt.addStyleName(row, 3, dataCellStyle);
- fmt.addStyleName(row, 4, dataCellStyle);
-
- setRowItem(row, k);
- }
-
- boolean hasTagCanDelete() {
- return canDelete;
- }
-
- void updateDeleteButton() {
- boolean on = false;
- for (int row = 1; row < table.getRowCount(); row++) {
- Widget w = table.getWidget(row, 1);
- if (w instanceof CheckBox) {
- CheckBox sel = (CheckBox) w;
- if (sel.getValue()) {
- on = true;
- break;
- }
- }
- }
- delTag.setEnabled(on);
- }
-
- @Override
- protected void onOpenRow(int row) {
- if (row > 0) {
- movePointerTo(row);
- }
- }
-
- @Override
- protected Object getRowItemKey(TagInfo item) {
- return item.ref();
- }
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- if (match != null) {
- filterTxt.setCursorPos(match.length());
- }
- filterTxt.setFocus(true);
- }
-
- private class Query {
- private String qMatch;
- private int qStart;
-
- Query(String match) {
- this.qMatch = match;
- }
-
- Query start(int start) {
- this.qStart = start;
- return this;
- }
-
- Query run() {
- // Retrieve one more tag than page size to determine if there are more
- // tags to display
- ProjectApi.getTags(
- getProjectKey(),
- pageSize + 1,
- qStart,
- qMatch,
- new ScreenLoadCallback<JsArray<TagInfo>>(ProjectTagsScreen.this) {
- @Override
- public void preDisplay(JsArray<TagInfo> result) {
- if (!isAttached()) {
- // View has been disposed.
- } else if (query == Query.this) {
- query = null;
- showList(result);
- } else {
- query.run();
- }
- }
- });
- return this;
- }
-
- void showList(JsArray<TagInfo> result) {
- setToken(getTokenForScreen(qMatch, qStart));
- ProjectTagsScreen.this.match = qMatch;
- ProjectTagsScreen.this.start = qStart;
-
- if (result.length() <= pageSize) {
- tagTable.display(Natives.asList(result));
- next.setVisible(false);
- } else {
- tagTable.displaySubset(Natives.asList(result), 0, result.length() - 1);
- setupNavigationLink(next, qMatch, qStart + pageSize);
- }
- if (qStart > 0) {
- setupNavigationLink(prev, qMatch, qStart - pageSize);
- } else {
- prev.setVisible(false);
- }
-
- delTag.setVisible(tagTable.hasTagCanDelete());
- Set<String> checkedRefs = tagTable.getCheckedRefs();
- tagTable.setChecked(checkedRefs);
- updateForm();
-
- if (!isCurrentView()) {
- display();
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RangeBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RangeBox.java
deleted file mode 100644
index 063a60c4f2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RangeBox.java
+++ /dev/null
@@ -1,85 +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.client.admin;
-
-import com.google.gwt.editor.client.IsEditor;
-import com.google.gwt.editor.client.adapters.TakesValueEditor;
-import com.google.gwt.text.shared.Renderer;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.IntegerBox;
-import com.google.gwt.user.client.ui.ValueBoxBase.TextAlignment;
-import com.google.gwt.user.client.ui.ValueListBox;
-import java.io.IOException;
-
-abstract class RangeBox extends Composite implements IsEditor<TakesValueEditor<Integer>> {
- static final RangeRenderer rangeRenderer = new RangeRenderer();
-
- private static class RangeRenderer implements Renderer<Integer> {
- @Override
- public String render(Integer object) {
- if (0 <= object) {
- return "+" + object;
- }
- return String.valueOf(object);
- }
-
- @Override
- public void render(Integer object, Appendable appendable) throws IOException {
- appendable.append(render(object));
- }
- }
-
- static class List extends RangeBox {
- final ValueListBox<Integer> list;
-
- List() {
- list = new ValueListBox<>(rangeRenderer);
- initWidget(list);
- }
-
- @Override
- void setEnabled(boolean on) {
- list.getElement().setPropertyBoolean("disabled", !on);
- }
-
- @Override
- public TakesValueEditor<Integer> asEditor() {
- return list.asEditor();
- }
- }
-
- static class Box extends RangeBox {
- private final IntegerBox box;
-
- Box() {
- box = new IntegerBox();
- box.setVisibleLength(10);
- box.setAlignment(TextAlignment.RIGHT);
- initWidget(box);
- }
-
- @Override
- void setEnabled(boolean on) {
- box.getElement().setPropertyBoolean("disabled", !on);
- }
-
- @Override
- public TakesValueEditor<Integer> asEditor() {
- return box.asEditor();
- }
- }
-
- abstract void setEnabled(boolean on);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java
deleted file mode 100644
index f1180cc98f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java
+++ /dev/null
@@ -1,95 +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.client.admin;
-
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.text.shared.Parser;
-import com.google.gwt.text.shared.Renderer;
-import com.google.gwt.user.client.ui.ValueBox;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import java.io.IOException;
-import java.text.ParseException;
-
-public class RefPatternBox extends ValueBox<String> {
- private static final Renderer<String> RENDERER =
- new Renderer<String>() {
- @Override
- public String render(String ref) {
- return ref;
- }
-
- @Override
- public void render(String ref, Appendable dst) throws IOException {
- dst.append(render(ref));
- }
- };
-
- private static final Parser<String> PARSER =
- new Parser<String>() {
- @Override
- public String parse(CharSequence text) throws ParseException {
- String ref = text.toString();
-
- if (ref.isEmpty()) {
- throw new ParseException(AdminConstants.I.refErrorEmpty(), 0);
- }
-
- if (ref.charAt(0) == '/') {
- throw new ParseException(AdminConstants.I.refErrorBeginSlash(), 0);
- }
-
- if (ref.charAt(0) == '^') {
- if (!ref.startsWith("^refs/")) {
- ref = "^refs/heads/" + ref.substring(1);
- }
- } else if (!ref.startsWith("refs/")) {
- ref = "refs/heads/" + ref;
- }
-
- for (int i = 0; i < ref.length(); i++) {
- final char c = ref.charAt(i);
-
- if (c == '/' && 0 < i && ref.charAt(i - 1) == '/') {
- throw new ParseException(AdminConstants.I.refErrorDoubleSlash(), i);
- }
-
- if (c == ' ') {
- throw new ParseException(AdminConstants.I.refErrorNoSpace(), i);
- }
-
- if (c < ' ') {
- throw new ParseException(AdminConstants.I.refErrorPrintable(), i);
- }
- }
- return ref;
- }
- };
-
- public RefPatternBox() {
- super(Document.get().createTextInputElement(), RENDERER, PARSER);
- addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
- addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getCharCode() == ' ') {
- event.preventDefault();
- }
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
deleted file mode 100644
index bbc8a1de41..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
+++ /dev/null
@@ -1,72 +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.client.admin;
-
-import com.google.gerrit.common.data.ProjectAdminService;
-import com.google.gerrit.extensions.client.ProjectState;
-import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class Util {
- public static final ProjectAdminService PROJECT_SVC;
-
- static {
- PROJECT_SVC = GWT.create(ProjectAdminService.class);
- JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
-
- AdminResources.I.css().ensureInjected();
- }
-
- public static String toLongString(SubmitType type) {
- if (type == null) {
- return "";
- }
- switch (type) {
- case INHERIT:
- return AdminConstants.I.projectSubmitType_INHERIT();
- case FAST_FORWARD_ONLY:
- return AdminConstants.I.projectSubmitType_FAST_FORWARD_ONLY();
- case MERGE_IF_NECESSARY:
- return AdminConstants.I.projectSubmitType_MERGE_IF_NECESSARY();
- case REBASE_IF_NECESSARY:
- return AdminConstants.I.projectSubmitType_REBASE_IF_NECESSARY();
- case REBASE_ALWAYS:
- return AdminConstants.I.projectSubmitType_REBASE_ALWAYS();
- case MERGE_ALWAYS:
- return AdminConstants.I.projectSubmitType_MERGE_ALWAYS();
- case CHERRY_PICK:
- return AdminConstants.I.projectSubmitType_CHERRY_PICK();
- default:
- return type.name();
- }
- }
-
- public static String toLongString(ProjectState type) {
- if (type == null) {
- return "";
- }
- switch (type) {
- case ACTIVE:
- return AdminConstants.I.projectState_ACTIVE();
- case READ_ONLY:
- return AdminConstants.I.projectState_READ_ONLY();
- case HIDDEN:
- return AdminConstants.I.projectState_HIDDEN();
- default:
- return type.name();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java
deleted file mode 100644
index ad614e5516..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java
+++ /dev/null
@@ -1,209 +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.client.admin;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.DivElement;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.editor.client.EditorError;
-import com.google.gwt.editor.client.HasEditorErrors;
-import com.google.gwt.editor.client.IsEditor;
-import com.google.gwt.editor.client.LeafValueEditor;
-import com.google.gwt.editor.ui.client.adapters.ValueBoxEditor;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiChild;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Focusable;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.ValueBoxBase;
-import com.google.gwt.user.client.ui.Widget;
-import java.text.ParseException;
-import java.util.List;
-
-public class ValueEditor<T> extends Composite
- implements HasEditorErrors<T>, IsEditor<ValueBoxEditor<T>>, LeafValueEditor<T>, Focusable {
- interface Binder extends UiBinder<Widget, ValueEditor<?>> {}
-
- static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField SimplePanel textPanel;
- private Label textLabel;
- private StartEditHandlers startHandlers;
-
- @UiField Image editIcon;
-
- @UiField SimplePanel editPanel;
-
- @UiField DivElement errorLabel;
-
- private ValueBoxBase<T> editChild;
- private ValueBoxEditor<T> editProxy;
- private boolean ignoreEditorValue;
- private T value;
-
- public ValueEditor() {
- startHandlers = new StartEditHandlers();
- initWidget(uiBinder.createAndBindUi(this));
- editPanel.setVisible(false);
- editIcon.addClickHandler(startHandlers);
- }
-
- public void edit() {
- textPanel.removeFromParent();
- textPanel = null;
- textLabel = null;
-
- editIcon.removeFromParent();
- editIcon = null;
- startHandlers = null;
-
- editPanel.setVisible(true);
- }
-
- @Override
- public ValueBoxEditor<T> asEditor() {
- if (editProxy == null) {
- editProxy = new EditorProxy();
- }
- return editProxy;
- }
-
- @Override
- public T getValue() {
- return ignoreEditorValue ? value : asEditor().getValue();
- }
-
- @Override
- public void setValue(T value) {
- this.value = value;
- asEditor().setValue(value);
- }
-
- void setIgnoreEditorValue(boolean off) {
- ignoreEditorValue = off;
- }
-
- public void setEditTitle(String title) {
- editIcon.setTitle(title);
- }
-
- @UiChild(limit = 1, tagname = "display")
- public void setDisplay(Label widget) {
- textLabel = widget;
- textPanel.add(textLabel);
-
- textLabel.addClickHandler(startHandlers);
- textLabel.addDoubleClickHandler(startHandlers);
- }
-
- @UiChild(limit = 1, tagname = "editor")
- public void setEditor(ValueBoxBase<T> widget) {
- editChild = widget;
- editPanel.add(editChild);
- editProxy = null;
- }
-
- public void setEnabled(boolean enabled) {
- editIcon.setVisible(enabled);
- startHandlers.enabled = enabled;
- }
-
- @Override
- public void showErrors(List<EditorError> errors) {
- StringBuilder buf = new StringBuilder();
- for (EditorError error : errors) {
- if (error.getEditor().equals(editProxy)) {
- buf.append("\n");
- if (error.getUserData() instanceof ParseException) {
- buf.append(((ParseException) error.getUserData()).getMessage());
- } else {
- buf.append(error.getMessage());
- }
- }
- }
-
- if (0 < buf.length()) {
- errorLabel.setInnerText(buf.substring(1));
- errorLabel.getStyle().setDisplay(Display.BLOCK);
- } else {
- errorLabel.setInnerText("");
- errorLabel.getStyle().setDisplay(Display.NONE);
- }
- }
-
- @Override
- public void setAccessKey(char key) {
- editChild.setAccessKey(key);
- }
-
- @Override
- public void setFocus(boolean focused) {
- editChild.setFocus(focused);
- if (focused) {
- editChild.setCursorPos(editChild.getText().length());
- }
- }
-
- @Override
- public int getTabIndex() {
- return editChild.getTabIndex();
- }
-
- @Override
- public void setTabIndex(int index) {
- editChild.setTabIndex(index);
- }
-
- private class StartEditHandlers implements ClickHandler, DoubleClickHandler {
- boolean enabled;
-
- @Override
- public void onClick(ClickEvent event) {
- if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
- edit();
- }
- }
-
- @Override
- public void onDoubleClick(DoubleClickEvent event) {
- if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
- edit();
- }
- }
- }
-
- private class EditorProxy extends ValueBoxEditor<T> {
- EditorProxy() {
- super(editChild);
- }
-
- @Override
- public void setValue(T value) {
- super.setValue(value);
- if (textLabel == null) {
- setDisplay(new Label());
- }
- textLabel.setText(editChild.getText());
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml
deleted file mode 100644
index 137ad2b0d4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- >
-<ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
-<ui:style gss='false'>
- .panel {
- position: relative;
- white-space: nowrap;
- }
-
- .textPanel {
- width: 100%;
- padding-right: 21px;
- }
-
- .editIcon {
- position: absolute;
- top: 0;
- right: 5px;
- }
-
- .editPanel {
- width: 100%;
- }
-
- .errorLabel {
- display: none;
- color: red;
- white-space: pre;
- }
-</ui:style>
-<g:HTMLPanel stylePrimaryName='{style.panel}'>
- <g:Image
- ui:field='editIcon'
- resource='{ico.edit}'
- stylePrimaryName='{style.editIcon}'
- title='Edit'>
- <ui:attribute name='title'/>
- </g:Image>
- <g:SimplePanel ui:field='textPanel' stylePrimaryName='{style.textPanel}'/>
-
- <g:SimplePanel ui:field='editPanel' stylePrimaryName='{style.editPanel}'/>
- <div
- ui:field='errorLabel'
- class='{style.errorLabel}'/>
-</g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css
deleted file mode 100644
index eca4823b17..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css
+++ /dev/null
@@ -1,53 +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.
- */
-
-@eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-@eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
-@def deletedBackground #a9a9a9;
-
-@sprite .deleteIcon {
- gwt-image: 'deleteNormal';
- border: none;
-}
-
-@sprite .deleteIcon:hover {
- gwt-image: 'deleteHover';
- border: none;
-}
-
-@sprite .undoIcon {
- gwt-image: 'undoNormal';
- border: none;
-}
-
-.deleted {
- background-color: deletedBackground;
- color: #ffffff;
- white-space: nowrap;
- padding-left: 50px;
-}
-
-.deleted:hover {
- background-color: selectionColor;
- color: textColor;
- }
-
-.deletedBorder {
- background: 1px solid deletedBackground;
-}
-
-.deleteSectionHover {
- background-color: selectionColor !important;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/arrow_undo.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/arrow_undo.png
deleted file mode 100644
index 6972c5e594..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/arrow_undo.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
deleted file mode 100644
index 9fde3fa521..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
deleted file mode 100644
index 47a1195789..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
deleted file mode 100644
index cf8de5492d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ActionContext.java
+++ /dev/null
@@ -1,293 +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.client.api;
-
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ActionContext extends JavaScriptObject {
- static final native void init() /*-{
- var Gerrit = $wnd.Gerrit;
- var doc = $wnd.document;
- var stopPropagation = function (e) {
- if (e && e.stopPropagation) e.stopPropagation();
- else $wnd.event.cancelBubble = true;
- };
-
- Gerrit.ActionContext = function(u){this._u=u};
- Gerrit.ActionContext.prototype = {
- go: Gerrit.go,
- refresh: Gerrit.refresh,
- refreshMenuBar: Gerrit.refreshMenuBar,
- isSignedIn: Gerrit.isSignedIn,
- showError: Gerrit.showError,
-
- br: function(){return doc.createElement('br')},
- hr: function(){return doc.createElement('hr')},
- button: function(label, o) {
- var e = doc.createElement('button');
- e.appendChild(this.div(doc.createTextNode(label)));
- if (o && o.onclick) e.onclick = o.onclick;
- return e;
- },
- checkbox: function() {
- var e = doc.createElement('input');
- e.type = 'checkbox';
- return e;
- },
- div: function() {
- var e = doc.createElement('div');
- for (var i = 0; i < arguments.length; i++)
- e.appendChild(arguments[i]);
- return e;
- },
- label: function(c,label) {
- var e = doc.createElement('label');
- e.appendChild(c);
- e.appendChild(doc.createTextNode(label));
- return e;
- },
- prependLabel: function(label,c) {
- var e = doc.createElement('label');
- e.appendChild(doc.createTextNode(label));
- e.appendChild(c);
- return e;
- },
- span: function() {
- var e = doc.createElement('span');
- for (var i = 0; i < arguments.length; i++)
- e.appendChild(arguments[i]);
- return e;
- },
- msg: function(label) {
- var e = doc.createElement('span');
- e.appendChild(doc.createTextNode(label));
- return e;
- },
- textarea: function(o) {
- var e = doc.createElement('textarea');
- e.onkeypress = stopPropagation;
- if (o && o.rows) e.rows = o.rows;
- if (o && o.cols) e.cols = o.cols;
- return e;
- },
- textfield: function() {
- var e = doc.createElement('input');
- e.type = 'text';
- e.onkeypress = stopPropagation;
- return e;
- },
- select: function(a,s) {
- var e = doc.createElement('select');
- for (var i = 0; i < a.length; i++) {
- var o = doc.createElement('option');
- if (i==s) {
- o.setAttributeNode(doc.createAttribute("selected"));
- }
- o.appendChild(doc.createTextNode(a[i]));
- e.appendChild(o);
- }
- return e;
- },
- selected: function(e) {
- return e.options[e.selectedIndex].text;
- },
-
- popup: function(e){
- this._p=@com.google.gerrit.client.api.PopupHelper::popup(
- Lcom/google/gerrit/client/api/ActionContext;Lcom/google/gwt/dom/client/Element;)(this,e)},
- hide: function() {
- this._p.@com.google.gerrit.client.api.PopupHelper::hide()();
- delete this['_p'];
- },
-
- call: function(i,b) {
- var m = this.action.method.toLowerCase();
- if (m == 'get' || m == 'delete' || i==null) this[m](b);
- else this[m](i,b);
- },
- get: function(b){@com.google.gerrit.client.api.ActionContext::get(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)},
- post: function(i,b){@com.google.gerrit.client.api.ActionContext::post(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(
- this._u,i,b)},
- put: function(i,b){@com.google.gerrit.client.api.ActionContext::put(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(
- this._u,i,b)},
- 'delete': function(b){@com.google.gerrit.client.api.ActionContext::delete(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)},
- del: function(b){@com.google.gerrit.client.api.ActionContext::delete(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._u,b)},
- };
- }-*/;
-
- static final native ActionContext create(RestApi f) /*-{
- return new $wnd.Gerrit.ActionContext(f);
- }-*/;
-
- final native void set(ActionInfo a) /*-{ this.action=a; }-*/;
-
- final native void set(ChangeInfo c) /*-{ this.change=c; }-*/;
-
- final native void set(EditInfo e) /*-{ this.edit=e; }-*/;
-
- final native void set(Project.NameKey p) /*-{ this.project=p; }-*/;
-
- final native void set(BranchInfo b) /*-{ this.branch=b }-*/;
-
- final native void set(RevisionInfo r) /*-{ this.revision=r; }-*/;
-
- final native void button(ActionButton b) /*-{ this._b=b; }-*/;
-
- final native ActionButton button() /*-{ return this._b; }-*/;
-
- public final native boolean has_popup() /*-{ return this.hasOwnProperty('_p') }-*/;
-
- public final native void hide() /*-{ this.hide(); }-*/;
-
- protected ActionContext() {}
-
- static final void get(RestApi api, JavaScriptObject cb) {
- api.get(wrap(cb));
- }
-
- /**
- * The same as {@link #get(RestApi, JavaScriptObject)} but without converting a {@link
- * NativeString} result to String.
- */
- static final void getRaw(RestApi api, JavaScriptObject cb) {
- api.get(wrapRaw(cb));
- }
-
- static final void post(RestApi api, JavaScriptObject in, JavaScriptObject cb) {
- if (NativeString.is(in)) {
- post(api, ((NativeString) in).asString(), cb);
- } else {
- api.post(in, wrap(cb));
- }
- }
-
- /**
- * The same as {@link #post(RestApi, JavaScriptObject, JavaScriptObject)} but without converting a
- * {@link NativeString} result to String.
- */
- static final void postRaw(RestApi api, JavaScriptObject in, JavaScriptObject cb) {
- if (NativeString.is(in)) {
- postRaw(api, ((NativeString) in).asString(), cb);
- } else {
- api.post(in, wrapRaw(cb));
- }
- }
-
- static final void post(RestApi api, String in, JavaScriptObject cb) {
- api.post(in, wrap(cb));
- }
-
- /**
- * The same as {@link #post(RestApi, String, JavaScriptObject)} but without converting a {@link
- * NativeString} result to String.
- */
- static final void postRaw(RestApi api, String in, JavaScriptObject cb) {
- api.post(in, wrapRaw(cb));
- }
-
- static final void put(RestApi api, JavaScriptObject cb) {
- api.put(wrap(cb));
- }
-
- /**
- * The same as {@link #put(RestApi, JavaScriptObject)} but without converting a {@link
- * NativeString} result to String.
- */
- static final void putRaw(RestApi api, JavaScriptObject cb) {
- api.put(wrapRaw(cb));
- }
-
- static final void put(RestApi api, JavaScriptObject in, JavaScriptObject cb) {
- if (NativeString.is(in)) {
- put(api, ((NativeString) in).asString(), cb);
- } else {
- api.put(in, wrap(cb));
- }
- }
-
- /**
- * The same as {@link #put(RestApi, JavaScriptObject, JavaScriptObject)} but without converting a
- * {@link NativeString} result to String.
- */
- static final void putRaw(RestApi api, JavaScriptObject in, JavaScriptObject cb) {
- if (NativeString.is(in)) {
- putRaw(api, ((NativeString) in).asString(), cb);
- } else {
- api.put(in, wrapRaw(cb));
- }
- }
-
- static final void put(RestApi api, String in, JavaScriptObject cb) {
- api.put(in, wrap(cb));
- }
-
- /**
- * The same as {@link #put(RestApi, String, JavaScriptObject)} but without converting a {@link
- * NativeString} result to String.
- */
- static final void putRaw(RestApi api, String in, JavaScriptObject cb) {
- api.put(in, wrapRaw(cb));
- }
-
- static final void delete(RestApi api, JavaScriptObject cb) {
- api.delete(wrap(cb));
- }
-
- /**
- * The same as {@link #delete(RestApi, JavaScriptObject)} but without converting a {@link
- * NativeString} result to String.
- */
- static final void deleteRaw(RestApi api, JavaScriptObject cb) {
- api.delete(wrapRaw(cb));
- }
-
- private static GerritCallback<JavaScriptObject> wrap(JavaScriptObject cb) {
- return new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- if (NativeString.is(result)) {
- NativeString s = result.cast();
- ApiGlue.invoke(cb, s.asString());
- } else {
- ApiGlue.invoke(cb, result);
- }
- }
- };
- }
-
- private static GerritCallback<JavaScriptObject> wrapRaw(JavaScriptObject cb) {
- return new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- ApiGlue.invoke(cb, result);
- }
- };
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
deleted file mode 100644
index 294fa9b7df..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ApiGlue.java
+++ /dev/null
@@ -1,322 +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.client.api;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.info.ServerInfo;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-
-public class ApiGlue {
- private static String pluginName;
-
- public static void init() {
- init0();
- ActionContext.init();
- HtmlTemplate.init();
- Plugin.init();
- }
-
- private static native void init0() /*-{
- var serverUrl = @com.google.gwt.core.client.GWT::getHostPageBaseURL()();
- var ScreenDefinition = @com.google.gerrit.client.api.ExtensionScreen.Definition::TYPE;
- var SettingsScreenDefinition = @com.google.gerrit.client.api.ExtensionSettingsScreen.Definition::TYPE;
- var PanelDefinition = @com.google.gerrit.client.api.ExtensionPanel.Definition::TYPE;
- $wnd.Gerrit = {
- JsonString: @com.google.gerrit.client.rpc.NativeString::TYPE,
- events: {},
- plugins: {},
- screens: {},
- settingsScreens: {},
- panels: {},
- change_actions: {},
- edit_actions: {},
- revision_actions: {},
- project_actions: {},
- branch_actions: {},
-
- getPluginName: @com.google.gerrit.client.api.ApiGlue::getPluginName(),
- injectCss: @com.google.gwt.dom.client.StyleInjector::inject(Ljava/lang/String;),
- install: function (f) {
- var p = this._getPluginByUrl(@com.google.gerrit.client.api.PluginName::getCallerUrl()());
- @com.google.gerrit.client.api.ApiGlue::install(
- Lcom/google/gwt/core/client/JavaScriptObject;
- Lcom/google/gerrit/client/api/Plugin;)
- (f,p);
- },
- installGwt: function(u){return this._getPluginByUrl(u)},
- _getPluginByUrl: function(u) {
- return u.indexOf(serverUrl) == 0
- ? this.plugins[u.substring(serverUrl.length)]
- : this.plugins[u]
- },
-
- go: @com.google.gerrit.client.api.ApiGlue::go(Ljava/lang/String;),
- refresh: @com.google.gerrit.client.api.ApiGlue::refresh(),
- refreshMenuBar: @com.google.gerrit.client.api.ApiGlue::refreshMenuBar(),
- isSignedIn: @com.google.gerrit.client.api.ApiGlue::isSignedIn(),
- showError: @com.google.gerrit.client.api.ApiGlue::showError(Ljava/lang/String;),
- getServerInfo: @com.google.gerrit.client.api.ApiGlue::getServerInfo(),
- getCurrentUser: @com.google.gerrit.client.api.ApiGlue::getCurrentUser(),
- getUserPreferences: @com.google.gerrit.client.api.ApiGlue::getUserPreferences(),
- refreshUserPreferences: @com.google.gerrit.client.api.ApiGlue::refreshUserPreferences(),
-
- on: function (e,f){(this.events[e] || (this.events[e]=[])).push(f)},
- onAction: function (t,n,c){this._onAction(this.getPluginName(),t,n,c)},
- _onAction: function (p,t,n,c) {
- var i = p+'~'+n;
- if ('change' == t) this.change_actions[i]=c;
- else if ('edit' == t) this.edit_actions[i]=c;
- else if ('revision' == t) this.revision_actions[i]=c;
- else if ('project' == t) this.project_actions[i]=c;
- else if ('branch' == t) this.branch_actions[i]=c;
- else if ('screen' == t) _screen(p,t,c);
- },
- screen: function(r,c){this._screen(this.getPluginName(),r,c)},
- _screen: function(p,r,c){
- var s = new ScreenDefinition(r,c);
- (this.screens[p] || (this.screens[p]=[])).push(s);
- },
- settingsScreen: function(p,m,c){this._settingsScreen(this.getPluginName(),p,m,c)},
- _settingsScreen: function(n,p,m,c){
- var s = new SettingsScreenDefinition(p,m,c);
- (this.settingsScreens[n] || (this.settingsScreens[n]=[])).push(s);
- },
- panel: function(i,c,n){this._panel(this.getPluginName(),i,c,n)},
- _panel: function(n,i,c,x){
- var p = new PanelDefinition(n,c,x);
- (this.panels[i] || (this.panels[i]=[])).push(p);
- },
-
- url: function (d) {
- if (d && d.length > 0)
- return serverUrl + (d.charAt(0)=='/' ? d.substring(1) : d);
- return serverUrl;
- },
-
- _api: function(u) {
- return @com.google.gerrit.client.rpc.RestApi::new(Ljava/lang/String;)(u);
- },
- get: function(u,b) {
- @com.google.gerrit.client.api.ActionContext::get(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), b);
- },
- get_raw: function(u,b) {
- @com.google.gerrit.client.api.ActionContext::getRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), b);
- },
- post: function(u,i,b) {
- if (typeof i == 'string') {
- @com.google.gerrit.client.api.ActionContext::post(
- Lcom/google/gerrit/client/rpc/RestApi;
- Ljava/lang/String;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- } else {
- @com.google.gerrit.client.api.ActionContext::post(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- }
- },
- post_raw: function(u,i,b) {
- if (typeof i == 'string') {
- @com.google.gerrit.client.api.ActionContext::postRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Ljava/lang/String;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- } else {
- @com.google.gerrit.client.api.ActionContext::postRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- }
- },
- put: function(u,i,b) {
- if (b) {
- if (typeof i == 'string') {
- @com.google.gerrit.client.api.ActionContext::put(
- Lcom/google/gerrit/client/rpc/RestApi;
- Ljava/lang/String;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- } else {
- @com.google.gerrit.client.api.ActionContext::put(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- }
- } else {
- @com.google.gerrit.client.api.ActionContext::put(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i);
- }
- },
- put_raw: function(u,i,b) {
- if (b) {
- if (typeof i == 'string') {
- @com.google.gerrit.client.api.ActionContext::putRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Ljava/lang/String;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- } else {
- @com.google.gerrit.client.api.ActionContext::putRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i, b);
- }
- } else {
- @com.google.gerrit.client.api.ActionContext::putRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), i);
- }
- },
- 'delete': function(u,b) {
- @com.google.gerrit.client.api.ActionContext::delete(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), b);
- },
- del: function(u,b) {
- @com.google.gerrit.client.api.ActionContext::delete(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), b);
- },
- del_raw: function(u,b) {
- @com.google.gerrit.client.api.ActionContext::deleteRaw(
- Lcom/google/gerrit/client/rpc/RestApi;
- Lcom/google/gwt/core/client/JavaScriptObject;)
- (this._api(u), b);
- },
- };
- }-*/;
-
- private static void install(JavaScriptObject cb, Plugin p) throws Exception {
- try {
- pluginName = p.name();
- invoke(cb, p);
- p._initialized();
- } catch (Exception e) {
- p.failure(e);
- throw e;
- } finally {
- pluginName = null;
- PluginLoader.loaded();
- }
- }
-
- private static String getPluginName() {
- if (pluginName != null) {
- return pluginName;
- }
- return PluginName.fromUrl(PluginName.getCallerUrl());
- }
-
- private static void go(String urlOrToken) {
- if (urlOrToken.startsWith("http:")
- || urlOrToken.startsWith("https:")
- || urlOrToken.startsWith("//")) {
- Window.Location.assign(urlOrToken);
- } else {
- Gerrit.display(urlOrToken);
- }
- }
-
- private static void refresh() {
- Gerrit.display(History.getToken());
- }
-
- private static ServerInfo getServerInfo() {
- return Gerrit.info();
- }
-
- private static AccountInfo getCurrentUser() {
- return Gerrit.getUserAccount();
- }
-
- private static GeneralPreferences getUserPreferences() {
- return Gerrit.getUserPreferences();
- }
-
- private static void refreshUserPreferences() {
- Gerrit.refreshUserPreferences();
- }
-
- private static void refreshMenuBar() {
- Gerrit.refreshMenuBar();
- }
-
- private static boolean isSignedIn() {
- return Gerrit.isSignedIn();
- }
-
- private static void showError(String message) {
- new ErrorDialog(message).center();
- }
-
- static final native void invoke(JavaScriptObject f) /*-{ f(); }-*/;
-
- static final native void invoke(JavaScriptObject f, JavaScriptObject a) /*-{ f(a); }-*/;
-
- static final native void invoke(
- JavaScriptObject f, JavaScriptObject a, JavaScriptObject b) /*-{ f(a,b) }-*/;
-
- static final native void invoke(JavaScriptObject f, String a) /*-{ f(a); }-*/;
-
- public static final void fireEvent(String event, String a) {
- JsArray<JavaScriptObject> h = getEventHandlers(event);
- for (int i = 0; i < h.length(); i++) {
- invoke(h.get(i), a);
- }
- }
-
- public static final void fireEvent(String event, Element e) {
- JsArray<JavaScriptObject> h = getEventHandlers(event);
- for (int i = 0; i < h.length(); i++) {
- invoke(h.get(i), e);
- }
- }
-
- static final void fireEvent(String event, JavaScriptObject a, JavaScriptObject b) {
- JsArray<JavaScriptObject> h = getEventHandlers(event);
- for (int i = 0; i < h.length(); i++) {
- invoke(h.get(i), a, b);
- }
- }
-
- static final native JsArray<JavaScriptObject> getEventHandlers(String e)
- /*-{ return $wnd.Gerrit.events[e] || [] }-*/ ;
-
- private ApiGlue() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java
deleted file mode 100644
index c7f005174e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ChangeGlue.java
+++ /dev/null
@@ -1,63 +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.client.api;
-
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-
-public class ChangeGlue {
- public static void fireShowChange(ChangeInfo change, RevisionInfo rev) {
- ApiGlue.fireEvent("showchange", change, rev);
- }
-
- public static boolean onSubmitChange(ChangeInfo change, RevisionInfo rev) {
- JsArray<JavaScriptObject> h = ApiGlue.getEventHandlers("submitchange");
- for (int i = 0; i < h.length(); i++) {
- if (!invoke(h.get(i), change, rev)) {
- return false;
- }
- }
- return true;
- }
-
- public static void onAction(ChangeInfo change, ActionInfo action, ActionButton button) {
- RestApi api = ChangeApi.change(change.project(), change.legacyId().get()).view(action.id());
- JavaScriptObject f = get(action.id());
- if (f != null) {
- ActionContext c = ActionContext.create(api);
- c.set(action);
- c.set(change);
- c.button(button);
- ApiGlue.invoke(f, c);
- } else {
- DefaultActions.invoke(change, action, api);
- }
- }
-
- private static native JavaScriptObject get(String id) /*-{
- return $wnd.Gerrit.change_actions[id];
- }-*/;
-
- private static native boolean invoke(JavaScriptObject h, ChangeInfo a, RevisionInfo r)
- /*-{ return h(a,r) }-*/ ;
-
- private ChangeGlue() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
deleted file mode 100644
index 0c4aacd4b3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/DefaultActions.java
+++ /dev/null
@@ -1,99 +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.client.api;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-class DefaultActions {
- static void invoke(ChangeInfo change, ActionInfo action, RestApi api) {
- invoke(action, api, callback(PageLinks.toChange(change.projectNameKey(), change.legacyId())));
- }
-
- static void invoke(Project.NameKey project, ActionInfo action, RestApi api) {
- invoke(action, api, callback(PageLinks.toProject(project)));
- }
-
- private static AsyncCallback<JavaScriptObject> callback(String target) {
- return new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject in) {
- UiResult result = asUiResult(in);
- if (result == null) {
- Gerrit.display(target);
- return;
- }
-
- if (result.alert() != null) {
- Window.alert(result.alert());
- }
-
- if (result.redirectUrl() != null && result.openWindow()) {
- Window.open(result.redirectUrl(), "_blank", null);
- } else if (result.redirectUrl() != null) {
- Location.assign(result.redirectUrl());
- } else {
- Gerrit.display(target);
- }
- }
-
- private UiResult asUiResult(JavaScriptObject in) {
- if (NativeString.is(in)) {
- String str = ((NativeString) in).asString();
- return str.isEmpty() ? UiResult.none() : UiResult.alert(str);
- }
- return in.cast();
- }
- };
- }
-
- private static void invoke(ActionInfo action, RestApi api, AsyncCallback<JavaScriptObject> cb) {
- if ("GET".equalsIgnoreCase(action.method())) {
- api.get(cb);
- } else if ("PUT".equalsIgnoreCase(action.method())) {
- api.put(JavaScriptObject.createObject(), cb);
- } else if ("DELETE".equalsIgnoreCase(action.method())) {
- api.delete(cb);
- } else {
- api.post(JavaScriptObject.createObject(), cb);
- }
- }
-
- private DefaultActions() {}
-
- private static class UiResult extends JavaScriptObject {
- static native UiResult alert(String m) /*-{ return {'alert':m} }-*/;
-
- static native UiResult none() /*-{ return {} }-*/;
-
- final native String alert() /*-{ return this.alert }-*/;
-
- final native String redirectUrl() /*-{ return this.url }-*/;
-
- final native boolean openWindow() /*-{ return this.open_window || false }-*/;
-
- protected UiResult() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
deleted file mode 100644
index 85cfde66de..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/EditGlue.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2014 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.client.api;
-
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class EditGlue {
- public static void onAction(
- ChangeInfo change, EditInfo edit, ActionInfo action, ActionButton button) {
- RestApi api = ChangeApi.edit(change.project(), change.legacyId().get()).view(action.id());
-
- JavaScriptObject f = get(action.id());
- if (f != null) {
- ActionContext c = ActionContext.create(api);
- c.set(action);
- c.set(change);
- c.set(edit);
- c.button(button);
- ApiGlue.invoke(f, c);
- } else {
- DefaultActions.invoke(change, action, api);
- }
- }
-
- private static native JavaScriptObject get(String id) /*-{
- return $wnd.Gerrit.edit_actions[id];
- }-*/;
-
- private EditGlue() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionPanel.java
deleted file mode 100644
index 6d3dd600bb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionPanel.java
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright (C) 2015 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.client.api;
-
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class ExtensionPanel extends FlowPanel {
- private static final Logger logger = Logger.getLogger(ExtensionPanel.class.getName());
- private final GerritUiExtensionPoint extensionPoint;
- private final List<Context> contexts;
-
- public ExtensionPanel(GerritUiExtensionPoint extensionPoint) {
- this(extensionPoint, new ArrayList<String>());
- }
-
- public ExtensionPanel(GerritUiExtensionPoint extensionPoint, List<String> panelNames) {
- this.extensionPoint = extensionPoint;
- this.contexts = create(panelNames);
- }
-
- private List<Context> create(List<String> panelNames) {
- List<Context> contexts = new ArrayList<>();
- for (Definition def : getOrderedDefs(panelNames)) {
- SimplePanel p = new SimplePanel();
- add(p);
- contexts.add(Context.create(def, p));
- }
- return contexts;
- }
-
- private List<Definition> getOrderedDefs(List<String> panelNames) {
- if (panelNames == null) {
- panelNames = Collections.emptyList();
- }
- Map<String, List<Definition>> defsOrderedByName = new LinkedHashMap<>();
- for (String name : panelNames) {
- defsOrderedByName.put(name, new ArrayList<Definition>());
- }
- for (Definition def : Natives.asList(Definition.get(extensionPoint.name()))) {
- addDef(def, defsOrderedByName);
- }
- List<Definition> orderedDefs = new ArrayList<>();
- for (List<Definition> defList : defsOrderedByName.values()) {
- orderedDefs.addAll(defList);
- }
- return orderedDefs;
- }
-
- private static void addDef(Definition def, Map<String, List<Definition>> defsOrderedByName) {
- String panelName = def.getPanelName();
- if (panelName.equals(def.getPluginName() + ".undefined")) {
- /* Handle a partially undefined panel name from the
- javascript layer by generating a random panel name.
- This maintains support for panels that do not provide a name. */
- panelName =
- def.getPluginName() + "." + Long.toHexString(Double.doubleToLongBits(Math.random()));
- }
- if (defsOrderedByName.containsKey(panelName)) {
- defsOrderedByName.get(panelName).add(def);
- } else if (defsOrderedByName.containsKey(def.getPluginName())) {
- defsOrderedByName.get(def.getPluginName()).add(def);
- } else {
- defsOrderedByName.put(panelName, Collections.singletonList(def));
- }
- }
-
- public void put(GerritUiExtensionPoint.Key key, String value) {
- for (Context ctx : contexts) {
- ctx.put(key.name(), value);
- }
- }
-
- public void putInt(GerritUiExtensionPoint.Key key, int value) {
- for (Context ctx : contexts) {
- ctx.putInt(key.name(), value);
- }
- }
-
- public void putBoolean(GerritUiExtensionPoint.Key key, boolean value) {
- for (Context ctx : contexts) {
- ctx.putBoolean(key.name(), value);
- }
- }
-
- public void putObject(GerritUiExtensionPoint.Key key, JavaScriptObject value) {
- for (Context ctx : contexts) {
- ctx.putObject(key.name(), value);
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- for (Context ctx : contexts) {
- try {
- ctx.onLoad();
- } catch (RuntimeException e) {
- logger.log(
- Level.SEVERE,
- "Failed to load extension panel for extension point "
- + extensionPoint.name()
- + " from plugin "
- + ctx.getPluginName()
- + ": "
- + e.getMessage());
- }
- }
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- for (Context ctx : contexts) {
- for (JavaScriptObject u : Natives.asList(ctx.unload())) {
- ApiGlue.invoke(u);
- }
- }
- }
-
- static class Definition extends JavaScriptObject {
- static final JavaScriptObject TYPE = init();
-
- private static native JavaScriptObject init() /*-{
- function PanelDefinition(n, c, x) {
- this.pluginName = n;
- this.onLoad = c;
- this.name = x;
- };
- return PanelDefinition;
- }-*/;
-
- static native JsArray<Definition> get(String i) /*-{ return $wnd.Gerrit.panels[i] || [] }-*/;
-
- protected Definition() {}
-
- public final native String getPanelName() /*-{ return this.pluginName + "." + this.name; }-*/;
-
- public final native String getPluginName() /*-{ return this.pluginName; }-*/;
- }
-
- static class Context extends JavaScriptObject {
- static final Context create(Definition def, SimplePanel panel) {
- return create(TYPE, def, panel.getElement());
- }
-
- final native void onLoad() /*-{ this._d.onLoad(this) }-*/;
-
- final native JsArray<JavaScriptObject> unload() /*-{ return this._u }-*/;
-
- final native String getPluginName() /*-{ return this._d.pluginName; }-*/;
-
- final native void put(String k, String v) /*-{ this.p[k] = v; }-*/;
-
- final native void putInt(String k, int v) /*-{ this.p[k] = v; }-*/;
-
- final native void putBoolean(String k, boolean v) /*-{ this.p[k] = v; }-*/;
-
- final native void putObject(String k, JavaScriptObject v) /*-{ this.p[k] = v; }-*/;
-
- private static native Context create(JavaScriptObject T, Definition d, Element e)
- /*-{ return new T(d,e) }-*/ ;
-
- private static final JavaScriptObject TYPE = init();
-
- private static native JavaScriptObject init() /*-{
- var T = function(d,e) {
- this._d = d;
- this._u = [];
- this.body = e;
- this.p = {};
- };
- T.prototype = {
- onUnload: function(f){this._u.push(f)},
- };
- return T;
- }-*/;
-
- protected Context() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionScreen.java
deleted file mode 100644
index ff495b901b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionScreen.java
+++ /dev/null
@@ -1,134 +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.client.api;
-
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-
-/** Screen contributed by a plugin. */
-public class ExtensionScreen extends Screen {
- private Context ctx;
-
- public ExtensionScreen(String token) {
- if (token.contains("?")) {
- token = token.substring(0, token.indexOf('?'));
- }
- String name;
- String rest;
- int s = token.indexOf('/');
- if (0 < s) {
- name = token.substring(0, s);
- rest = token.substring(s + 1);
- } else {
- name = token;
- rest = "";
- }
- ctx = create(name, rest);
- }
-
- private Context create(String name, String rest) {
- for (Definition def : Natives.asList(Definition.get(name))) {
- JsArrayString m = def.match(rest);
- if (m != null) {
- return Context.create(def, this, m);
- }
- }
- return null;
- }
-
- public boolean isFound() {
- return ctx != null;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- setHeaderVisible(false);
- ctx.onLoad();
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- for (JavaScriptObject u : Natives.asList(ctx.unload())) {
- ApiGlue.invoke(u);
- }
- }
-
- static class Definition extends JavaScriptObject {
- static final JavaScriptObject TYPE = init();
-
- private static native JavaScriptObject init() /*-{
- function ScreenDefinition(r, c) {
- this.pattern = r;
- this.onLoad = c;
- };
- return ScreenDefinition;
- }-*/;
-
- static native JsArray<Definition> get(String n) /*-{ return $wnd.Gerrit.screens[n] || [] }-*/;
-
- final native JsArrayString match(String t) /*-{
- var p = this.pattern;
- if (p instanceof $wnd.RegExp) {
- var m = p.exec(t);
- return m && m[0] == t ? m : null;
- }
- return p == t ? [t] : null;
- }-*/;
-
- protected Definition() {}
- }
-
- static class Context extends JavaScriptObject {
- static final Context create(Definition def, ExtensionScreen view, JsArrayString match) {
- return create(TYPE, def, view, view.getBody().getElement(), match);
- }
-
- final native void onLoad() /*-{ this._d.onLoad(this) }-*/;
-
- final native JsArray<JavaScriptObject> unload() /*-{ return this._u }-*/;
-
- private static native Context create(
- JavaScriptObject T, Definition d, ExtensionScreen s, Element e, JsArrayString m)
- /*-{ return new T(d,s,e,m) }-*/ ;
-
- private static final JavaScriptObject TYPE = init();
-
- private static native JavaScriptObject init() /*-{
- var T = function(d,s,e,m) {
- this._d = d;
- this._s = s;
- this._u = [];
- this.body = e;
- this.token = m[0];
- this.token_match = m;
- };
- T.prototype = {
- setTitle: function(t){this._s.@com.google.gerrit.client.ui.Screen::setPageTitle(Ljava/lang/String;)(t)},
- setWindowTitle: function(t){this._s.@com.google.gerrit.client.ui.Screen::setWindowTitle(Ljava/lang/String;)(t)},
- show: function(){$entry(this._s.@com.google.gwtexpui.user.client.View::display()())},
- onUnload: function(f){this._u.push(f)},
- };
- return T;
- }-*/;
-
- protected Context() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionSettingsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionSettingsScreen.java
deleted file mode 100644
index e7d1ed1c16..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ExtensionSettingsScreen.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2015 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.client.api;
-
-import com.google.gerrit.client.account.SettingsScreen;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Element;
-import java.util.Set;
-
-/** SettingsScreen contributed by a plugin. */
-public class ExtensionSettingsScreen extends SettingsScreen {
- private Context ctx;
-
- public ExtensionSettingsScreen(String token) {
- if (token.contains("?")) {
- token = token.substring(0, token.indexOf('?'));
- }
- String name;
- String rest;
- int s = token.indexOf('/');
- if (0 < s) {
- name = token.substring(0, s);
- rest = token.substring(s + 1);
- } else {
- name = token;
- rest = "";
- }
- ctx = create(name, rest);
- }
-
- private Context create(String name, String rest) {
- for (Definition def : Natives.asList(Definition.get(name))) {
- if (def.matches(rest)) {
- return Context.create(def, this);
- }
- }
- return null;
- }
-
- public boolean isFound() {
- return ctx != null;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- setHeaderVisible(false);
- ctx.onLoad();
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- for (JavaScriptObject u : Natives.asList(ctx.unload())) {
- ApiGlue.invoke(u);
- }
- }
-
- public static class Definition extends JavaScriptObject {
- static final JavaScriptObject TYPE = init();
-
- private static native JavaScriptObject init() /*-{
- function SettingsScreenDefinition(p, m, c) {
- this.path = p;
- this.menu = m;
- this.onLoad = c;
- };
- return SettingsScreenDefinition;
- }-*/;
-
- public static native JsArray<Definition> get(String n)
- /*-{ return $wnd.Gerrit.settingsScreens[n] || [] }-*/ ;
-
- public static final Set<String> plugins() {
- return Natives.keys(settingsScreens());
- }
-
- private static native NativeMap<NativeString> settingsScreens()
- /*-{ return $wnd.Gerrit.settingsScreens; }-*/ ;
-
- public final native String getPath() /*-{ return this.path; }-*/;
-
- public final native String getMenu() /*-{ return this.menu; }-*/;
-
- final native boolean matches(String t) /*-{ return this.path == t; }-*/;
-
- protected Definition() {}
- }
-
- static class Context extends JavaScriptObject {
- static final Context create(Definition def, ExtensionSettingsScreen view) {
- return create(TYPE, def, view, view.getBody().getElement());
- }
-
- final native void onLoad() /*-{ this._d.onLoad(this) }-*/;
-
- final native JsArray<JavaScriptObject> unload() /*-{ return this._u }-*/;
-
- private static native Context create(
- JavaScriptObject T, Definition d, ExtensionSettingsScreen s, Element e)
- /*-{ return new T(d,s,e) }-*/ ;
-
- private static final JavaScriptObject TYPE = init();
-
- private static native JavaScriptObject init() /*-{
- var T = function(d,s,e) {
- this._d = d;
- this._s = s;
- this._u = [];
- this.body = e;
- };
- T.prototype = {
- setTitle: function(t){this._s.@com.google.gerrit.client.ui.Screen::setPageTitle(Ljava/lang/String;)(t)},
- setWindowTitle: function(t){this._s.@com.google.gerrit.client.ui.Screen::setWindowTitle(Ljava/lang/String;)(t)},
- show: function(){$entry(this._s.@com.google.gwtexpui.user.client.View::display()())},
- onUnload: function(f){this._u.push(f)},
- };
- return T;
- }-*/;
-
- protected Context() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/HtmlTemplate.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/HtmlTemplate.java
deleted file mode 100644
index ba9c659b7f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/HtmlTemplate.java
+++ /dev/null
@@ -1,153 +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.client.api;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.dom.client.StyleInjector;
-import com.google.gwt.user.client.DOM;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-final class HtmlTemplate {
- static native void init() /*-{
- var ElementSet = function(r,e) {
- this.root = r;
- this.elements = e;
- };
- ElementSet.prototype = {
- clear: function() {
- this.root = null;
- this.elements = null;
- },
- };
-
- $wnd.Gerrit.css = @com.google.gerrit.client.api.HtmlTemplate::css(Ljava/lang/String;);
- $wnd.Gerrit.html = function(h,r,w) {
- var i = {};
- if (r) {
- h = h.replace(
- /\sid=['"]\{([a-z_][a-z0-9_]*)\}['"]|\{([a-z0-9._-]+)\}/gi,
- function(m,a,b) {
- if (a)
- return @com.google.gerrit.client.api.HtmlTemplate::id(
- Lcom/google/gerrit/client/api/HtmlTemplate$IdMap;
- Ljava/lang/String;)
- (i,a);
- return @com.google.gerrit.client.api.HtmlTemplate::html(
- Lcom/google/gerrit/client/api/HtmlTemplate$ReplacementMap;
- Ljava/lang/String;)
- (r,b);
- });
- }
- var e = @com.google.gerrit.client.api.HtmlTemplate::parseHtml(
- Ljava/lang/String;Lcom/google/gerrit/client/api/HtmlTemplate$IdMap;
- Lcom/google/gerrit/client/api/HtmlTemplate$ReplacementMap;
- Z)
- (h,i,r,!!w);
- return w ? new ElementSet(e,i) : e;
- };
- }-*/;
-
- private static String css(String css) {
- String name = DOM.createUniqueId();
- StyleInjector.inject("." + name + "{" + css + "}");
- return name;
- }
-
- private static String id(IdMap idMap, String key) {
- String id = DOM.createUniqueId();
- idMap.put(id, key);
- return " id='" + id + "'";
- }
-
- private static String html(ReplacementMap opts, String id) {
- int d = id.indexOf('.');
- if (0 < d) {
- String name = id.substring(0, d);
- String rest = id.substring(d + 1);
- return html(opts.map(name), rest);
- }
- return new SafeHtmlBuilder().append(opts.str(id)).asString();
- }
-
- private static Node parseHtml(String html, IdMap ids, ReplacementMap opts, boolean wantElements) {
- Element div = Document.get().createDivElement();
- div.setInnerHTML(html);
- if (!ids.isEmpty()) {
- attachHandlers(div, ids, opts, wantElements);
- }
- if (div.getChildCount() == 1) {
- return div.getFirstChild();
- }
- return div;
- }
-
- private static void attachHandlers(
- Element e, IdMap ids, ReplacementMap opts, boolean wantElements) {
- if (e.getId() != null) {
- String key = ids.get(e.getId());
- if (key != null) {
- ids.remove(e.getId());
- if (wantElements) {
- ids.put(key, e);
- }
- e.setId(null);
- opts.map(key).attachHandlers(e);
- }
- }
- for (Element c = e.getFirstChildElement(); c != null; ) {
- attachHandlers(c, ids, opts, wantElements);
- c = c.getNextSiblingElement();
- }
- }
-
- private static class ReplacementMap extends JavaScriptObject {
- final native ReplacementMap map(String n) /*-{ return this[n] }-*/;
-
- final native String str(String n) /*-{ return ''+this[n] }-*/;
-
- final native void attachHandlers(Element e) /*-{
- for (var k in this) {
- var f = this[k];
- if (k.substring(0, 2) == 'on' && typeof f == 'function')
- e[k] = f;
- }
- }-*/;
-
- protected ReplacementMap() {}
- }
-
- private static class IdMap extends JavaScriptObject {
- final native String get(String i) /*-{ return this[i] }-*/;
-
- final native void remove(String i) /*-{ delete this[i] }-*/;
-
- final native void put(String i, String k) /*-{ this[i] = k }-*/;
-
- final native void put(String k, Element e) /*-{ this[k] = e }-*/;
-
- final native boolean isEmpty() /*-{
- for (var i in this)
- return false;
- return true;
- }-*/;
-
- protected IdMap() {}
- }
-
- private HtmlTemplate() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java
deleted file mode 100644
index 48a812c161..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/Plugin.java
+++ /dev/null
@@ -1,99 +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.client.api;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-final class Plugin extends JavaScriptObject {
- private static final JavaScriptObject TYPE = createType();
-
- static Plugin create(String url) {
- int s = "plugins/".length();
- int e = url.indexOf('/', s);
- String name = url.substring(s, e);
- return create(TYPE, url, name);
- }
-
- native String url() /*-{ return this._scriptUrl }-*/;
-
- native String name() /*-{ return this.name }-*/;
-
- native boolean loaded() /*-{ return this._success || this._failure != null }-*/;
-
- native Exception failure() /*-{ return this._failure }-*/;
-
- native void failure(Exception e) /*-{ this._failure = e }-*/;
-
- native boolean success() /*-{ return this._success || false }-*/;
-
- native void _initialized() /*-{ this._success = true }-*/;
-
- private static native Plugin create(JavaScriptObject T, String u, String n)
- /*-{ return new T(u,n) }-*/ ;
-
- private static native JavaScriptObject createType() /*-{
- function Plugin(u, n) {
- this._scriptUrl = u;
- this.name = n;
- }
- return Plugin;
- }-*/;
-
- static native void init() /*-{
- var G = $wnd.Gerrit;
- @com.google.gerrit.client.api.Plugin::TYPE.prototype = {
- getPluginName: function(){return this.name},
- getServerInfo: @com.google.gerrit.client.api.ApiGlue::getServerInfo(),
- getCurrentUser: @com.google.gerrit.client.api.ApiGlue::getCurrentUser(),
- getUserPreferences: @com.google.gerrit.client.api.ApiGlue::getUserPreferences(),
- refreshUserPreferences: @com.google.gerrit.client.api.ApiGlue::refreshUserPreferences(),
- go: @com.google.gerrit.client.api.ApiGlue::go(Ljava/lang/String;),
- refresh: @com.google.gerrit.client.api.ApiGlue::refresh(),
- refreshMenuBar: @com.google.gerrit.client.api.ApiGlue::refreshMenuBar(),
- isSignedIn: @com.google.gerrit.client.api.ApiGlue::isSignedIn(),
- showError: @com.google.gerrit.client.api.ApiGlue::showError(Ljava/lang/String;),
- on: function(e,f){G.on(e,f)},
- onAction: function(t,n,c){G._onAction(this.name,t,n,c)},
- screen: function(p,c){G._screen(this.name,p,c)},
- settingsScreen: function(p,m,c){G._settingsScreen(this.name,p,m,c)},
- panel: function(i,c,n){G._panel(this.name,i,c,n)},
-
- url: function (u){return G.url(this._url(u))},
- get: function(u,b){@com.google.gerrit.client.api.ActionContext::get(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)},
- post: function(u,i,b){@com.google.gerrit.client.api.ActionContext::post(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(
- this._api(u),i,b)},
- put: function(u,i,b){@com.google.gerrit.client.api.ActionContext::put(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)(
- this._api(u),i,b)},
- 'delete': function(u,b){@com.google.gerrit.client.api.ActionContext::delete(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)},
- del: function(u,b){@com.google.gerrit.client.api.ActionContext::delete(
- Lcom/google/gerrit/client/rpc/RestApi;Lcom/google/gwt/core/client/JavaScriptObject;)(this._api(u),b)},
-
- _loadedGwt: function(){@com.google.gerrit.client.api.PluginLoader::loaded()()},
- _api: function(u){return @com.google.gerrit.client.rpc.RestApi::new(Ljava/lang/String;)(this._url(u))},
- _url: function (d) {
- var u = 'plugins/' + this.name + '/';
- if (d && d.length > 0)
- return u + (d.charAt(0)=='/' ? d.substring(1) : d);
- return u;
- },
- };
- }-*/;
-
- protected Plugin() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java
deleted file mode 100644
index c1f4e16efc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginLoader.java
+++ /dev/null
@@ -1,196 +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.client.api;
-
-import static java.util.stream.Collectors.toList;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.Callback;
-import com.google.gwt.core.client.CodeDownloadException;
-import com.google.gwt.core.client.ScriptInjector;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.DialogBox;
-import com.google.gwtexpui.progress.client.ProgressBar;
-import java.util.List;
-
-/** Loads JavaScript plugins with a progress meter visible. */
-public class PluginLoader extends DialogBox {
- private static PluginLoader self;
-
- public static void load(
- List<String> plugins, int loadTimeout, AsyncCallback<VoidResult> callback) {
- if (plugins == null || plugins.isEmpty()) {
- callback.onSuccess(VoidResult.create());
- } else {
- plugins = plugins.stream().filter(p -> p.endsWith(".js")).collect(toList());
- if (plugins.isEmpty()) {
- callback.onSuccess(VoidResult.create());
- } else {
- self = new PluginLoader(loadTimeout, callback);
- self.load(plugins);
- self.startTimers();
- self.center();
- }
- }
- }
-
- static void loaded() {
- self.loadedOne();
- }
-
- private final int loadTimeout;
- private final AsyncCallback<VoidResult> callback;
- private ProgressBar progress;
- private Timer show;
- private Timer update;
- private Timer timeout;
- private boolean visible;
-
- private PluginLoader(int loadTimeout, AsyncCallback<VoidResult> cb) {
- super(/* auto hide */ false, /* modal */ true);
- callback = cb;
- this.loadTimeout = loadTimeout;
- progress = new ProgressBar(Gerrit.C.loadingPlugins());
-
- setStyleName(Gerrit.RESOURCES.css().errorDialog());
- addStyleName(Gerrit.RESOURCES.css().loadingPluginsDialog());
- }
-
- private void load(List<String> pluginUrls) {
- for (String url : pluginUrls) {
- Plugin plugin = Plugin.create(url);
- plugins().put(url, plugin);
- ScriptInjector.fromUrl(url)
- .setWindow(ScriptInjector.TOP_WINDOW)
- .setCallback(new LoadCallback(plugin))
- .inject();
- }
- }
-
- private void startTimers() {
- show =
- new Timer() {
- @Override
- public void run() {
- setText(Window.getTitle());
- setWidget(progress);
- setGlassEnabled(true);
- getGlassElement().addClassName(Gerrit.RESOURCES.css().errorDialogGlass());
- hide(true);
- center();
- visible = true;
- }
- };
- show.schedule(500);
-
- update =
- new Timer() {
- private int cycle;
-
- @Override
- public void run() {
- progress.setValue(100 * ++cycle * 250 / loadTimeout);
- }
- };
- update.scheduleRepeating(250);
-
- timeout =
- new Timer() {
- @Override
- public void run() {
- finish();
- }
- };
- timeout.schedule(loadTimeout);
- }
-
- private void loadedOne() {
- boolean done = true;
- for (Plugin plugin : Natives.asList(plugins().values())) {
- done &= plugin.loaded();
- }
- if (done) {
- finish();
- }
- }
-
- private void finish() {
- show.cancel();
- update.cancel();
- timeout.cancel();
- self = null;
-
- if (!hadFailures()) {
- if (visible) {
- progress.setValue(100);
- new Timer() {
- @Override
- public void run() {
- hide(true);
- }
- }.schedule(250);
- } else {
- hide(true);
- }
- }
-
- callback.onSuccess(VoidResult.create());
- }
-
- private boolean hadFailures() {
- boolean failed = false;
- for (Plugin plugin : Natives.asList(plugins().values())) {
- if (!plugin.success()) {
- failed = true;
-
- Exception e = plugin.failure();
- String msg;
- if (e instanceof CodeDownloadException) {
- msg = Gerrit.M.cannotDownloadPlugin(plugin.url());
- } else {
- msg = Gerrit.M.pluginFailed(plugin.name());
- }
- hide(true);
- new ErrorDialog(msg).center();
- }
- }
- return failed;
- }
-
- private static native NativeMap<Plugin> plugins() /*-{ return $wnd.Gerrit.plugins }-*/;
-
- private class LoadCallback implements Callback<Void, Exception> {
- private final Plugin plugin;
-
- LoadCallback(Plugin plugin) {
- this.plugin = plugin;
- }
-
- @Override
- public void onSuccess(Void result) {}
-
- @Override
- public void onFailure(Exception reason) {
- plugin.failure(reason);
- loadedOne();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginName.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginName.java
deleted file mode 100644
index 7cf4fbb3d1..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PluginName.java
+++ /dev/null
@@ -1,107 +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.client.api;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptException;
-import com.google.gwt.core.client.JsArrayString;
-
-/**
- * Determines the name a plugin has been installed under.
- *
- * <p>This implementation guesses the name a plugin runs under by looking at the JavaScript call
- * stack and identifying the URL of the script file calling {@code Gerrit.install()}. The simple
- * approach applied here is looking at the source URLs and extracting the name out of the string,
- * e.g.: {@code "http://localhost:8080/plugins/[name]/static/foo.js"}.
- */
-class PluginName {
- private static final String UNKNOWN = "<unknown>";
-
- private static String baseUrl() {
- return GWT.getHostPageBaseURL() + "plugins/";
- }
-
- static String getCallerUrl() {
- return GWT.<PluginName>create(PluginName.class).findCallerUrl();
- }
-
- static String fromUrl(String url) {
- String baseUrl = baseUrl();
- if (url != null && url.startsWith(baseUrl)) {
- int s = url.indexOf('/', baseUrl.length());
- if (s > 0) {
- return url.substring(baseUrl.length(), s);
- }
- }
- return UNKNOWN;
- }
-
- String findCallerUrl() {
- JavaScriptException err = makeException();
- if (hasStack(err)) {
- return PluginNameMoz.getUrl(err);
- }
-
- String baseUrl = baseUrl();
- StackTraceElement[] trace = getTrace(err);
- for (int i = trace.length - 1; i >= 0; i--) {
- String u = trace[i].getFileName();
- if (u != null && u.startsWith(baseUrl)) {
- return u;
- }
- }
- return UNKNOWN;
- }
-
- private static StackTraceElement[] getTrace(JavaScriptException err) {
- if (err.getStackTrace().length == 0) {
- err.fillInStackTrace();
- }
- return err.getStackTrace();
- }
-
- protected static final native JavaScriptException makeException()
- /*-{ try { null.a() } catch (e) { return e } }-*/ ;
-
- private static native boolean hasStack(JavaScriptException e) /*-{ return !!e.stack }-*/;
-
- /** Extracts URL from the stack frame. */
- static class PluginNameMoz extends PluginName {
- @Override
- String findCallerUrl() {
- return getUrl(makeException());
- }
-
- private static String getUrl(JavaScriptException e) {
- String baseUrl = baseUrl();
- JsArrayString stack = getStack(e);
- for (int i = stack.length() - 1; i >= 0; i--) {
- String frame = stack.get(i);
- int at = frame.indexOf(baseUrl);
- if (at >= 0) {
- int end = frame.indexOf(':', at + baseUrl.length());
- if (end < 0) {
- end = frame.length();
- }
- return frame.substring(at, end);
- }
- }
- return UNKNOWN;
- }
-
- private static native JsArrayString getStack(JavaScriptException e)
- /*-{ return e.stack ? e.stack.split('\n') : [] }-*/ ;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PopupHelper.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PopupHelper.java
deleted file mode 100644
index 173b369e8d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/PopupHelper.java
+++ /dev/null
@@ -1,72 +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.client.api;
-
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.change.Resources;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-class PopupHelper {
- static PopupHelper popup(ActionContext ctx, Element panel) {
- PopupHelper helper = new PopupHelper(ctx.button(), panel);
- helper.show();
- ctx.button().link(ctx);
- return helper;
- }
-
- private final ActionButton activatingButton;
- private final FlowPanel panel;
- private PopupPanel popup;
-
- PopupHelper(ActionButton button, Element child) {
- activatingButton = button;
- panel = new FlowPanel();
- panel.setStyleName(Resources.I.style().popupContent());
- panel.getElement().appendChild(child);
- }
-
- void show() {
- final PopupPanel p = new PopupPanel(true);
- p.setStyleName(Resources.I.style().popup());
- p.addAutoHidePartner(activatingButton.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- activatingButton.unlink();
- if (popup == p) {
- popup = null;
- }
- }
- });
- p.add(panel);
- p.showRelativeTo(activatingButton);
- GlobalKey.dialog(p);
- popup = p;
- }
-
- void hide() {
- if (popup != null) {
- activatingButton.unlink();
- popup.hide();
- popup = null;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java
deleted file mode 100644
index 92070f8d63..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/ProjectGlue.java
+++ /dev/null
@@ -1,65 +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.client.api;
-
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ProjectGlue {
- public static void onAction(
- Project.NameKey project, BranchInfo branch, ActionInfo action, ActionButton button) {
- RestApi api = ProjectApi.project(project).view("branches").id(branch.ref()).view(action.id());
- JavaScriptObject f = branchAction(action.id());
- if (f != null) {
- ActionContext c = ActionContext.create(api);
- c.set(action);
- c.set(project);
- c.set(branch);
- c.button(button);
- ApiGlue.invoke(f, c);
- } else {
- DefaultActions.invoke(project, action, api);
- }
- }
-
- public static void onAction(Project.NameKey project, ActionInfo action, ActionButton button) {
- RestApi api = ProjectApi.project(project).view(action.id());
- JavaScriptObject f = projectAction(action.id());
- if (f != null) {
- ActionContext c = ActionContext.create(api);
- c.set(action);
- c.set(project);
- c.button(button);
- ApiGlue.invoke(f, c);
- } else {
- DefaultActions.invoke(project, action, api);
- }
- }
-
- private static native JavaScriptObject projectAction(String id) /*-{
- return $wnd.Gerrit.project_actions[id];
- }-*/;
-
- private static native JavaScriptObject branchAction(String id) /*-{
- return $wnd.Gerrit.branch_actions[id];
- }-*/;
-
- private ProjectGlue() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java
deleted file mode 100644
index d1029b2971..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/api/RevisionGlue.java
+++ /dev/null
@@ -1,50 +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.client.api;
-
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class RevisionGlue {
- public static void onAction(
- ChangeInfo change, RevisionInfo revision, ActionInfo action, ActionButton button) {
- RestApi api =
- ChangeApi.revision(change.project(), change.legacyId().get(), revision.name())
- .view(action.id());
-
- JavaScriptObject f = get(action.id());
- if (f != null) {
- ActionContext c = ActionContext.create(api);
- c.set(action);
- c.set(change);
- c.set(revision);
- c.button(button);
- ApiGlue.invoke(f, c);
- } else {
- DefaultActions.invoke(change, action, api);
- }
- }
-
- private static native JavaScriptObject get(String id) /*-{
- return $wnd.Gerrit.revision_actions[id];
- }-*/;
-
- private RevisionGlue() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.java
deleted file mode 100644
index a0eaef7fed..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.java
+++ /dev/null
@@ -1,23 +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.client.auth.openid;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface OpenIdConstants extends Constants {
- String nameLaunchpad();
-
- String nameYahoo();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.properties
deleted file mode 100644
index d6e8de6635..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdConstants.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-nameLaunchpad = Launchpad ID
-nameYahoo = Yahoo! ID
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java
deleted file mode 100644
index d5b76845a2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java
+++ /dev/null
@@ -1,25 +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.client.auth.openid;
-
-import com.google.gwt.core.client.GWT;
-
-public class OpenIdUtil {
- public static final OpenIdConstants C;
-
- static {
- C = GWT.create(OpenIdConstants.class);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/blame/BlameInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/blame/BlameInfo.java
deleted file mode 100644
index 77fddebe7c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/blame/BlameInfo.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2015 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.client.blame;
-
-import com.google.gerrit.client.RangeInfo;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-
-public class BlameInfo extends JavaScriptObject {
- public final native String author() /*-{ return this.author; }-*/;
-
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String commitMsg() /*-{ return this.commit_msg; }-*/;
-
- public final native int time() /*-{ return this.time; }-*/;
-
- public final native JsArray<RangeInfo> ranges() /*-{ return this.ranges; }-*/;
-
- protected BlameInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java
deleted file mode 100644
index fd58959c5a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AbandonAction.java
+++ /dev/null
@@ -1,50 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.ui.Button;
-
-class AbandonAction extends ActionMessageBox {
- private final Project.NameKey project;
- private final Change.Id id;
-
- AbandonAction(Button b, Project.NameKey project, Change.Id id) {
- super(b);
- this.project = project;
- this.id = id;
- }
-
- @Override
- void send(String message) {
- ChangeApi.abandon(
- project.get(),
- id.get(),
- message,
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- Gerrit.display(PageLinks.toChange(project, id));
- hide();
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.java
deleted file mode 100644
index 5b3ee292d6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.java
+++ /dev/null
@@ -1,105 +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.client.change;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-
-abstract class ActionMessageBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, ActionMessageBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String popup();
- }
-
- private final Button activatingButton;
- private PopupPanel popup;
-
- @UiField Style style;
- @UiField NpTextArea message;
- @UiField Button send;
-
- ActionMessageBox(Button button) {
- this.activatingButton = button;
- initWidget(uiBinder.createAndBindUi(this));
- send.setText(button.getText());
- }
-
- abstract void send(String message);
-
- void show() {
- if (popup != null) {
- popup.hide();
- popup = null;
- return;
- }
-
- final PopupPanel p = new PopupPanel(true);
- p.setStyleName(style.popup());
- p.addAutoHidePartner(activatingButton.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (popup == p) {
- popup = null;
- }
- }
- });
- p.add(this);
- p.showRelativeTo(activatingButton);
- GlobalKey.dialog(p);
- message.setFocus(true);
- popup = p;
- }
-
- void hide() {
- if (popup != null) {
- popup.hide();
- popup = null;
- }
- }
-
- @UiHandler("message")
- void onMessageKey(KeyPressEvent event) {
- if ((event.getCharCode() == '\n' || event.getCharCode() == KeyCodes.KEY_ENTER)
- && event.isControlKeyDown()) {
- event.preventDefault();
- event.stopPropagation();
- onSend(null);
- }
- }
-
- @UiHandler("send")
- void onSend(@SuppressWarnings("unused") ClickEvent e) {
- send(message.getValue().trim());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml
deleted file mode 100644
index a9e1bb3214..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ActionMessageBox.ui.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.change.ActionMessageBox.Style'>
- @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-
- .popup { background-color: trimColor; }
- .section {
- padding: 5px 5px;
- border-bottom: 1px solid #b8b8b8;
- }
- </ui:style>
- <g:HTMLPanel>
- <div class='{style.section}'>
- <c:NpTextArea
- visibleLines='3'
- characterWidth='40'
- ui:field='message'/>
- </div>
- <div class='{style.section}'>
- <g:Button ui:field='send'
- title='(Shortcut: Ctrl-Enter)'
- styleName='{res.style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Send</ui:msg></div>
- </g:Button>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
deleted file mode 100644
index e4f5e5761c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
+++ /dev/null
@@ -1,260 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.actions.ActionButton;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.TreeSet;
-
-class Actions extends Composite {
- private static final String[] CORE = {
- "abandon",
- "assignee",
- "cherrypick",
- "description",
- "followup",
- "hashtags",
- "move",
- "publish",
- "rebase",
- "restore",
- "revert",
- "submit",
- "topic",
- "private",
- "/",
- };
-
- interface Binder extends UiBinder<FlowPanel, Actions> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField Button cherrypick;
- @UiField Button move;
- @UiField Button rebase;
- @UiField Button revert;
- @UiField Button submit;
-
- @UiField Button abandon;
- private AbandonAction abandonAction;
-
- @UiField Button deleteChange;
-
- @UiField Button markPrivate;
- @UiField Button unmarkPrivate;
-
- @UiField Button restore;
- private RestoreAction restoreAction;
-
- @UiField Button followUp;
- private FollowUpAction followUpAction;
-
- private Change.Id changeId;
- private ChangeInfo changeInfo;
- private String revision;
- private Project.NameKey project;
- private String topic;
- private String subject;
- private String message;
- private String branch;
- private String key;
-
- private boolean rebaseParentNotCurrent = true;
-
- Actions() {
- initWidget(uiBinder.createAndBindUi(this));
- getElement().setId("change_actions");
- }
-
- void display(ChangeInfo info, String revision) {
- this.revision = revision;
-
- boolean hasUser = Gerrit.isSignedIn();
- RevisionInfo revInfo = info.revision(revision);
- CommitInfo commit = revInfo.commit();
- changeId = info.legacyId();
- project = info.projectNameKey();
- topic = info.topic();
- subject = commit.subject();
- message = commit.message();
- branch = info.branch();
- key = info.changeId();
- changeInfo = info;
-
- initChangeActions(info, hasUser);
-
- NativeMap<ActionInfo> actionMap =
- revInfo.hasActions() ? revInfo.actions() : NativeMap.<ActionInfo>create();
- actionMap.copyKeysIntoChildren("id");
- reloadRevisionActions(actionMap);
- }
-
- private void initChangeActions(ChangeInfo info, boolean hasUser) {
- NativeMap<ActionInfo> actions =
- info.hasActions() ? info.actions() : NativeMap.<ActionInfo>create();
- actions.copyKeysIntoChildren("id");
-
- if (hasUser) {
- a2b(actions, "abandon", abandon);
- a2b(actions, "/", deleteChange);
- a2b(actions, "move", move);
- a2b(actions, "restore", restore);
- a2b(actions, "revert", revert);
- a2b(actions, "followup", followUp);
- if (info.isPrivate()) {
- a2b(actions, "private", unmarkPrivate);
- } else {
- a2b(actions, "private", markPrivate);
- }
- for (String id : filterNonCore(actions)) {
- add(new ActionButton(info, actions.get(id)));
- }
- }
- }
-
- void reloadRevisionActions(NativeMap<ActionInfo> actions) {
- if (!Gerrit.isSignedIn()) {
- return;
- }
- boolean canSubmit = actions.containsKey("submit");
- if (canSubmit) {
- ActionInfo action = actions.get("submit");
- submit.setTitle(action.title());
- submit.setEnabled(action.enabled());
- submit.setHTML(new SafeHtmlBuilder().openDiv().append(action.label()).closeDiv());
- submit.setEnabled(action.enabled());
- }
- submit.setVisible(canSubmit);
-
- a2b(actions, "cherrypick", cherrypick);
- a2b(actions, "rebase", rebase);
-
- // The rebase button on change screen is always enabled.
- // It is the "Rebase" button in the RebaseDialog that might be disabled.
- rebaseParentNotCurrent = rebase.isEnabled();
- if (rebase.isVisible()) {
- rebase.setEnabled(true);
- }
- RevisionInfo revInfo = changeInfo.revision(revision);
- for (String id : filterNonCore(actions)) {
- add(new ActionButton(changeInfo, revInfo, actions.get(id)));
- }
- }
-
- private void add(ActionButton b) {
- ((FlowPanel) getWidget()).add(b);
- }
-
- private static TreeSet<String> filterNonCore(NativeMap<ActionInfo> m) {
- TreeSet<String> ids = new TreeSet<>(m.keySet());
- for (String id : CORE) {
- ids.remove(id);
- }
- return ids;
- }
-
- @UiHandler("followUp")
- void onFollowUp(@SuppressWarnings("unused") ClickEvent e) {
- if (followUpAction == null) {
- followUpAction = new FollowUpAction(followUp, project.get(), branch, topic, key);
- }
- followUpAction.show();
- }
-
- @UiHandler("abandon")
- void onAbandon(@SuppressWarnings("unused") ClickEvent e) {
- if (abandonAction == null) {
- abandonAction = new AbandonAction(abandon, project, changeId);
- }
- abandonAction.show();
- }
-
- @UiHandler("deleteChange")
- void onDeleteChange(@SuppressWarnings("unused") ClickEvent e) {
- if (Window.confirm(Resources.C.deleteChange())) {
- ChangeActions.delete(project, changeId, deleteChange);
- }
- }
-
- @UiHandler("markPrivate")
- void onMarkPrivate(@SuppressWarnings("unused") ClickEvent e) {
- ChangeActions.markPrivate(project, changeId, markPrivate);
- }
-
- @UiHandler("unmarkPrivate")
- void onUnmarkPrivate(@SuppressWarnings("unused") ClickEvent e) {
- ChangeActions.unmarkPrivate(project, changeId, unmarkPrivate);
- }
-
- @UiHandler("restore")
- void onRestore(@SuppressWarnings("unused") ClickEvent e) {
- if (restoreAction == null) {
- restoreAction = new RestoreAction(restore, project, changeId);
- }
- restoreAction.show();
- }
-
- @UiHandler("rebase")
- void onRebase(@SuppressWarnings("unused") ClickEvent e) {
- RebaseAction.call(
- rebase, project, changeInfo.branch(), changeId, revision, rebaseParentNotCurrent);
- }
-
- @UiHandler("submit")
- void onSubmit(@SuppressWarnings("unused") ClickEvent e) {
- SubmitAction.call(changeInfo, changeInfo.revision(revision));
- }
-
- @UiHandler("cherrypick")
- void onCherryPick(@SuppressWarnings("unused") ClickEvent e) {
- CherryPickAction.call(cherrypick, changeInfo, revision, project, message);
- }
-
- @UiHandler("move")
- void onMove(@SuppressWarnings("unused") ClickEvent e) {
- MoveAction.call(move, changeInfo, project);
- }
-
- @UiHandler("revert")
- void onRevert(@SuppressWarnings("unused") ClickEvent e) {
- RevertAction.call(revert, changeId, project, revision, subject);
- }
-
- private static void a2b(NativeMap<ActionInfo> actions, String a, Button b) {
- if (actions.containsKey(a)) {
- b.setVisible(true);
- ActionInfo actionInfo = actions.get(a);
- b.setTitle(actionInfo.title());
- b.setEnabled(actionInfo.enabled());
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
deleted file mode 100644
index 8aeba90ce2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.ui.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false'>
- @def BUTTON_HEIGHT 14px;
-
- #change_actions {
- padding-top: 2px;
- padding-bottom: 20px;
- }
-
- #change_actions button {
- margin: 6px 3px 0 0;
- text-align: center;
- font-size: 8pt;
- font-weight: bold;
- border: 2px solid;
- cursor: pointer;
- color: rgba(0, 0, 0, 0.15);
- background-color: #f5f5f5;
- -webkit-border-radius: 2px;
- -webkit-box-sizing: content-box;
- }
- #change_actions button div {
- color: #444;
- min-width: 54px;
- white-space: nowrap;
- height: BUTTON_HEIGHT;
- line-height: BUTTON_HEIGHT;
- }
-
- #change_actions button.submit {
- float: right;
- background-color: #4d90fe;
- background-image: -webkit-linear-gradient(top, #4d90fe, #4d90fe);
- }
- #change_actions button.submit div {color: #fff;}
-
- #change_actions button:disabled {
- font-weight: normal;
- background-color: #999;
- background-image: -webkit-linear-gradient(top, #999, #999);
- }
- </ui:style>
-
- <g:FlowPanel>
- <g:Button ui:field='cherrypick' styleName='' visible='false'>
- <div><ui:msg>Cherry Pick</ui:msg></div>
- </g:Button>
- <g:Button ui:field='move' styleName='' visible='false'>
- <div><ui:msg>Move Change</ui:msg></div>
- </g:Button>
- <g:Button ui:field='rebase' styleName='' visible='false'>
- <div><ui:msg>Rebase</ui:msg></div>
- </g:Button>
- <g:Button ui:field='revert' styleName='' visible='false'>
- <div><ui:msg>Revert</ui:msg></div>
- </g:Button>
- <g:Button ui:field='abandon' styleName='' visible='false'>
- <div><ui:msg>Abandon</ui:msg></div>
- </g:Button>
- <g:Button ui:field='deleteChange' styleName='' visible='false'>
- <div><ui:msg>Delete Change</ui:msg></div>
- </g:Button>
- <g:Button ui:field='restore' styleName='' visible='false'>
- <div><ui:msg>Restore</ui:msg></div>
- </g:Button>
- <g:Button ui:field='followUp' styleName='' visible='false'>
- <div><ui:msg>Follow-Up</ui:msg></div>
- </g:Button>
- <g:Button ui:field='markPrivate' styleName='' visible='false'>
- <div><ui:msg>Mark Private</ui:msg></div>
- </g:Button>
- <g:Button ui:field='unmarkPrivate' styleName='' visible='false'>
- <div><ui:msg>Unmark Private</ui:msg></div>
- </g:Button>
-
- <g:Button ui:field='submit' styleName='{style.submit}' visible='false'/>
- </g:FlowPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java
deleted file mode 100644
index 2080a0e7d0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileAction.java
+++ /dev/null
@@ -1,82 +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.client.change;
-
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-class AddFileAction {
- private final Project.NameKey project;
- private final Change.Id changeId;
- private final RevisionInfo revision;
- private final ChangeScreen.Style style;
- private final Widget addButton;
- private final FileTable files;
-
- private AddFileBox addBox;
- private PopupPanel popup;
-
- AddFileAction(
- Project.NameKey project,
- Change.Id changeId,
- RevisionInfo revision,
- ChangeScreen.Style style,
- Widget addButton,
- FileTable files) {
- this.project = project;
- this.changeId = changeId;
- this.revision = revision;
- this.style = style;
- this.addButton = addButton;
- this.files = files;
- }
-
- public void onEdit() {
- if (popup != null) {
- popup.hide();
- return;
- }
-
- files.unregisterKeys();
- if (addBox == null) {
- addBox = new AddFileBox(project, changeId, revision, files);
- }
- addBox.clearPath();
-
- final PopupPanel p = new PopupPanel(true);
- p.setStyleName(style.replyBox());
- p.addAutoHidePartner(addButton.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (popup == p) {
- popup = null;
- }
- }
- });
- p.add(addBox);
- p.showRelativeTo(addButton);
- GlobalKey.dialog(p);
- addBox.setFocus(true);
- popup = p;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java
deleted file mode 100644
index cd862d2a30..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.java
+++ /dev/null
@@ -1,114 +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.client.change;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-class AddFileBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, AddFileBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private final Project.NameKey project;
- private final Change.Id changeId;
- private final RevisionInfo revision;
- private final FileTable fileTable;
-
- @UiField Button open;
- @UiField Button cancel;
-
- @UiField(provided = true)
- RemoteSuggestBox path;
-
- AddFileBox(Project.NameKey project, Change.Id changeId, RevisionInfo revision, FileTable files) {
- this.project = project;
- this.changeId = changeId;
- this.revision = revision;
- this.fileTable = files;
-
- path = new RemoteSuggestBox(new PathSuggestOracle(project, changeId, revision));
- path.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- open(event.getSelectedItem());
- }
- });
- path.addCloseHandler(
- new CloseHandler<RemoteSuggestBox>() {
- @Override
- public void onClose(CloseEvent<RemoteSuggestBox> event) {
- hide();
- fileTable.registerKeys();
- }
- });
-
- initWidget(uiBinder.createAndBindUi(this));
- }
-
- void setFocus(boolean focus) {
- path.setFocus(focus);
- }
-
- void clearPath() {
- path.setText("");
- }
-
- @UiHandler("open")
- void onOpen(@SuppressWarnings("unused") ClickEvent e) {
- open(path.getText());
- }
-
- private void open(String path) {
- hide();
- Gerrit.display(
- Dispatcher.toEditScreen(project, new PatchSet.Id(changeId, revision._number()), path));
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- hide();
- fileTable.registerKeys();
- }
-
- private void hide() {
- for (Widget w = getParent(); w != null; w = w.getParent()) {
- if (w instanceof PopupPanel) {
- ((PopupPanel) w).hide();
- break;
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml
deleted file mode 100644
index c3539bc67b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AddFileBox.ui.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:u='urn:import:com.google.gerrit.client.ui'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- .cancel { float: right; }
- </ui:style>
- <g:HTMLPanel>
- <div class='{res.style.section}'>
- <ui:msg>Path: <u:RemoteSuggestBox ui:field='path' visibleLength='86'/></ui:msg>
- </div>
- <div class='{res.style.section}'>
- <g:Button ui:field='open'
- title='Open file in editor'
- styleName='{res.style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Open</ui:msg></div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.java
deleted file mode 100644
index a376782b8c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.java
+++ /dev/null
@@ -1,239 +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.client.change;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotSignedInDialog;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.EventListener;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.UIObject;
-
-/** Edit assignee using auto-completion. */
-public class Assignee extends Composite {
- interface Binder extends UiBinder<HTMLPanel, Assignee> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField Element show;
- @UiField InlineHyperlink assigneeLink;
- @UiField Image editAssigneeIcon;
- @UiField Element form;
- @UiField Element error;
-
- @UiField(provided = true)
- RemoteSuggestBox suggestBox;
-
- private AssigneeSuggestOracle assigneeSuggestOracle;
- private Change.Id changeId;
- private Project.NameKey project;
- private boolean canEdit;
- private AccountInfo currentAssignee;
-
- Assignee() {
- assigneeSuggestOracle = new AssigneeSuggestOracle();
- suggestBox = new RemoteSuggestBox(assigneeSuggestOracle);
- suggestBox.setVisibleLength(55);
- suggestBox.setHintText(Util.C.approvalTableEditAssigneeHint());
- suggestBox.addCloseHandler(
- new CloseHandler<RemoteSuggestBox>() {
- @Override
- public void onClose(CloseEvent<RemoteSuggestBox> event) {
- Assignee.this.onCancel(null);
- }
- });
- suggestBox.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- editAssignee(event.getSelectedItem());
- }
- });
-
- initWidget(uiBinder.createAndBindUi(this));
- editAssigneeIcon.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- onOpenForm();
- }
- },
- ClickEvent.getType());
- }
-
- void set(ChangeInfo info) {
- this.changeId = info.legacyId();
- this.project = info.projectNameKey();
- this.canEdit = info.hasActions() && info.actions().containsKey("assignee");
- assigneeSuggestOracle.setChange(info);
- setAssignee(info.assignee());
- editAssigneeIcon.setVisible(canEdit);
- if (!canEdit) {
- show.setTitle(null);
- }
- }
-
- void onOpenForm() {
- UIObject.setVisible(form, true);
- UIObject.setVisible(show, false);
- UIObject.setVisible(error, false);
- editAssigneeIcon.setVisible(false);
- suggestBox.setFocus(true);
- if (currentAssignee != null) {
- suggestBox.setText(FormatUtil.nameEmail(currentAssignee));
- suggestBox.selectAll();
- } else {
- suggestBox.setText("");
- }
- }
-
- void onCloseForm() {
- UIObject.setVisible(form, false);
- UIObject.setVisible(show, true);
- UIObject.setVisible(error, false);
- editAssigneeIcon.setVisible(true);
- suggestBox.setFocus(false);
- }
-
- @UiHandler("assign")
- void onEditAssignee(@SuppressWarnings("unused") ClickEvent e) {
- if (canEdit) {
- editAssignee(suggestBox.getText());
- }
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- onCloseForm();
- }
-
- private void editAssignee(String assignee) {
- if (assignee.trim().isEmpty()) {
- ChangeApi.deleteAssignee(
- project.get(),
- changeId.get(),
- new GerritCallback<AccountInfo>() {
- @Override
- public void onSuccess(AccountInfo result) {
- onCloseForm();
- setAssignee(null);
- }
-
- @Override
- public void onFailure(Throwable err) {
- if (isSigninFailure(err)) {
- new NotSignedInDialog().center();
- } else {
- UIObject.setVisible(error, true);
- error.setInnerText(
- err instanceof StatusCodeException
- ? ((StatusCodeException) err).getEncodedResponse()
- : err.getMessage());
- }
- }
- });
- } else {
- ChangeApi.setAssignee(
- project.get(),
- changeId.get(),
- assignee,
- new GerritCallback<AccountInfo>() {
- @Override
- public void onSuccess(AccountInfo result) {
- onCloseForm();
- setAssignee(result);
- Reviewers reviewers = getReviewers();
- if (reviewers != null) {
- reviewers.updateReviewerList();
- }
- }
-
- @Override
- public void onFailure(Throwable err) {
- if (isSigninFailure(err)) {
- new NotSignedInDialog().center();
- } else {
- UIObject.setVisible(error, true);
- error.setInnerText(
- err instanceof StatusCodeException
- ? ((StatusCodeException) err).getEncodedResponse()
- : err.getMessage());
- }
- }
- });
- }
- }
-
- private void setAssignee(AccountInfo assignee) {
- currentAssignee = assignee;
- assigneeLink.setText(assignee != null ? getName(assignee) : null);
- assigneeLink.setTargetHistoryToken(
- assignee != null
- ? PageLinks.toAssigneeQuery(
- assignee.name() != null
- ? assignee.name()
- : assignee.email() != null
- ? assignee.email()
- : String.valueOf(assignee._accountId()))
- : "");
- }
-
- private Reviewers getReviewers() {
- Element e = DOM.getParent(getElement());
- for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
- EventListener l = DOM.getEventListener(e);
- if (l instanceof ChangeScreen) {
- ChangeScreen screen = (ChangeScreen) l;
- return screen.reviewers;
- }
- }
- return null;
- }
-
- private String getName(AccountInfo info) {
- if (info.name() != null) {
- return info.name();
- }
- if (info.email() != null) {
- return info.email();
- }
- return Gerrit.info().user().anonymousCowardName();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.ui.xml
deleted file mode 100644
index d5a72392e9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Assignee.ui.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:u='urn:import:com.google.gerrit.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- .suggestBox {
- margin-bottom: 2px;
- }
-
- .error {
- color: #D33D3D;
- font-weight: bold;
- }
-
- .editAssignee,
- .cancel {
- cursor: pointer;
- float: right;
- }
- </ui:style>
- <g:HTMLPanel>
- <div ui:field='show'>
- <u:InlineHyperlink ui:field='assigneeLink'
- title='Search for changes assigned to this user'/>
- <g:Image ui:field='editAssigneeIcon'
- resource='{ico.editUser}'
- styleName='{style.editAssignee}'
- title='Assign User to Change'/>
- </div>
- <div ui:field='form' style='display: none' aria-hidden='true'>
- <u:RemoteSuggestBox ui:field='suggestBox' styleName='{style.suggestBox}'/>
- <div ui:field='error'
- class='{style.error}'
- style='display: none' aria-hidden='true'/>
- <div>
- <g:Button ui:field='assign' styleName='{res.style.button}'>
- <div>Assign</div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </div>
- </g:HTMLPanel>
- </ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AssigneeSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AssigneeSuggestOracle.java
deleted file mode 100644
index c8bbfc3fad..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/AssigneeSuggestOracle.java
+++ /dev/null
@@ -1,69 +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.client.change;
-
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.AccountSuggestOracle.AccountSuggestion;
-import com.google.gerrit.client.ui.SuggestAfterTypingNCharsOracle;
-import com.google.gwt.core.client.JsArray;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** REST API based suggestion Oracle for assignee */
-public class AssigneeSuggestOracle extends SuggestAfterTypingNCharsOracle {
-
- private ChangeInfo change;
-
- public void setChange(ChangeInfo change) {
- this.change = change;
- }
-
- @Override
- protected void _onRequestSuggestions(Request req, Callback cb) {
- AccountApi.suggest(
- getQuery(req),
- req.getLimit(),
- new GerritCallback<JsArray<AccountInfo>>() {
- @Override
- public void onSuccess(JsArray<AccountInfo> result) {
- List<AccountSuggestion> r = new ArrayList<>(result.length());
- for (AccountInfo reviewer : Natives.asList(result)) {
- r.add(new AccountSuggestion(reviewer, req.getQuery()));
- }
- cb.onSuggestionsReady(req, new Response(r));
- }
-
- @Override
- public void onFailure(Throwable err) {
- List<Suggestion> r = Collections.emptyList();
- cb.onSuggestionsReady(req, new Response(r));
- }
- });
- }
-
- private String getQuery(Request req) {
- StringBuilder query = new StringBuilder();
- query.append(req.getQuery());
- if (change != null) {
- query.append(" cansee:").append(change._number());
- }
- return query.toString();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeActions.java
deleted file mode 100644
index 0bc74e4f96..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeActions.java
+++ /dev/null
@@ -1,91 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-
-public class ChangeActions {
-
- static void delete(Project.NameKey project, Change.Id id, Button... draftButtons) {
- ChangeApi.deleteChange(project.get(), id.get(), mine(draftButtons));
- }
-
- static void markPrivate(Project.NameKey project, Change.Id id, Button... draftButtons) {
- ChangeApi.markPrivate(project.get(), id.get(), cs(project, id, draftButtons));
- }
-
- static void unmarkPrivate(Project.NameKey project, Change.Id id, Button... draftButtons) {
- ChangeApi.unmarkPrivate(project.get(), id.get(), cs(project, id, draftButtons));
- }
-
- public static GerritCallback<JavaScriptObject> cs(
- Project.NameKey project, final Change.Id id, Button... draftButtons) {
- setEnabled(false, draftButtons);
- return new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- Gerrit.display(PageLinks.toChange(project, id));
- }
-
- @Override
- public void onFailure(Throwable err) {
- setEnabled(true, draftButtons);
- if (SubmitFailureDialog.isConflict(err)) {
- new SubmitFailureDialog(err.getMessage()).center();
- Gerrit.display(PageLinks.toChange(project, id));
- } else {
- super.onFailure(err);
- }
- }
- };
- }
-
- private static AsyncCallback<JavaScriptObject> mine(Button... draftButtons) {
- setEnabled(false, draftButtons);
- return new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- Gerrit.display(PageLinks.MINE);
- }
-
- @Override
- public void onFailure(Throwable err) {
- setEnabled(true, draftButtons);
- if (SubmitFailureDialog.isConflict(err)) {
- new SubmitFailureDialog(err.getMessage()).center();
- Gerrit.display(PageLinks.MINE);
- } else {
- super.onFailure(err);
- }
- }
- };
- }
-
- private static void setEnabled(boolean enabled, Button... draftButtons) {
- if (draftButtons != null) {
- for (Button b : draftButtons) {
- b.setEnabled(enabled);
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
deleted file mode 100644
index ed67846d9a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.java
+++ /dev/null
@@ -1,79 +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.client.change;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface ChangeConstants extends Constants {
- String previousChange();
-
- String nextChange();
-
- String openChange();
-
- String reviewedFileTitle();
-
- String editFileInline();
-
- String removeFileInline();
-
- String restoreFileInline();
-
- String openLastFile();
-
- String openCommitMessage();
-
- String patchSet();
-
- String commit();
-
- String date();
-
- String author();
-
- String notAvailable();
-
- String relatedChanges();
-
- String relatedChangesTooltip();
-
- String conflictingChanges();
-
- String conflictingChangesTooltip();
-
- String cherryPicks();
-
- String cherryPicksTooltip();
-
- String sameTopic();
-
- String sameTopicTooltip();
-
- String submittedTogether();
-
- String submittedTogetherTooltip();
-
- String noChanges();
-
- String indirectAncestor();
-
- String merged();
-
- String abandoned();
-
- String deleteChangeEdit();
-
- String deleteChange();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
deleted file mode 100644
index 90001494ed..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeConstants.properties
+++ /dev/null
@@ -1,36 +0,0 @@
-previousChange = Previous related change
-nextChange = Next related change
-openChange = Open related change
-reviewedFileTitle = Mark file as reviewed (Shortcut: r)
-editFileInline = Edit file inline
-removeFileInline = Remove file inline
-restoreFileInline = Restore file inline
-
-openLastFile = Open last file
-openCommitMessage = Open commit message
-
-patchSet = Patch Set
-commit = Commit
-date = Date
-author = Author / Committer
-
-notAvailable = N/A
-relatedChanges = Related Changes
-relatedChangesTooltip = Same branch changes connected by Git history
-conflictingChanges = Conflicts With
-conflictingChangesTooltip = Open changes that conflict with this change
-cherryPicks = Cherry-Picks
-cherryPicksTooltip = Changes with the same Change-Id
-sameTopic = Same Topic
-sameTopicTooltip = Changes with the same topic
-submittedTogether = Submitted Together
-submittedTogetherTooltip = Changes submitted together with this change
-noChanges = No Changes
-indirectAncestor = Indirect ancestor
-merged = Merged
-abandoned = Abandoned
-
-deleteChangeEdit = Delete Change Edit?\n\
- \n\
- All changes made in the edit revision will be lost.
-deleteChange = Delete Change?
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java
deleted file mode 100644
index 4eead56e8a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.java
+++ /dev/null
@@ -1,47 +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.client.change;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface ChangeMessages extends Messages {
- String patchSets(String currentlyViewedPatchSet, int currentPatchSet);
-
- String changeWithNoRevisions(int changeId);
-
- String relatedChanges(int count);
-
- String relatedChanges(String count);
-
- String conflictingChanges(int count);
-
- String conflictingChanges(String count);
-
- String cherryPicks(int count);
-
- String cherryPicks(String count);
-
- String sameTopic(int count);
-
- String sameTopic(String count);
-
- String submittedTogether(int count);
-
- String submittedTogether(String count);
-
- String editPatchSet(int patchSet);
-
- String failedToLoadFileList(String error);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.properties
deleted file mode 100644
index 743945d95e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeMessages.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-patchSets = Patch Sets ({0}/{1})
-changeWithNoRevisions = Cannot display change {0} because it has no revisions.
-relatedChanges = Related Changes ({0})
-conflictingChanges = Conflicts With ({0})
-cherryPicks = Cherry-Picks ({0})
-sameTopic = Same Topic ({0})
-submittedTogether = Submitted Together ({0})
-editPatchSet = edit:{0}
-failedToLoadFileList = Failed to load file list: {0}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
deleted file mode 100644
index ec7bc4a100..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
+++ /dev/null
@@ -1,1653 +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.client.change;
-
-import com.google.gerrit.client.AvatarImage;
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.NotFoundScreen;
-import com.google.gerrit.client.api.ChangeGlue;
-import com.google.gerrit.client.api.ExtensionPanel;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeList;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.changes.QueryScreen;
-import com.google.gerrit.client.changes.RevisionInfoCache;
-import com.google.gerrit.client.changes.StarredChanges;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.diff.DiffApi;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.AccountInfo.AvatarInfo;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.info.FileInfo;
-import com.google.gerrit.client.info.GpgKeyInfo;
-import com.google.gerrit.client.info.PushCertificateInfo;
-import com.google.gerrit.client.projects.ConfigInfoCache;
-import com.google.gerrit.client.projects.ConfigInfoCache.Entry;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.BranchLink;
-import com.google.gerrit.client.ui.ChangeLink;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.client.ui.UserActivityMonitor;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.extensions.client.SubmitType;
-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.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.AnchorElement;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.dom.client.SelectElement;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.EventListener;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.ToggleButton;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtorm.client.KeyUtil;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import net.codemirror.lib.CodeMirror;
-
-public class ChangeScreen extends Screen {
- private static final Logger logger = Logger.getLogger(ChangeScreen.class.getName());
-
- interface Binder extends UiBinder<HTMLPanel, ChangeScreen> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String avatar();
-
- String hashtagName();
-
- String hashtagIcon();
-
- String highlight();
-
- String labelName();
-
- String label_may();
-
- String label_need();
-
- String label_ok();
-
- String label_reject();
-
- String label_user();
-
- String pushCertStatus();
-
- String replyBox();
-
- String selected();
-
- String notCurrentPatchSet();
- }
-
- static ChangeScreen get(NativeEvent in) {
- Element e = in.getEventTarget().cast();
- for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
- EventListener l = DOM.getEventListener(e);
- if (l instanceof ChangeScreen) {
- return (ChangeScreen) l;
- }
- }
- return null;
- }
-
- private final Change.Id changeId;
- @Nullable private Project.NameKey project;
- private DiffObject base;
- private String revision;
- private ChangeInfo changeInfo;
- private boolean hasDraftComments;
- private CommentLinkProcessor commentLinkProcessor;
- private EditInfo edit;
- private LocalComments lc;
-
- private List<HandlerRegistration> handlers = new ArrayList<>(4);
- private UpdateCheckTimer updateCheck;
- private Timestamp lastDisplayedUpdate;
- private UpdateAvailableBar updateAvailable;
- private boolean openReplyBox;
- private boolean loaded;
- private FileTable.Mode fileTableMode;
-
- @UiField HTMLPanel headerLine;
- @UiField SimplePanel headerExtension;
- @UiField SimplePanel headerExtensionMiddle;
- @UiField SimplePanel headerExtensionRight;
- @UiField Style style;
- @UiField ToggleButton star;
- @UiField Anchor permalink;
-
- @UiField Assignee assignee;
- @UiField Element assigneeRow;
- @UiField Element ccText;
- @UiField Reviewers reviewers;
- @UiField Hashtags hashtags;
- @UiField Element hashtagTableRow;
-
- @UiField FlowPanel ownerPanel;
- @UiField InlineHyperlink ownerLink;
-
- @UiField Element uploaderRow;
- @UiField FlowPanel uploaderPanel;
- @UiField InlineLabel uploaderName;
-
- @UiField Element statusText;
- @UiField Element privateText;
- @UiField Element wipText;
- @UiField Image projectSettings;
- @UiField AnchorElement projectSettingsLink;
- @UiField InlineHyperlink projectDashboard;
- @UiField InlineHyperlink branchLink;
- @UiField Element strategy;
- @UiField Element submitActionText;
- @UiField Element notMergeable;
- @UiField Topic topic;
- @UiField Element actionText;
- @UiField Element actionDate;
- @UiField SimplePanel changeExtension;
- @UiField SimplePanel relatedExtension;
- @UiField SimplePanel commitExtension;
-
- @UiField Actions actions;
- @UiField Labels labels;
- @UiField CommitBox commit;
- @UiField RelatedChanges related;
- @UiField FileTable files;
- @UiField ListBox diffBase;
- @UiField History history;
- @UiField SimplePanel historyExtensionRight;
-
- @UiField Button includedIn;
- @UiField Button patchSets;
- @UiField Element patchSetsText;
- @UiField Button download;
- @UiField Button reply;
- @UiField Button publishEdit;
- @UiField Button rebaseEdit;
- @UiField Button deleteEdit;
- @UiField Button openAll;
- @UiField Button editMode;
- @UiField Button reviewMode;
- @UiField Button addFile;
- @UiField Button deleteFile;
- @UiField Button renameFile;
- @UiField Button expandAll;
- @UiField Button collapseAll;
- @UiField Button hideTaggedComments;
- @UiField Button showTaggedComments;
- @UiField QuickApprove quickApprove;
-
- private ReplyAction replyAction;
- private IncludedInAction includedInAction;
- private PatchSetsAction patchSetsAction;
- private DownloadAction downloadAction;
- private AddFileAction addFileAction;
- private DeleteFileAction deleteFileAction;
- private RenameFileAction renameFileAction;
-
- public ChangeScreen(
- @Nullable Project.NameKey project,
- Change.Id changeId,
- DiffObject base,
- String revision,
- boolean openReplyBox,
- FileTable.Mode mode) {
- this.project = project;
- this.changeId = changeId;
- this.base = base;
- this.revision = normalize(revision);
- this.openReplyBox = openReplyBox;
- this.fileTableMode = mode;
- this.lc = new LocalComments(project, changeId);
- add(uiBinder.createAndBindUi(this));
- }
-
- public Project.NameKey getProject() {
- return project;
- }
-
- PatchSet.Id getPatchSetId() {
- return new PatchSet.Id(changeInfo.legacyId(), changeInfo.revisions().get(revision)._number());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- loadChangeScreen();
- }
-
- private void loadChangeScreen() {
- if (project == null) {
- // Load the project if it is not already present. This is the case when the user used a URL
- // that doesn't include the project. Setting it here will rewrite the URL token to include the
- // project (visible to the user) and all future API calls made from the change screen will use
- // project/+/changeId to identify the change.
- String query = "change:" + changeId.get();
- ChangeList.query(
- query,
- Collections.emptySet(),
- new AsyncCallback<ChangeList>() {
- @Override
- public void onSuccess(ChangeList result) {
- if (result.length() == 0) {
- Gerrit.display(getToken(), new NotFoundScreen());
- } else if (result.length() > 1) {
- Gerrit.display(PageLinks.toChangeQuery(query), QueryScreen.forQuery(query));
- } else {
- // Initialize current screen with newly obtained project
- project = result.get(0).projectNameKey();
- loadChangeScreen();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- GerritCallback.showFailure(caught);
- }
- });
-
- return;
- }
- CallbackGroup group = new CallbackGroup();
- if (Gerrit.isSignedIn()) {
- ChangeList.query(
- "change:" + changeId.get() + " has:draft",
- Collections.emptySet(),
- group.add(
- new AsyncCallback<ChangeList>() {
- @Override
- public void onSuccess(ChangeList result) {
- hasDraftComments = result.length() > 0;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- ChangeApi.editWithFiles(
- Project.NameKey.asStringOrNull(project),
- changeId.get(),
- group.add(
- new AsyncCallback<EditInfo>() {
- @Override
- public void onSuccess(EditInfo result) {
- edit = result;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- }
- loadChangeInfo(
- true,
- group.addFinal(
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo info) {
- info.init();
- initCurrentRevision(info);
- final RevisionInfo rev = info.revision(revision);
- CallbackGroup group = new CallbackGroup();
- loadCommit(rev, group);
-
- group.addListener(
- new GerritCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- if (base.isBase() && rev.isMerge()) {
- base =
- DiffObject.parse(
- info.legacyId(),
- Gerrit.getUserPreferences().defaultBaseForMerges().getBase());
- }
- loadConfigInfo(info, base);
- JsArray<MessageInfo> mAr = info.messages();
- for (int i = 0; i < mAr.length(); i++) {
- if (mAr.get(i).tag() != null) {
- hideTaggedComments.setVisible(true);
- break;
- }
- }
- }
- });
- group.done();
- }
- }));
- }
-
- private RevisionInfo initCurrentRevision(ChangeInfo info) {
- info.revisions().copyKeysIntoChildren("name");
- if (edit != null) {
- edit.setName(edit.commit().commit());
- info.setEdit(edit);
- if (edit.hasFiles()) {
- edit.files().copyKeysIntoChildren("path");
- }
- info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
- JsArray<RevisionInfo> list = info.revisions().values();
-
- // Edit is converted to a regular revision (with number = 0) and
- // added to the list of revisions. Additionally under certain
- // circumstances change edit is assigned to be the current revision
- // and is selected to be shown on the change screen.
- // We have two different strategies to assign edit to the current ps:
- // 1. revision == null: no revision is selected, so use the edit only
- // if it is based on the latest patch set
- // 2. edit was selected explicitly from ps drop down:
- // use the edit regardless of which patch set it is based on
- if (revision == null) {
- RevisionInfo.sortRevisionInfoByNumber(list);
- RevisionInfo rev = list.get(list.length() - 1);
- if (rev.isEdit()) {
- info.setCurrentRevision(rev.name());
- }
- } else if (revision.equals("edit") || revision.equals("0")) {
- for (int i = 0; i < list.length(); i++) {
- RevisionInfo r = list.get(i);
- if (r.isEdit()) {
- info.setCurrentRevision(r.name());
- break;
- }
- }
- }
- }
- return resolveRevisionToDisplay(info);
- }
-
- private void addExtensionPoints(ChangeInfo change, RevisionInfo rev, Entry result) {
- addExtensionPoint(GerritUiExtensionPoint.CHANGE_SCREEN_HEADER, headerExtension, change, rev);
- addExtensionPoint(
- GerritUiExtensionPoint.CHANGE_SCREEN_HEADER_RIGHT_OF_BUTTONS,
- headerExtensionMiddle,
- change,
- rev);
- addExtensionPoint(
- GerritUiExtensionPoint.CHANGE_SCREEN_HEADER_RIGHT_OF_POP_DOWNS,
- headerExtensionRight,
- change,
- rev);
- addExtensionPoint(
- GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
- changeExtension,
- change,
- rev,
- result.getExtensionPanelNames(
- GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK.toString()));
- addExtensionPoint(
- GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_RELATED_INFO_BLOCK,
- relatedExtension,
- change,
- rev);
- addExtensionPoint(
- GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK, commitExtension, change, rev);
- addExtensionPoint(
- GerritUiExtensionPoint.CHANGE_SCREEN_HISTORY_RIGHT_OF_BUTTONS,
- historyExtensionRight,
- change,
- rev);
- }
-
- private void addExtensionPoint(
- GerritUiExtensionPoint extensionPoint,
- Panel p,
- ChangeInfo change,
- RevisionInfo rev,
- List<String> panelNames) {
- ExtensionPanel extensionPanel = new ExtensionPanel(extensionPoint, panelNames);
- extensionPanel.putObject(GerritUiExtensionPoint.Key.CHANGE_INFO, change);
- extensionPanel.putObject(GerritUiExtensionPoint.Key.REVISION_INFO, rev);
- p.add(extensionPanel);
- }
-
- private void addExtensionPoint(
- GerritUiExtensionPoint extensionPoint, Panel p, ChangeInfo change, RevisionInfo rev) {
- addExtensionPoint(extensionPoint, p, change, rev, Collections.emptyList());
- }
-
- private boolean enableSignedPush() {
- return Gerrit.info().receive().enableSignedPush();
- }
-
- void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
- RestApi call = ChangeApi.detail(Project.NameKey.asStringOrNull(project), changeId.get());
- EnumSet<ListChangesOption> opts =
- EnumSet.of(ListChangesOption.ALL_REVISIONS, ListChangesOption.CHANGE_ACTIONS);
- if (enableSignedPush()) {
- opts.add(ListChangesOption.PUSH_CERTIFICATES);
- }
- ChangeList.addOptions(call, opts);
- if (!fg) {
- call.background();
- }
- call.get(cb);
- }
-
- void loadRevisionInfo() {
- RestApi call = ChangeApi.actions(getProject().get(), changeId.get(), revision);
- call.background();
- call.get(
- new GerritCallback<NativeMap<ActionInfo>>() {
- @Override
- public void onSuccess(NativeMap<ActionInfo> actionMap) {
- actionMap.copyKeysIntoChildren("id");
- renderRevisionInfo(changeInfo, actionMap);
- }
- });
- }
-
- @Override
- protected void onUnload() {
- if (replyAction != null) {
- replyAction.hide();
- }
- if (updateCheck != null) {
- updateCheck.cancel();
- updateCheck = null;
- }
- for (HandlerRegistration h : handlers) {
- h.removeHandler();
- }
- handlers.clear();
- super.onUnload();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setHeaderVisible(false);
- Resources.I.style().ensureInjected();
- star.setVisible(Gerrit.isSignedIn());
- labels.init(style);
- reviewers.init(style, ccText);
- hashtags.init(style);
- }
-
- private void initReplyButton(ChangeInfo info, String revision) {
- if (!info.revision(revision).isEdit()) {
- reply.setTitle(Gerrit.info().change().replyLabel());
- reply.setHTML(
- new SafeHtmlBuilder().openDiv().append(Gerrit.info().change().replyLabel()).closeDiv());
- if (hasDraftComments || lc.hasReplyComment()) {
- reply.setStyleName(style.highlight());
- }
- reply.setVisible(true);
- }
- }
-
- private void gotoSibling(int offset) {
- if (offset > 0
- && changeInfo.currentRevision() != null
- && changeInfo.currentRevision().equals(revision)) {
- return;
- }
-
- if (offset < 0 && changeInfo.revision(revision)._number() == 1) {
- return;
- }
-
- JsArray<RevisionInfo> revisions = changeInfo.revisions().values();
- RevisionInfo.sortRevisionInfoByNumber(revisions);
- for (int i = 0; i < revisions.length(); i++) {
- if (revision.equals(revisions.get(i).name())) {
- if (0 <= i + offset && i + offset < revisions.length()) {
- Gerrit.display(
- PageLinks.toChange(
- project,
- new PatchSet.Id(changeInfo.legacyId(), revisions.get(i + offset)._number())));
- return;
- }
- return;
- }
- }
- }
-
- private void initIncludedInAction(ChangeInfo info) {
- if (info.status() == Status.MERGED) {
- includedInAction =
- new IncludedInAction(
- info.projectNameKey(), info.legacyId(), style, headerLine, includedIn);
- includedIn.setVisible(true);
- }
- }
-
- private void updatePatchSetsTextStyle(boolean isPatchSetCurrent) {
- if (isPatchSetCurrent) {
- patchSetsText.removeClassName(style.notCurrentPatchSet());
- } else {
- patchSetsText.addClassName(style.notCurrentPatchSet());
- }
- }
-
- private void initRevisionsAction(ChangeInfo info, String revision) {
- int currentPatchSet;
- if (info.currentRevision() != null && info.revisions().containsKey(info.currentRevision())) {
- currentPatchSet = info.revision(info.currentRevision())._number();
- } else {
- JsArray<RevisionInfo> revList = info.revisions().values();
- RevisionInfo.sortRevisionInfoByNumber(revList);
- currentPatchSet = revList.get(revList.length() - 1)._number();
- }
-
- String currentlyViewedPatchSet;
- boolean isPatchSetCurrent = true;
- String revisionId = info.revision(revision).id();
- if (revisionId.equals("edit")) {
- currentlyViewedPatchSet =
- Resources.M.editPatchSet(RevisionInfo.findEditParent(info.revisions().values()));
- currentPatchSet = info.revisions().values().length() - 1;
- } else {
- currentlyViewedPatchSet = revisionId;
- if (!currentlyViewedPatchSet.equals(Integer.toString(currentPatchSet))) {
- isPatchSetCurrent = false;
- }
- }
- patchSetsText.setInnerText(Resources.M.patchSets(currentlyViewedPatchSet, currentPatchSet));
- updatePatchSetsTextStyle(isPatchSetCurrent);
- patchSetsAction =
- new PatchSetsAction(
- info.projectNameKey(), info.legacyId(), revision, edit, style, headerLine, patchSets);
- }
-
- private void initDownloadAction(ChangeInfo info, String revision) {
- downloadAction = new DownloadAction(info, revision, style, headerLine, download);
- }
-
- private void initProjectLinks(ChangeInfo info) {
- projectSettingsLink.setHref("#" + PageLinks.toProject(info.projectNameKey()));
- projectSettings.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- if (Hyperlink.impl.handleAsClick((Event) event.getNativeEvent())) {
- event.stopPropagation();
- event.preventDefault();
- Gerrit.display(PageLinks.toProject(info.projectNameKey()));
- }
- }
- },
- ClickEvent.getType());
- projectDashboard.setText(info.project());
- projectDashboard.setTargetHistoryToken(
- PageLinks.toProjectDefaultDashboard(info.projectNameKey()));
- }
-
- private void initBranchLink(ChangeInfo info) {
- branchLink.setText(info.branch());
- branchLink.setTargetHistoryToken(
- PageLinks.toChangeQuery(
- BranchLink.query(info.projectNameKey(), info.status(), info.branch(), null)));
- }
-
- private void initEditMode(ChangeInfo info, String revision) {
- if (Gerrit.isSignedIn()) {
- RevisionInfo rev = info.revision(revision);
- if (info.status().isOpen()) {
- if (isEditModeEnabled(info, rev)) {
- editMode.setVisible(fileTableMode == FileTable.Mode.REVIEW);
- addFile.setVisible(!editMode.isVisible());
- deleteFile.setVisible(!editMode.isVisible());
- renameFile.setVisible(!editMode.isVisible());
- reviewMode.setVisible(!editMode.isVisible());
- addFileAction =
- new AddFileAction(
- info.projectNameKey(), changeId, info.revision(revision), style, addFile, files);
- deleteFileAction =
- new DeleteFileAction(
- info.projectNameKey(), changeId, info.revision(revision), style, addFile);
- renameFileAction =
- new RenameFileAction(
- info.projectNameKey(), changeId, info.revision(revision), style, addFile);
- } else {
- editMode.setVisible(false);
- addFile.setVisible(false);
- reviewMode.setVisible(false);
- }
-
- if (rev.isEdit()) {
- if (info.hasEditBasedOnCurrentPatchSet()) {
- publishEdit.setVisible(true);
- } else {
- rebaseEdit.setVisible(true);
- }
- deleteEdit.setVisible(true);
- }
- } else if (rev.isEdit()) {
- deleteEdit.setStyleName(style.highlight());
- deleteEdit.setVisible(true);
- }
- }
- }
-
- private boolean isEditModeEnabled(ChangeInfo info, RevisionInfo rev) {
- if (rev.isEdit()) {
- return true;
- }
- if (edit == null) {
- return revision.equals(info.currentRevision());
- }
- return rev._number() == RevisionInfo.findEditParent(info.revisions().values());
- }
-
- @UiHandler("publishEdit")
- void onPublishEdit(@SuppressWarnings("unused") ClickEvent e) {
- EditActions.publishEdit(getProject(), changeId, publishEdit, rebaseEdit, deleteEdit);
- }
-
- @UiHandler("rebaseEdit")
- void onRebaseEdit(@SuppressWarnings("unused") ClickEvent e) {
- EditActions.rebaseEdit(getProject(), changeId, publishEdit, rebaseEdit, deleteEdit);
- }
-
- @UiHandler("deleteEdit")
- void onDeleteEdit(@SuppressWarnings("unused") ClickEvent e) {
- if (Window.confirm(Resources.C.deleteChangeEdit())) {
- EditActions.deleteEdit(getProject(), changeId, publishEdit, rebaseEdit, deleteEdit);
- }
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
-
- KeyCommandSet keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
- keysNavigation.add(
- new KeyCommand(0, 'u', Util.C.upToChangeList()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.displayLastChangeList();
- }
- });
- keysNavigation.add(
- new KeyCommand(0, 'R', Util.C.keyReloadChange()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChange(project, changeId));
- }
- });
- keysNavigation.add(
- new KeyCommand(0, 'n', Util.C.keyNextPatchSet()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- gotoSibling(1);
- }
- },
- new KeyCommand(0, 'p', Util.C.keyPreviousPatchSet()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- gotoSibling(-1);
- }
- });
- handlers.add(GlobalKey.add(this, keysNavigation));
-
- KeyCommandSet keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
- keysAction.add(
- new KeyCommand(0, 'a', Util.C.keyPublishComments()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (Gerrit.isSignedIn()) {
- onReply(null);
- } else {
- Gerrit.doSignIn(getToken());
- }
- }
- });
- keysAction.add(
- new KeyCommand(0, 'x', Util.C.keyExpandAllMessages()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- onExpandAll(null);
- }
- });
- keysAction.add(
- new KeyCommand(0, 'z', Util.C.keyCollapseAllMessages()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- onCollapseAll(null);
- }
- });
- keysAction.add(
- new KeyCommand(0, 's', Util.C.changeTableStar()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (Gerrit.isSignedIn()) {
- star.setValue(!star.getValue(), true);
- } else {
- Gerrit.doSignIn(getToken());
- }
- }
- });
- keysAction.add(
- new KeyCommand(0, 'c', Util.C.keyAddReviewers()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (Gerrit.isSignedIn()) {
- reviewers.onOpenForm();
- } else {
- Gerrit.doSignIn(getToken());
- }
- }
- });
- keysAction.add(
- new KeyCommand(0, 't', Util.C.keyEditTopic()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (Gerrit.isSignedIn()) {
- // In Firefox this event is mistakenly called when F5 is pressed so
- // differentiate F5 from 't' by checking the charCode(F5=0, t=116).
- if (event.getNativeEvent().getCharCode() == 0) {
- Window.Location.reload();
- return;
- }
- if (topic.canEdit()) {
- topic.onEdit();
- }
- } else {
- Gerrit.doSignIn(getToken());
- }
- }
- });
- handlers.add(GlobalKey.add(this, keysAction));
- files.registerKeys();
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- commit.onShowView();
- related.setMaxHeight(commit.getElement().getParentElement().getOffsetHeight());
-
- if (openReplyBox) {
- onReply();
- } else {
- String prior = Gerrit.getPriorView();
- if (prior != null && prior.startsWith("/c/")) {
- scrollToPath(prior.substring(3));
- }
- }
-
- ChangeGlue.fireShowChange(changeInfo, changeInfo.revision(revision));
- CodeMirror.preload();
- startPoller();
- }
-
- private void scrollToPath(String token) {
- ProjectChangeId cId;
- try {
- cId = ProjectChangeId.create(token);
- } catch (IllegalArgumentException e) {
- // Scrolling is best-effort.
- return;
- }
- if (!changeId.equals(cId.getChangeId())) {
- return; // Unrelated URL, do not scroll.
- }
-
- // Extract the start of a file path. The patch set is always contained in the URL and separated
- // by from the changeId by a forward slash. Example: /c/project/+/123/1/folder/file.txt
- int s = token.indexOf('/', cId.identifierLength() + 1);
- if (s < 0) {
- return; // URL does not name a file.
- }
-
- int c = token.lastIndexOf(',');
- if (0 <= c) {
- token = token.substring(s + 1, c);
- } else {
- token = token.substring(s + 1);
- }
-
- if (!token.isEmpty()) {
- files.scrollToPath(KeyUtil.decode(token));
- }
- }
-
- @UiHandler("star")
- void onToggleStar(ValueChangeEvent<Boolean> e) {
- StarredChanges.toggleStar(changeId, e.getValue());
- }
-
- @UiHandler("includedIn")
- void onIncludedIn(@SuppressWarnings("unused") ClickEvent e) {
- includedInAction.show();
- }
-
- @UiHandler("download")
- void onDownload(@SuppressWarnings("unused") ClickEvent e) {
- downloadAction.show();
- }
-
- @UiHandler("patchSets")
- void onPatchSets(@SuppressWarnings("unused") ClickEvent e) {
- patchSetsAction.show();
- }
-
- @UiHandler("reply")
- void onReply(@SuppressWarnings("unused") ClickEvent e) {
- onReply();
- }
-
- @UiHandler("permalink")
- void onReload(ClickEvent e) {
- e.preventDefault();
- Gerrit.display(PageLinks.toChange(project, changeId));
- }
-
- private void onReply() {
- if (Gerrit.isSignedIn()) {
- replyAction.onReply(null);
- } else {
- Gerrit.doSignIn(getToken());
- }
- }
-
- @UiHandler("openAll")
- void onOpenAll(@SuppressWarnings("unused") ClickEvent e) {
- files.openAll();
- }
-
- @UiHandler("editMode")
- void onEditMode(@SuppressWarnings("unused") ClickEvent e) {
- fileTableMode = FileTable.Mode.EDIT;
- refreshFileTable();
- editMode.setVisible(false);
- addFile.setVisible(true);
- deleteFile.setVisible(true);
- renameFile.setVisible(true);
- reviewMode.setVisible(true);
- }
-
- @UiHandler("reviewMode")
- void onReviewMode(@SuppressWarnings("unused") ClickEvent e) {
- fileTableMode = FileTable.Mode.REVIEW;
- refreshFileTable();
- editMode.setVisible(true);
- addFile.setVisible(false);
- deleteFile.setVisible(false);
- renameFile.setVisible(false);
- reviewMode.setVisible(false);
- }
-
- @UiHandler("addFile")
- void onAddFile(@SuppressWarnings("unused") ClickEvent e) {
- addFileAction.onEdit();
- }
-
- @UiHandler("deleteFile")
- void onDeleteFile(@SuppressWarnings("unused") ClickEvent e) {
- deleteFileAction.onDelete();
- }
-
- @UiHandler("renameFile")
- void onRenameFile(@SuppressWarnings("unused") ClickEvent e) {
- renameFileAction.onRename();
- }
-
- private void refreshFileTable() {
- int idx = diffBase.getSelectedIndex();
- if (0 <= idx) {
- String n = diffBase.getValue(idx);
- loadConfigInfo(changeInfo, DiffObject.parse(changeInfo.legacyId(), n));
- }
- }
-
- @UiHandler("showTaggedComments")
- void onShowTaggedComments(@SuppressWarnings("unused") ClickEvent e) {
- showTaggedComments.setVisible(false);
- hideTaggedComments.setVisible(true);
- int n = history.getWidgetCount();
- for (int i = 0; i < n; i++) {
- Message m = ((Message) history.getWidget(i));
- m.setVisible(true);
- }
- }
-
- @UiHandler("hideTaggedComments")
- void onHideTaggedComments(@SuppressWarnings("unused") ClickEvent e) {
- hideTaggedComments.setVisible(false);
- showTaggedComments.setVisible(true);
- int n = history.getWidgetCount();
- for (int i = 0; i < n; i++) {
- Message m = ((Message) history.getWidget(i));
- if (m.getMessageInfo().tag() != null) {
- m.setVisible(false);
- }
- }
- }
-
- @UiHandler("expandAll")
- void onExpandAll(@SuppressWarnings("unused") ClickEvent e) {
- int n = history.getWidgetCount();
- for (int i = 0; i < n; i++) {
- ((Message) history.getWidget(i)).setOpen(true);
- }
- expandAll.setVisible(false);
- collapseAll.setVisible(true);
- }
-
- @UiHandler("collapseAll")
- void onCollapseAll(@SuppressWarnings("unused") ClickEvent e) {
- int n = history.getWidgetCount();
- for (int i = 0; i < n; i++) {
- ((Message) history.getWidget(i)).setOpen(false);
- }
- expandAll.setVisible(true);
- collapseAll.setVisible(false);
- }
-
- @UiHandler("diffBase")
- void onChangeRevision(@SuppressWarnings("unused") ChangeEvent e) {
- int idx = diffBase.getSelectedIndex();
- if (0 <= idx) {
- String n = diffBase.getValue(idx);
- loadConfigInfo(changeInfo, DiffObject.parse(changeInfo.legacyId(), n));
- }
- }
-
- private void loadConfigInfo(ChangeInfo info, DiffObject base) {
- final RevisionInfo rev = info.revision(revision);
- if (base.isAutoMerge() && !initCurrentRevision(info).isMerge()) {
- Gerrit.display(getToken(), new NotFoundScreen());
- }
-
- updateToken(info, base, rev);
-
- RevisionInfo baseRev = resolveRevisionOrPatchSetId(info, base.asString(), null);
-
- CallbackGroup group = new CallbackGroup();
- Timestamp lastReply = myLastReply(info);
- if (rev.isEdit()) {
- // Comments are filtered for the current revision. Use parent
- // patch set for edits, as edits themself can never have comments.
- RevisionInfo p = RevisionInfo.findEditParentRevision(info.revisions().values());
- List<NativeMap<JsArray<CommentInfo>>> comments = loadComments(p, group);
- loadFileList(base, baseRev, rev, lastReply, group, comments, null);
- } else {
- loadDiff(base, baseRev, rev, lastReply, group);
- }
- group.addListener(
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- loadConfigInfo(info, rev);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- logger.log(
- Level.SEVERE,
- "Loading file list and inline comments failed: " + caught.getMessage());
- loadConfigInfo(info, rev);
- }
- });
- group.done();
- }
-
- private void loadConfigInfo(ChangeInfo info, RevisionInfo rev) {
- if (loaded) {
- return;
- }
-
- RevisionInfoCache.add(changeId, rev);
- ConfigInfoCache.add(info);
- ConfigInfoCache.get(
- info.projectNameKey(),
- new ScreenLoadCallback<ConfigInfoCache.Entry>(this) {
- @Override
- protected void preDisplay(Entry result) {
- loaded = true;
- commentLinkProcessor = result.getCommentLinkProcessor();
- setTheme(result.getTheme());
- renderChangeInfo(info);
- loadRevisionInfo();
- }
- });
- ConfigInfoCache.get(
- info.projectNameKey(),
- new GerritCallback<Entry>() {
- @Override
- public void onSuccess(Entry entry) {
- addExtensionPoints(info, rev, entry);
- }
- });
- }
-
- private void updateToken(ChangeInfo info, DiffObject base, RevisionInfo rev) {
- StringBuilder token =
- new StringBuilder("/c/")
- .append(PageLinks.toChangeId(info.projectNameKey(), info.legacyId()))
- .append("/");
- if (base.asString() != null) {
- token.append(base.asString()).append("..");
- }
- if (base.asString() != null || !rev.name().equals(info.currentRevision())) {
- token.append(rev._number());
- }
- setToken(token.toString());
- }
-
- static Timestamp myLastReply(ChangeInfo info) {
- if (Gerrit.isSignedIn() && info.messages() != null) {
- int self = Gerrit.getUserAccount()._accountId();
- for (int i = info.messages().length() - 1; i >= 0; i--) {
- MessageInfo m = info.messages().get(i);
- if (m.author() != null && m.author()._accountId() == self) {
- return m.date();
- }
- }
- }
- return null;
- }
-
- private void loadDiff(
- DiffObject base,
- RevisionInfo baseRev,
- RevisionInfo rev,
- Timestamp myLastReply,
- CallbackGroup group) {
- List<NativeMap<JsArray<CommentInfo>>> comments = loadComments(rev, group);
- List<NativeMap<JsArray<CommentInfo>>> drafts = loadDrafts(rev, group);
- loadFileList(base, baseRev, rev, myLastReply, group, comments, drafts);
-
- if (Gerrit.isSignedIn() && fileTableMode == FileTable.Mode.REVIEW) {
- ChangeApi.revision(getProject().get(), changeId.get(), rev.name())
- .view("files")
- .addParameterTrue("reviewed")
- .get(
- group.add(
- new AsyncCallback<JsArrayString>() {
- @Override
- public void onSuccess(JsArrayString result) {
- files.markReviewed(result);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- }
- }
-
- private void loadFileList(
- final DiffObject base,
- final RevisionInfo baseRev,
- final RevisionInfo rev,
- final Timestamp myLastReply,
- CallbackGroup group,
- final List<NativeMap<JsArray<CommentInfo>>> comments,
- final List<NativeMap<JsArray<CommentInfo>>> drafts) {
- DiffApi.list(
- getProject().get(),
- changeId.get(),
- rev.name(),
- baseRev,
- group.add(
- new AsyncCallback<NativeMap<FileInfo>>() {
- @Override
- public void onSuccess(NativeMap<FileInfo> m) {
- files.set(
- base,
- new PatchSet.Id(changeId, rev._number()),
- getProject(),
- style,
- reply,
- fileTableMode,
- edit != null);
- files.setValue(
- m,
- myLastReply,
- comments != null ? comments.get(0) : null,
- drafts != null ? drafts.get(0) : null);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- files.showError(caught);
- }
- }));
- }
-
- private List<NativeMap<JsArray<CommentInfo>>> loadComments(
- final RevisionInfo rev, CallbackGroup group) {
- final List<NativeMap<JsArray<CommentInfo>>> r = new ArrayList<>(1);
- // TODO(dborowitz): Could eliminate this call by adding an option to include
- // inline comments in the change detail.
- ChangeApi.comments(getProject().get(), changeId.get())
- .get(
- group.add(
- new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- // Return value is used for populating the file table, so only count
- // comments for the current revision. Still include all comments in
- // the history table.
- r.add(filterForRevision(result, rev._number()));
- history.addComments(result);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- return r;
- }
-
- private static NativeMap<JsArray<CommentInfo>> filterForRevision(
- NativeMap<JsArray<CommentInfo>> comments, int id) {
- NativeMap<JsArray<CommentInfo>> filtered = NativeMap.create();
- for (String k : comments.keySet()) {
- JsArray<CommentInfo> allRevisions = comments.get(k);
- JsArray<CommentInfo> thisRevision = JsArray.createArray().cast();
- for (int i = 0; i < allRevisions.length(); i++) {
- CommentInfo c = allRevisions.get(i);
- if (c.patchSet() == id) {
- thisRevision.push(c);
- }
- }
- filtered.put(k, thisRevision);
- }
- return filtered;
- }
-
- private List<NativeMap<JsArray<CommentInfo>>> loadDrafts(RevisionInfo rev, CallbackGroup group) {
- final List<NativeMap<JsArray<CommentInfo>>> r = new ArrayList<>(1);
- if (Gerrit.isSignedIn()) {
- ChangeApi.revision(getProject().get(), changeId.get(), rev.name())
- .view("drafts")
- .get(
- group.add(
- new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- r.add(result);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- } else {
- r.add(NativeMap.<JsArray<CommentInfo>>create());
- }
- return r;
- }
-
- private void loadCommit(RevisionInfo rev, CallbackGroup group) {
- if (rev.isEdit() || rev.commit() != null) {
- return;
- }
-
- ChangeApi.commitWithLinks(
- getProject().get(),
- changeId.get(),
- rev.name(),
- group.add(
- new AsyncCallback<CommitInfo>() {
- @Override
- public void onSuccess(CommitInfo info) {
- rev.setCommit(info);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- }
-
- private void renderSubmitType(Change.Status status, boolean canSubmit, SubmitType submitType) {
- if (status == Change.Status.NEW && !changeInfo.isWorkInProgress()) {
- if (canSubmit) {
- statusText.setInnerText(
- changeInfo.mergeable() ? Util.C.readyToSubmit() : Util.C.mergeConflict());
- }
- setVisible(notMergeable, !changeInfo.mergeable());
- }
- submitActionText.setInnerText(com.google.gerrit.client.admin.Util.toLongString(submitType));
- }
-
- private RevisionInfo resolveRevisionToDisplay(ChangeInfo info) {
- RevisionInfo rev = resolveRevisionOrPatchSetId(info, revision, info.currentRevision());
- if (rev != null) {
- revision = rev.name();
- return rev;
- }
-
- // the revision is not visible to the calling user (maybe it is a draft?)
- // or the change is corrupt, take the last revision that was returned,
- // if no revision was returned display an error
- JsArray<RevisionInfo> revisions = info.revisions().values();
- if (revisions.length() > 0) {
- RevisionInfo.sortRevisionInfoByNumber(revisions);
- rev = revisions.get(revisions.length() - 1);
- revision = rev.name();
- return rev;
- }
- new ErrorDialog(Resources.M.changeWithNoRevisions(info.legacyId().get())).center();
- throw new IllegalStateException("no revision, cannot proceed");
- }
-
- /**
- * Resolve a revision or patch set id string to RevisionInfo. When this view is created from the
- * changes table, revision is passed as a real revision. When this view is created from side by
- * side (by closing it with 'u') patch set id is passed.
- *
- * @param info change info
- * @param revOrId revision or patch set id
- * @param defaultValue value returned when revOrId is null
- * @return resolved revision or default value
- */
- private RevisionInfo resolveRevisionOrPatchSetId(
- ChangeInfo info, String revOrId, String defaultValue) {
- int parentNum;
- if (revOrId == null) {
- revOrId = defaultValue;
- } else if ((parentNum = toParentNum(revOrId)) > 0) {
- CommitInfo commitInfo = info.revision(revision).commit();
- JsArray<CommitInfo> parents = commitInfo.parents();
- if (parents.length() >= parentNum) {
- return RevisionInfo.forParent(-parentNum, parents.get(parentNum - 1));
- }
- } else if (!info.revisions().containsKey(revOrId)) {
- JsArray<RevisionInfo> list = info.revisions().values();
- for (int i = 0; i < list.length(); i++) {
- RevisionInfo r = list.get(i);
- if (revOrId.equals(String.valueOf(r._number()))) {
- revOrId = r.name();
- break;
- }
- }
- }
- return revOrId != null ? info.revision(revOrId) : null;
- }
-
- private boolean isSubmittable(ChangeInfo info) {
- boolean canSubmit = info.status().isOpen() && revision.equals(info.currentRevision());
- if (canSubmit && info.status() == Change.Status.NEW) {
- for (String name : info.labels()) {
- LabelInfo label = info.label(name);
- switch (label.status()) {
- case NEED:
- statusText.setInnerText(Util.M.needs(name));
- canSubmit = false;
- break;
- case REJECT:
- case IMPOSSIBLE:
- if (label.blocking()) {
- statusText.setInnerText(Util.M.blockedOn(name));
- canSubmit = false;
- }
- break;
- case MAY:
- case OK:
- default:
- break;
- }
- }
- }
- return canSubmit;
- }
-
- private void renderChangeInfo(ChangeInfo info) {
- RevisionInfo revisionInfo = info.revision(revision);
- changeInfo = info;
- lastDisplayedUpdate = info.updated();
-
- labels.set(info);
-
- renderOwner(info);
- renderUploader(info, revisionInfo);
- renderActionTextDate(info);
- renderDiffBaseListBox(info);
- initReplyButton(info, revision);
- initIncludedInAction(info);
- initDownloadAction(info, revision);
- initProjectLinks(info);
- initBranchLink(info);
- initEditMode(info, revision);
- actions.display(info, revision);
-
- star.setValue(info.starred());
- permalink.setHref(ChangeLink.permalink(changeId));
- permalink.setText(String.valueOf(info.legacyId()));
- topic.set(info, revision);
- commit.set(commentLinkProcessor, info, revision);
- related.set(info, revision);
- reviewers.set(info);
- assignee.set(info);
- if (Gerrit.isNoteDbEnabled()) {
- hashtags.set(info, revision);
- } else {
- setVisible(hashtagTableRow, false);
- }
-
- StringBuilder sb = new StringBuilder();
- sb.append(Util.M.changeScreenTitleId(info.idAbbreviated()));
- if (info.subject() != null) {
- sb.append(": ");
- sb.append(info.subject());
- }
- setWindowTitle(sb.toString());
-
- // Although this is related to the revision, we can process it early to
- // render it faster.
- if (!info.status().isOpen()
- || !revision.equals(info.currentRevision())
- || revisionInfo.isEdit()) {
- setVisible(strategy, false);
- }
-
- // Properly render revision actions initially while waiting for
- // the callback to populate them correctly.
- NativeMap<ActionInfo> emptyMap = NativeMap.<ActionInfo>create();
- initRevisionsAction(info, revision);
- quickApprove.setVisible(false);
- actions.reloadRevisionActions(emptyMap);
-
- boolean current = revision.equals(info.currentRevision()) && !revisionInfo.isEdit();
-
- if (revisionInfo.isEdit()) {
- statusText.setInnerText(Util.C.changeEdit());
- } else if (!current) {
- statusText.setInnerText(Util.C.notCurrent());
- labels.setVisible(false);
- } else {
- statusText.setInnerText(Util.toLongString(info.status()));
- }
-
- if (info.isPrivate()) {
- privateText.setInnerText(Util.C.isPrivate());
- }
-
- if (info.isWorkInProgress()) {
- wipText.setInnerText(Util.C.isWorkInProgress());
- }
-
- if (Gerrit.isSignedIn()) {
- replyAction =
- new ReplyAction(
- info, revision, hasDraftComments, style, commentLinkProcessor, reply, quickApprove);
- }
- history.set(commentLinkProcessor, replyAction, changeId, info);
-
- if (current && info.status().isOpen()) {
- quickApprove.set(info, revision, replyAction);
- renderSubmitType(info.status(), isSubmittable(info), info.submitType());
- } else {
- quickApprove.setVisible(false);
- }
- }
-
- private void renderRevisionInfo(ChangeInfo info, NativeMap<ActionInfo> actionMap) {
- initRevisionsAction(info, revision);
- commit.setParentNotCurrent(
- actionMap.containsKey("rebase") && actionMap.get("rebase").enabled());
- actions.reloadRevisionActions(actionMap);
- }
-
- private void renderOwner(ChangeInfo info) {
- // TODO info card hover
- String name = name(info.owner());
- if (info.owner().avatar(AvatarInfo.DEFAULT_SIZE) != null) {
- ownerPanel.insert(new AvatarImage(info.owner()), 0);
- }
- ownerLink.setText(name);
- ownerLink.setTitle(email(info.owner(), name));
- ownerLink.setTargetHistoryToken(
- PageLinks.toAccountQuery(
- info.owner().name() != null
- ? info.owner().name()
- : info.owner().email() != null
- ? info.owner().email()
- : String.valueOf(info.owner()._accountId()),
- Change.Status.NEW));
- }
-
- private void renderUploader(ChangeInfo changeInfo, RevisionInfo revInfo) {
- AccountInfo uploader = revInfo.uploader();
- boolean isOwner = uploader == null || uploader._accountId() == changeInfo.owner()._accountId();
- renderPushCertificate(revInfo, isOwner ? ownerPanel : uploaderPanel);
- if (isOwner) {
- uploaderRow.getStyle().setDisplay(Display.NONE);
- return;
- }
- uploaderRow.getStyle().setDisplay(Display.TABLE_ROW);
-
- if (uploader.avatar(AvatarInfo.DEFAULT_SIZE) != null) {
- uploaderPanel.insert(new AvatarImage(uploader), 0);
- }
- String name = name(uploader);
- uploaderName.setText(name);
- uploaderName.setTitle(email(uploader, name));
- }
-
- private void renderPushCertificate(RevisionInfo revInfo, FlowPanel panel) {
- if (!enableSignedPush()) {
- return;
- }
- Image status = new Image();
- panel.add(status);
- status.setStyleName(style.pushCertStatus());
- if (!revInfo.hasPushCertificate() || revInfo.pushCertificate().key() == null) {
- status.setResource(Gerrit.RESOURCES.question());
- status.setTitle(Util.C.pushCertMissing());
- return;
- }
- PushCertificateInfo certInfo = revInfo.pushCertificate();
- GpgKeyInfo.Status s = certInfo.key().status();
- switch (s) {
- case BAD:
- status.setResource(Gerrit.RESOURCES.redNot());
- status.setTitle(problems(Util.C.pushCertBad(), certInfo));
- break;
- case OK:
- status.setResource(Gerrit.RESOURCES.warning());
- status.setTitle(problems(Util.C.pushCertOk(), certInfo));
- break;
- case TRUSTED:
- status.setResource(Gerrit.RESOURCES.greenCheck());
- status.setTitle(Util.C.pushCertTrusted());
- break;
- }
- }
-
- private static String name(AccountInfo info) {
- return info.name() != null ? info.name() : Gerrit.info().user().anonymousCowardName();
- }
-
- private static String email(AccountInfo info, String name) {
- return info.email() != null ? info.email() : name;
- }
-
- private static String problems(String msg, PushCertificateInfo info) {
- if (info.key() == null || !info.key().hasProblems() || info.key().problems().length() == 0) {
- return msg;
- }
-
- StringBuilder sb = new StringBuilder();
- sb.append(msg).append(':');
- for (String problem : Natives.asList(info.key().problems())) {
- sb.append('\n').append(problem);
- }
- return sb.toString();
- }
-
- private void renderActionTextDate(ChangeInfo info) {
- String action;
- if (info.created().equals(info.updated())) {
- action = Util.C.changeInfoBlockUploaded();
- } else {
- action = Util.C.changeInfoBlockUpdated();
- }
- actionText.setInnerText(action);
- actionDate.setInnerText(FormatUtil.relativeFormat(info.updated()));
- }
-
- private void renderDiffBaseListBox(ChangeInfo info) {
- JsArray<RevisionInfo> list = info.revisions().values();
- RevisionInfo.sortRevisionInfoByNumber(list);
- int selectedIdx = list.length();
- for (int i = list.length() - 1; i >= 0; i--) {
- RevisionInfo r = list.get(i);
- diffBase.addItem(r.id() + ": " + r.name().substring(0, 6), r.id());
- if (r.name().equals(revision)) {
- SelectElement.as(diffBase.getElement())
- .getOptions()
- .getItem(diffBase.getItemCount() - 1)
- .setDisabled(true);
- }
- if (base.isPatchSet() && base.asPatchSetId().get() == r._number()) {
- selectedIdx = diffBase.getItemCount() - 1;
- }
- }
-
- RevisionInfo rev = info.revisions().get(revision);
- JsArray<CommitInfo> parents = rev.commit().parents();
- if (parents.length() > 1) {
- diffBase.addItem(Util.C.autoMerge(), DiffObject.AUTO_MERGE);
- for (int i = 0; i < parents.length(); i++) {
- int parentNum = i + 1;
- diffBase.addItem(Util.M.diffBaseParent(parentNum), String.valueOf(-parentNum));
- }
-
- if (base.isParent()) {
- selectedIdx = list.length() + base.getParentNum();
- }
- } else {
- diffBase.addItem(Util.C.baseDiffItem(), "");
- }
-
- diffBase.setSelectedIndex(selectedIdx);
- }
-
- void showUpdates(ChangeInfo newInfo) {
- if (!isAttached() || newInfo.updated().equals(lastDisplayedUpdate)) {
- return;
- }
-
- JsArray<MessageInfo> om = changeInfo.messages();
- JsArray<MessageInfo> nm = newInfo.messages();
-
- if (om == null) {
- om = JsArray.createArray().cast();
- }
- if (nm == null) {
- nm = JsArray.createArray().cast();
- }
-
- if (om.length() == nm.length()) {
- return;
- }
-
- if (updateAvailable == null) {
- updateAvailable =
- new UpdateAvailableBar() {
- @Override
- void onShow() {
- Gerrit.display(PageLinks.toChange(project, changeId));
- }
-
- @Override
- void onIgnore(Timestamp newTime) {
- lastDisplayedUpdate = newTime;
- }
- };
- }
- updateAvailable.set(Natives.asList(nm).subList(om.length(), nm.length()), newInfo.updated());
- if (!updateAvailable.isAttached()) {
- add(updateAvailable);
- }
- }
-
- private void startPoller() {
- if (Gerrit.isSignedIn() && 0 < Gerrit.info().change().updateDelay()) {
- updateCheck = new UpdateCheckTimer(this);
- updateCheck.schedule();
- handlers.add(UserActivityMonitor.addValueChangeHandler(updateCheck));
- }
- }
-
- private static String normalize(String r) {
- return r != null && !r.isEmpty() ? r : null;
- }
-
- /**
- * @param parentToken
- * @return 1-based parentNum if parentToken is a String which can be parsed as a negative integer
- * i.e. "-1", "-2", etc. If parentToken cannot be parsed as a negative integer, return zero.
- */
- private static int toParentNum(String parentToken) {
- try {
- int n = Integer.parseInt(parentToken);
- if (n < 0) {
- return -n;
- }
- return 0;
- } catch (NumberFormatException e) {
- return 0;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml
deleted file mode 100644
index d629fc2126..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml
+++ /dev/null
@@ -1,634 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gerrit.client.change'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.change.ChangeScreen.Style'>
- @eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
- @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-
- @def COMMIT_WIDTH 560px;
- @def HEADER_HEIGHT 30px;
-
- @def BUTTON_HEIGHT 14px;
-
- .cs2 {
- margin-bottom: 1em;
- }
-
- .headerLine {
- position: relative;
- background-color: trimColor;
- height: HEADER_HEIGHT;
- margin: 0 -5px;
- padding: 0 5px;
- }
-
- .subjectLine {
- position: relative;
- width: COMMIT_WIDTH;
- height: HEADER_HEIGHT;
- background-color: trimColor;
- color: textColor;
- font-family: sans-serif;
- }
- .subjectText {
- width: 460px;
- height: HEADER_HEIGHT;
- line-height: HEADER_HEIGHT;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .infoLine {
- position: absolute;
- top: 0;
- left: COMMIT_WIDTH;
- height: HEADER_HEIGHT;
- padding-left: 25px;
- }
-
- .infoLineHeaderButtons {
- display: inline-block;
- height: HEADER_HEIGHT;
- }
- .statusRight {
- position: absolute;
- top: 0;
- right: 0;
- height: HEADER_HEIGHT;
- }
- .idAndStatus {
- display: inline-block;
- position: relative;
- height: HEADER_HEIGHT;
- }
- .star {
- position: absolute;
- top: 5px;
- right: 2px;
- cursor: pointer;
- outline: none;
- }
- .changeId {
- width: 300px;
- white-space: nowrap;
- line-height: HEADER_HEIGHT;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .statusText {
- font-weight: bold;
- }
- .privateText {
- font-weight: bold;
- }
-
- .wipText {
- font-weight: bold;
- }
-
- div.popdown {
- display: inline-block;
- margin-top: 2px;
- margin-left: 5px;
- margin-right: 25px;
- }
-
- .popdown button {
- cursor: pointer;
- height: 25px;
- border: none;
- background-color: trimColor;
- margin: 0 0 0 -2px;
- padding-left: 2px;
- padding-right: 2px;
- min-width: 100px;
- }
- .popdown button div {
- padding-left: 6px;
- padding-right: 6px;
- }
- .popdown button div:after {
- content: " \25bc";
- }
- .popdown button.selected {
- font-weight: bold;
- }
- .popdown button:focus {
- outline: none;
- }
-
- .headerButtons button:disabled,
- #change_infoTable button:disabled,
- .popdown button:disabled {
- background-color: #999;
- background-image: -webkit-linear-gradient(top, #999, #999);
- }
-
- .infoTable {
- border-spacing: 0;
- }
-
- .infoTable th {
- width: 60px;
- color: #444;
- font-weight: normal;
- vertical-align: top;
- text-align: left;
- padding: 0 5px 0 0;
- }
-
- .projectSettings {
- float: right;
- cursor: pointer;
- }
-
- .infoColumn {
- width: 440px;
- padding-left: 17px;
- padding-right: 17px;
- vertical-align: top;
- }
-
- #change_infoTable {
- border-spacing: 0;
- width: 100%;
- margin-left: 2px;
- margin-right: 5px;
- }
-
- .notMergeable {
- float: right;
- font-weight: bold;
- color: #d00;
- }
-
- .commitColumn, .relatedColumn {
- padding: 0;
- vertical-align: top;
- }
- .commitColumn { width: COMMIT_WIDTH; }
- .relatedColumn { width: 375px; }
-
- .labels {
- border-spacing: 0;
- padding: 0;
- }
- .labelName {
- color: #444;
- vertical-align: top;
- text-align: left;
- padding-top: 3px;
- padding-right: 5px;
- white-space: nowrap;
- }
-
- .label_user {
- display: inline-block;
- margin-bottom: 2px;
- padding: 1px 3px 0px 3px;
- border-radius: 5px;
- -webkit-border-radius: 5px;
- background: trimColor;
- border: 1px solid trimColor;
- white-space: nowrap;
- }
- .label_user img.avatar {
- margin: 0 2px 0 0;
- width: 16px;
- height: 16px;
- vertical-align: bottom;
- }
- .label_user button {
- cursor: pointer;
- padding: 0;
- margin: 0 0 0 5px;
- border: 0;
- background-color: transparent;
- white-space: nowrap;
- }
-
- .label_ok {color: #060;}
- .label_reject {color: #d14836;}
- .label_need {color: #000;}
- .label_may {color: #777;}
-
- .hashtagName {
- display: inline-block;
- height: 15px;
- margin-bottom: 2px;
- padding: 1px 3px 1px 3px;
- border-radius: 5px;
- -webkit-border-radius: 5px;
- background: #E2F5FF;
- border: 1px solid #579FDA;
- white-space: nowrap;
- }
-
- .hashtagName a,
- .hashtagName button {
- position: relative;
- top: -4px;
- }
-
- .hashtagName button {
- cursor: pointer;
- padding: 0;
- margin: 0 0 0 5px;
- border: 0;
- background-color: transparent;
- white-space: nowrap;
- }
-
- .hashtagIcon img {
- position: relative;
- top: 4px;
- }
-
- .headerButtons button {
- margin: 5.286px 3px 0 0;
- text-align: center;
- font-size: 8pt;
- font-weight: bold;
- cursor: pointer;
- border: 2px solid;
- color: rgba(0, 0, 0, 0.15);
- background-color: #f5f5f5;
- -webkit-border-radius: 2px;
- -webkit-box-sizing: content-box;
- }
- .headerButtons button div {
- color: #444;
- min-width: 54px;
- white-space: nowrap;
- height: BUTTON_HEIGHT;
- line-height: BUTTON_HEIGHT;
- }
- button.highlight {
- background-color: #4d90fe;
- }
- button.highlight div { color: #fff; }
-
- .sectionHeader {
- position: relative;
- background-color: trimColor;
- font-weight: bold;
- color: textColor;
- height: 20px;
- line-height: 20px;
- margin: 0 -5px;
- padding: 5px 5px;
- }
- .sectionHeader .headerButtons {
- position: absolute;
- left: 300px;
- top: 2px;
- height: 18px;
- line-height: 18px;
- border-left: 1px inset #fff;
- padding-left: 5px;
- padding-top: 3px;
- padding-bottom: 3px;
- }
- .sectionHeader button { margin-top: 0; }
-
- .diffBase {
- display: inline-block;
- height: 18px;
- line-height: 18px;
- font-size: smaller;
- font-weight: normal;
- vertical-align: top;
- }
- .diffBase select {
- margin: 0;
- border: 2px solid rgba(0, 0, 0, 0.15);
- height: 20px;
- font-size: 8pt;
- font-weight: bold;
- border-radius: 2px;
- background-color: #f5f5f5
- }
-
- .replyBox {
- background-color: trimColor;
- }
-
- .ownerPanel img, .uploaderPanel img {
- margin: 0 2px 0 0;
- width: 16px;
- height: 16px !important;
- vertical-align: bottom;
- }
-
- .headerExtension {
- display: inline-block;
- float: right;
- }
-
- .headerExtension>div>div {
- float: left;
- }
-
- .changeExtension {
- padding-top: 5px;
- }
-
- .relatedExtension {
- padding-top: 5px;
- }
-
- .commitExtension {
- padding-top: 5px;
- }
-
- .historyExtension {
- display: inline-block;
- float: right;
- }
-
- .pushCertStatus {
- padding-left: 5px;
- }
-
- .notCurrentPatchSet {
- background-color: #FFA62F;
- }
- </ui:style>
-
- <g:HTMLPanel styleName='{style.cs2}'>
- <g:HTMLPanel styleName='{style.headerLine}' ui:field='headerLine'>
- <div class='{style.subjectLine}'>
- <div class='{style.idAndStatus}'>
- <span class='{style.changeId}'>
- <ui:msg>Change <g:Anchor ui:field='permalink' title='Reload the change (Shortcut: R)'>
- <ui:attribute name='title'/>
- </g:Anchor> - <span ui:field='statusText' class='{style.statusText}'/>
- <span ui:field='privateText' class='{style.privateText}'/>
- <span ui:field='wipText' class='{style.wipText}'/></ui:msg>
- </span>
- <g:SimplePanel ui:field='headerExtension' styleName='{style.headerExtension}'/>
- </div>
- </div>
-
- <div class='{style.infoLine}'>
- <div class='{style.headerButtons} {style.infoLineHeaderButtons}'>
- <g:Button ui:field='reply'
- styleName=''
- title=''
- visible='false'>
- <ui:attribute name='title'/>
- </g:Button>
- <c:QuickApprove ui:field='quickApprove'
- styleName='{style.highlight}'
- title='Apply score with one click'>
- <ui:attribute name='title'/>
- </c:QuickApprove>
- <g:Button ui:field='publishEdit'
- styleName='{style.highlight}' visible='false'>
- <div><ui:msg>Publish Edit</ui:msg></div>
- </g:Button>
- <g:Button ui:field='rebaseEdit'
- styleName='{style.highlight}' visible='false'>
- <div><ui:msg>Rebase Edit</ui:msg></div>
- </g:Button>
- <g:Button ui:field='deleteEdit' styleName='' visible='false'>
- <div><ui:msg>Delete Edit</ui:msg></div>
- </g:Button>
- <g:SimplePanel ui:field='headerExtensionMiddle' styleName='{style.headerExtension}'/>
- </div>
- </div>
-
- <div class='{style.statusRight}'>
- <g:FlowPanel styleName='{style.popdown}'>
- <g:Button ui:field='includedIn' styleName='' visible="false">
- <div><ui:msg>Included in</ui:msg></div>
- </g:Button>
- <g:Button ui:field='patchSets' styleName=''>
- <div ui:field='patchSetsText'/>
- </g:Button>
- <g:Button ui:field='download' styleName=''>
- <div><ui:msg>Download</ui:msg></div>
- </g:Button>
- <g:SimplePanel ui:field='headerExtensionRight' styleName='{style.headerExtension}'/>
- </g:FlowPanel>
- <c:StarIcon ui:field='star' styleName='{style.star}' title='Star the change (Shortcut: s)'>
- <ui:attribute name='title'/>
- </c:StarIcon>
- </div>
- </g:HTMLPanel>
-
- <table class='{style.infoTable}'>
- <tr>
- <td class='{style.commitColumn}'>
- <c:CommitBox ui:field='commit'/>
- <g:SimplePanel ui:field='commitExtension' styleName='{style.commitExtension}'/>
- </td>
- <td class='{style.infoColumn}'>
- <table id='change_infoTable'>
- <tr>
- <th><ui:msg>Owner</ui:msg></th>
- <td>
- <g:FlowPanel ui:field='ownerPanel' styleName='{style.ownerPanel}'>
- <x:InlineHyperlink ui:field='ownerLink'/>
- </g:FlowPanel>
- </td>
- </tr>
- <tr ui:field='uploaderRow'>
- <th><ui:msg>Uploader</ui:msg></th>
- <td>
- <g:FlowPanel ui:field='uploaderPanel' styleName='{style.uploaderPanel}'>
- <g:InlineLabel ui:field='uploaderName'/>
- </g:FlowPanel>
- </td>
- </tr>
- <tr ui:field='assigneeRow'>
- <th><ui:msg>Assignee</ui:msg></th>
- <td>
- <c:Assignee ui:field='assignee'/>
- </td>
- </tr>
- <tr>
- <th><ui:msg>Reviewers</ui:msg></th>
- <td>
- <c:Reviewers ui:field='reviewers'/>
- </td>
- </tr>
- <tr>
- <th/>
- <td ui:field='ccText'/>
- </tr>
- <tr>
- <th><ui:msg>Project</ui:msg></th>
- <td><x:InlineHyperlink ui:field='projectDashboard'
- title='Go to project dashboard'>
- <ui:attribute name='title'/>
- </x:InlineHyperlink>
- <a ui:field='projectSettingsLink'
- class='{style.projectSettings}'>
- <g:Image
- ui:field='projectSettings'
- resource='{ico.gear}'
- title='Go to project settings'>
- <ui:attribute name='title'/>
- </g:Image>
- </a>
- </td>
- </tr>
- <tr>
- <th><ui:msg>Branch</ui:msg></th>
- <td><x:InlineHyperlink ui:field='branchLink'
- title='Search for changes on this branch'>
- <ui:attribute name='title'/>
- </x:InlineHyperlink>
- </td>
- </tr>
- <tr>
- <th><ui:msg>Topic</ui:msg></th>
- <td><c:Topic ui:field='topic'/></td>
- </tr>
- <tr ui:field='strategy'>
- <th><ui:msg>Strategy</ui:msg></th>
- <td>
- <span ui:field='submitActionText'/>
- <div ui:field='notMergeable'
- class='{style.notMergeable}'
- style='display: none'
- aria-hidden='true'
- title='The change cannot be merged due to a path conflict. Rebase the change and upload the rebased commit for review.'>
- <ui:attribute name='title'/>
- <ui:msg>Cannot Merge</ui:msg>
- </div>
- </td>
- </tr>
- <tr>
- <th ui:field='actionText'/>
- <td ui:field='actionDate'/>
- </tr>
- <tr ui:field='hashtagTableRow'>
- <th><ui:msg>Hashtags</ui:msg></th>
- <td colspan='2'>
- <c:Hashtags ui:field='hashtags'/>
- </td>
- </tr>
- <tr><td colspan='2'><c:Actions ui:field='actions'/></td></tr>
- </table>
- <hr/>
- <c:Labels ui:field='labels' styleName='{style.labels}'/>
- <g:SimplePanel ui:field='changeExtension' styleName='{style.changeExtension}'/>
- <div id='change_plugins'/>
- </td>
- <td class='{style.relatedColumn}'>
- <c:RelatedChanges ui:field='related'/>
- <g:SimplePanel ui:field='relatedExtension' styleName='{style.relatedExtension}'/>
- </td>
- </tr>
- </table>
-
- <div class='{style.sectionHeader} {style.headerButtons}'>
- <ui:msg>Files</ui:msg>
- <g:Button ui:field='addFile'
- title='Add file to this change'
- styleName=''
- visible='false'>
- <ui:attribute name='title'/>
- <div><ui:msg>Add&#8230;</ui:msg></div>
- </g:Button>
- <g:Button ui:field='deleteFile'
- title='Delete file from the repository'
- styleName=''
- visible='false'>
- <ui:attribute name='title'/>
- <div><ui:msg>Delete&#8230;</ui:msg></div>
- </g:Button>
- <g:Button ui:field='renameFile'
- title='Rename file in the repository'
- styleName=''
- visible='false'>
- <ui:attribute name='title'/>
- <div><ui:msg>Rename&#8230;</ui:msg></div>
- </g:Button>
- <div class='{style.headerButtons}'>
- <g:Button ui:field='openAll'
- styleName=''
- title='Open each file in a new tab'>
- <ui:attribute name='title'/>
- <div><ui:msg>Open All</ui:msg></div>
- </g:Button>
- <div class='{style.diffBase}'>
- <ui:msg>Diff against: <g:ListBox ui:field='diffBase' styleName=''/></ui:msg>
- </div>
- <g:Button ui:field='editMode'
- styleName=''
- visible='false'
- title='Switch file table to edit mode'>
- <ui:attribute name='title'/>
- <div><ui:msg>Edit</ui:msg></div>
- </g:Button>
- <g:Button ui:field='reviewMode'
- styleName=''
- visible='false'
- title='Done with edit mode'>
- <ui:attribute name='title'/>
- <div><ui:msg>Done Editing</ui:msg></div>
- </g:Button>
- </div>
- </div>
- <c:FileTable ui:field='files'/>
-
- <div class='{style.sectionHeader}'>
- <ui:msg>History</ui:msg>
- <div class='{style.headerButtons}'>
- <g:Button ui:field='expandAll'
- styleName=''
- title='Expand all messages in the change history'>
- <ui:attribute name='title'/>
- <div><ui:msg>Expand All</ui:msg></div>
- </g:Button>
- <g:Button ui:field='collapseAll'
- styleName=''
- visible='false'
- title='Collapse all messages in the change history'>
- <ui:attribute name='title'/>
- <div><ui:msg>Collapse All</ui:msg></div>
- </g:Button>
- <g:Button ui:field='hideTaggedComments'
- styleName=''
- visible='false'
- title='Hide tagged comments'>
- <ui:attribute name='title'/>
- <div><ui:msg>Hide tagged comments</ui:msg></div>
- </g:Button>
- <g:Button ui:field='showTaggedComments'
- styleName=''
- visible='false'
- title='Show tagged comments'>
- <ui:attribute name='title'/>
- <div><ui:msg>Show tagged comments</ui:msg></div>
- </g:Button>
- <g:SimplePanel ui:field='historyExtensionRight' styleName='{style.historyExtension}'/>
- </div>
- </div>
- <c:History ui:field='history'/>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
deleted file mode 100644
index be011d29a2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CherryPickAction.java
+++ /dev/null
@@ -1,80 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.CherryPickDialog;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.PopupPanel;
-
-class CherryPickAction {
- static void call(
- final Button b,
- final ChangeInfo info,
- final String revision,
- final Project.NameKey project,
- final String commitMessage) {
- // TODO Replace CherryPickDialog with a nicer looking display.
- b.setEnabled(false);
- new CherryPickDialog(project) {
- {
- sendButton.setText(Util.C.buttonCherryPickChangeSend());
- if (info.status() == Change.Status.MERGED) {
- message.setText(Util.M.cherryPickedChangeDefaultMessage(commitMessage.trim(), revision));
- } else {
- message.setText(commitMessage.trim());
- }
- }
-
- @Override
- public void onSend() {
- ChangeApi.cherrypick(
- info.project(),
- info.legacyId().get(),
- revision,
- getDestinationBranch(),
- getMessageText(),
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- sent = true;
- hide();
- Gerrit.display(PageLinks.toChange(project, result.legacyId()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableButtons(true);
- super.onFailure(caught);
- }
- });
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- super.onClose(event);
- b.setEnabled(true);
- }
- }.center();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
deleted file mode 100644
index 011257993e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
+++ /dev/null
@@ -1,219 +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.client.change;
-
-import com.google.gerrit.client.AvatarImage;
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.GitPerson;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.TableRowElement;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-class CommitBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, CommitBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String collapsed();
-
- String expanded();
-
- String clippy();
-
- String parentWebLink();
- }
-
- @UiField Style style;
- @UiField FlowPanel authorPanel;
- @UiField FlowPanel committerPanel;
- @UiField Image mergeCommit;
- @UiField CopyableLabel commitName;
- @UiField FlowPanel webLinkPanel;
- @UiField TableRowElement firstParent;
- @UiField FlowPanel parentCommits;
- @UiField FlowPanel parentWebLinks;
- @UiField InlineHyperlink authorNameEmail;
- @UiField Element authorDate;
- @UiField InlineHyperlink committerNameEmail;
- @UiField Element committerDate;
- @UiField CopyableLabel idText;
- @UiField HTML text;
- @UiField ScrollPanel scroll;
- @UiField Button more;
- @UiField Element parentNotCurrentText;
- private boolean expanded;
-
- CommitBox() {
- initWidget(uiBinder.createAndBindUi(this));
- addStyleName(style.collapsed());
- }
-
- void onShowView() {
- more.setVisible(scroll.getMaximumVerticalScrollPosition() > 0);
- }
-
- @UiHandler("more")
- void onMore(@SuppressWarnings("unused") ClickEvent e) {
- if (expanded) {
- removeStyleName(style.expanded());
- addStyleName(style.collapsed());
- } else {
- removeStyleName(style.collapsed());
- addStyleName(style.expanded());
- }
- expanded = !expanded;
- }
-
- void set(CommentLinkProcessor commentLinkProcessor, ChangeInfo change, String revision) {
- RevisionInfo revInfo = change.revision(revision);
- CommitInfo commit = revInfo.commit();
-
- commitName.setText(revision);
- idText.setText("Change-Id: " + change.changeId());
- idText.setPreviewText(change.changeId());
-
- formatLink(commit.author(), authorPanel, authorNameEmail, authorDate, change);
- formatLink(commit.committer(), committerPanel, committerNameEmail, committerDate, change);
- text.setHTML(
- commentLinkProcessor.apply(new SafeHtmlBuilder().append(commit.message()).linkify()));
- setWebLinks(webLinkPanel, revInfo.commit());
-
- if (revInfo.commit().parents().length() > 1) {
- mergeCommit.setVisible(true);
- }
-
- setParents(revInfo.commit().parents());
- }
-
- void setParentNotCurrent(boolean parentNotCurrent) {
- // display the orange ball if parent has moved on (not current)
- UIObject.setVisible(parentNotCurrentText, parentNotCurrent);
- parentNotCurrentText.setInnerText(parentNotCurrent ? "\u25CF" : "");
- }
-
- private void setWebLinks(FlowPanel panel, CommitInfo commit) {
- JsArray<WebLinkInfo> links = commit.webLinks();
- if (links != null) {
- for (WebLinkInfo link : Natives.asList(links)) {
- panel.add(link.toAnchor());
- }
- }
- }
-
- private void setParents(JsArray<CommitInfo> commits) {
- setVisible(firstParent, true);
- TableRowElement next = firstParent;
- TableRowElement previous = null;
- for (CommitInfo c : Natives.asList(commits)) {
- if (next == firstParent) {
- CopyableLabel copyLabel = getCommitLabel(c);
- parentCommits.add(copyLabel);
- setWebLinks(parentWebLinks, c);
- } else {
- next.appendChild(DOM.createTD());
- Element td1 = DOM.createTD();
- td1.appendChild(getCommitLabel(c).getElement());
- next.appendChild(td1);
- FlowPanel linksPanel = new FlowPanel();
- linksPanel.addStyleName(style.parentWebLink());
- setWebLinks(linksPanel, c);
- Element td2 = DOM.createTD();
- td2.appendChild(linksPanel.getElement());
- next.appendChild(td2);
- previous.getParentElement().insertAfter(next, previous);
- }
- previous = next;
- next = DOM.createTR().cast();
- }
- }
-
- private CopyableLabel getCommitLabel(CommitInfo c) {
- CopyableLabel copyLabel;
- copyLabel = new CopyableLabel(c.commit());
- copyLabel.setTitle(c.subject());
- copyLabel.setStyleName(style.clippy());
- return copyLabel;
- }
-
- private static void formatLink(
- GitPerson person, FlowPanel p, InlineHyperlink name, Element date, ChangeInfo change) {
- // only try to fetch the avatar image for author and committer if an avatar
- // plugin is installed, if the change owner has no avatar info assume that
- // no avatar plugin is installed
- if (change.owner().hasAvatarInfo()) {
- AvatarImage avatar;
- if (sameEmail(change.owner(), person)) {
- avatar = new AvatarImage(change.owner());
- } else {
- avatar = new AvatarImage(AccountInfo.create(0, person.name(), person.email(), null));
- }
- p.insert(avatar, 0);
- }
-
- name.setText(renderName(person));
- name.setTargetHistoryToken(PageLinks.toAccountQuery(owner(person), change.status()));
- date.setInnerText(FormatUtil.mediumFormat(person.date()));
- }
-
- private static String renderName(GitPerson person) {
- return person.name() + " <" + person.email() + ">";
- }
-
- private static String owner(GitPerson person) {
- if (person.email() != null) {
- return person.email();
- } else if (person.name() != null) {
- return person.name();
- } else {
- return "";
- }
- }
-
- private static boolean sameEmail(@Nullable AccountInfo p1, @Nullable GitPerson p2) {
- return p1 != null
- && p2 != null
- && p1.email() != null
- && p2.email() != null
- && p1.email().equals(p2.email());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
deleted file mode 100644
index 5f476becd0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
+++ /dev/null
@@ -1,198 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'
- xmlns:clippy='urn:import:com.google.gwtexpui.clippy.client'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:image field="toggle" src="moreLess.png"/>
- <ui:style gss='false' type='com.google.gerrit.client.change.CommitBox.Style'>
- @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-
- .collapsed .scroll { height: 250px }
- .scroll, .more { width: 560px }
- .scroll {
- border-right: 1px solid trimColor;
- border-bottom: 1px solid trimColor;
- }
-
- .text {
- font-family: monospace;
- white-space: pre;
- }
-
- .more {
- height: 8px;
- line-height: 8px;
- text-align: center;
- }
- .moreButton {
- padding: 0 5px 0 5px;
- margin: 0;
- border: none;
- height: 8px;
- background-color: #F7F7F7;
- }
- .moreButton:focus {
- outline: none;
- }
-
- @sprite .toggle {
- gwt-image: "toggle";
- width: 13px;
- height: 8px;
- padding: 0;
- }
- .collapsed .toggle { background-position: -13px -8px }
- .expanded .toggle { background-position: 0px -8px }
- .collapsed button:hover .toggle { background-position: -13px 0px }
- .expanded button:hover .toggle { background-position: 0px 0px }
-
- .header {
- border-spacing: 0;
- padding: 0;
- width: 560px;
- }
- .header th { width: 72px; }
- .header td { white-space: nowrap; }
- .date { width: 132px; }
-
- .clippy {
- position: relative;
- }
- .clippy div {
- position: absolute;
- top: 0px;
- right: -16px;
- }
- <!-- To make room for the copyableLabel from the adjacent column -->
- .webLinkPanel a:first-child {
- margin-left:16px;
- }
- .webLinkPanel>a {
- margin-left:2px;
- }
-
- .parentWebLink a:first-child {
- margin-left:16px;
- }
- .parentWebLink>a {
- margin-left:2px;
- }
-
- .commit {
- margin-right: 3px;
- float: left;
- }
-
- .userPanel img {
- margin: 0 2px 0 0;
- width: 16px;
- height: 16px !important;
- vertical-align: bottom;
- }
-
- .parent {
- margin-right: 3px;
- float: left;
- }
- .parentNotCurrent {
- color: #FFA62F; <!-- orange -->
- font-weight: bold;
- }
-
- </ui:style>
- <g:HTMLPanel>
- <g:ScrollPanel styleName='{style.scroll}' ui:field='scroll'>
- <g:HTML styleName='{style.text}' ui:field='text'/>
- </g:ScrollPanel>
- <div class='{style.more}'>
- <g:Button ui:field='more'
- styleName='{style.moreButton}'
- title='Expand/Collapse'>
- <ui:attribute name='title'/>
- <div class='{style.toggle}'/>
- </g:Button>
- </div>
- <table class='{style.header}'>
- <tr>
- <th><ui:msg>Author</ui:msg></th>
- <td>
- <g:FlowPanel ui:field='authorPanel' styleName='{style.userPanel}'>
- <x:InlineHyperlink ui:field='authorNameEmail'
- title='Search for changes by this user'>
- <ui:attribute name='title'/>
- </x:InlineHyperlink>
- </g:FlowPanel>
- </td>
- <td ui:field='authorDate' class='{style.date}' colspan="2"/>
- </tr>
- <tr>
- <th><ui:msg>Committer</ui:msg></th>
- <td>
- <g:FlowPanel ui:field='committerPanel' styleName='{style.userPanel}'>
- <x:InlineHyperlink ui:field='committerNameEmail'
- title='Search for changes by this user'>
- <ui:attribute name='title'/>
- </x:InlineHyperlink>
- </g:FlowPanel>
- </td>
- <td ui:field='committerDate' class='{style.date}' colspan="2"/>
- </tr>
- <tr>
- <th>
- <div class='{style.commit}'>
- <ui:msg>Commit</ui:msg>
- </div>
- <g:Image
- ui:field='mergeCommit'
- resource='{ico.merge}'
- visible='false'
- title='Merge Commit'>
- <ui:attribute name='title'/>
- </g:Image>
- </th>
- <td><clippy:CopyableLabel styleName='{style.clippy}' ui:field='commitName'/></td>
- <td>
- <g:FlowPanel ui:field='webLinkPanel' styleName='{style.webLinkPanel}'/>
- </td>
- </tr>
- <tr ui:field='firstParent' style='display: none'>
- <th>
- <div class='{style.parent}'>
- <ui:msg>Parent(s)</ui:msg>
- </div>
- <div ui:field='parentNotCurrentText'
- title='Not current - rebase possible'
- class='{style.parentNotCurrent}'
- style='display: none' aria-hidden='true'/>
- </th>
- <td>
- <g:FlowPanel ui:field='parentCommits'/>
- </td>
- <td>
- <g:FlowPanel ui:field='parentWebLinks' styleName='{style.parentWebLink}'/>
- </td>
- </tr>
- <tr>
- <th><ui:msg>Change-Id</ui:msg></th>
- <td><clippy:CopyableLabel styleName='{style.clippy}' ui:field='idText'/></td>
- </tr>
- </table>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java
deleted file mode 100644
index 9369c18e4f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileAction.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2015 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.client.change;
-
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-class DeleteFileAction {
- private final Project.NameKey project;
- private final Change.Id changeId;
- private final RevisionInfo revision;
- private final ChangeScreen.Style style;
- private final Widget deleteButton;
-
- private DeleteFileBox deleteBox;
- private PopupPanel popup;
-
- DeleteFileAction(
- Project.NameKey project,
- Change.Id changeId,
- RevisionInfo revision,
- ChangeScreen.Style style,
- Widget deleteButton) {
- this.project = project;
- this.changeId = changeId;
- this.revision = revision;
- this.style = style;
- this.deleteButton = deleteButton;
- }
-
- void onDelete() {
- if (popup != null) {
- popup.hide();
- return;
- }
-
- if (deleteBox == null) {
- deleteBox = new DeleteFileBox(project, changeId, revision);
- }
- deleteBox.clearPath();
-
- final PopupPanel p = new PopupPanel(true);
- p.setStyleName(style.replyBox());
- p.addAutoHidePartner(deleteButton.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (popup == p) {
- popup = null;
- }
- }
- });
- p.add(deleteBox);
- p.showRelativeTo(deleteButton);
- GlobalKey.dialog(p);
- deleteBox.setFocus(true);
- popup = p;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java
deleted file mode 100644
index 1885293d4e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2015 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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.changes.ChangeEditApi;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-class DeleteFileBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, DeleteFileBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private final Project.NameKey project;
- private final Change.Id changeId;
-
- @UiField Button delete;
- @UiField Button cancel;
-
- @UiField(provided = true)
- RemoteSuggestBox path;
-
- DeleteFileBox(Project.NameKey project, Change.Id changeId, RevisionInfo revision) {
- this.project = project;
- this.changeId = changeId;
-
- path = new RemoteSuggestBox(new PathSuggestOracle(project, changeId, revision));
- path.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- delete(event.getSelectedItem());
- }
- });
- path.addCloseHandler(
- new CloseHandler<RemoteSuggestBox>() {
- @Override
- public void onClose(CloseEvent<RemoteSuggestBox> event) {
- hide();
- }
- });
-
- initWidget(uiBinder.createAndBindUi(this));
- }
-
- void setFocus(boolean focus) {
- path.setFocus(focus);
- }
-
- void clearPath() {
- path.setText("");
- }
-
- @UiHandler("delete")
- void onDelete(@SuppressWarnings("unused") ClickEvent e) {
- delete(path.getText());
- }
-
- private void delete(String path) {
- hide();
- ChangeEditApi.delete(
- project.get(),
- changeId.get(),
- path,
- new AsyncCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- Gerrit.display(PageLinks.toChangeInEditMode(project, changeId));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- hide();
- }
-
- private void hide() {
- for (Widget w = getParent(); w != null; w = w.getParent()) {
- if (w instanceof PopupPanel) {
- ((PopupPanel) w).hide();
- break;
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml
deleted file mode 100644
index 9e79f75250..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DeleteFileBox.ui.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:u='urn:import:com.google.gerrit.client.ui'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- .cancel { float: right; }
- </ui:style>
- <g:HTMLPanel>
- <div class='{res.style.section}'>
- <ui:msg>Path: <u:RemoteSuggestBox ui:field='path' visibleLength='86'/></ui:msg>
- </div>
- <div class='{res.style.section}'>
- <g:Button ui:field='delete'
- title='Delete file from the repository'
- styleName='{res.style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Delete</ui:msg></div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java
deleted file mode 100644
index 8e4ea84601..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadAction.java
+++ /dev/null
@@ -1,41 +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.client.change;
-
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-
-class DownloadAction extends RightSidePopdownAction {
- private final DownloadBox downloadBox;
-
- DownloadAction(
- ChangeInfo info,
- String revision,
- ChangeScreen.Style style,
- UIObject relativeTo,
- Widget downloadButton) {
- super(style, relativeTo, downloadButton);
- this.downloadBox =
- new DownloadBox(
- info, revision, new PatchSet.Id(info.legacyId(), info.revision(revision)._number()));
- }
-
- @Override
- Widget getWidget() {
- return downloadBox;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
deleted file mode 100644
index 547f3d563e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/DownloadBox.java
+++ /dev/null
@@ -1,261 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeList;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.FetchInfo;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.List;
-
-class DownloadBox extends VerticalPanel {
- private final ChangeInfo change;
- private final String revision;
- private final PatchSet.Id psId;
- private final FlexTable commandTable;
- private final ListBox scheme;
- private NativeMap<FetchInfo> fetch;
-
- DownloadBox(ChangeInfo change, String revision, PatchSet.Id psId) {
- this.change = change;
- this.revision = revision;
- this.psId = psId;
- this.commandTable = new FlexTable();
- this.scheme = new ListBox();
- this.scheme.addChangeHandler(
- new ChangeHandler() {
- @Override
- public void onChange(ChangeEvent event) {
- renderCommands();
- if (Gerrit.isSignedIn()) {
- saveScheme();
- }
- }
- });
-
- setStyleName(Gerrit.RESOURCES.css().downloadBox());
- commandTable.setStyleName(Gerrit.RESOURCES.css().downloadBoxTable());
- scheme.setStyleName(Gerrit.RESOURCES.css().downloadBoxScheme());
- add(commandTable);
- }
-
- @Override
- protected void onLoad() {
- if (fetch == null) {
- if (psId.get() == 0) {
- ChangeApi.editWithCommands(change.project(), change.legacyId().get())
- .get(
- new AsyncCallback<EditInfo>() {
- @Override
- public void onSuccess(EditInfo result) {
- fetch = result.fetch();
- renderScheme();
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- } else {
- RestApi call = ChangeApi.detail(change.project(), change.legacyId().get());
- ChangeList.addOptions(
- call,
- EnumSet.of(
- revision.equals(change.currentRevision())
- ? ListChangesOption.CURRENT_REVISION
- : ListChangesOption.ALL_REVISIONS,
- ListChangesOption.DOWNLOAD_COMMANDS));
- call.get(
- new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- fetch = result.revision(revision).fetch();
- renderScheme();
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
- }
-
- private void renderCommands() {
- commandTable.removeAllRows();
-
- if (scheme.getItemCount() > 0) {
- FetchInfo fetchInfo = fetch.get(scheme.getValue(scheme.getSelectedIndex()));
- for (String commandName : fetchInfo.commands().sortedKeys()) {
- CopyableLabel copyLabel = new CopyableLabel(fetchInfo.command(commandName));
- copyLabel.setStyleName(Gerrit.RESOURCES.css().downloadBoxCopyLabel());
- insertCommand(commandName, copyLabel);
- }
- }
- if (change.revision(revision).commit().parents().length() == 1) {
- insertPatch();
- }
- insertArchive();
- insertCommand(null, scheme);
- }
-
- private void insertPatch() {
- String id = revision.substring(0, 7);
- Anchor patchBase64 = new Anchor(id + ".diff.base64");
- patchBase64.setHref(
- new RestApi("/changes/")
- .id(psId.getParentKey().get())
- .view("revisions")
- .id(revision)
- .view("patch")
- .addParameterTrue("download")
- .url());
-
- Anchor patchZip = new Anchor(id + ".diff.zip");
- patchZip.setHref(
- new RestApi("/changes/")
- .id(psId.getParentKey().get())
- .view("revisions")
- .id(revision)
- .view("patch")
- .addParameterTrue("zip")
- .url());
-
- HorizontalPanel p = new HorizontalPanel();
- p.add(patchBase64);
- InlineLabel spacer = new InlineLabel("|");
- spacer.setStyleName(Gerrit.RESOURCES.css().downloadBoxSpacer());
- p.add(spacer);
- p.add(patchZip);
- insertCommand("Patch-File", p);
- }
-
- private void insertArchive() {
- List<String> activated = Gerrit.info().download().archives();
- if (activated.isEmpty()) {
- return;
- }
-
- List<Anchor> anchors = new ArrayList<>(activated.size());
- for (String f : activated) {
- Anchor archive = new Anchor(f);
- archive.setHref(
- new RestApi("/changes/")
- .id(psId.getParentKey().get())
- .view("revisions")
- .id(revision)
- .view("archive")
- .addParameter("format", f)
- .url());
- anchors.add(archive);
- }
-
- HorizontalPanel p = new HorizontalPanel();
- Iterator<Anchor> it = anchors.iterator();
- while (it.hasNext()) {
- Anchor a = it.next();
- p.add(a);
- if (it.hasNext()) {
- InlineLabel spacer = new InlineLabel("|");
- spacer.setStyleName(Gerrit.RESOURCES.css().downloadBoxSpacer());
- p.add(spacer);
- }
- }
- insertCommand("Archive", p);
- }
-
- private void insertCommand(String commandName, Widget w) {
- int row = commandTable.getRowCount();
- commandTable.insertRow(row);
- commandTable
- .getCellFormatter()
- .addStyleName(row, 0, Gerrit.RESOURCES.css().downloadBoxTableCommandColumn());
- if (commandName != null) {
- commandTable.setText(row, 0, commandName);
- }
- if (w != null) {
- commandTable.setWidget(row, 1, w);
- }
- }
-
- private void renderScheme() {
- for (String id : fetch.sortedKeys()) {
- scheme.addItem(id);
- }
- if (scheme.getItemCount() == 0) {
- scheme.setVisible(false);
- } else {
- if (scheme.getItemCount() == 1) {
- scheme.setSelectedIndex(0);
- scheme.setVisible(false);
- } else {
- int select = 0;
- String find = Gerrit.getUserPreferences().downloadScheme();
- if (find != null) {
- for (int i = 0; i < scheme.getItemCount(); i++) {
- if (find.equals(scheme.getValue(i))) {
- select = i;
- break;
- }
- }
- }
- scheme.setSelectedIndex(select);
- }
- }
- renderCommands();
- }
-
- private void saveScheme() {
- String schemeStr = scheme.getValue(scheme.getSelectedIndex());
- GeneralPreferences prefs = Gerrit.getUserPreferences();
- if (Gerrit.isSignedIn() && !schemeStr.equals(prefs.downloadScheme())) {
- prefs.downloadScheme(schemeStr);
- GeneralPreferences in = GeneralPreferences.create();
- in.downloadScheme(schemeStr);
- AccountApi.self()
- .view("preferences")
- .put(
- in,
- new AsyncCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {}
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java
deleted file mode 100644
index f075c166d6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditActions.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2014 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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.ui.Button;
-
-public class EditActions {
-
- static void deleteEdit(Project.NameKey project, Change.Id id, Button... editButtons) {
- ChangeApi.deleteEdit(project.get(), id.get(), cs(project, id, editButtons));
- }
-
- static void publishEdit(Project.NameKey project, Change.Id id, Button... editButtons) {
- ChangeApi.publishEdit(project.get(), id.get(), cs(project, id, editButtons));
- }
-
- static void rebaseEdit(Project.NameKey project, Change.Id id, Button... editButtons) {
- ChangeApi.rebaseEdit(project.get(), id.get(), cs(project, id, editButtons));
- }
-
- public static GerritCallback<JavaScriptObject> cs(
- Project.NameKey project, final Change.Id id, Button... editButtons) {
- setEnabled(false, editButtons);
- return new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- Gerrit.display(PageLinks.toChange(project, id));
- }
-
- @Override
- public void onFailure(Throwable err) {
- setEnabled(true, editButtons);
- if (SubmitFailureDialog.isConflict(err)) {
- new SubmitFailureDialog(err.getMessage()).center();
- Gerrit.display(PageLinks.toChange(project, id));
- } else {
- super.onFailure(err);
- }
- }
- };
- }
-
- private static void setEnabled(boolean enabled, Button... editButtons) {
- if (editButtons != null) {
- for (Button b : editButtons) {
- b.setEnabled(enabled);
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java
deleted file mode 100644
index 083c824911..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.java
+++ /dev/null
@@ -1,57 +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.client.change;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import java.util.List;
-
-class FileComments extends Composite {
- interface Binder extends UiBinder<HTMLPanel, FileComments> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField InlineHyperlink path;
- @UiField FlowPanel comments;
-
- FileComments(
- CommentLinkProcessor clp,
- Project.NameKey project,
- PatchSet.Id defaultPs,
- String title,
- List<CommentInfo> list) {
- initWidget(uiBinder.createAndBindUi(this));
-
- path.setTargetHistoryToken(url(project, defaultPs, list.get(0)));
- path.setText(title);
- for (CommentInfo c : list) {
- comments.add(new LineComment(clp, project, defaultPs, c));
- }
- }
-
- private static String url(Project.NameKey project, PatchSet.Id ps, CommentInfo info) {
- return Dispatcher.toPatch(project, null, ps, info.path());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml
deleted file mode 100644
index e463e959f0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileComments.ui.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gerrit.client.ui'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false'>
- .box {
- }
- .path {
- display: block;
- white-space: nowrap;
- }
- .comments {
- margin-left: 1em;
- }
- </ui:style>
-
- <g:HTMLPanel styleName='{style.box}'>
- <c:InlineHyperlink styleName='{style.path}' ui:field='path'/>
- <g:FlowPanel styleName='{style.comments}' ui:field='comments'/>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
deleted file mode 100644
index 30554b694b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FileTable.java
+++ /dev/null
@@ -1,940 +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.client.change;
-
-import static com.google.gerrit.client.FormatUtil.formatAbsBytes;
-import static com.google.gerrit.client.FormatUtil.formatAbsPercentage;
-import static com.google.gerrit.client.FormatUtil.formatBytes;
-import static com.google.gerrit.client.FormatUtil.formatPercentage;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeEditApi;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.changes.ReviewInfo;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.FileInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.Side;
-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.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.RepeatingCommand;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.InputElement;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.EventListener;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.progress.client.ProgressBar;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.sql.Timestamp;
-
-public class FileTable extends FlowPanel {
- private static final FileTableResources R = GWT.create(FileTableResources.class);
-
- interface FileTableResources extends ClientBundle {
- @Source("file_table.css")
- FileTableCss css();
- }
-
- interface FileTableCss extends CssResource {
- String table();
-
- String nohover();
-
- String pointer();
-
- String reviewed();
-
- String status();
-
- String pathColumn();
-
- String commonPrefix();
-
- String renameCopySource();
-
- String draftColumn();
-
- String newColumn();
-
- String commentColumn();
-
- String deltaColumn1();
-
- String deltaColumn2();
-
- String inserted();
-
- String deleted();
-
- String restoreDelete();
-
- String error();
- }
-
- public enum Mode {
- REVIEW,
- EDIT
- }
-
- private static final String DELETE;
- private static final String RESTORE;
- private static final String REVIEWED;
- private static final String OPEN;
- private static final int C_PATH = 3;
- private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class);
-
- static {
- DELETE = DOM.createUniqueId().replace('-', '_');
- RESTORE = DOM.createUniqueId().replace('-', '_');
- REVIEWED = DOM.createUniqueId().replace('-', '_');
- OPEN = DOM.createUniqueId().replace('-', '_');
- init(DELETE, RESTORE, REVIEWED, OPEN);
- }
-
- private static native void init(String d, String t, String r, String o) /*-{
- $wnd[d] = $entry(function(e,i) {
- @com.google.gerrit.client.change.FileTable::onDelete(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
- });
- $wnd[t] = $entry(function(e,i) {
- @com.google.gerrit.client.change.FileTable::onRestore(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
- });
- $wnd[r] = $entry(function(e,i) {
- @com.google.gerrit.client.change.FileTable::onReviewed(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i)
- });
- $wnd[o] = $entry(function(e,i) {
- return @com.google.gerrit.client.change.FileTable::onOpen(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i);
- });
- }-*/;
-
- private static void onDelete(NativeEvent e, int idx) {
- MyTable t = getMyTable(e);
- if (t != null) {
- t.onDelete(idx);
- }
- }
-
- private static boolean onRestore(NativeEvent e, int idx) {
- MyTable t = getMyTable(e);
- if (t != null) {
- t.onRestore(idx);
- e.preventDefault();
- e.stopPropagation();
- return false;
- }
- return true;
- }
-
- private static void onReviewed(NativeEvent e, int idx) {
- MyTable t = getMyTable(e);
- if (t != null) {
- t.onReviewed(InputElement.as(Element.as(e.getEventTarget())), idx);
- }
- }
-
- private static boolean onOpen(NativeEvent e, int idx) {
- if (link.handleAsClick(e.<Event>cast())) {
- MyTable t = getMyTable(e);
- if (t != null) {
- t.onOpenRow(1 + idx);
- e.preventDefault();
- e.stopPropagation();
- return false;
- }
- }
- return true;
- }
-
- private static MyTable getMyTable(NativeEvent event) {
- Element e = event.getEventTarget().cast();
- for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
- EventListener l = DOM.getEventListener(e);
- if (l instanceof MyTable) {
- return (MyTable) l;
- }
- }
- return null;
- }
-
- private DiffObject base;
- private PatchSet.Id curr;
- private Project.NameKey project;
- private MyTable table;
- private boolean register;
- private JsArrayString reviewed;
- private String scrollToPath;
- private ChangeScreen.Style style;
- private Widget replyButton;
- private boolean editExists;
- private Mode mode;
-
- @Override
- protected void onLoad() {
- super.onLoad();
- R.css().ensureInjected();
- }
-
- public void set(
- DiffObject base,
- PatchSet.Id curr,
- Project.NameKey project,
- ChangeScreen.Style style,
- Widget replyButton,
- Mode mode,
- boolean editExists) {
- this.base = base;
- this.curr = curr;
- this.project = project;
- this.style = style;
- this.replyButton = replyButton;
- this.mode = mode;
- this.editExists = editExists;
- }
-
- void setValue(
- NativeMap<FileInfo> fileMap,
- Timestamp myLastReply,
- @Nullable NativeMap<JsArray<CommentInfo>> comments,
- @Nullable NativeMap<JsArray<CommentInfo>> drafts) {
- JsArray<FileInfo> list = fileMap.values();
- FileInfo.sortFileInfoByPath(list);
-
- DisplayCommand cmd = new DisplayCommand(fileMap, list, myLastReply, comments, drafts);
- if (cmd.execute()) {
- cmd.showProgressBar();
- Scheduler.get().scheduleIncremental(cmd);
- }
- }
-
- void showError(Throwable t) {
- clear();
- Label l = new Label(Resources.M.failedToLoadFileList(t.getMessage()));
- add(l);
- l.setStyleName(R.css().error());
- }
-
- void markReviewed(JsArrayString reviewed) {
- if (table != null) {
- table.markReviewed(reviewed);
- } else {
- this.reviewed = reviewed;
- }
- }
-
- void unregisterKeys() {
- register = false;
-
- if (table != null) {
- table.setRegisterKeys(false);
- }
- }
-
- void registerKeys() {
- register = true;
-
- if (table != null) {
- table.setRegisterKeys(true);
- }
- }
-
- void scrollToPath(String path) {
- if (table != null) {
- table.scrollToPath(path);
- } else {
- scrollToPath = path;
- }
- }
-
- void openAll() {
- if (table != null) {
- String self = Gerrit.selfRedirect(null);
- for (FileInfo info : Natives.asList(table.list)) {
- if (canOpen(info.path())) {
- Window.open(self + "#" + url(info), "_blank", null);
- }
- }
- }
- }
-
- private boolean canOpen(String path) {
- return mode != Mode.EDIT || !Patch.isMagic(path) || Patch.COMMIT_MSG.equals(path);
- }
-
- private void setTable(MyTable table) {
- clear();
- add(table);
- this.table = table;
-
- if (register) {
- table.setRegisterKeys(true);
- }
- if (reviewed != null) {
- table.markReviewed(reviewed);
- reviewed = null;
- }
- if (scrollToPath != null) {
- table.scrollToPath(scrollToPath);
- scrollToPath = null;
- }
- }
-
- private String url(FileInfo info) {
- return info.binary()
- ? Dispatcher.toUnified(project, base, curr, info.path())
- : mode == Mode.REVIEW
- ? Dispatcher.toPatch(project, base, curr, info.path())
- : Dispatcher.toEditScreen(project, curr, info.path());
- }
-
- private final class MyTable extends NavigationTable<FileInfo> {
- private final NativeMap<FileInfo> map;
- private final JsArray<FileInfo> list;
-
- MyTable(NativeMap<FileInfo> map, JsArray<FileInfo> list) {
- this.map = map;
- this.list = list;
- table.setWidth("");
-
- keysNavigation.add(
- new PrevKeyCommand(0, 'k', Util.C.patchTablePrev()),
- new NextKeyCommand(0, 'j', Util.C.patchTableNext()));
- keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.patchTableOpenDiff()));
- keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C.patchTableOpenDiff()));
-
- keysNavigation.add(
- new OpenFileCommand(list.length() - 1, 0, '[', Resources.C.openLastFile()),
- new OpenFileCommand(0, 0, ']', Resources.C.openCommitMessage()));
-
- keysAction.add(
- new KeyCommand(0, 'r', PatchUtil.C.toggleReviewed()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- int row = getCurrentRow();
- if (1 <= row && row <= MyTable.this.list.length()) {
- FileInfo info = MyTable.this.list.get(row - 1);
- InputElement b = getReviewed(info);
- boolean c = !b.isChecked();
- setReviewed(info, c);
- b.setChecked(c);
- }
- }
- });
-
- setSavePointerId((!base.isBase() ? base.asString() + ".." : "") + curr.toString());
- }
-
- void onDelete(int idx) {
- String path = list.get(idx).path();
- ChangeEditApi.delete(
- project.get(),
- curr.getParentKey().get(),
- path,
- new AsyncCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- Gerrit.display(PageLinks.toChangeInEditMode(project, curr.getParentKey()));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- void onRestore(int idx) {
- String path = list.get(idx).path();
- ChangeEditApi.restore(
- project.get(),
- curr.getParentKey().get(),
- path,
- new AsyncCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- Gerrit.display(PageLinks.toChangeInEditMode(project, curr.getParentKey()));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- void onReviewed(InputElement checkbox, int idx) {
- setReviewed(list.get(idx), checkbox.isChecked());
- }
-
- private void setReviewed(FileInfo info, boolean r) {
- RestApi api =
- ChangeApi.revision(project.get(), curr).view("files").id(info.path()).view("reviewed");
- if (r) {
- api.put(CallbackGroup.<ReviewInfo>emptyCallback());
- } else {
- api.delete(CallbackGroup.<ReviewInfo>emptyCallback());
- }
- }
-
- void markReviewed(JsArrayString reviewed) {
- for (int i = 0; i < reviewed.length(); i++) {
- FileInfo info = map.get(reviewed.get(i));
- if (info != null) {
- getReviewed(info).setChecked(true);
- }
- }
- }
-
- private InputElement getReviewed(FileInfo info) {
- CellFormatter fmt = table.getCellFormatter();
- Element e = fmt.getElement(1 + info._row(), 1);
- return InputElement.as(e.getFirstChildElement());
- }
-
- void scrollToPath(String path) {
- FileInfo info = map.get(path);
- if (info != null) {
- movePointerTo(1 + info._row(), true);
- }
- }
-
- @Override
- protected Object getRowItemKey(FileInfo item) {
- return item.path();
- }
-
- @Override
- protected int findRow(Object id) {
- FileInfo info = map.get((String) id);
- return info != null ? 1 + info._row() : -1;
- }
-
- @Override
- protected FileInfo getRowItem(int row) {
- if (1 <= row && row <= list.length()) {
- return list.get(row - 1);
- }
- return null;
- }
-
- @Override
- protected void onOpenRow(int row) {
- if (1 <= row && row <= list.length()) {
- FileInfo info = list.get(row - 1);
- if (canOpen(info.path())) {
- Gerrit.display(url(info));
- }
- }
- }
-
- @Override
- protected void onCellSingleClick(Event event, int row, int column) {
- if (column == C_PATH && link.handleAsClick(event)) {
- onOpenRow(row);
- } else {
- super.onCellSingleClick(event, row, column);
- }
- }
-
- private class OpenFileCommand extends KeyCommand {
- private final int index;
-
- OpenFileCommand(int index, int modifiers, char c, String helpText) {
- super(modifiers, c, helpText);
- this.index = index;
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- FileInfo info = list.get(index);
- if (canOpen(info.path())) {
- Gerrit.display(url(info));
- }
- }
- }
- }
-
- private final class DisplayCommand implements RepeatingCommand {
- private final SafeHtmlBuilder sb = new SafeHtmlBuilder();
- private final MyTable myTable;
- private final JsArray<FileInfo> list;
- private final Timestamp myLastReply;
- private final NativeMap<JsArray<CommentInfo>> comments;
- private final NativeMap<JsArray<CommentInfo>> drafts;
- private final boolean hasUser;
- private final boolean showChangeSizeBars;
- private boolean attached;
- private int row;
- private double start;
- private ProgressBar meter;
- private String lastPath = "";
-
- private boolean hasBinaryFile;
- private boolean hasNonBinaryFile;
- private int inserted;
- private int deleted;
- private long binOldSize;
- private long bytesInserted;
- private long bytesDeleted;
-
- private DisplayCommand(
- NativeMap<FileInfo> map,
- JsArray<FileInfo> list,
- Timestamp myLastReply,
- @Nullable NativeMap<JsArray<CommentInfo>> comments,
- @Nullable NativeMap<JsArray<CommentInfo>> drafts) {
- this.myTable = new MyTable(map, list);
- this.list = list;
- this.myLastReply = myLastReply;
- this.comments = comments;
- this.drafts = drafts;
- this.hasUser = Gerrit.isSignedIn();
- this.showChangeSizeBars = Gerrit.getUserPreferences().sizeBarInChangeTable();
- myTable.addStyleName(R.css().table());
- }
-
- @Override
- public boolean execute() {
- boolean attachedNow = isAttached();
- if (!attached && attachedNow) {
- // Remember that we have been attached at least once. If
- // later we find we aren't attached we should stop running.
- attached = true;
- } else if (attached && !attachedNow) {
- // If the user navigated away, we aren't in the DOM anymore.
- // Don't continue to render.
- return false;
- }
-
- start = System.currentTimeMillis();
- if (row == 0) {
- header(sb);
- computeInsertedDeleted();
- }
- while (row < list.length()) {
- FileInfo info = list.get(row);
- info._row(row);
- render(sb, info);
- if ((++row % 10) == 0 && longRunning()) {
- updateMeter();
- return true;
- }
- }
- footer(sb);
- myTable.resetHtml(sb);
- myTable.finishDisplay();
- setTable(myTable);
- return false;
- }
-
- private void computeInsertedDeleted() {
- inserted = 0;
- deleted = 0;
- binOldSize = 0;
- bytesInserted = 0;
- bytesDeleted = 0;
- for (int i = 0; i < list.length(); i++) {
- FileInfo info = list.get(i);
- if (!Patch.isMagic(info.path())) {
- if (!info.binary()) {
- hasNonBinaryFile = true;
- inserted += info.linesInserted();
- deleted += info.linesDeleted();
- } else {
- hasBinaryFile = true;
- binOldSize += info.size() - info.sizeDelta();
- if (info.sizeDelta() >= 0) {
- bytesInserted += info.sizeDelta();
- } else {
- bytesDeleted += info.sizeDelta();
- }
- }
- }
- }
- }
-
- void showProgressBar() {
- if (meter == null) {
- meter = new ProgressBar(Util.M.loadingPatchSet(curr.get()));
- FileTable.this.clear();
- FileTable.this.add(meter);
- }
- updateMeter();
- }
-
- void updateMeter() {
- if (meter != null) {
- int n = list.length();
- meter.setValue((100 * row) / n);
- }
- }
-
- private boolean longRunning() {
- return System.currentTimeMillis() - start > 200;
- }
-
- private void header(SafeHtmlBuilder sb) {
- sb.openTr().setStyleName(R.css().nohover());
- sb.openTh().setStyleName(R.css().pointer()).closeTh();
- if (mode == Mode.REVIEW) {
- sb.openTh().setStyleName(R.css().reviewed()).closeTh();
- } else {
- sb.openTh().setStyleName(R.css().restoreDelete()).closeTh();
- }
- sb.openTh().setStyleName(R.css().status()).closeTh();
- sb.openTh().append(Util.C.patchTableColumnName()).closeTh();
- sb.openTh().setAttribute("colspan", 3).append(Util.C.patchTableColumnComments()).closeTh();
- sb.openTh().setAttribute("colspan", 2).append(Util.C.patchTableColumnSize()).closeTh();
- sb.closeTr();
- }
-
- private void render(SafeHtmlBuilder sb, FileInfo info) {
- sb.openTr();
- sb.openTd().setStyleName(R.css().pointer()).closeTd();
- if (mode == Mode.REVIEW) {
- columnReviewed(sb, info);
- } else {
- columnDeleteRestore(sb, info);
- }
- columnStatus(sb, info);
- columnPath(sb, info);
- columnComments(sb, info);
- columnDelta1(sb, info);
- columnDelta2(sb, info);
- sb.closeTr();
- }
-
- private void columnReviewed(SafeHtmlBuilder sb, FileInfo info) {
- sb.openTd().setStyleName(R.css().reviewed());
- if (hasUser) {
- sb.openElement("input")
- .setAttribute("title", Resources.C.reviewedFileTitle())
- .setAttribute("type", "checkbox")
- .setAttribute("onclick", REVIEWED + "(event," + info._row() + ")")
- .closeSelf();
- }
- sb.closeTd();
- }
-
- private void columnDeleteRestore(SafeHtmlBuilder sb, FileInfo info) {
- sb.openTd().setStyleName(R.css().restoreDelete());
- if (hasUser) {
- if (!Patch.isMagic(info.path())) {
- boolean editable = isEditable(info);
- sb.openDiv()
- .openElement("button")
- .setAttribute("title", Resources.C.restoreFileInline())
- .setAttribute("onclick", RESTORE + "(event," + info._row() + ")")
- .append(new ImageResourceRenderer().render(Gerrit.RESOURCES.editUndo()))
- .closeElement("button");
- if (editable) {
- sb.openElement("button")
- .setAttribute("title", Resources.C.removeFileInline())
- .setAttribute("onclick", DELETE + "(event," + info._row() + ")")
- .append(new ImageResourceRenderer().render(Gerrit.RESOURCES.redNot()))
- .closeElement("button");
- }
- sb.closeDiv();
- }
- }
- sb.closeTd();
- }
-
- private boolean isEditable(FileInfo info) {
- String status = info.status();
- return status == null || !ChangeType.DELETED.matches(status);
- }
-
- private void columnStatus(SafeHtmlBuilder sb, FileInfo info) {
- sb.openTd().setStyleName(R.css().status());
- if (!Patch.isMagic(info.path())
- && info.status() != null
- && !ChangeType.MODIFIED.matches(info.status())) {
- sb.append(info.status());
- }
- sb.closeTd();
- }
-
- private void columnPath(SafeHtmlBuilder sb, FileInfo info) {
- String path = info.path();
-
- sb.openTd().setStyleName(R.css().pathColumn());
-
- if (!canOpen(path)) {
- sb.openDiv();
- appendPath(path);
- sb.closeDiv();
- sb.closeTd();
- return;
- }
-
- sb.openAnchor();
-
- if (mode == Mode.EDIT && !isEditable(info)) {
- sb.setAttribute("onclick", RESTORE + "(event," + info._row() + ")");
- } else {
- sb.setAttribute("href", "#" + url(info))
- .setAttribute("onclick", OPEN + "(event," + info._row() + ")");
- }
- appendPath(path);
- sb.closeAnchor();
- if (info.oldPath() != null) {
- sb.br();
- sb.openSpan().setStyleName(R.css().renameCopySource()).append(info.oldPath()).closeSpan();
- }
- sb.closeTd();
- }
-
- private void appendPath(String path) {
- if (Patch.COMMIT_MSG.equals(path)) {
- sb.append(Util.C.commitMessage());
- } else if (Patch.MERGE_LIST.equals(path)) {
- sb.append(Util.C.mergeList());
- } else if (Gerrit.getUserPreferences().muteCommonPathPrefixes()) {
- int commonPrefixLen = commonPrefix(path);
- if (commonPrefixLen > 0) {
- sb.openSpan()
- .setStyleName(R.css().commonPrefix())
- .append(path.substring(0, commonPrefixLen))
- .closeSpan();
- }
- sb.append(path.substring(commonPrefixLen));
- lastPath = path;
- } else {
- sb.append(path);
- }
- }
-
- private int commonPrefix(String path) {
- for (int n = path.length(); n > 0; ) {
- int s = path.lastIndexOf('/', n);
- if (s < 0) {
- return 0;
- }
-
- String p = path.substring(0, s + 1);
- if (lastPath.startsWith(p)) {
- return s + 1;
- }
- n = s - 1;
- }
- return 0;
- }
-
- private void columnComments(SafeHtmlBuilder sb, FileInfo info) {
- JsArray<CommentInfo> cList = filterForParent(get(info.path(), comments));
- JsArray<CommentInfo> dList = filterForParent(get(info.path(), drafts));
-
- sb.openTd().setStyleName(R.css().draftColumn());
- if (dList.length() > 0) {
- sb.append("drafts: ").append(dList.length());
- }
- sb.closeTd();
-
- int cntAll = cList.length();
- int cntNew = 0;
- if (myLastReply != null) {
- for (int i = cntAll - 1; i >= 0; i--) {
- CommentInfo m = cList.get(i);
- if (m.updated().compareTo(myLastReply) > 0) {
- cntNew++;
- } else {
- break;
- }
- }
- }
-
- sb.openTd().setStyleName(R.css().newColumn());
- if (cntNew > 0) {
- sb.append("new: ").append(cntNew);
- }
- sb.closeTd();
-
- sb.openTd().setStyleName(R.css().commentColumn());
- if (cntAll - cntNew > 0) {
- sb.append("comments: ").append(cntAll - cntNew);
- }
- sb.closeTd();
- }
-
- private JsArray<CommentInfo> filterForParent(JsArray<CommentInfo> list) {
- JsArray<CommentInfo> result = JsArray.createArray().cast();
- for (CommentInfo c : Natives.asList(list)) {
- if (c.side() == Side.REVISION) {
- result.push(c);
- } else if (base.isBaseOrAutoMerge() && !c.hasParent()) {
- result.push(c);
- } else if (base.isParent() && c.parent() == base.getParentNum()) {
- result.push(c);
- }
- }
- return result;
- }
-
- private JsArray<CommentInfo> get(String p, NativeMap<JsArray<CommentInfo>> m) {
- JsArray<CommentInfo> r = null;
- if (m != null) {
- r = m.get(p);
- }
- if (r == null) {
- r = JsArray.createArray().cast();
- }
- return r;
- }
-
- private void columnDelta1(SafeHtmlBuilder sb, FileInfo info) {
- sb.openTd().setStyleName(R.css().deltaColumn1());
- if (!Patch.isMagic(info.path()) && !info.binary()) {
- if (showChangeSizeBars) {
- sb.append(info.linesInserted() + info.linesDeleted());
- } else if (!ChangeType.DELETED.matches(info.status())) {
- if (ChangeType.ADDED.matches(info.status())) {
- sb.append(info.linesInserted()).append(" lines");
- } else {
- sb.append("+").append(info.linesInserted()).append(", -").append(info.linesDeleted());
- }
- }
- } else if (info.binary()) {
- sb.append(formatBytes(info.sizeDelta()));
- long oldSize = info.size() - info.sizeDelta();
- if (oldSize != 0) {
- sb.append(" (").append(formatPercentage(oldSize, info.sizeDelta())).append(")");
- }
- }
- sb.closeTd();
- }
-
- private void columnDelta2(SafeHtmlBuilder sb, FileInfo info) {
- sb.openTd().setStyleName(R.css().deltaColumn2());
- if (showChangeSizeBars
- && !Patch.isMagic(info.path())
- && !info.binary()
- && (info.linesInserted() != 0 || info.linesDeleted() != 0)) {
- int w = 80;
- int t = inserted + deleted;
- int i = Math.max(5, (int) (((double) w) * info.linesInserted() / t));
- int d = Math.max(5, (int) (((double) w) * info.linesDeleted() / t));
-
- sb.setAttribute(
- "title", Util.M.patchTableSize_LongModify(info.linesInserted(), info.linesDeleted()));
-
- if (0 < info.linesInserted()) {
- sb.openDiv()
- .setStyleName(R.css().inserted())
- .setAttribute("style", "width:" + i + "px")
- .closeDiv();
- }
- if (0 < info.linesDeleted()) {
- sb.openDiv()
- .setStyleName(R.css().deleted())
- .setAttribute("style", "width:" + d + "px")
- .closeDiv();
- }
- }
- sb.closeTd();
- }
-
- private void footer(SafeHtmlBuilder sb) {
- sb.openTr().setStyleName(R.css().nohover());
- sb.openTh().setStyleName(R.css().pointer()).closeTh();
- if (mode == Mode.REVIEW) {
- sb.openTh().setStyleName(R.css().reviewed()).closeTh();
- } else {
- sb.openTh().setStyleName(R.css().restoreDelete()).closeTh();
- }
- sb.openTh().setStyleName(R.css().status()).closeTh();
- sb.openTd().closeTd(); // path
- sb.openTd().setAttribute("colspan", 3).closeTd(); // comments
-
- // delta1
- sb.openTh().setStyleName(R.css().deltaColumn1());
- if (hasNonBinaryFile) {
- sb.append(Util.M.patchTableSize_Modify(inserted, deleted));
- }
- if (hasBinaryFile) {
- if (hasNonBinaryFile) {
- sb.br();
- }
- if (binOldSize != 0) {
- sb.append(
- Util.M.patchTableSize_ModifyBinaryFilesWithPercentages(
- formatAbsBytes(bytesInserted),
- formatAbsPercentage(binOldSize, bytesInserted),
- formatAbsBytes(bytesDeleted),
- formatAbsPercentage(binOldSize, bytesDeleted)));
- } else {
- sb.append(
- Util.M.patchTableSize_ModifyBinaryFiles(
- formatAbsBytes(bytesInserted), formatAbsBytes(bytesDeleted)));
- }
- }
- sb.closeTh();
-
- // delta2
- sb.openTh().setStyleName(R.css().deltaColumn2());
- if (showChangeSizeBars) {
- int w = 80;
- int t = inserted + deleted;
- int i = Math.max(1, (int) (((double) w) * inserted / t));
- int d = Math.max(1, (int) (((double) w) * deleted / t));
- if (i + d > w && i > d) {
- i = w - d;
- } else if (i + d > w && d > i) {
- d = w - i;
- }
- if (0 < inserted) {
- sb.openDiv()
- .setStyleName(R.css().inserted())
- .setAttribute("style", "width:" + i + "px")
- .closeDiv();
- }
- if (0 < deleted) {
- sb.openDiv()
- .setStyleName(R.css().deleted())
- .setAttribute("style", "width:" + d + "px")
- .closeDiv();
- }
- }
- sb.closeTh();
-
- sb.closeTr();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
deleted file mode 100644
index a4c90b8fdc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/FollowUpAction.java
+++ /dev/null
@@ -1,54 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.user.client.ui.Button;
-
-class FollowUpAction extends ActionMessageBox {
- private final String project;
- private final String branch;
- private final String topic;
- private final String base;
-
- FollowUpAction(Button b, String project, String branch, String topic, String key) {
- super(b);
- this.project = project;
- this.branch = branch;
- this.topic = topic;
- this.base = project + "~" + branch + "~" + key;
- }
-
- @Override
- void send(String message) {
- ChangeApi.createChange(
- project,
- branch,
- topic,
- message,
- base,
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- Gerrit.display(PageLinks.toChange(result.projectNameKey(), result.legacyId()));
- hide();
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java
deleted file mode 100644
index 1044828e5e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.java
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright (C) 2014 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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.Iterator;
-
-public class Hashtags extends Composite {
-
- interface Binder extends UiBinder<HTMLPanel, Hashtags> {}
-
- private static final int VISIBLE_LENGTH = 55;
- private static final Binder uiBinder = GWT.create(Binder.class);
- private static final String REMOVE;
- private static final String DATA_ID = "data-id";
-
- private PatchSet.Id psId;
- private boolean canEdit;
-
- static {
- REMOVE = DOM.createUniqueId().replace('-', '_');
- init(REMOVE);
- }
-
- private static native void init(String r) /*-{
- $wnd[r] = $entry(function(e) {
- @com.google.gerrit.client.change.Hashtags::onRemove(Lcom/google/gwt/dom/client/NativeEvent;)(e)
- });
- }-*/;
-
- private static void onRemove(NativeEvent event) {
- String hashtags = getDataId(event);
- if (hashtags != null) {
- final ChangeScreen screen = ChangeScreen.get(event);
- final PatchSet.Id psId = screen.getPatchSetId();
- ChangeApi.hashtags(screen.getProject().get(), psId.getParentKey().get())
- .post(
- PostInput.create(null, hashtags),
- new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- if (screen.isCurrentView()) {
- Gerrit.display(PageLinks.toChange(screen.getProject(), psId));
- }
- }
- });
- }
- }
-
- private static String getDataId(NativeEvent event) {
- Element e = event.getEventTarget().cast();
- while (e != null) {
- String v = e.getAttribute(DATA_ID);
- if (!v.isEmpty()) {
- return v;
- }
- e = e.getParentElement();
- }
- return null;
- }
-
- @UiField Element hashtagsText;
- @UiField Image addHashtagIcon;
- @UiField Element form;
- @UiField Element error;
- @UiField NpTextBox hashtagTextBox;
-
- private ChangeScreen.Style style;
- private Change.Id changeId;
- private Project.NameKey project;
-
- public Hashtags() {
-
- initWidget(uiBinder.createAndBindUi(this));
-
- hashtagTextBox.setVisibleLength(VISIBLE_LENGTH);
- hashtagTextBox.addKeyDownHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent e) {
- if (e.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
- onCancel(null);
- } else if (e.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
- onAdd(null);
- }
- }
- });
-
- addHashtagIcon.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- onOpenForm();
- }
- },
- ClickEvent.getType());
- }
-
- void init(ChangeScreen.Style style) {
- this.style = style;
- }
-
- void set(ChangeInfo info, String revision) {
- psId = new PatchSet.Id(info.legacyId(), info.revisions().get(revision)._number());
- project = info.projectNameKey();
-
- canEdit = info.hasActions() && info.actions().containsKey("hashtags");
- this.changeId = info.legacyId();
- display(info);
- addHashtagIcon.setVisible(canEdit);
- }
-
- void onOpenForm() {
- UIObject.setVisible(form, true);
- UIObject.setVisible(error, false);
- addHashtagIcon.setVisible(false);
- hashtagTextBox.setFocus(true);
- }
-
- private void display(ChangeInfo info) {
- hashtagsText.setInnerSafeHtml(formatHashtags(info));
- }
-
- private void display(JsArrayString hashtags) {
- hashtagsText.setInnerSafeHtml(formatHashtags(hashtags));
- }
-
- private SafeHtmlBuilder formatHashtags(ChangeInfo info) {
- if (info.hashtags() != null) {
- return formatHashtags(info.hashtags());
- }
- return new SafeHtmlBuilder();
- }
-
- private SafeHtmlBuilder formatHashtags(JsArrayString hashtags) {
- SafeHtmlBuilder html = new SafeHtmlBuilder();
- Iterator<String> itr = Natives.asList(hashtags).iterator();
- while (itr.hasNext()) {
- String hashtagName = itr.next();
- html.openSpan()
- .setAttribute(DATA_ID, hashtagName)
- .setStyleName(style.hashtagName())
- .openAnchor()
- .setAttribute("href", "#" + PageLinks.toChangeQuery("hashtag:\"" + hashtagName + "\""))
- .setAttribute("role", "listitem")
- .openSpan()
- .setStyleName(style.hashtagIcon())
- .append(new ImageResourceRenderer().render(Gerrit.RESOURCES.hashtag()))
- .closeSpan()
- .append(" ")
- .append(hashtagName)
- .closeAnchor();
- if (canEdit) {
- html.openElement("button")
- .setAttribute("title", "Remove hashtag")
- .setAttribute("onclick", REMOVE + "(event)")
- .append("×")
- .closeElement("button");
- }
- html.closeSpan();
- if (itr.hasNext()) {
- html.append(' ');
- }
- }
- return html;
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- addHashtagIcon.setVisible(true);
- UIObject.setVisible(form, false);
- hashtagTextBox.setFocus(false);
- }
-
- @UiHandler("add")
- void onAdd(@SuppressWarnings("unused") ClickEvent e) {
- String hashtag = hashtagTextBox.getText();
- if (!hashtag.isEmpty()) {
- addHashtag(hashtag);
- }
- }
-
- private void addHashtag(String hashtags) {
- ChangeApi.hashtags(project.get(), changeId.get())
- .post(
- PostInput.create(hashtags, null),
- new GerritCallback<JsArrayString>() {
- @Override
- public void onSuccess(JsArrayString result) {
- Gerrit.display(
- PageLinks.toChange(project, psId.getParentKey(), String.valueOf(psId.get())));
- }
-
- @Override
- public void onFailure(Throwable err) {
- UIObject.setVisible(error, true);
- error.setInnerText(
- err instanceof StatusCodeException
- ? ((StatusCodeException) err).getEncodedResponse()
- : err.getMessage());
- hashtagTextBox.setEnabled(true);
- }
- });
- }
-
- public static class PostInput extends JavaScriptObject {
- public static PostInput create(String add, String remove) {
- PostInput input = createObject().cast();
- input.init(toJsArrayString(add), toJsArrayString(remove));
- return input;
- }
-
- private static JsArrayString toJsArrayString(String commaSeparated) {
- if (commaSeparated == null || commaSeparated.equals("")) {
- return null;
- }
- JsArrayString array = JsArrayString.createArray().cast();
- for (String hashtag : commaSeparated.split(",")) {
- array.push(hashtag.trim());
- }
- return array;
- }
-
- private native void init(JsArrayString add, JsArrayString remove) /*-{
- this.add = add;
- this.remove = remove;
- }-*/;
-
- protected PostInput() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml
deleted file mode 100644
index c0bfd1cd7b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Hashtags.ui.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- button.openAdd {
- margin: 3px 3px 0 0;
- float: right;
- color: rgba(0, 0, 0, 0.15);
- background-color: #f5f5f5;
- background-image: -webkit-linear-gradient(top, #f5f5f5, #f1f1f1);
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
- border-radius: 2px;
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- }
- button.openAdd div {
- width: auto;
- color: #444;
- }
-
- .hashtagTextBox {
- margin-bottom: 2px;
- }
-
- .error {
- color: #D33D3D;
- font-weight: bold;
- }
-
- .addHashtag,
- .cancel {
- cursor: pointer;
- float: right;
- }
- </ui:style>
- <g:HTMLPanel>
- <div>
- <span ui:field='hashtagsText'/>
- <g:Image ui:field='addHashtagIcon'
- resource='{ico.addHashtag}'
- styleName='{style.addHashtag}'
- title='Add Hashtag'/>
- </div>
- <div ui:field='form' style='display: none' aria-hidden='true'>
- <c:NpTextBox ui:field='hashtagTextBox' styleName='{style.hashtagTextBox}'/>
- <div ui:field='error'
- class='{style.error}'
- style='display: none' aria-hidden='true'/>
- <div>
- <g:Button ui:field='add' styleName='{res.style.button}'>
- <div>Add</div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </div>
- </g:HTMLPanel>
- </ui:UiBinder> \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
deleted file mode 100644
index 55e021f68d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/History.java
+++ /dev/null
@@ -1,141 +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.client.change;
-
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-class History extends FlowPanel {
- private CommentLinkProcessor clp;
- private ReplyAction replyAction;
- private Change.Id changeId;
- private Project.NameKey project;
-
- private final Map<Integer, List<CommentInfo>> byAuthor = new HashMap<>();
-
- void set(CommentLinkProcessor clp, ReplyAction ra, Change.Id id, ChangeInfo info) {
- this.clp = clp;
- this.replyAction = ra;
- this.changeId = id;
- this.project = info.projectNameKey();
-
- JsArray<MessageInfo> messages = info.messages();
- if (messages != null) {
- for (MessageInfo msg : Natives.asList(messages)) {
- Message ui = new Message(this, msg);
- ui.addComments(comments(msg));
- add(ui);
- }
- autoOpen(ChangeScreen.myLastReply(info));
- }
- }
-
- private void autoOpen(Timestamp lastReply) {
- if (lastReply == null) {
- for (Widget child : getChildren()) {
- ((Message) child).autoOpen();
- }
- } else {
- for (int i = getChildren().size() - 1; i >= 0; i--) {
- Message ui = (Message) getChildren().get(i);
- MessageInfo msg = ui.getMessageInfo();
- if (lastReply.compareTo(msg.date()) < 0) {
- ui.autoOpen();
- } else {
- break;
- }
- }
- }
- }
-
- CommentLinkProcessor getCommentLinkProcessor() {
- return clp;
- }
-
- Change.Id getChangeId() {
- return changeId;
- }
-
- Project.NameKey getProject() {
- return project;
- }
-
- void replyTo(MessageInfo info) {
- replyAction.onReply(info);
- }
-
- void addComments(NativeMap<JsArray<CommentInfo>> map) {
- for (String path : map.keySet()) {
- for (CommentInfo c : Natives.asList(map.get(path))) {
- c.path(path);
- if (c.author() != null) {
- int authorId = c.author()._accountId();
- List<CommentInfo> l = byAuthor.get(authorId);
- if (l == null) {
- l = new ArrayList<>();
- byAuthor.put(authorId, l);
- }
- l.add(c);
- }
- }
- }
- }
-
- private List<CommentInfo> comments(MessageInfo msg) {
- if (msg.author() == null) {
- return Collections.emptyList();
- }
-
- int authorId = msg.author()._accountId();
- List<CommentInfo> list = byAuthor.get(authorId);
- if (list == null) {
- return Collections.emptyList();
- }
-
- Timestamp when = msg.date();
- List<CommentInfo> match = new ArrayList<>();
- List<CommentInfo> other = new ArrayList<>();
- for (CommentInfo c : list) {
- if (c.updated().compareTo(when) <= 0) {
- match.add(c);
- } else {
- other.add(c);
- }
- }
- if (match.isEmpty()) {
- return Collections.emptyList();
- } else if (other.isEmpty()) {
- byAuthor.remove(authorId);
- } else {
- byAuthor.put(authorId, other);
- }
- return match;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInAction.java
deleted file mode 100644
index 5557f909b0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInAction.java
+++ /dev/null
@@ -1,39 +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.client.change;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-
-class IncludedInAction extends RightSidePopdownAction {
- private final IncludedInBox includedInBox;
-
- IncludedInAction(
- Project.NameKey project,
- Change.Id changeId,
- ChangeScreen.Style style,
- UIObject relativeTo,
- Widget includedInButton) {
- super(style, relativeTo, includedInButton);
- this.includedInBox = new IncludedInBox(project, changeId);
- }
-
- @Override
- Widget getWidget() {
- return includedInBox;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.java
deleted file mode 100644
index 9751f54717..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.java
+++ /dev/null
@@ -1,106 +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.client.change;
-
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo.IncludedInInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.TableCellElement;
-import com.google.gwt.dom.client.TableElement;
-import com.google.gwt.dom.client.TableRowElement;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.safehtml.shared.SafeHtml;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-class IncludedInBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, IncludedInBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String includedInElement();
- }
-
- private final Project.NameKey project;
- private final Change.Id changeId;
- private boolean loaded;
-
- @UiField Style style;
- @UiField TableElement table;
- @UiField Element branches;
- @UiField Element tags;
-
- IncludedInBox(Project.NameKey project, Change.Id changeId) {
- this.project = project;
- this.changeId = changeId;
- initWidget(uiBinder.createAndBindUi(this));
- }
-
- @Override
- protected void onLoad() {
- if (!loaded) {
- ChangeApi.includedIn(
- project.get(),
- changeId.get(),
- new AsyncCallback<IncludedInInfo>() {
- @Override
- public void onSuccess(IncludedInInfo r) {
- branches.setInnerSafeHtml(formatList(r.branches()));
- tags.setInnerSafeHtml(formatList(r.tags()));
- for (String n : r.externalNames()) {
- JsArrayString external = r.external(n);
- if (external.length() > 0) {
- appendRow(n, external);
- }
- }
- loaded = true;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
-
- private SafeHtml formatList(JsArrayString l) {
- SafeHtmlBuilder html = new SafeHtmlBuilder();
- int size = l.length();
- for (int i = 0; i < size; i++) {
- html.openSpan().addStyleName(style.includedInElement()).append(l.get(i)).closeSpan();
- if (i < size - 1) {
- html.append(", ");
- }
- }
- return html;
- }
-
- private void appendRow(String title, JsArrayString l) {
- TableRowElement row = table.insertRow(-1);
- TableCellElement th = Document.get().createTHElement();
- th.setInnerText(title);
- row.appendChild(th);
- row.insertCell(-1).setInnerSafeHtml(formatList(l));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml
deleted file mode 100644
index 36ac7344af..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/IncludedInBox.ui.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false' type='com.google.gerrit.client.change.IncludedInBox.Style'>
- .includedInBox {
- min-width: 300px;
- max-width: 580px;
- margin: 5px;
- }
-
- .includedInTable {
- border-spacing: 0;
- }
-
- .includedInTable th {
- width: 60px;
- color: #444;
- font-weight: normal;
- vertical-align: top;
- text-align: left;
- padding-right: 5px;
- }
-
- .includedInElement {
- font-size: smaller;
- font-family: monospace;
- }
-
- .includedInElement span {
- width: 500px;
- white-space: nowrap;
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- .includedInElement .gwt-TextBox {
- padding: 0;
- margin: 0;
- border: 0;
- max-height: 18px;
- width: 500px;
- }
-
- .includedInElement div {
- float: right;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.includedInBox}'>
- <table class='{style.includedInTable}' ui:field='table'>
- <tr>
- <th><ui:msg>Branches</ui:msg></th>
- <td ui:field='branches'/>
- </tr>
- <tr>
- <th><ui:msg>Tags</ui:msg></th>
- <td ui:field='tags'/>
- </tr>
- </table>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
deleted file mode 100644
index 267a983ebc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
+++ /dev/null
@@ -1,353 +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.client.change;
-
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toCollection;
-import static java.util.stream.Collectors.toList;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.AccountInfo.AvatarInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.ApprovalInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Displays a table of label and reviewer scores. */
-class Labels extends Grid {
- private static final String DATA_ID = "data-id";
- private static final String DATA_VOTE = "data-vote";
- private static final String REMOVE_REVIEWER;
- private static final String REMOVE_VOTE;
-
- static {
- REMOVE_REVIEWER = DOM.createUniqueId().replace('-', '_');
- REMOVE_VOTE = DOM.createUniqueId().replace('-', '_');
- init(REMOVE_REVIEWER, REMOVE_VOTE);
- }
-
- private static native void init(String r, String v) /*-{
- $wnd[r] = $entry(function(e) {
- @com.google.gerrit.client.change.Labels::onRemoveReviewer(Lcom/google/gwt/dom/client/NativeEvent;)(e)
- });
- $wnd[v] = $entry(function(e) {
- @com.google.gerrit.client.change.Labels::onRemoveVote(Lcom/google/gwt/dom/client/NativeEvent;)(e)
- });
- }-*/;
-
- private static void onRemoveReviewer(NativeEvent event) {
- Integer user = getDataId(event);
- if (user != null) {
- final ChangeScreen screen = ChangeScreen.get(event);
- final Change.Id changeId = screen.getPatchSetId().getParentKey();
- ChangeApi.reviewer(screen.getProject().get(), changeId.get(), user)
- .delete(
- new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- if (screen.isCurrentView()) {
- Gerrit.display(PageLinks.toChange(screen.getProject(), changeId));
- }
- }
- });
- }
- }
-
- private static void onRemoveVote(NativeEvent event) {
- Integer user = getDataId(event);
- String vote = getVoteId(event);
- if (user != null && vote != null) {
- final ChangeScreen screen = ChangeScreen.get(event);
- final Change.Id changeId = screen.getPatchSetId().getParentKey();
- ChangeApi.vote(screen.getProject().get(), changeId.get(), user, vote)
- .delete(
- new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- if (screen.isCurrentView()) {
- Gerrit.display(PageLinks.toChange(screen.getProject(), changeId));
- }
- }
- });
- }
- }
-
- private static Integer getDataId(NativeEvent event) {
- Element e = event.getEventTarget().cast();
- while (e != null) {
- String v = e.getAttribute(DATA_ID);
- if (!v.isEmpty()) {
- return Integer.parseInt(v);
- }
- e = e.getParentElement();
- }
- return null;
- }
-
- private static String getVoteId(NativeEvent event) {
- Element e = event.getEventTarget().cast();
- while (e != null) {
- String v = e.getAttribute(DATA_VOTE);
- if (!v.isEmpty()) {
- return v;
- }
- e = e.getParentElement();
- }
- return null;
- }
-
- private ChangeScreen.Style style;
-
- void init(ChangeScreen.Style style) {
- this.style = style;
- }
-
- void set(ChangeInfo info) {
- List<String> names =
- info.labels().stream()
- .sorted()
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
- Set<Integer> removable = info.removableReviewerIds();
-
- resize(names.size(), 2);
-
- for (int row = 0; row < names.size(); row++) {
- String name = names.get(row);
- LabelInfo label = info.label(name);
- setText(row, 0, name);
- if (label.all() != null) {
- setWidget(row, 1, renderUsers(label, removable));
- }
- getCellFormatter().setStyleName(row, 0, style.labelName());
- getCellFormatter().addStyleName(row, 0, getStyleForLabel(label));
- }
- }
-
- private Widget renderUsers(LabelInfo label, Set<Integer> removable) {
- Map<Integer, List<ApprovalInfo>> m = new HashMap<>(4);
- int approved = 0;
- int rejected = 0;
-
- for (ApprovalInfo ai : Natives.asList(label.all())) {
- if (ai.value() != 0) {
- List<ApprovalInfo> l = m.get(Integer.valueOf(ai.value()));
- if (l == null) {
- l = new ArrayList<>(label.all().length());
- m.put(Integer.valueOf(ai.value()), l);
- }
- l.add(ai);
-
- if (isRejected(label, ai)) {
- rejected = ai.value();
- } else if (isApproved(label, ai)) {
- approved = ai.value();
- }
- }
- }
-
- SafeHtmlBuilder html = new SafeHtmlBuilder();
- for (Integer v : sort(m.keySet(), approved, rejected)) {
- if (!html.isEmpty()) {
- html.br();
- }
-
- String val = LabelValue.formatValue(v.shortValue());
- html.openSpan();
- html.setAttribute("title", label.valueText(val));
- if (v.intValue() == approved) {
- html.setStyleName(style.label_ok());
- } else if (v.intValue() == rejected) {
- html.setStyleName(style.label_reject());
- }
- html.append(val).append(" ");
- html.append(formatUserList(style, m.get(v), removable, label.name(), null));
- html.closeSpan();
- }
- return html.toBlockWidget();
- }
-
- private static List<Integer> sort(Set<Integer> keySet, int a, int b) {
- List<Integer> r = keySet.stream().sorted().collect(toCollection(ArrayList::new));
- if (keySet.contains(a)) {
- r.remove(Integer.valueOf(a));
- r.add(0, a);
- } else if (keySet.contains(b)) {
- r.remove(Integer.valueOf(b));
- r.add(0, b);
- }
- return r;
- }
-
- private static boolean isApproved(LabelInfo label, ApprovalInfo ai) {
- return label.approved() != null && label.approved()._accountId() == ai._accountId();
- }
-
- private static boolean isRejected(LabelInfo label, ApprovalInfo ai) {
- return label.rejected() != null && label.rejected()._accountId() == ai._accountId();
- }
-
- private String getStyleForLabel(LabelInfo label) {
- switch (label.status()) {
- case OK:
- return style.label_ok();
- case NEED:
- return style.label_need();
- case REJECT:
- case IMPOSSIBLE:
- return style.label_reject();
- default:
- case MAY:
- return style.label_may();
- }
- }
-
- static SafeHtml formatUserList(
- ChangeScreen.Style style,
- Collection<? extends AccountInfo> in,
- Set<Integer> removable,
- String label,
- Map<Integer, VotableInfo> votable) {
- List<AccountInfo> users =
- in.stream()
- .sorted(
- new Comparator<AccountInfo>() {
- @Override
- public int compare(AccountInfo a, AccountInfo b) {
- String as = name(a);
- String bs = name(b);
- if (as.isEmpty()) {
- return 1;
- } else if (bs.isEmpty()) {
- return -1;
- }
- return as.compareTo(bs);
- }
-
- private String name(AccountInfo a) {
- if (a.name() != null) {
- return a.name();
- } else if (a.email() != null) {
- return a.email();
- }
- return "";
- }
- })
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
-
- SafeHtmlBuilder html = new SafeHtmlBuilder();
- Iterator<? extends AccountInfo> itr = users.iterator();
- while (itr.hasNext()) {
- AccountInfo ai = itr.next();
- AvatarInfo img = ai.avatar(AvatarInfo.DEFAULT_SIZE);
- String name;
- if (ai.name() != null) {
- name = ai.name();
- } else if (ai.email() != null) {
- name = ai.email();
- } else {
- name = Integer.toString(ai._accountId());
- }
-
- String votableCategories = "";
- if (votable != null) {
- VotableInfo vi = votable.get(ai._accountId());
- if (vi != null) {
- Set<String> s = vi.votableLabels();
- if (!s.isEmpty()) {
- StringBuilder sb = new StringBuilder(Util.C.votable());
- sb.append(" ");
- for (Iterator<String> it = vi.votableLabels().iterator(); it.hasNext(); ) {
- sb.append(it.next());
- if (it.hasNext()) {
- sb.append(", ");
- }
- }
- votableCategories = sb.toString();
- }
- }
- }
- html.openSpan()
- .setAttribute("role", "listitem")
- .setAttribute(DATA_ID, ai._accountId())
- .setAttribute("title", getTitle(ai, votableCategories))
- .setStyleName(style.label_user());
- if (label != null) {
- html.setAttribute(DATA_VOTE, label);
- }
- if (img != null) {
- html.openElement("img").setStyleName(style.avatar()).setAttribute("src", img.url());
- if (img.width() > 0) {
- html.setAttribute("width", img.width());
- }
- if (img.height() > 0) {
- html.setAttribute("height", img.height());
- }
- html.closeSelf();
- }
- html.append(name);
- if (removable.contains(ai._accountId())) {
- html.openElement("button");
- if (label != null) {
- html.setAttribute("title", Util.M.removeVote(label))
- .setAttribute("onclick", REMOVE_VOTE + "(event)");
- } else {
- html.setAttribute("title", Util.M.removeReviewer(name))
- .setAttribute("onclick", REMOVE_REVIEWER + "(event)");
- }
- html.append("×").closeElement("button");
- }
- html.closeSpan();
- if (itr.hasNext()) {
- html.append(' ');
- }
- }
- return html;
- }
-
- private static String getTitle(AccountInfo ai, String votableCategories) {
- String title = ai.email() != null ? ai.email() : "";
- if (!votableCategories.isEmpty()) {
- if (!title.isEmpty()) {
- title += " ";
- }
- title += votableCategories;
- }
- return title;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java
deleted file mode 100644
index 5a0cc59dda..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.java
+++ /dev/null
@@ -1,100 +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.client.change;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.api.ApiGlue;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.diff.DisplaySide;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.extensions.client.Side;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-class LineComment extends Composite {
- interface Binder extends UiBinder<HTMLPanel, LineComment> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField Element sideLoc;
- @UiField Element psLoc;
- @UiField Element psNum;
- @UiField Element fileLoc;
- @UiField Element lineLoc;
- @UiField InlineHyperlink line;
- @UiField Element message;
-
- LineComment(
- CommentLinkProcessor clp, Project.NameKey project, PatchSet.Id defaultPs, CommentInfo info) {
- initWidget(uiBinder.createAndBindUi(this));
-
- PatchSet.Id ps;
- if (info.patchSet() != defaultPs.get()) {
- ps = new PatchSet.Id(defaultPs.getParentKey(), info.patchSet());
- psNum.setInnerText(Integer.toString(info.patchSet()));
- sideLoc.removeFromParent();
- sideLoc = null;
- } else if (info.side() == Side.PARENT) {
- ps = defaultPs;
- psLoc.removeFromParent();
- psLoc = null;
- psNum = null;
- } else {
- ps = defaultPs;
- sideLoc.removeFromParent();
- sideLoc = null;
- psLoc.removeFromParent();
- psLoc = null;
- psNum = null;
- }
-
- if (info.hasLine()) {
- fileLoc.removeFromParent();
- fileLoc = null;
-
- line.setTargetHistoryToken(url(project, ps, info));
- line.setText(Integer.toString(info.line()));
-
- } else {
- lineLoc.removeFromParent();
- lineLoc = null;
- line = null;
- }
-
- if (info.message() != null) {
- message.setInnerSafeHtml(
- clp.apply(new SafeHtmlBuilder().append(info.message().trim()).wikify()));
- ApiGlue.fireEvent("comment", message);
- }
- }
-
- private static String url(Project.NameKey project, PatchSet.Id ps, CommentInfo info) {
- return Dispatcher.toPatch(
- project,
- null,
- ps,
- info.path(),
- info.side() == Side.PARENT ? DisplaySide.A : DisplaySide.B,
- info.line());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml
deleted file mode 100644
index f33ba51ea4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LineComment.ui.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gerrit.client.ui'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false'>
- .box {
- position: relative;
- }
- .location {
- position: absolute;
- top: 0;
- left: 0;
- font-weight: bold;
- }
- .message {
- margin-left: 135px;
- }
- </ui:style>
-
- <g:HTMLPanel styleName='{style.box}'>
- <div class='{style.location}'>
- <span ui:field='sideLoc'><ui:msg>Base, </ui:msg></span>
- <span ui:field='psLoc'><ui:msg>PS<span ui:field='psNum'/>, </ui:msg></span>
- <span ui:field='fileLoc'><ui:msg>File Comment</ui:msg></span>
- <span ui:field='lineLoc'><ui:msg>Line <c:InlineHyperlink ui:field='line'/>:</ui:msg></span>
- </div>
- <div class='{style.message}' ui:field='message'/>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LocalComments.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LocalComments.java
deleted file mode 100644
index d2f031ad88..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/LocalComments.java
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright (C) 2015 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.client.change;
-
-import com.google.gerrit.client.changes.CommentApi;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.diff.CommentRange;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.Side;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.storage.client.Storage;
-import com.google.gwt.user.client.Cookies;
-import java.util.ArrayList;
-import java.util.Collection;
-
-public class LocalComments {
- @Nullable private final Project.NameKey project;
- private final Change.Id changeId;
- private final PatchSet.Id psId;
- private final StorageBackend storage;
-
- private static class InlineComment {
- @Nullable final Project.NameKey project;
- final PatchSet.Id psId;
- final CommentInfo commentInfo;
-
- InlineComment(@Nullable Project.NameKey project, PatchSet.Id psId, CommentInfo commentInfo) {
- this.project = project;
- this.psId = psId;
- this.commentInfo = commentInfo;
- }
- }
-
- private static class StorageBackend {
- private final Storage storageBackend;
-
- StorageBackend() {
- storageBackend =
- (Storage.isLocalStorageSupported())
- ? Storage.getLocalStorageIfSupported()
- : Storage.getSessionStorageIfSupported();
- }
-
- String getItem(String key) {
- if (storageBackend == null) {
- return Cookies.getCookie(key);
- }
- return storageBackend.getItem(key);
- }
-
- void setItem(String key, String value) {
- if (storageBackend == null) {
- Cookies.setCookie(key, value);
- return;
- }
- storageBackend.setItem(key, value);
- }
-
- void removeItem(String key) {
- if (storageBackend == null) {
- Cookies.removeCookie(key);
- return;
- }
- storageBackend.removeItem(key);
- }
-
- Collection<String> getKeys() {
- if (storageBackend == null) {
- return Cookies.getCookieNames();
- }
- ArrayList<String> result = new ArrayList<>(storageBackend.getLength());
- for (int i = 0; i < storageBackend.getLength(); i++) {
- result.add(storageBackend.key(i));
- }
- return result;
- }
- }
-
- public LocalComments(@Nullable Project.NameKey project, Change.Id changeId) {
- this.project = project;
- this.changeId = changeId;
- this.psId = null;
- this.storage = new StorageBackend();
- }
-
- public LocalComments(@Nullable Project.NameKey project, PatchSet.Id psId) {
- this.project = project;
- this.changeId = psId.getParentKey();
- this.psId = psId;
- this.storage = new StorageBackend();
- }
-
- public String getReplyComment() {
- String comment = storage.getItem(getReplyCommentName());
- storage.removeItem(getReplyCommentName());
- return comment;
- }
-
- public void setReplyComment(String comment) {
- storage.setItem(getReplyCommentName(), comment.trim());
- }
-
- public boolean hasReplyComment() {
- return storage.getKeys().contains(getReplyCommentName());
- }
-
- public void removeReplyComment() {
- if (hasReplyComment()) {
- storage.removeItem(getReplyCommentName());
- }
- }
-
- private String getReplyCommentName() {
- return "savedReplyComment~" + PageLinks.toChangeId(project, changeId);
- }
-
- public static void saveInlineComments() {
- final StorageBackend storage = new StorageBackend();
- for (String cookie : storage.getKeys()) {
- if (isInlineComment(cookie)) {
- InlineComment input = getInlineComment(cookie);
- if (input.commentInfo.id() == null) {
- CommentApi.createDraft(
- Project.NameKey.asStringOrNull(input.project),
- input.psId,
- input.commentInfo,
- new GerritCallback<CommentInfo>() {
- @Override
- public void onSuccess(CommentInfo result) {
- storage.removeItem(cookie);
- }
- });
- } else {
- CommentApi.updateDraft(
- Project.NameKey.asStringOrNull(input.project),
- input.psId,
- input.commentInfo.id(),
- input.commentInfo,
- new GerritCallback<CommentInfo>() {
- @Override
- public void onSuccess(CommentInfo result) {
- storage.removeItem(cookie);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (RestApi.isNotFound(caught)) {
- // the draft comment, that was supposed to be updated,
- // was deleted in the meantime
- storage.removeItem(cookie);
- } else {
- super.onFailure(caught);
- }
- }
- });
- }
- }
- }
- }
-
- public void setInlineComment(CommentInfo comment) {
- String name = getInlineCommentName(comment);
- if (name == null) {
- // Failed to get the store key -- so we can't continue.
- return;
- }
- storage.setItem(name, comment.message().trim());
- }
-
- public boolean hasInlineComments() {
- for (String cookie : storage.getKeys()) {
- if (isInlineComment(cookie)) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean isInlineComment(String key) {
- return key.startsWith("patchCommentEdit~")
- || key.startsWith("patchReply~")
- || key.startsWith("patchComment~");
- }
-
- private static InlineComment getInlineComment(String key) {
- String path;
- Side side;
- int line;
- CommentRange range;
- StorageBackend storage = new StorageBackend();
-
- String[] elements = key.split("~");
- int offset = 1;
- if (key.startsWith("patchReply~") || key.startsWith("patchCommentEdit~")) {
- offset = 2;
- }
- ProjectChangeId id = ProjectChangeId.create(elements[offset + 0]);
- PatchSet.Id psId = new PatchSet.Id(id.getChangeId(), Integer.parseInt(elements[offset + 1]));
- path = atob(elements[offset + 2]);
- side = (Side.PARENT.toString().equals(elements[offset + 3])) ? Side.PARENT : Side.REVISION;
- range = null;
- if (elements[offset + 4].startsWith("R")) {
- String rangeStart = elements[offset + 4].substring(1);
- String rangeEnd = elements[offset + 5];
- String[] split = rangeStart.split(",");
- int sl = Integer.parseInt(split[0]);
- int sc = Integer.parseInt(split[1]);
- split = rangeEnd.split(",");
- int el = Integer.parseInt(split[0]);
- int ec = Integer.parseInt(split[1]);
- range = CommentRange.create(sl, sc, el, ec);
- line = sl;
- } else {
- line = Integer.parseInt(elements[offset + 4]);
- }
- CommentInfo info = CommentInfo.create(path, side, line, range, false);
- info.message(storage.getItem(key));
- if (key.startsWith("patchReply~")) {
- info.inReplyTo(elements[1]);
- } else if (key.startsWith("patchCommentEdit~")) {
- info.id(elements[1]);
- }
- InlineComment inlineComment = new InlineComment(id.getProject(), psId, info);
- return inlineComment;
- }
-
- private String getInlineCommentName(CommentInfo comment) {
- if (psId == null) {
- return null;
- }
- String result = "patchComment~";
- if (comment.id() != null) {
- result = "patchCommentEdit~" + comment.id() + "~";
- } else if (comment.inReplyTo() != null) {
- result = "patchReply~" + comment.inReplyTo() + "~";
- }
-
- result += PageLinks.toChangeId(project, changeId);
- result += "~" + psId.getId() + "~" + btoa(comment.path()) + "~" + comment.side() + "~";
- if (comment.hasRange()) {
- result +=
- "R"
- + comment.range().startLine()
- + ","
- + comment.range().startCharacter()
- + "~"
- + comment.range().endLine()
- + ","
- + comment.range().endCharacter();
- } else {
- result += comment.line();
- }
- return result;
- }
-
- private static native String btoa(String a) /*-{ return btoa(a); }-*/;
-
- private static native String atob(String b) /*-{ return atob(b); }-*/;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java
deleted file mode 100644
index cadaf97955..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.java
+++ /dev/null
@@ -1,209 +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.client.change;
-
-import com.google.gerrit.client.AvatarImage;
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.api.ApiGlue;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Visibility;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-class Message extends Composite {
- interface Binder extends UiBinder<HTMLPanel, Message> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String closed();
- }
-
- @UiField Style style;
- @UiField HTMLPanel header;
- @UiField Element name;
- @UiField Element summary;
- @UiField Element date;
- @UiField Button reply;
- @UiField Element message;
- @UiField FlowPanel comments;
-
- private final History history;
- private final MessageInfo info;
- private List<CommentInfo> commentList;
- private boolean autoOpen;
-
- @UiField(provided = true)
- AvatarImage avatar;
-
- Message(History parent, MessageInfo info) {
- if (info.author() != null) {
- avatar = new AvatarImage(info.author());
- avatar.setSize("", "");
- } else {
- avatar = new AvatarImage();
- }
-
- initWidget(uiBinder.createAndBindUi(this));
- header.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- setOpen(!isOpen());
- }
- },
- ClickEvent.getType());
-
- this.history = parent;
- this.info = info;
-
- setName(false);
- date.setInnerText(FormatUtil.shortFormatDayTime(info.date()));
- if (info.message() != null) {
- String msg = info.message().trim();
- summary.setInnerText(msg);
- message.setInnerSafeHtml(
- history.getCommentLinkProcessor().apply(new SafeHtmlBuilder().append(msg).wikify()));
- ApiGlue.fireEvent("comment", message);
- } else {
- reply.getElement().getStyle().setVisibility(Visibility.HIDDEN);
- }
- }
-
- @UiHandler("reply")
- void onReply(ClickEvent e) {
- e.stopPropagation();
-
- if (Gerrit.isSignedIn()) {
- history.replyTo(info);
- } else {
- Gerrit.doSignIn(com.google.gwt.user.client.History.getToken());
- }
- }
-
- MessageInfo getMessageInfo() {
- return info;
- }
-
- private boolean isOpen() {
- return UIObject.isVisible(message);
- }
-
- void setOpen(boolean open) {
- if (open && info._revisionNumber() > 0 && !commentList.isEmpty()) {
- renderComments(commentList);
- commentList = Collections.emptyList();
- }
- setName(open);
-
- UIObject.setVisible(summary, !open);
- UIObject.setVisible(message, open);
- comments.setVisible(open && comments.getWidgetCount() > 0);
- if (open) {
- removeStyleName(style.closed());
- } else {
- addStyleName(style.closed());
- }
- }
-
- private void setName(boolean open) {
- name.setInnerText(
- open ? authorName(info) : com.google.gerrit.common.FormatUtil.elide(authorName(info), 20));
- }
-
- void autoOpen() {
- if (commentList == null) {
- autoOpen = true;
- } else if (!commentList.isEmpty()) {
- setOpen(true);
- }
- }
-
- void addComments(List<CommentInfo> list) {
- if (isOpen()) {
- renderComments(list);
- comments.setVisible(comments.getWidgetCount() > 0);
- commentList = Collections.emptyList();
- } else {
- commentList = list;
- if (autoOpen && !commentList.isEmpty()) {
- setOpen(true);
- }
- }
- }
-
- private void renderComments(List<CommentInfo> list) {
- CommentLinkProcessor clp = history.getCommentLinkProcessor();
- PatchSet.Id ps = new PatchSet.Id(history.getChangeId(), info._revisionNumber());
- TreeMap<String, List<CommentInfo>> m = byPath(list);
- List<CommentInfo> l = m.remove(Patch.COMMIT_MSG);
- if (l != null) {
- comments.add(new FileComments(clp, history.getProject(), ps, Util.C.commitMessage(), l));
- }
- l = m.remove(Patch.MERGE_LIST);
- if (l != null) {
- comments.add(new FileComments(clp, history.getProject(), ps, Util.C.mergeList(), l));
- }
- for (Map.Entry<String, List<CommentInfo>> e : m.entrySet()) {
- comments.add(new FileComments(clp, history.getProject(), ps, e.getKey(), e.getValue()));
- }
- }
-
- private static TreeMap<String, List<CommentInfo>> byPath(List<CommentInfo> list) {
- TreeMap<String, List<CommentInfo>> m = new TreeMap<>();
- for (CommentInfo c : list) {
- List<CommentInfo> l = m.get(c.path());
- if (l == null) {
- l = new ArrayList<>();
- m.put(c.path(), l);
- }
- l.add(c);
- }
- return m;
- }
-
- static String authorName(MessageInfo info) {
- if (info.author() != null) {
- if (info.author().name() != null) {
- return info.author().name();
- }
- return Gerrit.info().user().anonymousCowardName();
- }
- return Util.C.messageNoAuthor();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml
deleted file mode 100644
index e362c07fa5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Message.ui.xml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gerrit.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false' type='com.google.gerrit.client.change.Message.Style'>
- .messageBox {
- position: relative;
- width: 1168px;
- padding: 2px 0 2px 0;
- border-left: 1px solid #e3e9ff;
- border-right: 1px solid #e3e9ff;
- border-bottom: 1px solid #e3e9ff;
- -webkit-border-bottom-left-radius: 8px;
- -webkit-border-bottom-right-radius: 8px;
- }
-
- .header {
- cursor: pointer;
- }
-
- .avatar {
- position: absolute;
- width: 26px;
- height: 26px;
- }
- .closed .avatar {
- position: absolute;
- top: 0;
- left: -1px;
- width: 20px;
- height: 20px;
- }
-
- .contents {
- margin-left: 28px;
- position: relative;
- }
-
- .contents p,
- .contents blockquote {
- -webkit-margin-before: 0;
- -webkit-margin-after: 0.3em;
- white-space: pre-wrap;
- }
-
- .name {
- white-space: nowrap;
- font-weight: bold;
- }
- .closed .name {
- width: 150px;
- overflow: hidden;
- font-weight: normal;
- }
-
- .summary {
- color: #777;
- position: absolute;
- top: 0;
- left: 150px;
- width: 880px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .date {
- white-space: nowrap;
- position: absolute;
- top: 0;
- right: 18px;
- }
-
- .reply {
- position: absolute;
- top: 0;
- right: 1px;
- cursor: pointer;
- outline: none;
- border: none;
- background: transparent;
- margin: 0;
- padding: 0;
- line-height: 15px;
- font-family: Arial Unicode MS, sans-serif;
- font-size: 18px;
- }
- .closed .reply {
- visibility: hidden;
- }
- .comment {
- }
- </ui:style>
-
- <g:HTMLPanel
- styleName='{style.messageBox}'
- addStyleNames='{style.closed}'>
- <c:AvatarImage ui:field='avatar' styleName='{style.avatar}'/>
- <div class='{style.contents}'>
- <g:HTMLPanel ui:field='header' styleName='{style.header}'>
- <div class='{style.name}' ui:field='name'/>
- <div ui:field='summary' class='{style.summary}'/>
- <div class='{style.date}' ui:field='date'/>
- <g:Button styleName='{style.reply}'
- ui:field='reply'
- title='Reply to this message'>
- <ui:attribute name='title'/>
- <div>&#x21a9;</div>
- </g:Button>
- </g:HTMLPanel>
- <div style='overflow: auto'>
- <div ui:field='message'
- aria-hidden='true'
- style='display: NONE'
- styleName='{style.comment}'/>
- <g:FlowPanel ui:field='comments' visible='false'/>
- </div>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java
deleted file mode 100644
index e3e9525e8a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/MoveAction.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.MoveDialog;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.PopupPanel;
-
-class MoveAction {
- static void call(Button b, ChangeInfo info, Project.NameKey project) {
- b.setEnabled(false);
- new MoveDialog(project) {
- {
- sendButton.setText(Util.C.moveChangeSend());
- }
-
- @Override
- public void onSend() {
- ChangeApi.move(
- info.project(),
- info.legacyId().get(),
- getDestinationBranch(),
- getMessageText(),
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- sent = true;
- hide();
- Gerrit.display(PageLinks.toChange(project, result.legacyId()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableButtons(true);
- super.onFailure(caught);
- }
- });
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- super.onClose(event);
- b.setEnabled(true);
- }
- }.center();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsAction.java
deleted file mode 100644
index faf2516294..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsAction.java
+++ /dev/null
@@ -1,42 +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.client.change;
-
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-
-class PatchSetsAction extends RightSidePopdownAction {
- private final PatchSetsBox revisionBox;
-
- PatchSetsAction(
- Project.NameKey project,
- Change.Id changeId,
- String revision,
- EditInfo edit,
- ChangeScreen.Style style,
- UIObject relativeTo,
- Widget downloadButton) {
- super(style, relativeTo, downloadButton);
- this.revisionBox = new PatchSetsBox(project, changeId, revision, edit);
- }
-
- @Override
- Widget getWidget() {
- return revisionBox;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
deleted file mode 100644
index 35cab4e2fb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.java
+++ /dev/null
@@ -1,233 +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.client.change;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeList;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.FancyFlexTableImpl;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.EventListener;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.Collections;
-import java.util.EnumSet;
-
-class PatchSetsBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, PatchSetsBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private static final String OPEN;
- private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class);
-
- static {
- OPEN = DOM.createUniqueId().replace('-', '_');
- init(OPEN);
- }
-
- private static native void init(String o) /*-{
- $wnd[o] = $entry(function(e,i) {
- return @com.google.gerrit.client.change.PatchSetsBox::onOpen(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i);
- });
- }-*/;
-
- private static boolean onOpen(NativeEvent e, int idx) {
- if (link.handleAsClick(e.<Event>cast())) {
- PatchSetsBox t = getRevisionBox(e);
- if (t != null) {
- t.onOpenRow(idx);
- e.preventDefault();
- return false;
- }
- }
- return true;
- }
-
- private static PatchSetsBox getRevisionBox(NativeEvent event) {
- Element e = event.getEventTarget().cast();
- for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
- EventListener l = DOM.getEventListener(e);
- if (l instanceof PatchSetsBox) {
- return (PatchSetsBox) l;
- }
- }
- return null;
- }
-
- interface Style extends CssResource {
- String current();
-
- String legacy_id();
-
- String commit();
-
- String draft_comment();
- }
-
- private final Change.Id changeId;
- private final Project.NameKey project;
- private final String revision;
- private final EditInfo edit;
- private boolean loaded;
- private JsArray<RevisionInfo> revisions;
-
- @UiField FlexTable table;
- @UiField Style style;
-
- PatchSetsBox(Project.NameKey project, Change.Id changeId, String revision, EditInfo edit) {
- this.project = project;
- this.changeId = changeId;
- this.revision = revision;
- this.edit = edit;
- initWidget(uiBinder.createAndBindUi(this));
- }
-
- @Override
- protected void onLoad() {
- if (!loaded) {
- RestApi call = ChangeApi.detail(project.get(), changeId.get());
- ChangeList.addOptions(
- call, EnumSet.of(ListChangesOption.ALL_COMMITS, ListChangesOption.ALL_REVISIONS));
- call.get(
- new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- if (edit != null) {
- edit.setName(edit.commit().commit());
- result.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
- }
- render(result.revisions());
- loaded = true;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
-
- private void onOpenRow(int idx) {
- closeParent();
- Gerrit.display(url(revisions.get(idx)));
- }
-
- private void render(NativeMap<RevisionInfo> map) {
- map.copyKeysIntoChildren("name");
-
- revisions = map.values();
- RevisionInfo.sortRevisionInfoByNumber(revisions);
- Collections.reverse(Natives.asList(revisions));
-
- SafeHtmlBuilder sb = new SafeHtmlBuilder();
- header(sb);
- for (int i = 0; i < revisions.length(); i++) {
- revision(sb, i, revisions.get(i));
- }
-
- GWT.<FancyFlexTableImpl>create(FancyFlexTableImpl.class).resetHtml(table, sb);
- }
-
- private void header(SafeHtmlBuilder sb) {
- sb.openTr()
- .openTh()
- .setStyleName(style.legacy_id())
- .append(Resources.C.patchSet())
- .closeTh()
- .openTh()
- .append(Resources.C.commit())
- .closeTh()
- .openTh()
- .append(Resources.C.date())
- .closeTh()
- .openTh()
- .append(Resources.C.author())
- .closeTh()
- .closeTr();
- }
-
- private void revision(SafeHtmlBuilder sb, int index, RevisionInfo r) {
- CommitInfo c = r.commit();
- sb.openTr();
- if (revision.equals(r.name())) {
- sb.setStyleName(style.current());
- }
-
- sb.openTd().setStyleName(style.legacy_id());
- sb.append(r.id());
- sb.closeTd();
-
- sb.openTd()
- .setStyleName(style.commit())
- .openAnchor()
- .setAttribute("href", "#" + url(r))
- .setAttribute("onclick", OPEN + "(event," + index + ")")
- .append(r.name().substring(0, 10))
- .closeAnchor()
- .closeTd();
-
- sb.openTd().append(FormatUtil.shortFormatDayTime(c.committer().date())).closeTd();
-
- String an = c.author() != null ? c.author().name() : "";
- String cn = c.committer() != null ? c.committer().name() : "";
- sb.openTd();
- sb.append(an);
- if (!"".equals(an) && !"".equals(cn) && !an.equals(cn)) {
- sb.append(" / ").append(cn);
- }
- sb.closeTd();
-
- sb.closeTr();
- }
-
- private String url(RevisionInfo r) {
- return PageLinks.toChange(project, changeId, r.id());
- }
-
- private void closeParent() {
- for (Widget w = getParent(); w != null; w = w.getParent()) {
- if (w instanceof PopupPanel) {
- ((PopupPanel) w).hide(true);
- break;
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml
deleted file mode 100644
index 7537aa4756..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PatchSetsBox.ui.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.change.PatchSetsBox.Style'>
- @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-
- .revisionBox {
- min-width: 300px;
- margin: 10px 0px 5px 5px;
- }
-
- .scroll {
- min-width: 300px;
- height: 200px;
- }
-
- .table {
- border-spacing: 0;
- width: 100%;
- }
-
- .table td, .table th {
- padding-left: 5px;
- padding-right: 5px;
- border-right: 2px solid #ddd;
- white-space: nowrap;
- }
-
- .table tr.current {
- background-color: selectionColor;
- }
- .table tr.current a {
- pointer-events: none;
- color: #000;
- }
-
- .legacy_id {
- min-width: 50px;
- text-align: right;
- font-weight: bold;
- }
-
- .commit {
- font-family: monospace;
- }
-
- .draft_comment {
- margin: 0 2px 0 0;
- width: 16px;
- height: 16px;
- vertical-align: bottom;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.revisionBox}'>
- <g:ScrollPanel styleName='{style.scroll}'>
- <g:FlexTable ui:field='table' styleName='{style.table}'/>
- </g:ScrollPanel>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PathSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PathSuggestOracle.java
deleted file mode 100644
index 7668f0f982..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/PathSuggestOracle.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2015 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.client.change;
-
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-class PathSuggestOracle extends HighlightSuggestOracle {
-
- private final Project.NameKey project;
- private final Change.Id changeId;
- private final RevisionInfo revision;
-
- PathSuggestOracle(Project.NameKey project, Change.Id changeId, RevisionInfo revision) {
- this.project = project;
- this.changeId = changeId;
- this.revision = revision;
- }
-
- @Override
- protected void onRequestSuggestions(Request req, Callback cb) {
- RestApi api = ChangeApi.revision(project.get(), changeId.get(), revision.name()).view("files");
- if (req.getQuery() != null) {
- api.addParameter("q", req.getQuery() == null ? "" : req.getQuery());
- }
- api.background()
- .get(
- new AsyncCallback<JsArrayString>() {
- @Override
- public void onSuccess(JsArrayString result) {
- List<Suggestion> r = new ArrayList<>();
- for (String path : Natives.asList(result)) {
- r.add(new PathSuggestion(path));
- }
- cb.onSuggestionsReady(req, new Response(r));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- List<Suggestion> none = Collections.emptyList();
- cb.onSuggestionsReady(req, new Response(none));
- }
- });
- }
-
- private static class PathSuggestion implements Suggestion {
- private final String path;
-
- PathSuggestion(String path) {
- this.path = path;
- }
-
- @Override
- public String getDisplayString() {
- return path;
- }
-
- @Override
- public String getReplacementString() {
- return path;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ProjectChangeId.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ProjectChangeId.java
deleted file mode 100644
index 684867be2e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ProjectChangeId.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// 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.client.change;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import java.util.Objects;
-
-/** Provides logic for parsing a numeric change id and project from a URL. */
-public class ProjectChangeId {
-
- /** Parses a {@link ProjectChangeId} from it's string representation. */
- public static ProjectChangeId create(String token) {
- String mutableToken = token;
- // Try parsing /c/project/+/numericChangeId where token is project/+/numericChangeId
- int delimiter = mutableToken.indexOf(PageLinks.PROJECT_CHANGE_DELIMITER);
- Project.NameKey project = null;
- if (delimiter > 0) {
- project = new Project.NameKey(token.substring(0, delimiter));
- mutableToken =
- mutableToken.substring(delimiter + PageLinks.PROJECT_CHANGE_DELIMITER.length());
- }
-
- // Try parsing /c/numericChangeId where token is numericChangeId
- int s = mutableToken.indexOf('/');
- if (s > 0) {
- mutableToken = mutableToken.substring(0, s);
- }
- // Special case: project/+/1233,edit/
- s = mutableToken.indexOf(",edit");
- if (s > 0) {
- mutableToken = mutableToken.substring(0, s);
- }
- Integer cId = tryParse(mutableToken);
- if (cId != null) {
- return new ProjectChangeId(project, new Change.Id(cId));
- }
-
- throw new IllegalArgumentException(token + " is not a valid change identifier");
- }
-
- @Nullable private final Project.NameKey project;
- private final Change.Id changeId;
-
- @VisibleForTesting
- ProjectChangeId(@Nullable Project.NameKey project, Change.Id changeId) {
- this.project = project;
- this.changeId = changeId;
- }
-
- @Nullable
- public Project.NameKey getProject() {
- return project;
- }
-
- public Change.Id getChangeId() {
- return changeId;
- }
-
- /**
- * Calculate the length of the string representation of the change ID that was parsed from the
- * token.
- *
- * @return the length of the {@link com.google.gerrit.reviewdb.client.Change.Id} if no project was
- * parsed from the token. The length of {@link
- * com.google.gerrit.reviewdb.client.Project.NameKey} + the delimiter + the length of {@link
- * com.google.gerrit.reviewdb.client.Change.Id} otherwise.
- */
- public int identifierLength() {
- if (project == null) {
- return String.valueOf(changeId).length();
- }
- return PageLinks.toChangeId(project, changeId).length();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof ProjectChangeId) {
- ProjectChangeId other = (ProjectChangeId) obj;
- return Objects.equals(changeId, other.changeId) && Objects.equals(project, other.project);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(changeId, project);
- }
-
- @Override
- public String toString() {
- return "ProjectChangeId.Result{changeId: " + changeId + ", project: " + project + "}";
- }
-
- private static Integer tryParse(String s) {
- try {
- return Integer.parseInt(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
deleted file mode 100644
index 56cc7a7852..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
+++ /dev/null
@@ -1,111 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ReviewInput;
-import com.google.gerrit.client.changes.ReviewInput.DraftHandling;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-/** Applies a label with one mouse click. */
-class QuickApprove extends Button implements ClickHandler {
- private Change.Id changeId;
- private Project.NameKey project;
- private String revision;
- private ReviewInput input;
- private ReplyAction replyAction;
-
- QuickApprove() {
- addClickHandler(this);
- }
-
- void set(ChangeInfo info, String commit, ReplyAction action) {
- if (!info.hasPermittedLabels() || !info.status().isOpen()) {
- // Quick approve needs at least one label on an open change.
- setVisible(false);
- return;
- }
- if (info.revision(commit).isEdit()) {
- setVisible(false);
- return;
- }
-
- String qName = null;
- String qValueStr = null;
- short qValue = 0;
-
- int index = info.getMissingLabelIndex();
- if (index != -1) {
- LabelInfo label = Natives.asList(info.allLabels().values()).get(index);
- JsArrayString values = info.permittedValues(label.name());
- String s = values.get(values.length() - 1);
- short v = LabelInfo.parseValue(s);
- if (v > 0 && s.equals(label.maxValue())) {
- qName = label.name();
- qValueStr = s;
- qValue = v;
- }
- }
-
- if (qName != null) {
- changeId = info.legacyId();
- project = info.projectNameKey();
- revision = commit;
- input = ReviewInput.create();
- input.drafts(DraftHandling.PUBLISH_ALL_REVISIONS);
- input.label(qName, qValue);
- replyAction = action;
- setText(qName + qValueStr);
- setVisible(true);
- } else {
- setVisible(false);
- }
- }
-
- @Override
- public void setText(String text) {
- setHTML(new SafeHtmlBuilder().openDiv().append(text).closeDiv());
- }
-
- @Override
- public void onClick(ClickEvent event) {
- if (replyAction != null && replyAction.isVisible()) {
- replyAction.quickApprove(input);
- } else {
- ChangeApi.revision(project.get(), changeId.get(), revision)
- .view("review")
- .post(
- input,
- new GerritCallback<ReviewInput>() {
- @Override
- public void onSuccess(ReviewInput result) {
- Gerrit.display(PageLinks.toChange(project, changeId));
- }
- });
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java
deleted file mode 100644
index 0e3e8358dc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RebaseAction.java
+++ /dev/null
@@ -1,70 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.RebaseDialog;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.PopupPanel;
-
-class RebaseAction {
- static void call(
- final Button b,
- final Project.NameKey project,
- final String branch,
- final Change.Id id,
- final String revision,
- final boolean enabled) {
- b.setEnabled(false);
-
- new RebaseDialog(project, branch, id, enabled) {
- @Override
- public void onSend() {
- ChangeApi.rebase(
- project.get(),
- id.get(),
- revision,
- getBase(),
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- sent = true;
- hide();
- Gerrit.display(PageLinks.toChange(project, id));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableButtons(true);
- super.onFailure(caught);
- }
- });
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- super.onClose(event);
- b.setEnabled(true);
- }
- }.center();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
deleted file mode 100644
index 2c52800231..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChanges.java
+++ /dev/null
@@ -1,459 +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.client.change;
-
-import static com.google.gerrit.common.PageLinks.op;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeList;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.TabBar;
-import com.google.gwt.user.client.ui.TabPanel;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-
-public class RelatedChanges extends TabPanel {
- static final RelatedChangesResources R = GWT.create(RelatedChangesResources.class);
-
- interface RelatedChangesResources extends ClientBundle {
- @Source("related_changes.css")
- RelatedChangesCss css();
- }
-
- interface RelatedChangesCss extends CssResource {
- String activeRow();
-
- String current();
-
- String gitweb();
-
- String indirect();
-
- String notCurrent();
-
- String pointer();
-
- String row();
-
- String subject();
-
- String strikedSubject();
-
- String submittable();
-
- String tabPanel();
- }
-
- enum Tab {
- RELATED_CHANGES(Resources.C.relatedChanges(), Resources.C.relatedChangesTooltip()) {
- @Override
- String getTitle(int count) {
- return Resources.M.relatedChanges(count);
- }
-
- @Override
- String getTitle(String count) {
- return Resources.M.relatedChanges(count);
- }
- },
-
- SUBMITTED_TOGETHER(Resources.C.submittedTogether(), Resources.C.submittedTogether()) {
- @Override
- String getTitle(int count) {
- return Resources.M.submittedTogether(count);
- }
-
- @Override
- String getTitle(String count) {
- return Resources.M.submittedTogether(count);
- }
- },
-
- SAME_TOPIC(Resources.C.sameTopic(), Resources.C.sameTopicTooltip()) {
- @Override
- String getTitle(int count) {
- return Resources.M.sameTopic(count);
- }
-
- @Override
- String getTitle(String count) {
- return Resources.M.sameTopic(count);
- }
- },
-
- CONFLICTING_CHANGES(Resources.C.conflictingChanges(), Resources.C.conflictingChangesTooltip()) {
- @Override
- String getTitle(int count) {
- return Resources.M.conflictingChanges(count);
- }
-
- @Override
- String getTitle(String count) {
- return Resources.M.conflictingChanges(count);
- }
- },
-
- CHERRY_PICKS(Resources.C.cherryPicks(), Resources.C.cherryPicksTooltip()) {
- @Override
- String getTitle(int count) {
- return Resources.M.cherryPicks(count);
- }
-
- @Override
- String getTitle(String count) {
- return Resources.M.cherryPicks(count);
- }
- };
-
- final String defaultTitle;
- final String tooltip;
-
- abstract String getTitle(int count);
-
- abstract String getTitle(String count);
-
- Tab(String defaultTitle, String tooltip) {
- this.defaultTitle = defaultTitle;
- this.tooltip = tooltip;
- }
- }
-
- private static Tab savedTab;
-
- private final List<RelatedChangesTab> tabs;
- private int maxHeightWithHeader;
- private int selectedTab;
- private int outstandingCallbacks;
-
- RelatedChanges() {
- tabs = new ArrayList<>(Tab.values().length);
- selectedTab = -1;
-
- setVisible(false);
- addStyleName(R.css().tabPanel());
- initTabBar();
- }
-
- private void initTabBar() {
- TabBar tabBar = getTabBar();
- tabBar.addSelectionHandler(
- new SelectionHandler<Integer>() {
- @Override
- public void onSelection(SelectionEvent<Integer> event) {
- if (selectedTab >= 0) {
- tabs.get(selectedTab).registerKeys(false);
- }
- selectedTab = event.getSelectedItem();
- tabs.get(selectedTab).registerKeys(true);
- }
- });
-
- for (Tab tabInfo : Tab.values()) {
- RelatedChangesTab panel = new RelatedChangesTab(tabInfo);
- add(panel, tabInfo.defaultTitle);
- tabs.add(panel);
-
- TabBar.Tab tab = tabBar.getTab(tabInfo.ordinal());
- tab.setWordWrap(false);
- ((Composite) tab).setTitle(tabInfo.tooltip);
-
- setTabEnabled(tabInfo, false);
- }
- getTab(Tab.RELATED_CHANGES).setShowIndirectAncestors(true);
- getTab(Tab.CHERRY_PICKS).setShowBranches(true);
- getTab(Tab.SAME_TOPIC).setShowBranches(true);
- getTab(Tab.SAME_TOPIC).setShowProjects(true);
- getTab(Tab.SAME_TOPIC).setShowSubmittable(true);
- getTab(Tab.SUBMITTED_TOGETHER).setShowBranches(true);
- getTab(Tab.SUBMITTED_TOGETHER).setShowProjects(true);
- getTab(Tab.SUBMITTED_TOGETHER).setShowSubmittable(true);
- }
-
- void set(ChangeInfo info, String revision) {
- if (info.status().isOpen()) {
- setForOpenChange(info, revision);
- }
-
- ChangeApi.revision(info.project(), info.legacyId().get(), revision)
- .view("related")
- .get(
- new TabCallback<RelatedInfo>(Tab.RELATED_CHANGES, info.project(), revision) {
- @Override
- public JsArray<ChangeAndCommit> convert(RelatedInfo result) {
- return result.changes();
- }
- });
-
- StringBuilder cherryPicksQuery = new StringBuilder();
- cherryPicksQuery.append(op("project", info.project()));
- cherryPicksQuery.append(" ").append(op("change", info.changeId()));
- cherryPicksQuery.append(" ").append(op("-change", info.legacyId().get()));
- cherryPicksQuery.append(" -is:abandoned");
- ChangeList.query(
- cherryPicksQuery.toString(),
- EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT),
- new TabChangeListCallback(Tab.CHERRY_PICKS, info.project(), revision));
-
- if (info.currentRevision() != null && info.currentRevision().equals(revision)) {
- ChangeApi.change(info.project(), info.legacyId().get())
- .view("submitted_together")
- .addParameter("o", "CURRENT_COMMIT")
- .get(new TabChangeListCallback(Tab.SUBMITTED_TOGETHER, info.project(), revision));
- }
-
- if (!Gerrit.info().change().isSubmitWholeTopicEnabled()
- && info.topic() != null
- && !"".equals(info.topic())) {
- StringBuilder topicQuery =
- new StringBuilder()
- .append("status:open")
- .append(" ")
- .append(op("-change", info.legacyId().get()))
- .append(" ")
- .append(op("topic", info.topic()));
- ChangeList.query(
- topicQuery.toString(),
- EnumSet.of(
- ListChangesOption.CURRENT_REVISION,
- ListChangesOption.CURRENT_COMMIT,
- ListChangesOption.DETAILED_LABELS,
- ListChangesOption.LABELS),
- new TabChangeListCallback(Tab.SAME_TOPIC, info.project(), revision));
- }
- }
-
- private void setForOpenChange(ChangeInfo info, String revision) {
- if (info.mergeable()) {
- StringBuilder conflictsQuery = new StringBuilder();
- conflictsQuery.append("status:open");
- conflictsQuery.append(" is:mergeable");
- conflictsQuery.append(" ").append(op("conflicts", info.legacyId().get()));
- ChangeList.query(
- conflictsQuery.toString(),
- EnumSet.of(ListChangesOption.CURRENT_REVISION, ListChangesOption.CURRENT_COMMIT),
- new TabChangeListCallback(Tab.CONFLICTING_CHANGES, info.project(), revision));
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- R.css().ensureInjected();
- }
-
- static void setSavedTab(Tab subject) {
- savedTab = subject;
- }
-
- private RelatedChangesTab getTab(Tab tabInfo) {
- return tabs.get(tabInfo.ordinal());
- }
-
- private void setTabTitle(Tab tabInfo, String title) {
- getTabBar().setTabText(tabInfo.ordinal(), title);
- }
-
- private void setTabEnabled(Tab tabInfo, boolean enabled) {
- getTabBar().setTabEnabled(tabInfo.ordinal(), enabled);
- }
-
- void setMaxHeight(int height) {
- maxHeightWithHeader = height;
- if (isVisible()) {
- applyMaxHeight();
- }
- }
-
- private void applyMaxHeight() {
- int header = getTabBar().getOffsetHeight() + 2 /* padding */;
- for (int i = 0; i < getTabBar().getTabCount(); i++) {
- tabs.get(i).setMaxHeight(maxHeightWithHeader - header);
- }
- }
-
- private abstract class TabCallback<T> implements AsyncCallback<T> {
- private final Tab tabInfo;
- private final String project;
- private final String revision;
-
- TabCallback(Tab tabInfo, String project, String revision) {
- this.tabInfo = tabInfo;
- this.project = project;
- this.revision = revision;
- outstandingCallbacks++;
- }
-
- protected abstract JsArray<ChangeAndCommit> convert(T result);
-
- @Override
- public void onSuccess(T result) {
- if (isAttached()) {
- JsArray<ChangeAndCommit> changes = convert(result);
- if (changes.length() > 0) {
- setTabTitle(tabInfo, tabInfo.getTitle(changes.length()));
- getTab(tabInfo).setChanges(project, revision, changes);
- }
- onDone(changes.length() > 0);
- }
- }
-
- @Override
- public void onFailure(Throwable err) {
- if (isAttached()) {
- setTabTitle(tabInfo, tabInfo.getTitle(Resources.C.notAvailable()));
- getTab(tabInfo).setError(err.getMessage());
- onDone(true);
- }
- }
-
- private void onDone(boolean enabled) {
- setTabEnabled(tabInfo, enabled);
- outstandingCallbacks--;
- if (outstandingCallbacks == 0 || (enabled && tabInfo == Tab.RELATED_CHANGES)) {
- outstandingCallbacks = 0; // Only execute this block once
- for (int i = 0; i < getTabBar().getTabCount(); i++) {
- if (getTabBar().isTabEnabled(i)) {
- selectTab(i);
- setVisible(true);
- applyMaxHeight();
- break;
- }
- }
- }
-
- if (tabInfo == savedTab && enabled) {
- selectTab(savedTab.ordinal());
- }
- }
- }
-
- private class TabChangeListCallback extends TabCallback<ChangeList> {
- TabChangeListCallback(Tab tabInfo, String project, String revision) {
- super(tabInfo, project, revision);
- }
-
- @Override
- protected JsArray<ChangeAndCommit> convert(ChangeList l) {
- JsArray<ChangeAndCommit> arr = JavaScriptObject.createArray().cast();
- for (ChangeInfo i : Natives.asList(l)) {
- if (i.currentRevision() != null && i.revisions().containsKey(i.currentRevision())) {
- RevisionInfo currentRevision = i.revision(i.currentRevision());
- ChangeAndCommit c = ChangeAndCommit.create();
- c.setId(i.id());
- c.setCommit(currentRevision.commit());
- c.setChangeNumber(i.legacyId().get());
- c.setRevisionNumber(currentRevision._number());
- c.setBranch(i.branch());
- c.setProject(i.project());
- c.setSubmittable(i.submittable() && i.mergeable());
- c.setStatus(i.status().asChangeStatus().toString());
- arr.push(c);
- }
- }
- return arr;
- }
- }
-
- public static class RelatedInfo extends JavaScriptObject {
- public final native JsArray<ChangeAndCommit> changes() /*-{ return this.changes }-*/;
-
- protected RelatedInfo() {}
- }
-
- public static class ChangeAndCommit extends JavaScriptObject {
- static ChangeAndCommit create() {
- return (ChangeAndCommit) createObject();
- }
-
- public final native String id() /*-{ return this.change_id }-*/;
-
- public final native CommitInfo commit() /*-{ return this.commit }-*/;
-
- final native String branch() /*-{ return this.branch }-*/;
-
- final native String project() /*-{ return this.project }-*/;
-
- final native boolean submittable() /*-{ return this._submittable ? true : false; }-*/;
-
- final Change.Status status() {
- String s = statusRaw();
- return s != null ? Change.Status.valueOf(s) : null;
- }
-
- private native String statusRaw() /*-{ return this.status; }-*/;
-
- final native void setId(String i) /*-{ if(i)this.change_id=i; }-*/;
-
- final native void setCommit(CommitInfo c) /*-{ if(c)this.commit=c; }-*/;
-
- final native void setBranch(String b) /*-{ if(b)this.branch=b; }-*/;
-
- final native void setProject(String b) /*-{ if(b)this.project=b; }-*/;
-
- public final Change.Id legacyId() {
- return hasChangeNumber() ? new Change.Id(_changeNumber()) : null;
- }
-
- public final PatchSet.Id patchSetId() {
- return hasChangeNumber() && hasRevisionNumber()
- ? new PatchSet.Id(legacyId(), _revisionNumber())
- : null;
- }
-
- public final native boolean hasChangeNumber()
- /*-{ return this.hasOwnProperty('_change_number') }-*/ ;
-
- final native boolean hasRevisionNumber()
- /*-{ return this.hasOwnProperty('_revision_number') }-*/ ;
-
- final native boolean hasCurrentRevisionNumber()
- /*-{ return this.hasOwnProperty('_current_revision_number') }-*/ ;
-
- final native int _changeNumber() /*-{ return this._change_number }-*/;
-
- final native int _revisionNumber() /*-{ return this._revision_number }-*/;
-
- final native int _currentRevisionNumber() /*-{ return this._current_revision_number }-*/;
-
- final native void setChangeNumber(int n) /*-{ this._change_number=n; }-*/;
-
- final native void setRevisionNumber(int n) /*-{ this._revision_number=n; }-*/;
-
- final native void setCurrentRevisionNumber(int n) /*-{ this._current_revision_number=n; }-*/;
-
- final native void setSubmittable(boolean s) /*-{ this._submittable=s; }-*/;
-
- final native void setStatus(String s) /*-{ if(s)this.status=s; }-*/;
-
- protected ChangeAndCommit() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
deleted file mode 100644
index c53427bee3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RelatedChangesTab.java
+++ /dev/null
@@ -1,594 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.change.RelatedChanges.ChangeAndCommit;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.RepeatingCommand;
-import com.google.gwt.dom.client.AnchorElement;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.dom.client.Style;
-import com.google.gwt.dom.client.Style.Visibility;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.ScrollEvent;
-import com.google.gwt.event.dom.client.ScrollHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.safehtml.shared.SafeHtml;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.AbstractImagePrototype;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.IsWidget;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-class RelatedChangesTab implements IsWidget {
- private static final String OPEN = init(DOM.createUniqueId().replace('-', '_'));
- private static final HyperlinkImpl LINK = GWT.create(HyperlinkImpl.class);
- private static final SafeHtml POINTER_HTML =
- AbstractImagePrototype.create(Gerrit.RESOURCES.arrowRight()).getSafeHtml();
-
- private static native String init(String o) /*-{
- $wnd[o] = $entry(@com.google.gerrit.client.change.RelatedChangesTab::onOpen(
- Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/dom/client/Element;));
- return o + '(event,this)';
- }-*/;
-
- private static boolean onOpen(NativeEvent evt, Element e) {
- if (LINK.handleAsClick(evt.<Event>cast())) {
- Gerrit.display(e.getAttribute("href").substring(1));
- evt.preventDefault();
- return false;
- }
- return true;
- }
-
- private final SimplePanel panel;
- private final RelatedChanges.Tab subject;
-
- private boolean showBranches;
- private boolean showProjects;
- private boolean showSubmittable;
- private boolean showIndirectAncestors;
- private boolean registerKeys;
- private int maxHeight;
-
- private String project;
- private NavigationList view;
-
- RelatedChangesTab(RelatedChanges.Tab subject) {
- panel = new SimplePanel();
- this.subject = subject;
- }
-
- @Override
- public Widget asWidget() {
- return panel;
- }
-
- void setShowBranches(boolean showBranches) {
- this.showBranches = showBranches;
- }
-
- void setShowProjects(boolean showProjects) {
- this.showProjects = showProjects;
- }
-
- void setShowSubmittable(boolean submittable) {
- this.showSubmittable = submittable;
- }
-
- void setShowIndirectAncestors(boolean showIndirectAncestors) {
- this.showIndirectAncestors = showIndirectAncestors;
- }
-
- void setMaxHeight(int height) {
- maxHeight = height;
- if (view != null) {
- view.setHeight(height + "px");
- view.ensureRowMeasurements();
- view.movePointerTo(view.selectedRow, true);
- }
- }
-
- void registerKeys(boolean on) {
- registerKeys = on;
- if (view != null) {
- view.setRegisterKeys(on);
- }
- }
-
- void setError(String message) {
- panel.setWidget(new InlineLabel(message));
- view = null;
- project = null;
- }
-
- void setChanges(String project, String revision, JsArray<ChangeAndCommit> changes) {
- if (0 == changes.length()) {
- setError(Resources.C.noChanges());
- return;
- }
-
- this.project = project;
- view = new NavigationList();
- panel.setWidget(view);
-
- DisplayCommand display = new DisplayCommand(revision, changes, view);
- if (display.execute()) {
- Scheduler.get().scheduleIncremental(display);
- }
- }
-
- private final class DisplayCommand implements RepeatingCommand {
- private final String revision;
- private final JsArray<ChangeAndCommit> changes;
- private final List<SafeHtml> rows;
- private final Set<String> connected;
- private final NavigationList navList;
-
- private double start;
- private int row;
- private int connectedPos;
- private int selected;
-
- private DisplayCommand(
- String revision, JsArray<ChangeAndCommit> changes, NavigationList navList) {
- this.revision = revision;
- this.changes = changes;
- this.navList = navList;
- rows = new ArrayList<>(changes.length());
- connectedPos = changes.length() - 1;
- connected =
- showIndirectAncestors ? new HashSet<>(Math.max(changes.length() * 4 / 3, 16)) : null;
- }
-
- private boolean computeConnected() {
- // Since TOPO sorted, when can walk the list in reverse and find all
- // the connections.
- if (!connected.contains(revision)) {
- while (connectedPos >= 0) {
- CommitInfo c = changes.get(connectedPos).commit();
- connected.add(c.commit());
- if (longRunning(--connectedPos)) {
- return true;
- }
- if (c.commit().equals(revision)) {
- break;
- }
- }
- }
- while (connectedPos >= 0) {
- CommitInfo c = changes.get(connectedPos).commit();
- for (int j = 0; j < c.parents().length(); j++) {
- if (connected.contains(c.parents().get(j).commit())) {
- connected.add(c.commit());
- break;
- }
- }
- if (longRunning(--connectedPos)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean execute() {
- if (navList != view || !panel.isAttached()) {
- // If the user navigated away, we aren't in the DOM anymore.
- // Don't continue to render.
- return false;
- }
-
- start = System.currentTimeMillis();
-
- if (connected != null && computeConnected()) {
- return true;
- }
-
- while (row < changes.length()) {
- ChangeAndCommit info = changes.get(row);
- String commit = info.commit().commit();
- rows.add(new RowSafeHtml(info, connected != null && !connected.contains(commit)));
- if (revision.equals(commit)) {
- selected = row;
- }
- if (longRunning(++row)) {
- return true;
- }
- }
-
- navList.rows = rows;
- navList.ensureRowMeasurements();
- navList.movePointerTo(selected, true);
- return false;
- }
-
- private boolean longRunning(int i) {
- return (i % 10) == 0 && System.currentTimeMillis() - start > 50;
- }
- }
-
- private class RowSafeHtml implements SafeHtml {
- private static final long serialVersionUID = 1L;
-
- private String html;
- private ChangeAndCommit info;
- private final boolean notConnected;
-
- RowSafeHtml(ChangeAndCommit info, boolean notConnected) {
- this.info = info;
- this.notConnected = notConnected;
- }
-
- @Override
- public String asString() {
- if (html == null) {
- SafeHtmlBuilder sb = new SafeHtmlBuilder();
- renderRow(sb);
- html = sb.asString();
- info = null;
- }
- return html;
- }
-
- private void renderRow(SafeHtmlBuilder sb) {
- sb.openDiv().setStyleName(RelatedChanges.R.css().row());
-
- sb.openSpan().setStyleName(RelatedChanges.R.css().pointer());
- sb.append(POINTER_HTML);
- sb.closeSpan();
-
- if (info.status() == Change.Status.ABANDONED) {
- sb.openSpan().setStyleName(RelatedChanges.R.css().strikedSubject());
- } else {
- sb.openSpan().setStyleName(RelatedChanges.R.css().subject());
- }
- sb.setAttribute("data-branch", info.branch());
- sb.setAttribute("data-project", info.project());
- String url = url();
- if (url != null) {
- sb.openAnchor().setAttribute("href", url);
- if (url.startsWith("#")) {
- sb.setAttribute("onclick", OPEN);
- }
- sb.setAttribute("title", info.commit().subject());
- if (showProjects) {
- sb.append(info.project()).append(": ");
- }
- if (showBranches) {
- sb.append(info.branch()).append(": ");
- }
- sb.append(info.commit().subject());
- sb.closeAnchor();
- } else {
- sb.append(info.commit().subject());
- }
- sb.closeSpan();
-
- sb.openSpan();
- if (info.status() != null && !info.status().isOpen()) {
- sb.setStyleName(RelatedChanges.R.css().gitweb());
- sb.setAttribute("title", Util.toLongString(info.status()));
- sb.append('\u25CF'); // Unicode 'BLACK CIRCLE'
- } else if (notConnected) {
- sb.setStyleName(RelatedChanges.R.css().indirect());
- sb.setAttribute("title", Resources.C.indirectAncestor());
- sb.append('~');
- } else if (info.hasCurrentRevisionNumber()
- && info.hasRevisionNumber()
- && info._currentRevisionNumber() != info._revisionNumber()) {
- sb.setStyleName(RelatedChanges.R.css().notCurrent());
- sb.setAttribute("title", Util.C.notCurrent());
- sb.append('\u25CF'); // Unicode 'BLACK CIRCLE'
- } else if (showSubmittable && info.submittable()) {
- sb.setStyleName(RelatedChanges.R.css().submittable());
- sb.setAttribute("title", Util.C.submittable());
- sb.append('\u2713'); // Unicode 'CHECK MARK'
- } else {
- sb.setStyleName(RelatedChanges.R.css().current());
- }
- sb.closeSpan();
-
- sb.closeDiv();
- }
-
- private String url() {
- if (info.hasChangeNumber() && info.hasRevisionNumber()) {
- return "#" + PageLinks.toChange(new Project.NameKey(info.project()), info.patchSetId());
- }
- return null;
- }
- }
-
- private class NavigationList extends ScrollPanel
- implements ClickHandler, DoubleClickHandler, ScrollHandler {
- private final KeyCommandSet keysNavigation;
- private final Element body;
- private final Element surrogate;
- private final Node fragment = createDocumentFragment();
-
- List<SafeHtml> rows;
- private HandlerRegistration regNavigation;
- private int selectedRow;
- private int startRow;
- private int rowHeight;
- private int rowWidth;
-
- NavigationList() {
- addDomHandler(this, ClickEvent.getType());
- addDomHandler(this, DoubleClickEvent.getType());
- addScrollHandler(this);
-
- keysNavigation = new KeyCommandSet(Resources.C.relatedChanges());
- keysNavigation.add(
- new KeyCommand(0, 'K', Resources.C.previousChange()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- movePointerTo(selectedRow - 1, true);
- }
- },
- new KeyCommand(0, 'J', Resources.C.nextChange()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- movePointerTo(selectedRow + 1, true);
- }
- });
- keysNavigation.add(
- new KeyCommand(0, 'O', Resources.C.openChange()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- onOpenRow(getRow(selectedRow));
- }
- });
-
- if (maxHeight > 0) {
- setHeight(maxHeight + "px");
- }
-
- body = DOM.createDiv();
- body.getStyle().setPosition(Style.Position.RELATIVE);
- body.getStyle().setVisibility(Visibility.HIDDEN);
- getContainerElement().appendChild(body);
-
- surrogate = DOM.createDiv();
- surrogate.getStyle().setVisibility(Visibility.HIDDEN);
- }
-
- private boolean ensureRowMeasurements() {
- if (rowHeight == 0 && rows != null) {
- surrogate.setInnerSafeHtml(rows.get(0));
- getContainerElement().appendChild(surrogate);
- rowHeight = surrogate.getOffsetHeight();
- rowWidth = surrogate.getOffsetWidth();
- getContainerElement().removeChild(surrogate);
- getContainerElement().getStyle().setHeight(rowHeight * rows.size(), Style.Unit.PX);
- return true;
- }
- return false;
- }
-
- public void movePointerTo(int row, boolean scroll) {
- if (rows != null && 0 <= row && row < rows.size()) {
- renderSelected(selectedRow, false);
- selectedRow = row;
-
- if (scroll && rowHeight != 0) {
- // Position the selected row in the middle.
- setVerticalScrollPosition(Math.max(rowHeight * selectedRow - maxHeight / 2, 0));
- render();
- }
- renderSelected(selectedRow, true);
- }
- }
-
- private void renderSelected(int row, boolean selected) {
- Element e = getRow(row);
- if (e != null) {
- if (selected) {
- e.addClassName(RelatedChanges.R.css().activeRow());
- } else {
- e.removeClassName(RelatedChanges.R.css().activeRow());
- }
- }
- }
-
- private void render() {
- if (rows == null || rowHeight == 0) {
- return;
- }
-
- int currStart = startRow;
- int currEnd = startRow + body.getChildCount();
-
- int vpos = getVerticalScrollPosition();
- int start = Math.max(vpos / rowHeight - 5, 0);
- int end = Math.min((vpos + maxHeight) / rowHeight + 5, rows.size());
- if (currStart <= start && end <= currEnd) {
- return; // All of the required nodes are already in the DOM.
- }
-
- if (end <= currStart) {
- renderRange(start, end, true, true);
- } else if (start < currStart) {
- renderRange(start, currStart, false, true);
- } else if (start >= currEnd) {
- renderRange(start, end, true, false);
- } else if (end > currEnd) {
- renderRange(currEnd, end, false, false);
- }
-
- renderSelected(selectedRow, true);
-
- if (currEnd == 0) {
- // Account for the scroll bars
- int width = body.getOffsetWidth();
- if (rowWidth > width) {
- int w = 2 * rowWidth - width;
- setWidth(w + "px");
- }
- body.getStyle().clearVisibility();
- }
- }
-
- private void renderRange(int start, int end, boolean removeAll, boolean insertFirst) {
- SafeHtmlBuilder sb = new SafeHtmlBuilder();
- for (int i = start; i < end; i++) {
- sb.append(rows.get(i));
- }
-
- if (removeAll) {
- body.setInnerSafeHtml(sb);
- } else {
- surrogate.setInnerSafeHtml(sb);
- for (int cnt = surrogate.getChildCount(); cnt > 0; cnt--) {
- fragment.appendChild(surrogate.getFirstChild());
- }
- if (insertFirst) {
- body.insertFirst(fragment);
- } else {
- body.appendChild(fragment);
- }
- }
-
- if (insertFirst || removeAll) {
- startRow = start;
- body.getStyle().setTop(start * rowHeight, Style.Unit.PX);
- }
- }
-
- @Override
- public void onClick(ClickEvent event) {
- Element row = getRow(event.getNativeEvent().getEventTarget().<Element>cast());
- if (row != null) {
- movePointerTo(startRow + DOM.getChildIndex(body, row), false);
- event.stopPropagation();
- }
- saveSelectedTab();
- }
-
- @Override
- public void onDoubleClick(DoubleClickEvent event) {
- Element row = getRow(event.getNativeEvent().getEventTarget().<Element>cast());
- if (row != null) {
- movePointerTo(startRow + DOM.getChildIndex(body, row), false);
- onOpenRow(row);
- event.stopPropagation();
- }
- }
-
- @Override
- public void onScroll(ScrollEvent event) {
- render();
- }
-
- private Element getRow(Element e) {
- for (Element prev = e; e != null; prev = e) {
- if ((e = DOM.getParent(e)) == body) {
- return prev;
- }
- }
- return null;
- }
-
- private Element getRow(int row) {
- if (startRow <= row && row < startRow + body.getChildCount()) {
- return body.getChild(row - startRow).cast();
- }
- return null;
- }
-
- private void onOpenRow(Element row) {
- // Find the first HREF of the anchor of the select row (if any)
- if (row != null) {
- NodeList<Element> nodes = row.getElementsByTagName(AnchorElement.TAG);
- for (int i = 0; i < nodes.getLength(); i++) {
- String url = nodes.getItem(i).getAttribute("href");
- if (!url.isEmpty()) {
- if (url.startsWith("#")) {
- Gerrit.display(url.substring(1));
- } else {
- Window.Location.assign(url);
- }
- break;
- }
- }
- }
-
- saveSelectedTab();
- }
-
- private void saveSelectedTab() {
- RelatedChanges.setSavedTab(subject);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- setRegisterKeys(registerKeys);
- }
-
- @Override
- protected void onUnload() {
- setRegisterKeys(false);
- super.onUnload();
- }
-
- public void setRegisterKeys(boolean on) {
- if (on && isAttached()) {
- if (regNavigation == null) {
- regNavigation = GlobalKey.add(this, keysNavigation);
- }
- if (view.ensureRowMeasurements()) {
- view.movePointerTo(view.selectedRow, true);
- }
- } else if (regNavigation != null) {
- regNavigation.removeHandler();
- regNavigation = null;
- }
- }
- }
-
- private static native Node createDocumentFragment() /*-{
- return $doc.createDocumentFragment();
- }-*/;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java
deleted file mode 100644
index 1e7063ab62..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileAction.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2015 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.client.change;
-
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-class RenameFileAction {
- private final Project.NameKey project;
- private final Change.Id changeId;
- private final RevisionInfo revision;
- private final ChangeScreen.Style style;
- private final Widget renameButton;
-
- private RenameFileBox renameBox;
- private PopupPanel popup;
-
- RenameFileAction(
- Project.NameKey project,
- Change.Id changeId,
- RevisionInfo revision,
- ChangeScreen.Style style,
- Widget renameButton) {
- this.project = project;
- this.changeId = changeId;
- this.revision = revision;
- this.style = style;
- this.renameButton = renameButton;
- }
-
- void onRename() {
- if (popup != null) {
- popup.hide();
- return;
- }
-
- if (renameBox == null) {
- renameBox = new RenameFileBox(project, changeId, revision);
- }
- renameBox.clearPath();
-
- final PopupPanel p = new PopupPanel(true);
- p.setStyleName(style.replyBox());
- p.addAutoHidePartner(renameButton.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (popup == p) {
- popup = null;
- }
- }
- });
- p.add(renameBox);
- p.showRelativeTo(renameButton);
- GlobalKey.dialog(p);
- renameBox.setFocus(true);
- popup = p;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java
deleted file mode 100644
index f288dbe4e6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2015 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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.changes.ChangeEditApi;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-class RenameFileBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, RenameFileBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private final Project.NameKey project;
- private final Change.Id changeId;
-
- @UiField Button rename;
- @UiField Button cancel;
-
- @UiField(provided = true)
- RemoteSuggestBox path;
-
- @UiField NpTextBox newPath;
-
- RenameFileBox(Project.NameKey project, Change.Id changeId, RevisionInfo revision) {
- this.project = project;
- this.changeId = changeId;
-
- path = new RemoteSuggestBox(new PathSuggestOracle(project, changeId, revision));
- path.addCloseHandler(
- new CloseHandler<RemoteSuggestBox>() {
- @Override
- public void onClose(CloseEvent<RemoteSuggestBox> event) {
- hide();
- }
- });
-
- initWidget(uiBinder.createAndBindUi(this));
- }
-
- void setFocus(boolean focus) {
- path.setFocus(focus);
- }
-
- void clearPath() {
- path.setText("");
- }
-
- @UiHandler("rename")
- void onRename(@SuppressWarnings("unused") ClickEvent e) {
- rename(path.getText(), newPath.getText());
- }
-
- private void rename(String path, String newPath) {
- hide();
- ChangeEditApi.rename(
- project.get(),
- changeId.get(),
- path,
- newPath,
- new AsyncCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- Gerrit.display(PageLinks.toChangeInEditMode(project, changeId));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- hide();
- }
-
- private void hide() {
- for (Widget w = getParent(); w != null; w = w.getParent()) {
- if (w instanceof PopupPanel) {
- ((PopupPanel) w).hide();
- break;
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml
deleted file mode 100644
index 17e87974d2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RenameFileBox.ui.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:u='urn:import:com.google.gerrit.client.ui'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- .cancel { float: right; }
- </ui:style>
- <g:HTMLPanel>
- <div class='{res.style.section}'>
- <ui:msg>Old: <u:RemoteSuggestBox ui:field='path' visibleLength='86'/></ui:msg>
- </div>
- <div class='{res.style.section}'>
- <ui:msg>New: <c:NpTextBox ui:field='newPath' visibleLength='86'/></ui:msg>
- </div>
- <div class='{res.style.section}'>
- <g:Button ui:field='rename'
- title='Rename file in the repository'
- styleName='{res.style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Rename</ui:msg></div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java
deleted file mode 100644
index ff09ff5c35..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyAction.java
+++ /dev/null
@@ -1,127 +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.client.change;
-
-import com.google.gerrit.client.changes.ReviewInput;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-class ReplyAction {
- private final PatchSet.Id psId;
- private final Project.NameKey project;
- private final String revision;
- private final boolean hasDraftComments;
- private final ChangeScreen.Style style;
- private final CommentLinkProcessor clp;
- private final Widget replyButton;
- private final Widget quickApproveButton;
-
- private NativeMap<LabelInfo> allLabels;
- private NativeMap<JsArrayString> permittedLabels;
-
- private ReplyBox replyBox;
- private PopupPanel popup;
-
- ReplyAction(
- ChangeInfo info,
- String revision,
- boolean hasDraftComments,
- ChangeScreen.Style style,
- CommentLinkProcessor clp,
- Widget replyButton,
- Widget quickApproveButton) {
- this.psId = new PatchSet.Id(info.legacyId(), info.revisions().get(revision)._number());
- this.project = info.projectNameKey();
- this.revision = revision;
- this.hasDraftComments = hasDraftComments;
- this.style = style;
- this.clp = clp;
- this.replyButton = replyButton;
- this.quickApproveButton = quickApproveButton;
-
- boolean current = revision.equals(info.currentRevision());
- allLabels = info.allLabels();
- permittedLabels =
- current && info.hasPermittedLabels()
- ? info.permittedLabels()
- : NativeMap.<JsArrayString>create();
- }
-
- boolean isVisible() {
- return popup != null;
- }
-
- void quickApprove(ReviewInput input) {
- replyBox.quickApprove(input);
- }
-
- void hide() {
- if (popup != null) {
- popup.hide();
- }
- return;
- }
-
- void onReply(MessageInfo msg) {
- if (popup != null) {
- popup.hide();
- return;
- }
-
- if (replyBox == null) {
- replyBox = new ReplyBox(clp, project, psId, revision, allLabels, permittedLabels);
- allLabels = null;
- permittedLabels = null;
- }
- if (msg != null) {
- replyBox.replyTo(msg);
- }
-
- final PopupPanel p = new PopupPanel(true, false);
- p.setStyleName(style.replyBox());
- p.addAutoHidePartner(replyButton.getElement());
- p.addAutoHidePartner(quickApproveButton.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (popup == p) {
- popup = null;
- if (hasDraftComments || replyBox.hasMessage()) {
- replyButton.setStyleName(style.highlight());
- }
- }
- }
- });
- p.add(replyBox);
- Window.scrollTo(0, 0);
- replyButton.removeStyleName(style.highlight());
- p.showRelativeTo(replyButton);
- GlobalKey.dialog(p);
- popup = p;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
deleted file mode 100644
index addd692306..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ /dev/null
@@ -1,541 +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.client.change;
-
-import static com.google.gwt.event.dom.client.KeyCodes.KEY_ENTER;
-import static com.google.gwt.event.dom.client.KeyCodes.KEY_MAC_ENTER;
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toList;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.changes.ReviewInput;
-import com.google.gerrit.client.changes.ReviewInput.DraftHandling;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo.ApprovalInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.RepeatingCommand;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.dom.client.MouseOverHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.RadioButton;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.TextArea;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-public class ReplyBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, ReplyBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Styles extends CssResource {
- String label_name();
-
- String label_value();
-
- String label_help();
- }
-
- private final CommentLinkProcessor clp;
- private final Project.NameKey project;
- private final PatchSet.Id psId;
- private final String revision;
- private ReviewInput in = ReviewInput.create();
- private int labelHelpColumn;
- private LocalComments lc;
-
- @UiField Styles style;
- @UiField TextArea message;
- @UiField Element labelsParent;
- @UiField Grid labelsTable;
- @UiField Button post;
- @UiField Button cancel;
- @UiField ScrollPanel commentsPanel;
- @UiField FlowPanel comments;
-
- ReplyBox(
- CommentLinkProcessor clp,
- Project.NameKey project,
- PatchSet.Id psId,
- String revision,
- NativeMap<LabelInfo> all,
- NativeMap<JsArrayString> permitted) {
- this.clp = clp;
- this.project = project;
- this.psId = psId;
- this.revision = revision;
- this.lc = new LocalComments(project, psId.getParentKey());
- initWidget(uiBinder.createAndBindUi(this));
-
- List<String> names =
- permitted.keySet().stream()
- .sorted()
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
- if (names.isEmpty()) {
- UIObject.setVisible(labelsParent, false);
- } else {
- renderLabels(names, all, permitted);
- }
-
- addDomHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent e) {
- e.stopPropagation();
- if ((e.getNativeKeyCode() == KEY_ENTER || e.getNativeKeyCode() == KEY_MAC_ENTER)
- && (e.isControlKeyDown() || e.isMetaKeyDown())) {
- e.preventDefault();
- if (post.isEnabled()) {
- onPost(null);
- }
- }
- }
- },
- KeyDownEvent.getType());
- addDomHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent e) {
- e.stopPropagation();
- }
- },
- KeyPressEvent.getType());
- }
-
- @Override
- protected void onLoad() {
- commentsPanel.setVisible(false);
- post.setEnabled(false);
- if (lc.hasReplyComment()) {
- message.setText(lc.getReplyComment());
- lc.removeReplyComment();
- }
- ChangeApi.drafts(project.get(), psId.getParentKey().get())
- .get(
- new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- displayComments(result);
- post.setEnabled(true);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- post.setEnabled(true);
- }
- });
-
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- message.setFocus(true);
- }
- });
- Scheduler.get()
- .scheduleFixedDelay(
- new RepeatingCommand() {
- @Override
- public boolean execute() {
- String t = message.getText();
- if (t != null) {
- message.setCursorPos(t.length());
- }
- return false;
- }
- },
- 0);
- }
-
- @UiHandler("post")
- void onPost(@SuppressWarnings("unused") ClickEvent e) {
- postReview();
- }
-
- void quickApprove(ReviewInput quickApproveInput) {
- in.mergeLabels(quickApproveInput);
- postReview();
- }
-
- boolean hasMessage() {
- return !message.getText().trim().isEmpty();
- }
-
- private void postReview() {
- in.message(message.getText().trim());
- // Don't send any comments in the request; just publish everything, even if
- // e.g. a draft was modified in another tab since we last looked it up.
- in.drafts(DraftHandling.PUBLISH_ALL_REVISIONS);
- in.prePost();
- ChangeApi.revision(project.get(), psId.getParentKey().get(), revision)
- .view("review")
- .post(
- in,
- new GerritCallback<ReviewInput>() {
- @Override
- public void onSuccess(ReviewInput result) {
- Gerrit.display(PageLinks.toChange(project, psId));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (RestApi.isNotSignedIn(caught)) {
- lc.setReplyComment(message.getText());
- }
- super.onFailure(caught);
- }
- });
- hide();
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- message.setText("");
- hide();
- }
-
- void replyTo(MessageInfo msg) {
- if (msg.message() != null) {
- String t = message.getText();
- String m = quote(removePatchSetHeaderLine(msg.message()));
- if (t == null || t.isEmpty()) {
- t = m;
- } else if (t.endsWith("\n\n")) {
- t += m;
- } else if (t.endsWith("\n")) {
- t += "\n" + m;
- } else {
- t += "\n\n" + m;
- }
- message.setText(t);
- }
- }
-
- private static String removePatchSetHeaderLine(String msg) {
- msg = msg.trim();
- if (msg.startsWith("Patch Set ")) {
- int i = msg.indexOf('\n');
- if (i > 0) {
- msg = msg.substring(i + 1).trim();
- }
- }
- return msg;
- }
-
- public static String quote(String msg) {
- msg = msg.trim();
- StringBuilder quotedMsg = new StringBuilder();
- for (String line : msg.split("\\n")) {
- line = line.trim();
- while (line.length() > 67) {
- int i = line.lastIndexOf(' ', 67);
- if (i < 50) {
- i = line.indexOf(' ', 67);
- }
- if (i > 0) {
- quotedMsg.append(" > ").append(line.substring(0, i)).append("\n");
- line = line.substring(i + 1);
- } else {
- break;
- }
- }
- quotedMsg.append(" > ").append(line).append("\n");
- }
- quotedMsg.append("\n");
- return quotedMsg.toString();
- }
-
- private void hide() {
- for (Widget w = getParent(); w != null; w = w.getParent()) {
- if (w instanceof PopupPanel) {
- ((PopupPanel) w).hide();
- break;
- }
- }
- }
-
- private void renderLabels(
- List<String> names, NativeMap<LabelInfo> all, NativeMap<JsArrayString> permitted) {
- TreeSet<Short> values = new TreeSet<>();
- List<LabelAndValues> labels = new ArrayList<>(permitted.size());
- for (String id : names) {
- JsArrayString p = permitted.get(id);
- if (p != null) {
- if (!all.containsKey(id)) {
- continue;
- }
- Set<Short> a = new TreeSet<>();
- for (int i = 0; i < p.length(); i++) {
- a.add(LabelInfo.parseValue(p.get(i)));
- }
- labels.add(new LabelAndValues(all.get(id), a));
- values.addAll(a);
- }
- }
- List<Short> columns = new ArrayList<>(values);
-
- labelsTable.resize(1 + labels.size(), 2 + values.size());
- for (int c = 0; c < columns.size(); c++) {
- labelsTable.setText(0, 1 + c, LabelValue.formatValue(columns.get(c)));
- labelsTable.getCellFormatter().setStyleName(0, 1 + c, style.label_value());
- }
-
- List<LabelAndValues> checkboxes = new ArrayList<>(labels.size());
- int row = 1;
- for (LabelAndValues lv : labels) {
- if (isCheckBox(lv.info.valueSet())) {
- checkboxes.add(lv);
- } else {
- renderRadio(row++, columns, lv);
- }
- }
- for (LabelAndValues lv : checkboxes) {
- renderCheckBox(row++, lv);
- }
- }
-
- private Short normalizeDefaultValue(Short defaultValue, Set<Short> permittedValues) {
- Short pmin = Collections.min(permittedValues);
- Short pmax = Collections.max(permittedValues);
- Short dv = defaultValue;
- if (dv > pmax) {
- dv = pmax;
- } else if (dv < pmin) {
- dv = pmin;
- }
- return dv;
- }
-
- private void renderRadio(int row, List<Short> columns, LabelAndValues lv) {
- String id = lv.info.name();
- Short dv = normalizeDefaultValue(lv.info.defaultValue(), lv.permitted);
-
- labelHelpColumn = 1 + columns.size();
- labelsTable.setText(row, 0, id);
-
- CellFormatter fmt = labelsTable.getCellFormatter();
- fmt.setStyleName(row, 0, style.label_name());
- fmt.setStyleName(row, labelHelpColumn, style.label_help());
-
- ApprovalInfo self =
- Gerrit.isSignedIn() ? lv.info.forUser(Gerrit.getUserAccount()._accountId()) : null;
-
- final LabelRadioGroup group = new LabelRadioGroup(row, id, lv.permitted.size());
- for (int i = 0; i < columns.size(); i++) {
- Short v = columns.get(i);
- if (lv.permitted.contains(v)) {
- String text = lv.info.valueText(LabelValue.formatValue(v));
- LabelRadioButton b = new LabelRadioButton(group, text, v);
- if ((self != null && v == self.value()) || (self == null && v.equals(dv))) {
- b.setValue(true);
- group.select(b);
- in.label(group.label, v);
- labelsTable.setText(row, labelHelpColumn, b.text);
- }
- group.buttons.add(b);
- labelsTable.setWidget(row, 1 + i, b);
- }
- }
- }
-
- private void renderCheckBox(int row, LabelAndValues lv) {
- ApprovalInfo self =
- Gerrit.isSignedIn() ? lv.info.forUser(Gerrit.getUserAccount()._accountId()) : null;
-
- final String id = lv.info.name();
- final CheckBox b = new CheckBox();
- b.setText(id);
- b.setEnabled(lv.permitted.contains((short) 1));
- if (self != null && self.value() == 1) {
- b.setValue(true);
- }
- b.addValueChangeHandler(
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- in.label(id, event.getValue() ? (short) 1 : (short) 0);
- }
- });
- b.setStyleName(style.label_name());
- labelsTable.setWidget(row, 0, b);
-
- CellFormatter fmt = labelsTable.getCellFormatter();
- fmt.setStyleName(row, labelHelpColumn, style.label_help());
- labelsTable.setText(row, labelHelpColumn, lv.info.valueText("+1"));
- }
-
- private static boolean isCheckBox(Set<Short> values) {
- return values.size() == 2 && values.contains((short) 0) && values.contains((short) 1);
- }
-
- private void displayComments(NativeMap<JsArray<CommentInfo>> m) {
- comments.clear();
-
- JsArray<CommentInfo> l = m.get(Patch.COMMIT_MSG);
- if (l != null) {
- comments.add(
- new FileComments(
- clp, project, psId, Util.C.commitMessage(), copyPath(Patch.COMMIT_MSG, l)));
- }
- l = m.get(Patch.MERGE_LIST);
- if (l != null) {
- comments.add(
- new FileComments(
- clp, project, psId, Util.C.commitMessage(), copyPath(Patch.MERGE_LIST, l)));
- }
-
- List<String> paths =
- m.keySet().stream()
- .sorted()
- .collect(collectingAndThen(toList(), Collections::unmodifiableList));
-
- for (String path : paths) {
- if (!Patch.isMagic(path)) {
- comments.add(new FileComments(clp, project, psId, path, copyPath(path, m.get(path))));
- }
- }
-
- commentsPanel.setVisible(comments.getWidgetCount() > 0);
- }
-
- private static List<CommentInfo> copyPath(String path, JsArray<CommentInfo> l) {
- for (int i = 0; i < l.length(); i++) {
- l.get(i).path(path);
- }
- return Natives.asList(l);
- }
-
- private static class LabelAndValues {
- final LabelInfo info;
- final Set<Short> permitted;
-
- LabelAndValues(LabelInfo info, Set<Short> permitted) {
- this.info = info;
- this.permitted = permitted;
- }
- }
-
- private class LabelRadioGroup {
- final int row;
- final String label;
- final List<LabelRadioButton> buttons;
- LabelRadioButton selected;
-
- LabelRadioGroup(int row, String label, int cnt) {
- this.row = row;
- this.label = label;
- this.buttons = new ArrayList<>(cnt);
- }
-
- void select(LabelRadioButton b) {
- selected = b;
- labelsTable.setText(row, labelHelpColumn, b.text);
- }
- }
-
- private class LabelRadioButton extends RadioButton
- implements ValueChangeHandler<Boolean>, ClickHandler, MouseOverHandler, MouseOutHandler {
- private final LabelRadioGroup group;
- private final String text;
- private final short value;
-
- LabelRadioButton(LabelRadioGroup group, String text, short value) {
- super(group.label);
- this.group = group;
- this.text = text;
- this.value = value;
- addValueChangeHandler(this);
- addClickHandler(this);
- addMouseOverHandler(this);
- addMouseOutHandler(this);
- }
-
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- if (event.getValue()) {
- select();
- }
- }
-
- @Override
- public void onClick(ClickEvent event) {
- select();
- }
-
- void select() {
- group.select(this);
- in.label(group.label, value);
- }
-
- @Override
- public void onMouseOver(MouseOverEvent event) {
- labelsTable.setText(group.row, labelHelpColumn, text);
- }
-
- @Override
- public void onMouseOut(MouseOutEvent event) {
- LabelRadioButton b = group.selected;
- String s = b != null ? b.text : "";
- labelsTable.setText(group.row, labelHelpColumn, s);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml
deleted file mode 100644
index 6903b9125c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.ui.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.change.ReplyBox.Styles'>
- .replyBox {
- }
- .label_name {
- font-weight: bold;
- text-align: left;
- white-space: nowrap;
- }
- .label_name input { margin-left: 0; }
- .label_help {
- padding-left: 5px;
- white-space: nowrap;
- }
- .label_value {
- text-align: center;
- }
- .cancel {
- position: absolute;
- bottom: 5px;
- right: 5px;
- background-color: #eee;
- background-image: -webkit-linear-gradient(top, #eee, #eee);
- }
- .cancel div { color: #444; }
- .comments {
- max-height: 275px;
- width: 526px;
- }
- .comments p {
- margin: 5px 0 5px 0;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.replyBox}'>
- <div class='{res.style.section}'>
- <g:TextArea
- visibleLines='5'
- characterWidth='70'
- ui:field='message'/>
- </div>
- <div class='{res.style.section}' ui:field='labelsParent'>
- <g:Grid ui:field='labelsTable'/>
- </div>
- <g:ScrollPanel ui:field='commentsPanel'
- styleName='{style.comments}'
- addStyleNames='{res.style.section}'>
- <g:FlowPanel ui:field='comments'/>
- </g:ScrollPanel>
- <div class='{res.style.section}' style='position: relative'>
- <g:Button ui:field='post'
- title='Post reply (Shortcut: Ctrl-Enter)'
- styleName='{res.style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Post</ui:msg></div>
- </g:Button>
-
- <g:Button ui:field='cancel'
- title='Close reply form (Shortcut: Esc)'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Cancel</ui:msg></div>
- </g:Button>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java
deleted file mode 100644
index cfc4e23927..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java
+++ /dev/null
@@ -1,38 +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.client.change;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.CssResource;
-
-public interface Resources extends ClientBundle {
- Resources I = GWT.create(Resources.class);
- ChangeConstants C = GWT.create(ChangeConstants.class);
- ChangeMessages M = GWT.create(ChangeMessages.class);
-
- @Source("common.css")
- Style style();
-
- public interface Style extends CssResource {
- String button();
-
- String popup();
-
- String popupContent();
-
- String section();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java
deleted file mode 100644
index aa3a9ef544..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RestoreAction.java
+++ /dev/null
@@ -1,50 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.ui.Button;
-
-class RestoreAction extends ActionMessageBox {
- private final Project.NameKey project;
- private final Change.Id id;
-
- RestoreAction(Button b, Project.NameKey project, Change.Id id) {
- super(b);
- this.project = project;
- this.id = id;
- }
-
- @Override
- void send(String message) {
- ChangeApi.restore(
- project.get(),
- id.get(),
- message,
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- Gerrit.display(PageLinks.toChange(project, id));
- hide();
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
deleted file mode 100644
index 3fba125e77..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevertAction.java
+++ /dev/null
@@ -1,75 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.TextAreaActionDialog;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.PopupPanel;
-
-class RevertAction {
-
- static void call(
- final Button b,
- Change.Id id,
- Project.NameKey project,
- String revision,
- String commitSubject) {
- // TODO Replace ActionDialog with a nicer looking display.
- b.setEnabled(false);
- new TextAreaActionDialog(Util.C.revertChangeTitle(), Util.C.headingRevertMessage()) {
- {
- sendButton.setText(Util.C.buttonRevertChangeSend());
- message.setText(Util.M.revertChangeDefaultMessage(commitSubject, revision));
- }
-
- @Override
- public void onSend() {
- ChangeApi.revert(
- project.get(),
- id.get(),
- getMessageText(),
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- sent = true;
- hide();
- Gerrit.display(PageLinks.toChange(result.projectNameKey(), result.legacyId()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- enableButtons(true);
- super.onFailure(caught);
- }
- });
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- super.onClose(event);
- b.setEnabled(true);
- }
- }.center();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReviewerSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReviewerSuggestOracle.java
deleted file mode 100644
index 4e464dfafb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReviewerSuggestOracle.java
+++ /dev/null
@@ -1,104 +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.client.change;
-
-import com.google.gerrit.client.admin.AdminConstants;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GroupBaseInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.AccountSuggestOracle;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** REST API based suggestion Oracle for reviewers. */
-public class ReviewerSuggestOracle extends HighlightSuggestOracle {
- private Project.NameKey project;
- private Change.Id changeId;
-
- @Override
- protected void onRequestSuggestions(Request req, Callback cb) {
- ChangeApi.suggestReviewers(project.get(), changeId.get(), req.getQuery(), req.getLimit(), false)
- .get(
- new GerritCallback<JsArray<SuggestReviewerInfo>>() {
- @Override
- public void onSuccess(JsArray<SuggestReviewerInfo> result) {
- List<RestReviewerSuggestion> r = new ArrayList<>(result.length());
- for (SuggestReviewerInfo reviewer : Natives.asList(result)) {
- r.add(new RestReviewerSuggestion(reviewer, req.getQuery()));
- }
- cb.onSuggestionsReady(req, new Response(r));
- }
-
- @Override
- public void onFailure(Throwable err) {
- List<Suggestion> r = Collections.emptyList();
- cb.onSuggestionsReady(req, new Response(r));
- }
- });
- }
-
- @Override
- public void requestDefaultSuggestions(Request req, Callback cb) {
- requestSuggestions(req, cb);
- }
-
- public void setChange(Project.NameKey project, Change.Id changeId) {
- this.project = project;
- this.changeId = changeId;
- }
-
- public static class RestReviewerSuggestion implements Suggestion {
- private final String displayString;
- private final String replacementString;
-
- RestReviewerSuggestion(SuggestReviewerInfo reviewer, String query) {
- if (reviewer.account() != null) {
- this.replacementString =
- AccountSuggestOracle.AccountSuggestion.format(reviewer.account(), query);
- this.displayString = replacementString;
- } else {
- this.replacementString = reviewer.group().name();
- this.displayString =
- replacementString + " (" + AdminConstants.I.suggestedGroupLabel() + ")";
- }
- }
-
- @Override
- public String getDisplayString() {
- return displayString;
- }
-
- @Override
- public String getReplacementString() {
- return replacementString;
- }
- }
-
- public static class SuggestReviewerInfo extends JavaScriptObject {
- public final native AccountInfo account() /*-{ return this.account; }-*/;
-
- public final native GroupBaseInfo group() /*-{ return this.group; }-*/;
-
- protected SuggestReviewerInfo() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
deleted file mode 100644
index 859af19b4c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.java
+++ /dev/null
@@ -1,328 +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.client.change;
-
-import com.google.gerrit.client.ConfirmationCallback;
-import com.google.gerrit.client.ConfirmationDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotSignedInDialog;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.ApprovalInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.RemoteSuggestBox;
-import com.google.gerrit.extensions.client.ReviewerState;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Add reviewers. */
-public class Reviewers extends Composite {
- interface Binder extends UiBinder<HTMLPanel, Reviewers> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField Element reviewersText;
- @UiField Image addReviewerIcon;
- @UiField Button addMe;
- @UiField Element form;
- @UiField Element error;
-
- @UiField(provided = true)
- RemoteSuggestBox suggestBox;
-
- private ChangeScreen.Style style;
- private Element ccText;
-
- private ReviewerSuggestOracle reviewerSuggestOracle;
- private Change.Id changeId;
- private Project.NameKey project;
-
- Reviewers() {
- reviewerSuggestOracle = new ReviewerSuggestOracle();
- suggestBox = new RemoteSuggestBox(reviewerSuggestOracle);
- suggestBox.enableDefaultSuggestions();
- suggestBox.setVisibleLength(55);
- suggestBox.setHintText(Util.C.approvalTableAddReviewerHint());
- suggestBox.addCloseHandler(
- new CloseHandler<RemoteSuggestBox>() {
- @Override
- public void onClose(CloseEvent<RemoteSuggestBox> event) {
- Reviewers.this.onCancel(null);
- }
- });
- suggestBox.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- addReviewer(event.getSelectedItem(), false);
- }
- });
-
- initWidget(uiBinder.createAndBindUi(this));
- addReviewerIcon.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- onOpenForm();
- }
- },
- ClickEvent.getType());
- }
-
- void init(ChangeScreen.Style style, Element ccText) {
- this.style = style;
- this.ccText = ccText;
- }
-
- void set(ChangeInfo info) {
- this.changeId = info.legacyId();
- this.project = info.projectNameKey();
- display(info);
- reviewerSuggestOracle.setChange(project, changeId);
- addReviewerIcon.setVisible(Gerrit.isSignedIn());
- }
-
- void onOpenForm() {
- UIObject.setVisible(form, true);
- UIObject.setVisible(error, false);
- addReviewerIcon.setVisible(false);
- suggestBox.setServeSuggestionsOnOracle(true);
- suggestBox.setFocus(true);
- }
-
- @UiHandler("add")
- void onAdd(@SuppressWarnings("unused") ClickEvent e) {
- addReviewer(suggestBox.getText(), false);
- }
-
- @UiHandler("addMe")
- void onAddMe(@SuppressWarnings("unused") ClickEvent e) {
- String accountId = String.valueOf(Gerrit.getUserAccount()._accountId());
- addReviewer(accountId, false);
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- addReviewerIcon.setVisible(true);
- UIObject.setVisible(form, false);
- suggestBox.setFocus(false);
- suggestBox.setText("");
- suggestBox.setServeSuggestionsOnOracle(false);
- }
-
- private void addReviewer(String reviewer, boolean confirmed) {
- if (reviewer.isEmpty()) {
- return;
- }
-
- ChangeApi.reviewers(project.get(), changeId.get())
- .post(
- PostInput.create(reviewer, confirmed),
- new GerritCallback<PostResult>() {
- @Override
- public void onSuccess(PostResult result) {
- if (result.confirm()) {
- askForConfirmation(result.error());
- } else if (result.error() != null) {
- UIObject.setVisible(error, true);
- error.setInnerText(result.error());
- } else {
- UIObject.setVisible(error, false);
- error.setInnerText("");
- suggestBox.setText("");
-
- if (result.reviewers() != null && result.reviewers().length() > 0) {
- updateReviewerList();
- }
- }
- }
-
- private void askForConfirmation(String text) {
- new ConfirmationDialog(
- Util.C.approvalTableAddManyReviewersConfirmationDialogTitle(),
- new SafeHtmlBuilder().append(text),
- new ConfirmationCallback() {
- @Override
- public void onOk() {
- addReviewer(reviewer, true);
- }
- })
- .center();
- }
-
- @Override
- public void onFailure(Throwable err) {
- if (isSigninFailure(err)) {
- new NotSignedInDialog().center();
- } else {
- UIObject.setVisible(error, true);
- error.setInnerText(
- err instanceof StatusCodeException
- ? ((StatusCodeException) err).getEncodedResponse()
- : err.getMessage());
- }
- }
- });
- }
-
- void updateReviewerList() {
- ChangeApi.detail(
- project.get(),
- changeId.get(),
- new GerritCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- display(result);
- }
- });
- }
-
- private void display(ChangeInfo info) {
- Map<ReviewerState, List<AccountInfo>> reviewers = info.reviewers();
- Map<Integer, AccountInfo> r = byAccount(reviewers, ReviewerState.REVIEWER);
- Map<Integer, AccountInfo> cc = byAccount(reviewers, ReviewerState.CC);
- for (Integer i : r.keySet()) {
- cc.remove(i);
- }
- cc.remove(info.owner()._accountId());
- Set<Integer> removable = info.removableReviewerIds();
- Map<Integer, VotableInfo> votable = votable(info);
-
- SafeHtml rHtml = Labels.formatUserList(style, r.values(), removable, null, votable);
- SafeHtml ccHtml = Labels.formatUserList(style, cc.values(), removable, null, votable);
-
- reviewersText.setInnerSafeHtml(rHtml);
- ccText.setInnerSafeHtml(ccHtml);
- if (Gerrit.isSignedIn()) {
- int currentUser = Gerrit.getUserAccount()._accountId();
- boolean showAddMeButton =
- info.owner()._accountId() != currentUser
- && !cc.containsKey(currentUser)
- && !r.containsKey(currentUser);
- addMe.setVisible(showAddMeButton);
- }
- }
-
- private static Map<Integer, AccountInfo> byAccount(
- Map<ReviewerState, List<AccountInfo>> reviewers, ReviewerState state) {
- List<AccountInfo> accounts = reviewers.get(state);
- if (accounts == null) {
- return Collections.emptyMap();
- }
- Map<Integer, AccountInfo> result = new HashMap<>();
- for (AccountInfo a : accounts) {
- result.put(a._accountId(), a);
- }
- return result;
- }
-
- private static Map<Integer, VotableInfo> votable(ChangeInfo change) {
- Map<Integer, VotableInfo> d = new HashMap<>();
- for (String name : change.labels()) {
- LabelInfo label = change.label(name);
- Short labelMaxValue =
- label.valueSet().isEmpty() ? null : LabelInfo.parseValue(label.maxValue());
- if (label.all() != null) {
- for (ApprovalInfo ai : Natives.asList(label.all())) {
- int id = ai._accountId();
- VotableInfo ad = d.get(id);
- if (ad == null) {
- ad = new VotableInfo();
- d.put(id, ad);
- }
- if (labelMaxValue != null
- && ai.permittedVotingRange() != null
- && ai.permittedVotingRange().max() == labelMaxValue) {
- ad.votable(name + " (" + label.maxValue() + ") ");
- } else if (ai.hasValue()) {
- ad.votable(name);
- }
- }
- }
- }
- return d;
- }
-
- public static class PostInput extends JavaScriptObject {
- public static PostInput create(String reviewer, boolean confirmed) {
- PostInput input = createObject().cast();
- input.init(reviewer, confirmed);
- return input;
- }
-
- private native void init(String reviewer, boolean confirmed) /*-{
- this.reviewer = reviewer;
- if (confirmed) {
- this.confirmed = true;
- }
- }-*/;
-
- protected PostInput() {}
- }
-
- public static class ReviewerInfo extends AccountInfo {
- final Set<String> approvals() {
- return Natives.keys(_approvals());
- }
-
- final native String approval(String l) /*-{ return this.approvals[l]; }-*/;
-
- private native NativeMap<NativeString> _approvals() /*-{ return this.approvals; }-*/;
-
- protected ReviewerInfo() {}
- }
-
- public static class PostResult extends JavaScriptObject {
- public final native JsArray<ReviewerInfo> reviewers() /*-{ return this.reviewers; }-*/;
-
- public final native boolean confirm() /*-{ return this.confirm || false; }-*/;
-
- public final native String error() /*-{ return this.error; }-*/;
-
- protected PostResult() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml
deleted file mode 100644
index cf506e5a7f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reviewers.ui.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:u='urn:import:com.google.gerrit.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- .suggestBox {
- margin-bottom: 2px;
- }
-
- .error {
- color: #D33D3D;
- font-weight: bold;
- }
-
- .addReviewer,
- .cancel {
- cursor: pointer;
- float: right;
- }
- </ui:style>
- <g:HTMLPanel>
- <div>
- <span ui:field='reviewersText'/>
- <g:Image ui:field='addReviewerIcon'
- resource='{ico.addUser}'
- styleName='{style.addReviewer}'
- title='Add Reviewer'/>
- </div>
- <div ui:field='form' style='display: none' aria-hidden='true'>
- <u:RemoteSuggestBox ui:field='suggestBox' styleName='{style.suggestBox}'/>
- <div ui:field='error'
- class='{style.error}'
- style='display: none' aria-hidden='true'/>
- <div>
- <g:Button ui:field='add' styleName='{res.style.button}'>
- <div>Add</div>
- </g:Button>
- <g:Button ui:field='addMe'
- styleName='{res.style.button}' visible='false'>
- <div>Add Me</div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </div>
- </g:HTMLPanel>
- </ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RightSidePopdownAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RightSidePopdownAction.java
deleted file mode 100644
index 1383c5de21..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RightSidePopdownAction.java
+++ /dev/null
@@ -1,81 +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.client.change;
-
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Style;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-
-abstract class RightSidePopdownAction {
- private final ChangeScreen.Style style;
- private final Widget button;
- private final UIObject relativeTo;
- private PopupPanel popup;
-
- RightSidePopdownAction(ChangeScreen.Style style, UIObject relativeTo, Widget button) {
- this.style = style;
- this.relativeTo = relativeTo;
- this.button = button;
- }
-
- abstract Widget getWidget();
-
- void show() {
- if (popup != null) {
- button.removeStyleName(style.selected());
- popup.hide();
- return;
- }
-
- final PopupPanel p =
- new PopupPanel(true) {
- @Override
- public void setPopupPosition(int left, int top) {
- top -= Document.get().getBodyOffsetTop();
-
- int w = Window.getScrollLeft() + Window.getClientWidth();
- int r = relativeTo.getAbsoluteLeft() + relativeTo.getOffsetWidth();
- int right = w - r;
- Style style = getElement().getStyle();
- style.clearProperty("left");
- style.setPropertyPx("right", right);
- style.setPropertyPx("top", top);
- }
- };
- p.setStyleName(style.replyBox());
- p.addAutoHidePartner(button.getElement());
- p.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- if (popup == p) {
- button.removeStyleName(style.selected());
- popup = null;
- }
- }
- });
- p.add(getWidget());
- p.showRelativeTo(relativeTo);
- GlobalKey.dialog(p);
- button.addStyleName(style.selected());
- popup = p;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/StarIcon.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/StarIcon.java
deleted file mode 100644
index b7bf8debc1..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/StarIcon.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2012 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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.ToggleButton;
-
-class StarIcon extends ToggleButton {
- StarIcon() {
- super(new Image(Gerrit.RESOURCES.starOpen()), new Image(Gerrit.RESOURCES.starFilled()));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
deleted file mode 100644
index 4446e65000..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitAction.java
+++ /dev/null
@@ -1,57 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.api.ChangeGlue;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.SubmitInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-
-class SubmitAction {
- static void call(ChangeInfo changeInfo, RevisionInfo revisionInfo) {
- if (ChangeGlue.onSubmitChange(changeInfo, revisionInfo)) {
- final Change.Id changeId = changeInfo.legacyId();
- ChangeApi.submit(
- changeInfo.project(),
- changeId.get(),
- revisionInfo.name(),
- new GerritCallback<SubmitInfo>() {
- @Override
- public void onSuccess(SubmitInfo result) {
- redisplay();
- }
-
- @Override
- public void onFailure(Throwable err) {
- if (SubmitFailureDialog.isConflict(err)) {
- new SubmitFailureDialog(err.getMessage()).center();
- } else {
- super.onFailure(err);
- }
- redisplay();
- }
-
- private void redisplay() {
- Gerrit.display(PageLinks.toChange(changeInfo.projectNameKey(), changeId));
- }
- });
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitFailureDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitFailureDialog.java
deleted file mode 100644
index 77bf217520..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/SubmitFailureDialog.java
+++ /dev/null
@@ -1,31 +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.client.change;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.changes.Util;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-
-class SubmitFailureDialog extends ErrorDialog {
- static boolean isConflict(Throwable err) {
- return err instanceof RemoteJsonException && 409 == ((RemoteJsonException) err).getCode();
- }
-
- SubmitFailureDialog(String msg) {
- super(new SafeHtmlBuilder().append(msg.trim()).wikify());
- setText(Util.C.submitFailed());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
deleted file mode 100644
index f5c921ba1c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.java
+++ /dev/null
@@ -1,148 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-/** Displays (and edits) the change topic string. */
-class Topic extends Composite {
- interface Binder extends UiBinder<HTMLPanel, Topic> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private PatchSet.Id psId;
- private Project.NameKey project;
- private boolean canEdit;
-
- @UiField Element show;
- @UiField InlineHyperlink text;
- @UiField Image editIcon;
-
- @UiField Element form;
- @UiField NpTextBox input;
- @UiField Button save;
- @UiField Button cancel;
-
- Topic() {
- initWidget(uiBinder.createAndBindUi(this));
- editIcon.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- onEdit();
- }
- },
- ClickEvent.getType());
- }
-
- void set(ChangeInfo info, String revision) {
- canEdit = info.hasActions() && info.actions().containsKey("topic");
-
- psId = new PatchSet.Id(info.legacyId(), info.revisions().get(revision)._number());
- project = info.projectNameKey();
-
- initTopicLink(info);
- editIcon.setVisible(canEdit);
- if (!canEdit) {
- show.setTitle(null);
- }
- }
-
- private void initTopicLink(ChangeInfo info) {
- if (info.topic() != null && !info.topic().isEmpty()) {
- String topic = info.topic();
- text.setText(topic);
- text.setTargetHistoryToken(PageLinks.topicQuery(info.status(), topic));
- }
- }
-
- boolean canEdit() {
- return canEdit;
- }
-
- void onEdit() {
- if (canEdit) {
- UIObject.setVisible(show, false);
- UIObject.setVisible(form, true);
-
- input.setText(text.getText());
- input.setFocus(true);
- input.selectAll();
- }
- }
-
- @UiHandler("cancel")
- void onCancel(@SuppressWarnings("unused") ClickEvent e) {
- input.setFocus(false);
- UIObject.setVisible(form, false);
- UIObject.setVisible(show, true);
- }
-
- @UiHandler("input")
- void onKeyDownInput(KeyDownEvent e) {
- if (e.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
- onCancel(null);
- } else if (e.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
- e.stopPropagation();
- e.preventDefault();
- onSave(null);
- }
- }
-
- @UiHandler("save")
- void onSave(@SuppressWarnings("unused") ClickEvent e) {
- ChangeApi.topic(
- project.get(),
- psId.getParentKey().get(),
- input.getValue().trim(),
- new GerritCallback<String>() {
- @Override
- public void onSuccess(String result) {
- Gerrit.display(PageLinks.toChange(project, psId));
- }
- });
- onCancel(null);
- }
-
- @UiHandler("save")
- void onSaveKeyPress(KeyPressEvent e) {
- if (e.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- e.stopPropagation();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml
deleted file mode 100644
index c2a6fbd43c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Topic.ui.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
- <ui:style gss='false'>
- .edit { cursor: pointer; }
- .edit, .cancel { float: right; }
- </ui:style>
- <g:HTMLPanel>
- <div ui:field='show'>
- <x:InlineHyperlink ui:field='text'
- title='Search for changes on this topic'/>
- <g:Image ui:field='editIcon'
- resource='{ico.edit}'
- styleName='{style.edit}'
- title='Edit topic (Shortcut: t)'/>
- </div>
-
- <div ui:field='form' style='display: none' aria-hidden='true'>
- <div>
- <c:NpTextBox ui:field='input' visibleLength='55'/>
- </div>
- <div>
- <g:Button ui:field='save' styleName='{res.style.button}'>
- <div>Update</div>
- </g:Button>
- <g:Button ui:field='cancel'
- styleName='{res.style.button}'
- addStyleNames='{style.cancel}'>
- <div>Cancel</div>
- </g:Button>
- </div>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
deleted file mode 100644
index 520dc69323..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.java
+++ /dev/null
@@ -1,77 +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.client.change;
-
-import com.google.gerrit.client.info.ChangeInfo.MessageInfo;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import java.sql.Timestamp;
-import java.util.HashSet;
-import java.util.List;
-
-/** Displays the "New Message From ..." panel in bottom right on updates. */
-abstract class UpdateAvailableBar extends Composite {
- interface Binder extends UiBinder<HTMLPanel, UpdateAvailableBar> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private Timestamp updated;
-
- @UiField Element author;
- @UiField Anchor show;
- @UiField Anchor ignore;
-
- UpdateAvailableBar() {
- initWidget(uiBinder.createAndBindUi(this));
- }
-
- void set(List<MessageInfo> newMessages, Timestamp newTime) {
- HashSet<Integer> seen = new HashSet<>();
- StringBuilder r = new StringBuilder();
- for (MessageInfo m : newMessages) {
- int a = m.author() != null ? m.author()._accountId() : 0;
- if (seen.add(a)) {
- if (r.length() > 0) {
- r.append(", ");
- }
- r.append(Message.authorName(m));
- }
- }
- author.setInnerText(r.toString());
- updated = newTime;
- }
-
- @UiHandler("show")
- void onShow(@SuppressWarnings("unused") ClickEvent e) {
- onShow();
- }
-
- @UiHandler("ignore")
- void onIgnore(@SuppressWarnings("unused") ClickEvent e) {
- onIgnore(updated);
- removeFromParent();
- }
-
- abstract void onShow();
-
- abstract void onIgnore(Timestamp newTime);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml
deleted file mode 100644
index 1d5592b83f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateAvailableBar.ui.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false'>
- .popup {
- position: fixed;
- bottom: 0;
- right: 0;
- z-index: 10;
- padding: 5px;
- }
- .bar {
- background-color: #fff1a8;
- border: 1px solid #ccc;
- padding: 5px 10px;
- font-size: 80%;
- color: #222;
- white-space: nowrap;
- width: auto;
- height: auto;
- }
- a.action {
- color: #222;
- text-decoration: underline;
- display: inline-block;
- margin-left: 0.5em;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.popup}'>
- <div class='{style.bar}'>
- <ui:msg>Update from <span ui:field='author'/></ui:msg>
- <g:Anchor ui:field='show'
- styleName='{style.action}'
- href='javascript:;'
- title='Refresh screen and display updates'>
- <ui:attribute name='title'/>
- <ui:msg>Show</ui:msg>
- </g:Anchor>
- <g:Anchor ui:field='ignore'
- styleName='{style.action}'
- href='javascript:;'
- title='Ignore this update'>
- <ui:attribute name='title'/>
- <ui:msg>Ignore</ui:msg>
- </g:Anchor>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateCheckTimer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateCheckTimer.java
deleted file mode 100644
index 4a5af0551d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/UpdateCheckTimer.java
+++ /dev/null
@@ -1,94 +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.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.ui.UserActivityMonitor;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-class UpdateCheckTimer extends Timer implements ValueChangeHandler<Boolean> {
- private static final int MAX_PERIOD = 3 * 60 * 1000;
- private static final int IDLE_PERIOD = 2 * 3600 * 1000;
- private static final int POLL_PERIOD = Gerrit.info().change().updateDelay() * 1000;
-
- private final ChangeScreen screen;
- private int delay;
- private boolean running;
-
- UpdateCheckTimer(ChangeScreen screen) {
- this.screen = screen;
- this.delay = POLL_PERIOD;
- }
-
- void schedule() {
- scheduleRepeating(delay);
- }
-
- @Override
- public void run() {
- if (!screen.isAttached()) {
- // screen should have cancelled this timer.
- cancel();
- return;
- } else if (running) {
- return;
- }
-
- running = true;
- screen.loadChangeInfo(
- false,
- new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo info) {
- running = false;
- screen.showUpdates(info);
-
- int d = UserActivityMonitor.isActive() ? POLL_PERIOD : IDLE_PERIOD;
- if (d != delay) {
- delay = d;
- schedule();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // On failures increase the delay time and try again,
- // but place an upper bound on the delay.
- running = false;
- delay =
- (int)
- Math.max(
- delay * (1.5 + Math.random()),
- UserActivityMonitor.isActive() ? MAX_PERIOD : IDLE_PERIOD + MAX_PERIOD);
- schedule();
- }
- });
- }
-
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- if (event.getValue()) {
- delay = POLL_PERIOD;
- run();
- } else {
- delay = IDLE_PERIOD;
- }
- schedule();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/VotableInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/VotableInfo.java
deleted file mode 100644
index 33d8d12c0c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/VotableInfo.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2014 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.client.change;
-
-import java.util.HashSet;
-import java.util.Set;
-
-class VotableInfo {
- private Set<String> votable;
-
- void votable(String label) {
- if (votable == null) {
- votable = new HashSet<>();
- }
- votable.add(label);
- }
-
- Set<String> votableLabels() {
- Set<String> s = new HashSet<>();
- if (votable != null) {
- s.addAll(votable);
- }
- return s;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/common.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/common.css
deleted file mode 100644
index bb7cb2780d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/common.css
+++ /dev/null
@@ -1,63 +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.
- */
-
-@eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-
-.popup {
- background-color: trimColor;
- min-width: 300px;
- min-height: 90px;
-}
-
-.popupContent {
- padding: 5px;
-}
-
-.button,
-.popup button,
-.popup input[type='button'] {
- margin: 0 3px 0 0;
- border-color: rgba(0, 0, 0, 0.15) !important;
- text-align: center;
- font-size: 11px;
- font-weight: bold;
- border: 2px solid;
- cursor: pointer;
- color: #fff;
- background-color: #4d90fe;
- background-image: -webkit-linear-gradient(top, #4d90fe, #4d90fe);
- -webkit-border-radius: 2px;
- -webkit-box-sizing: content-box;
-}
-
-.button:disabled,
-.popup button:disabled,
-.popup input[type='button']:disabled {
- background-color: #999;
- background-image: -webkit-linear-gradient(top, #999, #999);
-}
-
-.button div, .popup button div {
- width: 54px;
- white-space: nowrap;
- color: #fff;
- height: 14px;
- line-height: 14px;
-}
-
-.section {
- padding: 5px 5px;
- border-bottom: 1px solid #b8b8b8;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
deleted file mode 100644
index 6f514dfbfb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/file_table.css
+++ /dev/null
@@ -1,115 +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.
- */
-
-.pointer, .reviewed, .restoreDelete {
- padding: 0px;
- vertical-align: top;
-}
-.pointer {
- width: 12px;
-}
-.reviewed {
- height: 19px;
- width: 20px;
-}
-
-.table th {
- vertical-align: top;
- text-align: left;
-}
-.table tr {
- vertical-align: top;
-}
-.table tr:hover {
- background: rgba(209, 245, 248, 0.32);
-}
-.table tr.nohover:hover {
- background: transparent;
-}
-
-.status {
- padding-right: 4px;
- color: #888;
-}
-
-.pathColumn {
- white-space: nowrap;
- min-width: 600px;
-}
-.pathColumn a {
- color: #000;
- cursor: pointer;
-}
-.commonPrefix {
- color: #888;
-}
-.renameCopySource {
- color: #888;
- font-size: smaller;
-}
-
-.draftColumn,
-.newColumn,
-.commentColumn {
- white-space: nowrap;
-}
-.draftColumn {
- color: #d44;
- font-weight: bold;
-}
-.newColumn {
- font-weight: bold;
-}
-
-.deltaColumn1 {
- white-space: nowrap;
- text-align: right !important;
-}
-
-.deltaColumn2 {
- padding-left: 5px;
- white-space: nowrap;
- text-align: right;
-}
-
-.inserted {
- height: 10px;
- display: inline-block;
- background-color: #4d4;
-}
-
-.deleted {
- height: 10px;
- display: inline-block;
- background-color: #d44;
-}
-
-.restoreDelete div {
- white-space: nowrap;
-}
-
-.restoreDelete button {
- cursor: pointer;
- padding: 0;
- margin: 0 0 0 5px;
- border: 0;
- background-color: transparent;
- white-space: nowrap;
-}
-
-.error {
- color: #D33D3D;
- font-weight: bold;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/moreLess.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/moreLess.png
deleted file mode 100644
index 298514ffc8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/moreLess.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css
deleted file mode 100644
index 5e0e4023f1..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/related_changes.css
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Copyright (C) 2014 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.
- */
-
-@external .gwt-TabBarItem;
-@external .gwt-TabBarItem-disabled;
-@external .gwt-TabBarItem-selected;
-@external .gwt-TabBarRest;
-@external .gwt-TabPanelBottom;
-@eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-
-.row {
- white-space: nowrap;
-}
-
-.activeRow {
- background-color: selectionColor !important;
-}
-
-.activeRow .pointer {
- visibility: visible;
-}
-
-.pointer {
- display: inline-block;
- vertical-align: top;
- visibility: hidden;
-}
-
-.subject, .strikedSubject {
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- width: 355px;
-}
-.strikedSubject {
- text-decoration: line-through;
-}
-
-.tabPanel .gwt-TabBarItem,
-.tabPanel .gwt-TabBarItem-selected,
-.tabPanel .gwt-TabBarRest {
- background: none repeat scroll 0 0 #FFF !important;
-}
-
-.tabPanel .gwt-TabPanelBottom {
- padding: 2px 0 0 0;
-}
-
-.tabPanel .gwt-TabBarItem-selected {
- border-bottom-color: #000 !important;
- color: #000 !important;
-}
-
-.tabPanel .gwt-TabBarItem-disabled {
- display: none;
-}
-
-.current,
-.gitweb,
-.indirect,
-.notCurrent,
-.submittable {
- display: inline-block;
- text-align: center;
- vertical-align: top;
- width: 12px;
-}
-
-.gitweb {
- color: #000;
-}
-
-.indirect {
- color: #090; /* green */
- font-weight: bold;
-}
-
-.notCurrent {
- color: #FFA62F; /* orange */
-}
-
-.submittable {
- color: #090; /* green */
- font-weight: bold;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
deleted file mode 100644
index 7ec1102115..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ /dev/null
@@ -1,212 +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.client.changes;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotFoundScreen;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.Set;
-
-public class AccountDashboardScreen extends Screen implements ChangeListScreen {
- // If changing default options, also update in
- // ChangeIT#defaultSearchDoesNotTouchDatabase().
- private static final Set<ListChangesOption> MY_DASHBOARD_OPTIONS;
-
- static {
- EnumSet<ListChangesOption> options = EnumSet.copyOf(ChangeTable.OPTIONS);
- options.add(ListChangesOption.REVIEWED);
- MY_DASHBOARD_OPTIONS = Collections.unmodifiableSet(options);
- }
-
- private final Integer ownerId;
- private final boolean mine;
- private ChangeTable table;
- private ChangeTable.Section workInProgress;
- private ChangeTable.Section outgoing;
- private ChangeTable.Section incoming;
- private ChangeTable.Section closed;
-
- public AccountDashboardScreen(Integer accountId) {
- ownerId = accountId;
- mine = Gerrit.isSignedIn() && ownerId == Gerrit.getUserAccount()._accountId();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- table =
- new ChangeTable() {
- {
- keysNavigation.add(
- new KeyCommand(0, 'R', Util.C.keyReloadSearch()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(getToken());
- }
- });
- }
- };
- table.addStyleName(Gerrit.RESOURCES.css().accountDashboard());
-
- workInProgress = new ChangeTable.Section();
- outgoing = new ChangeTable.Section();
- incoming = new ChangeTable.Section();
- closed = new ChangeTable.Section();
-
- String who = mine ? "self" : ownerId.toString();
- workInProgress.setTitleWidget(
- new InlineHyperlink(
- Util.C.workInProgress(), PageLinks.toChangeQuery(queryWorkInProgress(who))));
- outgoing.setTitleWidget(
- new InlineHyperlink(Util.C.outgoingReviews(), PageLinks.toChangeQuery(queryOutgoing(who))));
- incoming.setTitleWidget(
- new InlineHyperlink(Util.C.incomingReviews(), PageLinks.toChangeQuery(queryIncoming(who))));
- incoming.setHighlightUnreviewed(mine);
- closed.setTitleWidget(
- new InlineHyperlink(Util.C.recentlyClosed(), PageLinks.toChangeQuery(queryClosed(who))));
-
- table.addSection(workInProgress);
- table.addSection(outgoing);
- table.addSection(incoming);
- table.addSection(closed);
- add(table);
- table.setSavePointerId("owner:" + ownerId);
- }
-
- private static String queryWorkInProgress(String who) {
- return "is:open is:wip owner:" + who;
- }
-
- private static String queryOutgoing(String who) {
- return "is:open -is:wip owner:" + who;
- }
-
- private static String queryIncoming(String who) {
- return "is:open ((reviewer:"
- + who
- + " -owner:"
- + who
- + " -is:ignored) OR assignee:"
- + who
- + ")";
- }
-
- private static String queryClosed(String who) {
- return "is:closed (owner:" + who + " OR reviewer:" + who + " OR assignee:" + who + ")";
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- String who = mine ? "self" : ownerId.toString();
- ChangeList.queryMultiple(
- new ScreenLoadCallback<JsArray<ChangeList>>(this) {
- @Override
- protected void preDisplay(JsArray<ChangeList> result) {
- display(result);
- }
- },
- mine ? MY_DASHBOARD_OPTIONS : DashboardTable.OPTIONS,
- queryWorkInProgress(who),
- queryOutgoing(who),
- queryIncoming(who),
- queryClosed(who) + " -age:4w limit:10");
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-
- private void display(JsArray<ChangeList> result) {
- if (!mine && !hasChanges(result)) {
- // When no results are returned and the data is not for the
- // current user, the target user is presumed to not exist.
- Gerrit.display(getToken(), new NotFoundScreen());
- return;
- }
-
- ChangeList wip = result.get(0);
- ChangeList out = result.get(1);
- ChangeList in = result.get(2);
- ChangeList done = result.get(3);
-
- if (mine) {
- setWindowTitle(Util.C.myDashboardTitle());
- setPageTitle(Util.C.myDashboardTitle());
- } else {
- // The server doesn't tell us who the dashboard is for. Try to guess
- // by looking at a change started by the owner and extract the name.
- String name = guessName(out);
- if (name == null) {
- name = guessName(done);
- }
- if (name != null) {
- setWindowTitle(name);
- setPageTitle(Util.M.accountDashboardTitle(name));
- } else {
- setWindowTitle(Util.C.unknownDashboardTitle());
- setWindowTitle(Util.C.unknownDashboardTitle());
- }
- }
-
- Natives.asList(out).sort(outComparator());
-
- table.updateColumnsForLabels(wip, out, in, done);
- workInProgress.display(wip);
- outgoing.display(out);
- incoming.display(in);
- closed.display(done);
- table.finishDisplay();
- }
-
- private Comparator<ChangeInfo> outComparator() {
- return comparing(ChangeInfo::created).thenComparing(ChangeInfo::_number);
- }
-
- private boolean hasChanges(JsArray<ChangeList> result) {
- for (ChangeList list : Natives.asList(result)) {
- if (list.length() != 0) {
- return true;
- }
- }
- return false;
- }
-
- private static String guessName(ChangeList list) {
- for (ChangeInfo change : Natives.asList(list)) {
- if (change.owner() != null && change.owner().name() != null) {
- return change.owner().name();
- }
- }
- return null;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
deleted file mode 100644
index 02be8c7639..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.IncludedInInfo;
-import com.google.gerrit.client.rpc.CallbackGroup.Callback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** A collection of static methods which work on the Gerrit REST API for specific changes. */
-public class ChangeApi {
- /** Abandon the change, ending its review. */
- public static void abandon(
- @Nullable String project, int id, String msg, AsyncCallback<ChangeInfo> cb) {
- MessageInput input = MessageInput.create();
- input.message(emptyToNull(msg));
- call(project, id, "abandon").post(input, cb);
- }
-
- /** Create a new work-in-progress change. */
- public static void createChange(
- String project,
- String branch,
- String topic,
- String subject,
- String base,
- AsyncCallback<ChangeInfo> cb) {
- CreateChangeInput input = CreateChangeInput.create();
- input.project(emptyToNull(project));
- input.branch(emptyToNull(branch));
- input.topic(emptyToNull(topic));
- input.subject(emptyToNull(subject));
- input.baseChange(emptyToNull(base));
- input.workInProgress(true);
-
- new RestApi("/changes/").post(input, cb);
- }
-
- /** Restore a previously abandoned change to be open again. */
- public static void restore(
- @Nullable String project, int id, String msg, AsyncCallback<ChangeInfo> cb) {
- MessageInput input = MessageInput.create();
- input.message(emptyToNull(msg));
- call(project, id, "restore").post(input, cb);
- }
-
- /** Create a new change that reverts the delta caused by this change. */
- public static void revert(
- @Nullable String project, int id, String msg, AsyncCallback<ChangeInfo> cb) {
- MessageInput input = MessageInput.create();
- input.message(emptyToNull(msg));
- call(project, id, "revert").post(input, cb);
- }
-
- /** Update the topic of a change. */
- public static void topic(
- @Nullable String project, int id, String topic, AsyncCallback<String> cb) {
- RestApi call = call(project, id, "topic");
- topic = emptyToNull(topic);
- if (topic != null) {
- TopicInput input = TopicInput.create();
- input.topic(topic);
- call.put(input, NativeString.unwrap(cb));
- } else {
- call.delete(NativeString.unwrap(cb));
- }
- }
-
- public static void detail(@Nullable String project, int id, AsyncCallback<ChangeInfo> cb) {
- detail(project, id).get(cb);
- }
-
- public static RestApi detail(@Nullable String project, int id) {
- return call(project, id, "detail");
- }
-
- public static RestApi blame(@Nullable String project, PatchSet.Id id, String path, boolean base) {
- return revision(project, id).view("files").id(path).view("blame").addParameter("base", base);
- }
-
- public static RestApi actions(@Nullable String project, int id, String revision) {
- if (revision == null || revision.equals("")) {
- revision = "current";
- }
- return call(project, id, revision, "actions");
- }
-
- public static void deleteAssignee(
- @Nullable String project, int id, AsyncCallback<AccountInfo> cb) {
- change(project, id).view("assignee").delete(cb);
- }
-
- public static void setAssignee(
- @Nullable String project, int id, String user, AsyncCallback<AccountInfo> cb) {
- AssigneeInput input = AssigneeInput.create();
- input.assignee(user);
- change(project, id).view("assignee").put(input, cb);
- }
-
- public static void markPrivate(
- @Nullable String project, int id, AsyncCallback<JavaScriptObject> cb) {
- change(project, id).view("private").post(PrivateInput.create(), cb);
- }
-
- public static void unmarkPrivate(
- @Nullable String project, int id, AsyncCallback<JavaScriptObject> cb) {
- change(project, id).view("private.delete").post(PrivateInput.create(), cb);
- }
-
- public static RestApi comments(@Nullable String project, int id) {
- return call(project, id, "comments");
- }
-
- public static RestApi drafts(@Nullable String project, int id) {
- return call(project, id, "drafts");
- }
-
- public static void edit(@Nullable String project, int id, AsyncCallback<EditInfo> cb) {
- edit(project, id).get(cb);
- }
-
- public static void editWithFiles(@Nullable String project, int id, AsyncCallback<EditInfo> cb) {
- edit(project, id).addParameterTrue("list").get(cb);
- }
-
- public static RestApi edit(@Nullable String project, int id) {
- return change(project, id).view("edit");
- }
-
- public static RestApi editWithCommands(@Nullable String project, int id) {
- return edit(project, id).addParameterTrue("download-commands");
- }
-
- public static void includedIn(
- @Nullable String project, int id, AsyncCallback<IncludedInInfo> cb) {
- call(project, id, "in").get(cb);
- }
-
- public static RestApi revision(@Nullable String project, int id, String revision) {
- return change(project, id).view("revisions").id(revision);
- }
-
- public static RestApi revision(@Nullable String project, PatchSet.Id id) {
- int cn = id.getParentKey().get();
- String revision = RevisionInfoCache.get(id);
- if (revision != null) {
- return revision(project, cn, revision);
- }
- return change(project, cn).view("revisions").id(id.get());
- }
-
- public static RestApi reviewers(@Nullable String project, int id) {
- return change(project, id).view("reviewers");
- }
-
- public static RestApi suggestReviewers(
- @Nullable String project, int id, String q, int n, boolean e) {
- RestApi api =
- change(project, id).view("suggest_reviewers").addParameter("n", n).addParameter("e", e);
- if (q != null) {
- api.addParameter("q", q);
- }
- return api;
- }
-
- public static RestApi vote(@Nullable String project, int id, int reviewer, String vote) {
- return reviewer(project, id, reviewer).view("votes").id(vote);
- }
-
- public static RestApi reviewer(@Nullable String project, int id, int reviewer) {
- return change(project, id).view("reviewers").id(reviewer);
- }
-
- public static RestApi reviewer(@Nullable String project, int id, String reviewer) {
- return change(project, id).view("reviewers").id(reviewer);
- }
-
- public static RestApi hashtags(@Nullable String project, int changeId) {
- return change(project, changeId).view("hashtags");
- }
-
- public static RestApi hashtag(@Nullable String project, int changeId, String hashtag) {
- return change(project, changeId).view("hashtags").id(hashtag);
- }
-
- /** Submit a specific revision of a change. */
- public static void cherrypick(
- String project,
- int id,
- String commit,
- String destination,
- String message,
- AsyncCallback<ChangeInfo> cb) {
- CherryPickInput cherryPickInput = CherryPickInput.create();
- cherryPickInput.setMessage(message);
- cherryPickInput.setDestination(destination);
- call(project, id, commit, "cherrypick").post(cherryPickInput, cb);
- }
-
- /** Move change to another branch. */
- public static void move(
- String project, int id, String destination, String message, AsyncCallback<ChangeInfo> cb) {
- MoveInput moveInput = MoveInput.create();
- moveInput.setMessage(message);
- moveInput.setDestinationBranch(destination);
- change(project, id).view("move").post(moveInput, cb);
- }
-
- /** Edit commit message for specific revision of a change. */
- public static void message(
- @Nullable String project,
- int id,
- String commit,
- String message,
- AsyncCallback<JavaScriptObject> cb) {
- CherryPickInput input = CherryPickInput.create();
- input.setMessage(message);
- call(project, id, commit, "message").post(input, cb);
- }
-
- /** Submit a specific revision of a change. */
- public static void submit(
- @Nullable String project, int id, String commit, AsyncCallback<SubmitInfo> cb) {
- JavaScriptObject in = JavaScriptObject.createObject();
- call(project, id, commit, "submit").post(in, cb);
- }
-
- /** Delete a specific draft change. */
- public static void deleteChange(
- @Nullable String project, int id, AsyncCallback<JavaScriptObject> cb) {
- change(project, id).delete(cb);
- }
-
- /** Delete change edit. */
- public static void deleteEdit(
- @Nullable String project, int id, AsyncCallback<JavaScriptObject> cb) {
- edit(project, id).delete(cb);
- }
-
- /** Publish change edit. */
- public static void publishEdit(
- @Nullable String project, int id, AsyncCallback<JavaScriptObject> cb) {
- JavaScriptObject in = JavaScriptObject.createObject();
- change(project, id).view("edit:publish").post(in, cb);
- }
-
- /** Rebase change edit on latest patch set. */
- public static void rebaseEdit(
- @Nullable String project, int id, AsyncCallback<JavaScriptObject> cb) {
- JavaScriptObject in = JavaScriptObject.createObject();
- change(project, id).view("edit:rebase").post(in, cb);
- }
-
- /** Rebase a revision onto the branch tip or another change. */
- public static void rebase(
- @Nullable String project, int id, String commit, String base, AsyncCallback<ChangeInfo> cb) {
- RebaseInput rebaseInput = RebaseInput.create();
- rebaseInput.setBase(base);
- call(project, id, commit, "rebase").post(rebaseInput, cb);
- }
-
- private static class MessageInput extends JavaScriptObject {
- final native void message(String m) /*-{ if(m)this.message=m; }-*/;
-
- static MessageInput create() {
- return (MessageInput) createObject();
- }
-
- protected MessageInput() {}
- }
-
- private static class AssigneeInput extends JavaScriptObject {
- final native void assignee(String a) /*-{ if(a)this.assignee=a; }-*/;
-
- static AssigneeInput create() {
- return (AssigneeInput) createObject();
- }
-
- protected AssigneeInput() {}
- }
-
- private static class TopicInput extends JavaScriptObject {
- final native void topic(String t) /*-{ if(t)this.topic=t; }-*/;
-
- static TopicInput create() {
- return (TopicInput) createObject();
- }
-
- protected TopicInput() {}
- }
-
- private static class CreateChangeInput extends JavaScriptObject {
- static CreateChangeInput create() {
- return (CreateChangeInput) createObject();
- }
-
- public final native void branch(String b) /*-{ if(b)this.branch=b; }-*/;
-
- public final native void topic(String t) /*-{ if(t)this.topic=t; }-*/;
-
- public final native void project(String p) /*-{ if(p)this.project=p; }-*/;
-
- public final native void subject(String s) /*-{ if(s)this.subject=s; }-*/;
-
- public final native void status(String s) /*-{ if(s)this.status=s; }-*/;
-
- public final native void baseChange(String b) /*-{ if(b)this.base_change=b; }-*/;
-
- public final native void workInProgress(Boolean b) /*-{ if(b)this.work_in_progress=b; }-*/;
-
- protected CreateChangeInput() {}
- }
-
- private static class CherryPickInput extends JavaScriptObject {
- static CherryPickInput create() {
- return (CherryPickInput) createObject();
- }
-
- final native void setDestination(String d) /*-{ this.destination = d; }-*/;
-
- final native void setMessage(String m) /*-{ this.message = m; }-*/;
-
- protected CherryPickInput() {}
- }
-
- private static class MoveInput extends JavaScriptObject {
- static MoveInput create() {
- return (MoveInput) createObject();
- }
-
- final native void setDestinationBranch(String d) /*-{ this.destination_branch = d; }-*/;
-
- final native void setMessage(String m) /*-{ this.message = m; }-*/;
-
- protected MoveInput() {}
- }
-
- private static class PrivateInput extends JavaScriptObject {
- static PrivateInput create() {
- return (PrivateInput) createObject();
- }
-
- final native void setMessage(String m) /*-{ this.message = m; }-*/;
-
- protected PrivateInput() {}
- }
-
- private static class RebaseInput extends JavaScriptObject {
- final native void setBase(String b) /*-{ this.base = b; }-*/;
-
- static RebaseInput create() {
- return (RebaseInput) createObject();
- }
-
- protected RebaseInput() {}
- }
-
- private static RestApi call(@Nullable String project, int id, String action) {
- return change(project, id).view(action);
- }
-
- private static RestApi call(@Nullable String project, int id, String commit, String action) {
- return change(project, id).view("revisions").id(commit).view(action);
- }
-
- public static RestApi change(@Nullable String project, int id) {
- if (project == null) {
- return new RestApi("/changes/").id(String.valueOf(id));
- }
- return new RestApi("/changes/").id(project, id);
- }
-
- public static String emptyToNull(String str) {
- return str == null || str.isEmpty() ? null : str;
- }
-
- public static void commitWithLinks(
- @Nullable String project, int changeId, String revision, Callback<CommitInfo> callback) {
- revision(project, changeId, revision).view("commit").addParameterTrue("links").get(callback);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
deleted file mode 100644
index aa6c4ec6f6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ /dev/null
@@ -1,187 +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.client.changes;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface ChangeConstants extends Constants {
- String statusLongNew();
-
- String statusLongMerged();
-
- String statusLongAbandoned();
-
- String statusLongDraft();
-
- String submittable();
-
- String readyToSubmit();
-
- String mergeConflict();
-
- String notCurrent();
-
- String isPrivate();
-
- String isWorkInProgress();
-
- String changeEdit();
-
- String myDashboardTitle();
-
- String unknownDashboardTitle();
-
- String workInProgress();
-
- String incomingReviews();
-
- String outgoingReviews();
-
- String recentlyClosed();
-
- String changeTableColumnSubject();
-
- String changeTableColumnSize();
-
- String changeTableColumnStatus();
-
- String changeTableColumnOwner();
-
- String changeTableColumnAssignee();
-
- String changeTableColumnProject();
-
- String changeTableColumnBranch();
-
- String changeTableColumnLastUpdate();
-
- String changeTableColumnID();
-
- String changeTableNone();
-
- String changeTableNotMergeable();
-
- String changeItemHelp();
-
- String changeTableStar();
-
- String changeTablePagePrev();
-
- String changeTablePageNext();
-
- String upToChangeList();
-
- String keyReloadChange();
-
- String keyNextPatchSet();
-
- String keyPreviousPatchSet();
-
- String keyReloadSearch();
-
- String keyPublishComments();
-
- String keyEditTopic();
-
- String keyAddReviewers();
-
- String keyExpandAllMessages();
-
- String keyCollapseAllMessages();
-
- String patchTableColumnName();
-
- String patchTableColumnComments();
-
- String patchTableColumnSize();
-
- String commitMessage();
-
- String mergeList();
-
- String patchTablePrev();
-
- String patchTableNext();
-
- String patchTableOpenDiff();
-
- String approvalTableEditAssigneeHint();
-
- String approvalTableAddReviewerHint();
-
- String approvalTableAddManyReviewersConfirmationDialogTitle();
-
- String changeInfoBlockUploaded();
-
- String changeInfoBlockUpdated();
-
- String messageNoAuthor();
-
- String sideBySide();
-
- String unifiedDiff();
-
- String buttonRevertChangeSend();
-
- String headingRevertMessage();
-
- String revertChangeTitle();
-
- String buttonCherryPickChangeSend();
-
- String headingCherryPickBranch();
-
- String cherryPickCommitMessage();
-
- String cherryPickTitle();
-
- String moveChangeSend();
-
- String headingMoveBranch();
-
- String moveChangeMessage();
-
- String moveTitle();
-
- String buttonRebaseChangeSend();
-
- String rebaseConfirmMessage();
-
- String rebaseNotPossibleMessage();
-
- String rebasePlaceholderMessage();
-
- String rebaseTitle();
-
- String baseDiffItem();
-
- String autoMerge();
-
- String pagedChangeListPrev();
-
- String pagedChangeListNext();
-
- String submitFailed();
-
- String votable();
-
- String pushCertMissing();
-
- String pushCertBad();
-
- String pushCertOk();
-
- String pushCertTrusted();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
deleted file mode 100644
index 2d5a9f9b6c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ /dev/null
@@ -1,103 +0,0 @@
-statusLongNew = Review in Progress
-statusLongMerged = Merged
-statusLongAbandoned = Abandoned
-statusLongDraft = Draft
-submittable = Submittable
-readyToSubmit = Ready to Submit
-mergeConflict = Merge Conflict
-notCurrent = Not Current
-changeEdit = Change Edit
-isPrivate = (Private)
-isWorkInProgress = (Work in Progress)
-
-myDashboardTitle = My Reviews
-unknownDashboardTitle = Code Review Dashboard
-workInProgress Work in progress
-incomingReviews = Incoming reviews
-outgoingReviews = Outgoing reviews
-recentlyClosed = Recently closed
-
-changeTableColumnSubject = Subject
-changeTableColumnSize = Size
-changeTableColumnStatus = Status
-changeTableColumnOwner = Owner
-changeTableColumnAssignee = Assignee
-changeTableColumnProject = Project
-changeTableColumnBranch = Branch
-changeTableColumnLastUpdate = Updated
-changeTableColumnID = ID
-changeTableNone = (None)
-changeTableNotMergeable = Merge Conflict
-
-changeItemHelp = change
-changeTableStar = Star (or unstar) change
-changeTablePagePrev = Previous page of changes
-changeTablePageNext = Next page of changes
-upToChangeList = Up to change list
-keyReloadChange = Reload change
-keyNextPatchSet = Next patch set
-keyPreviousPatchSet = Previous patch set
-keyReloadSearch = Reload change list
-keyPublishComments = Review and publish comments
-keyEditTopic = Edit change topic
-keyAddReviewers = Add reviewers
-keyExpandAllMessages = Expand all messages
-keyCollapseAllMessages = Collapse all messages
-
-patchTableColumnName = File Path
-patchTableColumnComments = Comments
-patchTableColumnSize = Size
-commitMessage = Commit Message
-mergeList = Merge List
-
-patchTablePrev = Previous file
-patchTableNext = Next file
-patchTableOpenDiff = Open diff
-
-approvalTableEditAssigneeHint = Name or Email
-
-approvalTableAddReviewerHint = Name or Email or Group
-approvalTableAddManyReviewersConfirmationDialogTitle = Adding Group Members as Reviewers
-
-changeInfoBlockUploaded = Uploaded
-changeInfoBlockUpdated = Updated
-
-messageNoAuthor = Gerrit Code Review
-
-sideBySide = Side by Side
-unifiedDiff = Unified Diff
-
-baseDiffItem = Base
-autoMerge = Auto Merge
-
-buttonRevertChangeSend = Revert Change
-headingRevertMessage = Revert Commit Message:
-revertChangeTitle = Code Review - Revert Merged Change
-
-buttonCherryPickChangeSend = Cherry Pick Change
-headingCherryPickBranch = Cherry Pick to Branch:
-cherryPickCommitMessage = Cherry Pick Commit Message:
-cherryPickTitle = Code Review - Cherry Pick Change to Another Branch
-
-headingMoveBranch = Move Change to Branch:
-moveChangeSend = Move Change
-moveChangeMessage = Move Change Message:
-moveTitle = Code Review - Move Change to Another Branch
-
-buttonRebaseChangeSend = Rebase
-rebaseConfirmMessage = Change parent revision
-rebaseNotPossibleMessage = Change is already up to date
-rebasePlaceholderMessage = (subject, change number, or leave empty)
-rebaseTitle = Code Review - Rebase Change
-
-pagedChangeListPrev = &#x21e6;Prev
-pagedChangeListNext = Next&#x21e8;
-
-submitFailed = Submit Failed
-
-votable = Votable:
-
-pushCertMissing = This patch set was created without a push certificate
-pushCertBad = Push certificate is invalid
-pushCertOk = Push certificate is valid, but key is not trusted
-pushCertTrusted = Push certificate is valid and key is trusted
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
deleted file mode 100644
index 71b54f7d56..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeEditApi.java
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (C) 2014 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.client.changes;
-
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.editor.EditFileInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.HttpCallback;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** REST API helpers to remotely edit a change. */
-public class ChangeEditApi {
- /** Get file (or commit message) contents. */
- public static void get(
- @Nullable Project.NameKey project,
- PatchSet.Id id,
- String path,
- boolean base,
- HttpCallback<NativeString> cb) {
- RestApi api;
- if (id.get() != 0) {
- // Read from a published revision, when change edit doesn't
- // exist for the caller, or is not currently active.
- api =
- ChangeApi.revision(Project.NameKey.asStringOrNull(project), id)
- .view("files")
- .id(path)
- .view("content");
- } else if (Patch.COMMIT_MSG.equals(path)) {
- api =
- editMessage(Project.NameKey.asStringOrNull(project), id.getParentKey().get())
- .addParameter("base", base);
- } else {
- api =
- editFile(Project.NameKey.asStringOrNull(project), id.getParentKey().get(), path)
- .addParameter("base", base);
- }
- api.get(cb);
- }
-
- /** Get file (or commit message) contents of the edit. */
- public static void get(
- @Nullable Project.NameKey project,
- PatchSet.Id id,
- String path,
- HttpCallback<NativeString> cb) {
- get(project, id, path, false, cb);
- }
-
- /** Get meta info for change edit. */
- public static void getMeta(
- @Nullable String project, PatchSet.Id id, String path, AsyncCallback<EditFileInfo> cb) {
- if (id.get() != 0) {
- throw new IllegalStateException("only supported for edits");
- }
- editFile(project, id.getParentKey().get(), path).view("meta").get(cb);
- }
-
- /** Put message into a change edit. */
- public static void putMessage(
- @Nullable String project, int id, String m, GerritCallback<VoidResult> cb) {
- editMessage(project, id).put(m, cb);
- }
-
- /** Put contents into a file or commit message in a change edit. */
- public static void put(
- @Nullable String project,
- int id,
- String path,
- String content,
- GerritCallback<VoidResult> cb) {
- if (Patch.COMMIT_MSG.equals(path)) {
- putMessage(project, id, content, cb);
- } else {
- editFile(project, id, path).put(content, cb);
- }
- }
-
- /** Delete a file in the pending edit. */
- public static void delete(
- @Nullable String project, int id, String path, AsyncCallback<VoidResult> cb) {
- editFile(project, id, path).delete(cb);
- }
-
- /** Rename a file in the pending edit. */
- public static void rename(
- @Nullable String project, int id, String path, String newPath, AsyncCallback<VoidResult> cb) {
- Input in = Input.create();
- in.oldPath(path);
- in.newPath(newPath);
- ChangeApi.edit(project, id).post(in, cb);
- }
-
- /** Restore (undo delete/modify) a file in the pending edit. */
- public static void restore(
- @Nullable String project, int id, String path, AsyncCallback<VoidResult> cb) {
- Input in = Input.create();
- in.restorePath(path);
- ChangeApi.edit(project, id).post(in, cb);
- }
-
- private static RestApi editMessage(@Nullable String project, int id) {
- return ChangeApi.change(project, id).view("edit:message");
- }
-
- private static RestApi editFile(@Nullable String project, int id, String path) {
- return ChangeApi.edit(project, id).id(path);
- }
-
- private static class Input extends JavaScriptObject {
- static Input create() {
- return createObject().cast();
- }
-
- final native void restorePath(String p) /*-{ this.restore_path=p }-*/;
-
- final native void oldPath(String p) /*-{ this.old_path=p }-*/;
-
- final native void newPath(String p) /*-{ this.new_path=p }-*/;
-
- protected Input() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
deleted file mode 100644
index 5d525b663a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeList.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.KeyUtil;
-import java.util.Set;
-
-/** List of changes available from {@code /changes/}. */
-public class ChangeList extends JsArray<ChangeInfo> {
- private static final String URI = "/changes/";
-
- /** Run multiple queries in a single remote invocation. */
- public static void queryMultiple(
- final AsyncCallback<JsArray<ChangeList>> callback,
- Set<ListChangesOption> options,
- String... queries) {
- if (queries.length == 0) {
- return;
- }
- RestApi call = new RestApi(URI);
- for (String q : queries) {
- call.addParameterRaw("q", KeyUtil.encode(q));
- }
- addOptions(call, options);
- if (queries.length == 1) {
- // Server unwraps a single query, so wrap it back in an array for the
- // callback.
- call.get(
- new AsyncCallback<ChangeList>() {
- @Override
- public void onSuccess(ChangeList result) {
- JsArray<ChangeList> wrapped = JsArray.createArray(1).cast();
- wrapped.set(0, result);
- callback.onSuccess(wrapped);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- callback.onFailure(caught);
- }
- });
- } else {
- call.get(callback);
- }
- }
-
- public static void query(
- String query, Set<ListChangesOption> options, AsyncCallback<ChangeList> callback) {
- query(query, options, callback, 0, 0);
- }
-
- public static void query(
- String query,
- Set<ListChangesOption> options,
- AsyncCallback<ChangeList> callback,
- int start,
- int limit) {
- RestApi call = newQuery(query);
- if (limit > 0) {
- call.addParameter("n", limit);
- }
- addOptions(call, options);
- if (start != 0) {
- call.addParameter("S", start);
- }
- call.get(callback);
- }
-
- public static void addOptions(RestApi call, Set<ListChangesOption> s) {
- call.addParameterRaw("O", Integer.toHexString(ListChangesOption.toBits(s)));
- }
-
- private static RestApi newQuery(String query) {
- RestApi call = new RestApi(URI);
- // The server default is ?q=status:open so don't repeat it.
- if (!"status:open".equals(query) && !"is:open".equals(query)) {
- call.addParameterRaw("q", KeyUtil.encode(query));
- }
- return call;
- }
-
- protected ChangeList() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeListScreen.java
deleted file mode 100644
index ed5a6f22cd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeListScreen.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (C) 2010 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.client.changes;
-
-public interface ChangeListScreen {}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
deleted file mode 100644
index c64fe912ad..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
+++ /dev/null
@@ -1,57 +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.client.changes;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface ChangeMessages extends Messages {
- String accountDashboardTitle(String fullName);
-
- String revertChangeDefaultMessage(String commitMsg, String commitId);
-
- String cherryPickedChangeDefaultMessage(String commitMsg, String commitId);
-
- String changeScreenTitleId(String changeId);
-
- String loadingPatchSet(int id);
-
- String patchTableSize_Modify(int insertions, int deletions);
-
- String patchTableSize_ModifyBinaryFiles(String bytesInserted, String bytesDeleted);
-
- String patchTableSize_ModifyBinaryFilesWithPercentages(
- String bytesInserted,
- String percentageInserted,
- String bytesDeleted,
- String percentageDeleted);
-
- String patchTableSize_LongModify(int insertions, int deletions);
-
- String removeReviewer(String fullName);
-
- String removeVote(String label);
-
- String blockedOn(String labelName);
-
- String needs(String labelName);
-
- String changeQueryWindowTitle(String query);
-
- String changeQueryPageTitle(String query);
-
- String insertionsAndDeletions(int insertions, int deletions);
-
- String diffBaseParent(int parentNum);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
deleted file mode 100644
index 2b68492c73..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
+++ /dev/null
@@ -1,27 +0,0 @@
-# Changes to this file should also be made in
-# gerrit-server/src/main/resources/com/google/gerrit/server/change/ChangeMessages.properties
-accountDashboardTitle = Code Review Dashboard for {0}
-
-revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}.
-cherryPickedChangeDefaultMessage = {0}\n(cherry picked from commit {1})
-
-changeScreenTitleId = Change {0}
-loadingPatchSet = Loading Patch Set {0} ...
-
-patchTableSize_Modify = +{0}, -{1}
-patchTableSize_ModifyBinaryFiles = +{0}, -{1}
-patchTableSize_ModifyBinaryFilesWithPercentages = +{0} (+{1}), -{2} (-{3})
-patchTableSize_LongModify = {0} inserted, {1} deleted
-
-removeReviewer = Remove reviewer {0}
-removeVote = Remove vote {0}
-
-blockedOn = Blocked on {0} Label
-needs = Needs {0} Label
-
-changeQueryWindowTitle = {0}
-changeQueryPageTitle = Search for {0}
-
-insertionsAndDeletions = +{0}, -{1}
-
-diffBaseParent = Parent {0}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
deleted file mode 100644
index 4fda78bd5e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ /dev/null
@@ -1,558 +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.client.changes;
-
-import static com.google.gerrit.client.FormatUtil.relativeFormat;
-import static com.google.gerrit.client.FormatUtil.shortFormat;
-import static java.util.stream.Collectors.toList;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.AccountLinkPanel;
-import com.google.gerrit.client.ui.BranchLink;
-import com.google.gerrit.client.ui.ChangeLink;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
-import com.google.gerrit.client.ui.ProjectLink;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.ReviewCategoryStrategy;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-public class ChangeTable extends NavigationTable<ChangeInfo> {
- // If changing default options, also update in
- // ChangeIT#defaultSearchDoesNotTouchDatabase().
- static final Set<ListChangesOption> OPTIONS =
- Collections.unmodifiableSet(
- EnumSet.of(ListChangesOption.LABELS, ListChangesOption.DETAILED_ACCOUNTS));
-
- private static final int C_STAR = 1;
- private static final int C_ID = 2;
- private static final int C_SUBJECT = 3;
- private static final int C_STATUS = 4;
- private static final int C_OWNER = 5;
- private static final int C_ASSIGNEE = 6;
- private static final int C_PROJECT = 7;
- private static final int C_BRANCH = 8;
- private static final int C_LAST_UPDATE = 9;
- private static final int C_SIZE = 10;
- private static final int BASE_COLUMNS = 11;
-
- private final List<Section> sections;
- private int columns;
- private final boolean showAssignee;
- private final boolean showLegacyId;
- private List<String> labelNames;
-
- public ChangeTable() {
- super(Util.C.changeItemHelp());
- columns = BASE_COLUMNS;
- labelNames = Collections.emptyList();
- showAssignee = Gerrit.info().change().showAssigneeInChangesTable();
- showLegacyId = Gerrit.getUserPreferences().legacycidInChangeTable();
-
- if (Gerrit.isSignedIn()) {
- keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar()));
- }
-
- sections = new ArrayList<>();
- table.setText(0, C_STAR, "");
- table.setText(0, C_ID, Util.C.changeTableColumnID());
- table.setText(0, C_SUBJECT, Util.C.changeTableColumnSubject());
- table.setText(0, C_STATUS, Util.C.changeTableColumnStatus());
- table.setText(0, C_OWNER, Util.C.changeTableColumnOwner());
- table.setText(0, C_ASSIGNEE, Util.C.changeTableColumnAssignee());
- table.setText(0, C_PROJECT, Util.C.changeTableColumnProject());
- table.setText(0, C_BRANCH, Util.C.changeTableColumnBranch());
- table.setText(0, C_LAST_UPDATE, Util.C.changeTableColumnLastUpdate());
- table.setText(0, C_SIZE, Util.C.changeTableColumnSize());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, C_STAR, Gerrit.RESOURCES.css().iconHeader());
- for (int i = C_ID; i < columns; i++) {
- fmt.addStyleName(0, i, Gerrit.RESOURCES.css().dataHeader());
- }
- if (!showLegacyId) {
- fmt.addStyleName(0, C_ID, Gerrit.RESOURCES.css().dataHeaderHidden());
- }
- if (!showAssignee) {
- fmt.addStyleName(0, C_ASSIGNEE, Gerrit.RESOURCES.css().dataHeaderHidden());
- }
-
- table.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final Cell cell = table.getCellForEvent(event);
- if (cell == null) {
- return;
- }
- if (cell.getCellIndex() == C_STAR) {
- // Don't do anything (handled by star itself).
- } else if (cell.getCellIndex() == C_STATUS) {
- // Don't do anything.
- } else if (cell.getCellIndex() == C_OWNER) {
- // Don't do anything.
- } else if (getRowItem(cell.getRowIndex()) != null) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
- }
-
- @Override
- protected Object getRowItemKey(ChangeInfo item) {
- return item.legacyId();
- }
-
- @Override
- protected void onOpenRow(int row) {
- final ChangeInfo c = getRowItem(row);
- Gerrit.display(PageLinks.toChange(c.projectNameKey(), c.legacyId()));
- }
-
- private void insertNoneRow(int row) {
- insertRow(row);
- table.setText(row, 0, Util.C.changeTableNone());
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setColSpan(row, 0, columns);
- fmt.setStyleName(row, 0, Gerrit.RESOURCES.css().emptySection());
- }
-
- private void insertChangeRow(int row) {
- insertRow(row);
- applyDataRowStyle(row);
- }
-
- @Override
- protected void applyDataRowStyle(int row) {
- super.applyDataRowStyle(row);
- final CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(row, C_STAR, Gerrit.RESOURCES.css().iconCell());
- for (int i = C_ID; i < columns; i++) {
- fmt.addStyleName(row, i, Gerrit.RESOURCES.css().dataCell());
- }
- if (!showLegacyId) {
- fmt.addStyleName(row, C_ID, Gerrit.RESOURCES.css().dataCellHidden());
- }
- fmt.addStyleName(row, C_SUBJECT, Gerrit.RESOURCES.css().cSUBJECT());
- fmt.addStyleName(row, C_STATUS, Gerrit.RESOURCES.css().cSTATUS());
- fmt.addStyleName(row, C_OWNER, Gerrit.RESOURCES.css().cOWNER());
- fmt.addStyleName(
- row,
- C_ASSIGNEE,
- showAssignee
- ? Gerrit.RESOURCES.css().cASSIGNEE()
- : Gerrit.RESOURCES.css().dataCellHidden());
- fmt.addStyleName(row, C_LAST_UPDATE, Gerrit.RESOURCES.css().cLastUpdate());
- fmt.addStyleName(row, C_SIZE, Gerrit.RESOURCES.css().cSIZE());
-
- for (int i = C_SIZE + 1; i < columns; i++) {
- fmt.addStyleName(row, i, Gerrit.RESOURCES.css().cAPPROVAL());
- }
- }
-
- public void updateColumnsForLabels(ChangeList... lists) {
- labelNames =
- Arrays.stream(lists)
- .flatMap(l -> Natives.asList(l).stream())
- .flatMap(c -> c.labels().stream())
- .distinct()
- .sorted()
- .collect(toList());
-
- int baseColumns = BASE_COLUMNS;
- if (baseColumns + labelNames.size() < columns) {
- int n = columns - (baseColumns + labelNames.size());
- for (int row = 0; row < table.getRowCount(); row++) {
- table.removeCells(row, columns, n);
- }
- }
- columns = baseColumns + labelNames.size();
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- for (int i = 0; i < labelNames.size(); i++) {
- String name = labelNames.get(i);
- int col = baseColumns + i;
-
- String abbrev = getAbbreviation(name, "-");
- table.setText(0, col, abbrev);
- table.getCellFormatter().getElement(0, col).setTitle(name);
- fmt.addStyleName(0, col, Gerrit.RESOURCES.css().dataHeader());
- }
-
- for (Section s : sections) {
- if (s.titleRow >= 0) {
- fmt.setColSpan(s.titleRow, 0, columns);
- }
- }
- }
-
- private void populateChangeRow(int row, ChangeInfo c, boolean highlightUnreviewed) {
- CellFormatter fmt = table.getCellFormatter();
- if (Gerrit.isSignedIn()) {
- table.setWidget(row, C_STAR, StarredChanges.createIcon(c.legacyId(), c.starred()));
- }
- table.setWidget(row, C_ID, new TableChangeLink(String.valueOf(c.legacyId()), c));
-
- String subject = Util.cropSubject(c.subject());
- table.setWidget(row, C_SUBJECT, new TableChangeLink(subject, c));
-
- Change.Status status = c.status();
- if (status != Change.Status.NEW) {
- table.setText(
- row,
- C_STATUS,
- Util.toLongString(status) + (c.isPrivate() ? (" " + Util.C.isPrivate()) : ""));
- } else if (c.isWorkInProgress()) {
- table.setText(
- row,
- C_STATUS,
- Util.C.workInProgress() + (c.isPrivate() ? (" " + Util.C.isPrivate()) : ""));
- } else if (!c.mergeable()) {
- table.setText(
- row,
- C_STATUS,
- Util.C.changeTableNotMergeable() + (c.isPrivate() ? (" " + Util.C.isPrivate()) : ""));
- } else if (c.isPrivate()) {
- table.setText(row, C_STATUS, Util.C.isPrivate());
- }
-
- if (c.owner() != null) {
- table.setWidget(row, C_OWNER, AccountLinkPanel.withStatus(c.owner(), status));
- } else {
- table.setText(row, C_OWNER, "");
- }
-
- if (showAssignee) {
- if (c.assignee() != null) {
- table.setWidget(row, C_ASSIGNEE, AccountLinkPanel.forAssignee(c.assignee()));
- if (Gerrit.getUserPreferences().highlightAssigneeInChangeTable()
- && Objects.equals(c.assignee()._accountId(), Gerrit.getUserAccount()._accountId())) {
- table.getRowFormatter().addStyleName(row, Gerrit.RESOURCES.css().cASSIGNEDTOME());
- }
- } else {
- table.setText(row, C_ASSIGNEE, "");
- }
- }
-
- table.setWidget(row, C_PROJECT, new ProjectLink(c.projectNameKey()));
- table.setWidget(
- row, C_BRANCH, new BranchLink(c.projectNameKey(), c.status(), c.branch(), c.topic()));
- if (Gerrit.getUserPreferences().relativeDateInChangeTable()) {
- table.setText(row, C_LAST_UPDATE, relativeFormat(c.updated()));
- } else {
- table.setText(row, C_LAST_UPDATE, shortFormat(c.updated()));
- }
-
- int col = C_SIZE;
- if (!Gerrit.getUserPreferences().sizeBarInChangeTable()) {
- table.setText(row, col, Util.M.insertionsAndDeletions(c.insertions(), c.deletions()));
- } else {
- table.setWidget(row, col, getSizeWidget(c));
- fmt.getElement(row, col)
- .setTitle(Util.M.insertionsAndDeletions(c.insertions(), c.deletions()));
- }
- col++;
-
- for (int idx = 0; idx < labelNames.size(); idx++, col++) {
- String name = labelNames.get(idx);
-
- LabelInfo label = c.label(name);
- if (label == null) {
- fmt.getElement(row, col).setTitle(Gerrit.C.labelNotApplicable());
- fmt.addStyleName(row, col, Gerrit.RESOURCES.css().labelNotApplicable());
- continue;
- }
-
- String user;
- String info;
- ReviewCategoryStrategy reviewCategoryStrategy =
- Gerrit.getUserPreferences().reviewCategoryStrategy();
- if (label.rejected() != null) {
- user = label.rejected().name();
- info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.rejected());
- if (info != null) {
- FlowPanel panel = new FlowPanel();
- panel.add(new Image(Gerrit.RESOURCES.redNot()));
- panel.add(new InlineLabel(info));
- table.setWidget(row, col, panel);
- } else {
- table.setWidget(row, col, new Image(Gerrit.RESOURCES.redNot()));
- }
- } else if (label.approved() != null) {
- user = label.approved().name();
- info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.approved());
- if (info != null) {
- FlowPanel panel = new FlowPanel();
- panel.add(new Image(Gerrit.RESOURCES.greenCheck()));
- panel.add(new InlineLabel(info));
- table.setWidget(row, col, panel);
- } else {
- table.setWidget(row, col, new Image(Gerrit.RESOURCES.greenCheck()));
- }
- } else if (label.disliked() != null) {
- user = label.disliked().name();
- info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.disliked());
- String vstr = String.valueOf(label._value());
- if (info != null) {
- vstr = vstr + " " + info;
- }
- fmt.addStyleName(row, col, Gerrit.RESOURCES.css().negscore());
- table.setText(row, col, vstr);
- } else if (label.recommended() != null) {
- user = label.recommended().name();
- info = getReviewCategoryDisplayInfo(reviewCategoryStrategy, label.recommended());
- String vstr = "+" + label._value();
- if (info != null) {
- vstr = vstr + " " + info;
- }
- fmt.addStyleName(row, col, Gerrit.RESOURCES.css().posscore());
- table.setText(row, col, vstr);
- } else {
- table.clearCell(row, col);
- continue;
- }
- fmt.addStyleName(row, col, Gerrit.RESOURCES.css().singleLine());
-
- if (user != null) {
- // Some web browsers ignore the embedded newline; some like it;
- // so we include a space before the newline to accommodate both.
- fmt.getElement(row, col).setTitle(name + " \nby " + user);
- }
- }
-
- boolean needHighlight = false;
- if (highlightUnreviewed && !c.reviewed()) {
- needHighlight = true;
- }
- final Element tr = fmt.getElement(row, 0).getParentElement();
- UIObject.setStyleName(tr, Gerrit.RESOURCES.css().needsReview(), needHighlight);
-
- setRowItem(row, c);
- }
-
- private static String getReviewCategoryDisplayInfo(
- ReviewCategoryStrategy reviewCategoryStrategy, AccountInfo accountInfo) {
- switch (reviewCategoryStrategy) {
- case NAME:
- return accountInfo.name();
- case EMAIL:
- return accountInfo.email();
- case USERNAME:
- return accountInfo.username();
- case ABBREV:
- return getAbbreviation(accountInfo.name(), " ");
- case NONE:
- default:
- return null;
- }
- }
-
- private static String getAbbreviation(String name, String token) {
- StringBuilder abbrev = new StringBuilder();
- if (name != null) {
- for (String t : name.split(token)) {
- abbrev.append(t.substring(0, 1).toUpperCase());
- }
- }
- return abbrev.toString();
- }
-
- private static Widget getSizeWidget(ChangeInfo c) {
- int largeChangeSize = Gerrit.info().change().largeChange();
- int changedLines = c.insertions() + c.deletions();
- int p = 100;
- if (changedLines < largeChangeSize) {
- p = changedLines * 100 / largeChangeSize;
- }
-
- int width = Math.max(2, 70 * p / 100);
- int red = p >= 50 ? 255 : (int) Math.round((p) * 5.12);
- int green = p <= 50 ? 255 : (int) Math.round(256 - (p - 50) * 5.12);
- String bg = "#" + toHex(red) + toHex(green) + "00";
-
- SimplePanel panel = new SimplePanel();
- panel.setStyleName(Gerrit.RESOURCES.css().changeSize());
- panel.setWidth(width + "px");
- panel.getElement().getStyle().setBackgroundColor(bg);
- return panel;
- }
-
- private static String toHex(int i) {
- String hex = Integer.toHexString(i);
- return hex.length() == 1 ? "0" + hex : hex;
- }
-
- public void addSection(Section s) {
- assert s.parent == null;
-
- s.parent = this;
- s.titleRow = table.getRowCount();
- if (s.displayTitle()) {
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setColSpan(s.titleRow, 0, columns);
- fmt.addStyleName(s.titleRow, 0, Gerrit.RESOURCES.css().sectionHeader());
- } else {
- s.titleRow = -1;
- }
-
- s.dataBegin = table.getRowCount();
- insertNoneRow(s.dataBegin);
- sections.add(s);
- }
-
- private int insertRow(int beforeRow) {
- for (Section s : sections) {
- if (beforeRow <= s.titleRow) {
- s.titleRow++;
- }
- if (beforeRow < s.dataBegin) {
- s.dataBegin++;
- }
- }
- return table.insertRow(beforeRow);
- }
-
- private void removeRow(int row) {
- for (Section s : sections) {
- if (row < s.titleRow) {
- s.titleRow--;
- }
- if (row < s.dataBegin) {
- s.dataBegin--;
- }
- }
- table.removeRow(row);
- }
-
- public class StarKeyCommand extends NeedsSignInKeyCommand {
- public StarKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- int row = getCurrentRow();
- ChangeInfo c = getRowItem(row);
- if (c != null && Gerrit.isSignedIn()) {
- ((StarredChanges.Icon) table.getWidget(row, C_STAR)).toggleStar();
- }
- }
- }
-
- private final class TableChangeLink extends ChangeLink {
- private TableChangeLink(String text, ChangeInfo c) {
- super(c.projectNameKey(), c.legacyId(), text);
- }
-
- @Override
- public void go() {
- movePointerTo(cid);
- super.go();
- }
- }
-
- public static class Section {
- ChangeTable parent;
- String titleText;
- Widget titleWidget;
- int titleRow = -1;
- int dataBegin;
- int rows;
- private boolean highlightUnreviewed;
-
- public void setHighlightUnreviewed(boolean value) {
- this.highlightUnreviewed = value;
- }
-
- public void setTitleText(String text) {
- titleText = text;
- titleWidget = null;
- if (titleRow >= 0) {
- parent.table.setText(titleRow, 0, titleText);
- }
- }
-
- public void setTitleWidget(Widget title) {
- titleWidget = title;
- titleText = null;
- if (titleRow >= 0) {
- parent.table.setWidget(titleRow, 0, title);
- }
- }
-
- public boolean displayTitle() {
- if (titleText != null) {
- setTitleText(titleText);
- return true;
- } else if (titleWidget != null) {
- setTitleWidget(titleWidget);
- return true;
- }
- return false;
- }
-
- public void display(ChangeList changeList) {
- final int sz = changeList != null ? changeList.length() : 0;
- final boolean hadData = rows > 0;
-
- if (hadData) {
- while (sz < rows) {
- parent.removeRow(dataBegin);
- rows--;
- }
- } else {
- parent.removeRow(dataBegin);
- }
-
- if (sz == 0) {
- parent.insertNoneRow(dataBegin);
- return;
- }
-
- while (rows < sz) {
- parent.insertChangeRow(dataBegin + rows);
- rows++;
- }
- for (int i = 0; i < sz; i++) {
- parent.populateChangeRow(dataBegin + i, changeList.get(i), highlightUnreviewed);
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentApi.java
deleted file mode 100644
index 987b3820fc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentApi.java
+++ /dev/null
@@ -1,77 +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.client.changes;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-public class CommentApi {
-
- public static void comments(
- @Nullable String project, PatchSet.Id id, AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
- revision(project, id, "comments").get(cb);
- }
-
- public static void comment(
- @Nullable String project, PatchSet.Id id, String commentId, AsyncCallback<CommentInfo> cb) {
- revision(project, id, "comments").id(commentId).get(cb);
- }
-
- public static void drafts(
- @Nullable String project, PatchSet.Id id, AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
- revision(project, id, "drafts").get(cb);
- }
-
- public static void draft(
- @Nullable String project, PatchSet.Id id, String draftId, AsyncCallback<CommentInfo> cb) {
- revision(project, id, "drafts").id(draftId).get(cb);
- }
-
- public static void createDraft(
- @Nullable String project,
- PatchSet.Id id,
- CommentInfo content,
- AsyncCallback<CommentInfo> cb) {
- revision(project, id, "drafts").put(content, cb);
- }
-
- public static void updateDraft(
- @Nullable String project,
- PatchSet.Id id,
- String draftId,
- CommentInfo content,
- AsyncCallback<CommentInfo> cb) {
- revision(project, id, "drafts").id(draftId).put(content, cb);
- }
-
- public static void deleteDraft(
- @Nullable String project,
- PatchSet.Id id,
- String draftId,
- AsyncCallback<JavaScriptObject> cb) {
- revision(project, id, "drafts").id(draftId).delete(cb);
- }
-
- private static RestApi revision(@Nullable String project, PatchSet.Id id, String type) {
- return ChangeApi.revision(project, id).view(type);
- }
-
- private CommentApi() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java
deleted file mode 100644
index a11186091a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CommentInfo.java
+++ /dev/null
@@ -1,154 +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.client.changes;
-
-import com.google.gerrit.client.diff.CommentRange;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.extensions.client.Side;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.client.impl.ser.JavaSqlTimestamp_JsonSerializer;
-import java.sql.Timestamp;
-
-public class CommentInfo extends JavaScriptObject {
- public static CommentInfo create(
- String path, Side side, int line, CommentRange range, Boolean unresolved) {
- return create(path, side, 0, line, range, unresolved);
- }
-
- public static CommentInfo create(
- String path, Side side, int parent, int line, CommentRange range, boolean unresolved) {
- CommentInfo n = createObject().cast();
- n.path(path);
- n.side(side);
- n.parent(parent);
- if (range != null) {
- n.line(range.endLine());
- n.range(range);
- } else if (line > 0) {
- n.line(line);
- }
- n.unresolved(unresolved);
- return n;
- }
-
- public static CommentInfo createReply(CommentInfo r) {
- CommentInfo n = createObject().cast();
- n.path(r.path());
- n.side(r.side());
- n.parent(r.parent());
- n.inReplyTo(r.id());
- if (r.hasRange()) {
- n.line(r.range().endLine());
- n.range(r.range());
- } else if (r.hasLine()) {
- n.line(r.line());
- }
- n.unresolved(r.unresolved());
- return n;
- }
-
- public static CommentInfo copy(CommentInfo s) {
- CommentInfo n = createObject().cast();
- n.path(s.path());
- n.side(s.side());
- n.parent(s.parent());
- n.id(s.id());
- n.inReplyTo(s.inReplyTo());
- n.message(s.message());
- if (s.hasRange()) {
- n.line(s.range().endLine());
- n.range(s.range());
- } else if (s.hasLine()) {
- n.line(s.line());
- }
- n.unresolved(s.unresolved());
- return n;
- }
-
- public final native void path(String p) /*-{ this.path = p }-*/;
-
- public final native void id(String i) /*-{ this.id = i }-*/;
-
- public final native void line(int n) /*-{ this.line = n }-*/;
-
- public final native void range(CommentRange r) /*-{ this.range = r }-*/;
-
- public final native void inReplyTo(String i) /*-{ this.in_reply_to = i }-*/;
-
- public final native void message(String m) /*-{ this.message = m }-*/;
-
- public final native void unresolved(boolean b) /*-{ this.unresolved = b }-*/;
-
- public final void side(Side side) {
- sideRaw(side.toString());
- }
-
- private native void sideRaw(String s) /*-{ this.side = s }-*/;
-
- public final native void parent(int n) /*-{ this.parent = n }-*/;
-
- public final native boolean hasParent() /*-{ return this.hasOwnProperty('parent') }-*/;
-
- public final native String path() /*-{ return this.path }-*/;
-
- public final native String id() /*-{ return this.id }-*/;
-
- public final native String inReplyTo() /*-{ return this.in_reply_to }-*/;
-
- public final native int patchSet() /*-{ return this.patch_set }-*/;
-
- public final native boolean unresolved() /*-{ return this.unresolved }-*/;
-
- public final Side side() {
- String s = sideRaw();
- return s != null ? Side.valueOf(s) : Side.REVISION;
- }
-
- private native String sideRaw() /*-{ return this.side }-*/;
-
- public final native int parent() /*-{ return this.parent }-*/;
-
- public final Timestamp updated() {
- Timestamp r = updatedTimestamp();
- if (r == null) {
- String s = updatedRaw();
- if (s != null) {
- r = JavaSqlTimestamp_JsonSerializer.parseTimestamp(s);
- updatedTimestamp(r);
- }
- }
- return r;
- }
-
- private native String updatedRaw() /*-{ return this.updated }-*/;
-
- private native Timestamp updatedTimestamp() /*-{ return this._ts }-*/;
-
- private native void updatedTimestamp(Timestamp t) /*-{ this._ts = t }-*/;
-
- public final native AccountInfo author() /*-{ return this.author }-*/;
-
- public final native int line() /*-{ return this.line || 0 }-*/;
-
- public final native boolean hasLine() /*-{ return this.hasOwnProperty('line') }-*/;
-
- public final native boolean hasRange() /*-{ return this.hasOwnProperty('range') }-*/;
-
- public final native CommentRange range() /*-{ return this.range }-*/;
-
- public final native String message() /*-{ return this.message }-*/;
-
- protected CommentInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CustomDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CustomDashboardScreen.java
deleted file mode 100644
index 802e56cb79..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/CustomDashboardScreen.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.client.ui.Screen;
-
-public class CustomDashboardScreen extends Screen implements ChangeListScreen {
- private DashboardTable table;
- private String params;
-
- public CustomDashboardScreen(String params) {
- this.params = params;
- }
-
- @Override
- protected void onInitUI() {
- table =
- new DashboardTable(this, params) {
- @Override
- public void finishDisplay() {
- super.finishDisplay();
- display();
- }
- };
-
- super.onInitUI();
-
- String title = table.getTitle();
- if (title != null) {
- setWindowTitle(title);
- setPageTitle(title);
- }
-
- add(table);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
deleted file mode 100644
index aba4ee0d3c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DashboardTable.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.PageLinks;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.http.client.URL;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-
-public class DashboardTable extends ChangeTable {
- private List<Section> sections;
- private String title;
- private List<String> titles;
- private List<String> queries;
-
- public DashboardTable(Screen screen, String params) {
- titles = new ArrayList<>();
- queries = new ArrayList<>();
- String foreach = null;
- for (String kvPair : params.split("[,;&]")) {
- String[] kv = kvPair.split("=", 2);
- if (kv.length != 2 || kv[0].isEmpty()) {
- continue;
- }
-
- if ("title".equals(kv[0])) {
- title = URL.decodeQueryString(kv[1]);
- } else if ("foreach".equals(kv[0])) {
- foreach = URL.decodeQueryString(kv[1]);
- } else {
- titles.add(URL.decodeQueryString(kv[0]));
- queries.add(URL.decodeQueryString(kv[1]));
- }
- }
-
- if (foreach != null) {
- ListIterator<String> it = queries.listIterator();
- while (it.hasNext()) {
- it.set(it.next() + " " + foreach);
- }
- }
-
- addStyleName(Gerrit.RESOURCES.css().accountDashboard());
-
- sections = new ArrayList<>();
- int i = 0;
- for (String title : titles) {
- Section s = new Section();
- String query = removeLimitAndAge(queries.get(i++));
- s.setTitleWidget(new InlineHyperlink(title, PageLinks.toChangeQuery(query)));
- addSection(s);
- sections.add(s);
- }
-
- keysNavigation.add(
- new KeyCommand(0, 'R', Util.C.keyReloadSearch()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(screen.getToken());
- }
- });
- }
-
- private String removeLimitAndAge(String query) {
- StringBuilder unlimitedQuery = new StringBuilder();
- String[] operators = query.split(" ");
- for (String o : operators) {
- if (!o.startsWith("limit:") && !o.startsWith("age:") && !o.startsWith("-age:")) {
- unlimitedQuery.append(o).append(" ");
- }
- }
- return unlimitedQuery.toString().trim();
- }
-
- @Override
- public String getTitle() {
- return title;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- ChangeList.queryMultiple(
- new GerritCallback<JsArray<ChangeList>>() {
- @Override
- public void onSuccess(JsArray<ChangeList> result) {
- List<ChangeList> cls = Natives.asList(result);
- updateColumnsForLabels(cls.toArray(new ChangeList[cls.size()]));
- for (int i = 0; i < cls.size(); i++) {
- sections.get(i).display(cls.get(i));
- }
- finishDisplay();
- }
- },
- OPTIONS,
- queries.toArray(new String[queries.size()]));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java
deleted file mode 100644
index 1695eb9d45..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PagedSingleListScreen.java
+++ /dev/null
@@ -1,135 +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.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-
-public abstract class PagedSingleListScreen extends Screen {
- protected final int pageSize;
- protected final int start;
- private final String anchorPrefix;
-
- protected ChangeList changes;
- private ChangeTable table;
- private ChangeTable.Section section;
- private Hyperlink prev;
- private Hyperlink next;
-
- protected PagedSingleListScreen(String anchorToken, int start) {
- anchorPrefix = anchorToken;
- this.start = start;
- pageSize = Gerrit.getUserPreferences().changesPerPage();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- prev = new Hyperlink(Util.C.pagedChangeListPrev(), true, "");
- prev.setVisible(false);
-
- next = new Hyperlink(Util.C.pagedChangeListNext(), true, "");
- next.setVisible(false);
-
- table =
- new ChangeTable() {
- {
- keysNavigation.add(
- new DoLinkCommand(0, 'p', Util.C.changeTablePagePrev(), prev),
- new DoLinkCommand(0, 'n', Util.C.changeTablePageNext(), next));
-
- keysNavigation.add(
- new DoLinkCommand(0, '[', Util.C.changeTablePagePrev(), prev),
- new DoLinkCommand(0, ']', Util.C.changeTablePageNext(), next));
-
- keysNavigation.add(
- new KeyCommand(0, 'R', Util.C.keyReloadSearch()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(getToken());
- }
- });
- }
- };
- section = new ChangeTable.Section();
- table.addSection(section);
- table.setSavePointerId(anchorPrefix);
- add(table);
-
- final HorizontalPanel buttons = new HorizontalPanel();
- buttons.setStyleName(Gerrit.RESOURCES.css().changeTablePrevNextLinks());
- buttons.add(prev);
- buttons.add(next);
- add(buttons);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-
- protected AsyncCallback<ChangeList> loadCallback() {
- return new ScreenLoadCallback<ChangeList>(this) {
- @Override
- protected void preDisplay(ChangeList result) {
- display(result);
- }
- };
- }
-
- protected void display(ChangeList result) {
- changes = result;
- if (changes.length() != 0) {
- if (start > 0) {
- int p = start - pageSize;
- prev.setTargetHistoryToken(anchorPrefix + (p > 0 ? "," + p : ""));
- prev.setVisible(true);
- } else {
- prev.setVisible(false);
- }
-
- int n = start + changes.length();
- next.setTargetHistoryToken(anchorPrefix + "," + n);
- next.setVisible(changes.get(changes.length() - 1)._more_changes());
- }
- table.updateColumnsForLabels(result);
- section.display(result);
- table.finishDisplay();
- }
-
- private static final class DoLinkCommand extends KeyCommand {
- private final Hyperlink link;
-
- private DoLinkCommand(int mask, char key, String help, Hyperlink l) {
- super(mask, key, help);
- link = l;
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (link.isVisible()) {
- History.newItem(link.getTargetHistoryToken());
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ProjectDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ProjectDashboardScreen.java
deleted file mode 100644
index f511308114..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ProjectDashboardScreen.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.admin.ProjectScreen;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.ui.FlowPanel;
-
-public class ProjectDashboardScreen extends ProjectScreen implements ChangeListScreen {
- private DashboardTable table;
- private String params;
-
- public ProjectDashboardScreen(Project.NameKey toShow, String params) {
- super(toShow);
- this.params = params;
- }
-
- @Override
- protected void onInitUI() {
- table =
- new DashboardTable(this, params) {
- @Override
- public void finishDisplay() {
- super.finishDisplay();
- display();
- }
- };
-
- super.onInitUI();
-
- String title = table.getTitle();
- if (title != null) {
- FlowPanel fp = new FlowPanel();
- fp.setStyleName(Gerrit.RESOURCES.css().screenHeader());
- fp.add(new InlineHyperlink(title, PageLinks.toCustomDashboard(params)));
- add(fp);
- }
-
- add(table);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
deleted file mode 100644
index 8d580a35dd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/QueryScreen.java
+++ /dev/null
@@ -1,96 +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.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gwt.regexp.shared.RegExp;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.KeyUtil;
-
-public class QueryScreen extends PagedSingleListScreen implements ChangeListScreen {
- // Legacy numeric identifier.
- private static final RegExp NUMERIC_ID = RegExp.compile("^[1-9][0-9]*$");
- // Commit SHA1 hash
- private static final RegExp COMMIT_SHA1 = RegExp.compile("^([0-9a-fA-F]{4," + RevId.LEN + "})$");
- // Change-Id
- private static final String ID_PATTERN = "[iI][0-9a-f]{4,}$";
- private static final RegExp CHANGE_ID = RegExp.compile("^" + ID_PATTERN);
- private static final RegExp CHANGE_ID_TRIPLET = RegExp.compile("^(.)+~(.)+~" + ID_PATTERN);
-
- public static QueryScreen forQuery(String query) {
- return forQuery(query, 0);
- }
-
- public static QueryScreen forQuery(String query, int start) {
- return new QueryScreen(KeyUtil.encode(query), start);
- }
-
- private final String query;
-
- public QueryScreen(String encQuery, int start) {
- super(PageLinks.QUERY + encQuery, start);
- query = KeyUtil.decode(encQuery);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setWindowTitle(Util.M.changeQueryWindowTitle(query));
- setPageTitle(Util.M.changeQueryPageTitle(query));
- }
-
- @Override
- protected AsyncCallback<ChangeList> loadCallback() {
- return new GerritCallback<ChangeList>() {
- @Override
- public void onSuccess(ChangeList result) {
- if (isAttached()) {
- if (result.length() == 1 && isSingleQuery(query)) {
- ChangeInfo c = result.get(0);
- Change.Id id = c.legacyId();
- Gerrit.display(PageLinks.toChange(c.projectNameKey(), id));
- } else {
- display(result);
- QueryScreen.this.display();
- }
- }
- }
- };
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- Gerrit.setQueryString(query);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- ChangeList.query(query, ChangeTable.OPTIONS, loadCallback(), start, pageSize);
- }
-
- private static boolean isSingleQuery(String query) {
- return NUMERIC_ID.test(query)
- || CHANGE_ID.test(query)
- || CHANGE_ID_TRIPLET.test(query)
- || COMMIT_SHA1.test(query);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInfo.java
deleted file mode 100644
index 06d2484eba..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInfo.java
+++ /dev/null
@@ -1,25 +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.client.changes;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ReviewInfo extends JavaScriptObject {
-
- public final native NativeMap<?> labels() /*-{ return this.labels }-*/;
-
- protected ReviewInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
deleted file mode 100644
index f851d5e72e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
+++ /dev/null
@@ -1,88 +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.client.changes;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-
-public class ReviewInput extends JavaScriptObject {
- public enum NotifyHandling {
- NONE,
- OWNER,
- OWNER_REVIEWERS,
- ALL
- }
-
- public enum DraftHandling {
- DELETE,
- PUBLISH,
- KEEP,
- PUBLISH_ALL_REVISIONS
- }
-
- public static ReviewInput create() {
- ReviewInput r = createObject().cast();
- r.init();
- r.drafts(DraftHandling.PUBLISH);
- return r;
- }
-
- public final native void message(String m) /*-{ if(m)this.message=m; }-*/;
-
- public final native void label(String n, short v) /*-{ this.labels[n]=v; }-*/;
-
- public final native void comments(NativeMap<JsArray<CommentInfo>> m) /*-{ this.comments=m }-*/;
-
- public final void notify(NotifyHandling e) {
- _notify(e.name());
- }
-
- private native void _notify(String n) /*-{ this.notify=n; }-*/;
-
- public final void drafts(DraftHandling e) {
- _drafts(e.name());
- }
-
- private native void _drafts(String n) /*-{ this.drafts=n; }-*/;
-
- private native void init() /*-{
- this.labels = {};
- }-*/;
-
- public final native void prePost() /*-{
- var m=this.comments;
- if (m) {
- for (var p in m) {
- var l=m[p];
- for (var i=0;i<l.length;i++) {
- var c=l[i];
- delete c['path'];
- delete c['updated'];
- }
- }
- }
- }-*/;
-
- public final native void mergeLabels(ReviewInput o) /*-{
- var l=o.labels;
- if (l) {
- for (var n in l)
- this.labels[n]=l[n];
- }
- }-*/;
-
- protected ReviewInput() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/RevisionInfoCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/RevisionInfoCache.java
deleted file mode 100644
index fde2b055c9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/RevisionInfoCache.java
+++ /dev/null
@@ -1,49 +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.client.changes;
-
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/** Cache of PatchSet.Id to revision SHA-1 strings. */
-public class RevisionInfoCache {
- private static final int LIMIT = 10;
- private static final RevisionInfoCache IMPL = new RevisionInfoCache();
-
- public static void add(Change.Id change, RevisionInfo info) {
- IMPL.psToCommit.put(new PatchSet.Id(change, info._number()), info.name());
- }
-
- static String get(PatchSet.Id id) {
- return IMPL.psToCommit.get(id);
- }
-
- private final LinkedHashMap<PatchSet.Id, String> psToCommit;
-
- private RevisionInfoCache() {
- psToCommit =
- new LinkedHashMap<PatchSet.Id, String>(LIMIT) {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected boolean removeEldestEntry(Map.Entry<PatchSet.Id, String> e) {
- return size() > LIMIT;
- }
- };
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java
deleted file mode 100644
index b102842009..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.resources.client.ImageResource;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.web.bindery.event.shared.Event;
-import com.google.web.bindery.event.shared.HandlerRegistration;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/** Supports the star icon displayed on changes and tracking the status. */
-public class StarredChanges {
- private static final Event.Type<ChangeStarHandler> TYPE = new Event.Type<>();
-
- /** Handler that can receive notifications of a change's starred status. */
- public interface ChangeStarHandler {
- void onChangeStar(ChangeStarEvent event);
- }
-
- /** Event fired when a star changes status. The new status is reported. */
- public static class ChangeStarEvent extends Event<ChangeStarHandler> {
- private boolean starred;
-
- public ChangeStarEvent(Change.Id source, boolean starred) {
- setSource(source);
- this.starred = starred;
- }
-
- public boolean isStarred() {
- return starred;
- }
-
- @Override
- public Type<ChangeStarHandler> getAssociatedType() {
- return TYPE;
- }
-
- @Override
- protected void dispatch(ChangeStarHandler handler) {
- handler.onChangeStar(this);
- }
- }
-
- /**
- * Create a star icon for the given change, and current status. Returns null if the user is not
- * signed in and cannot support starred changes.
- */
- public static Icon createIcon(Change.Id source, boolean starred) {
- return Gerrit.isSignedIn() ? new Icon(source, starred) : null;
- }
-
- /** Make a key command that toggles the star for a change. */
- public static KeyCommand newKeyCommand(Icon icon) {
- return new KeyCommand(0, 's', Util.C.changeTableStar()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- icon.toggleStar();
- }
- };
- }
-
- /** Add a handler to listen for starred status to change. */
- public static HandlerRegistration addHandler(Change.Id source, ChangeStarHandler handler) {
- return Gerrit.EVENT_BUS.addHandlerToSource(TYPE, source, handler);
- }
-
- /**
- * Broadcast the current starred value of a change to UI widgets. This does not RPC to the server
- * and does not alter the starred status of a change.
- */
- public static void fireChangeStarEvent(Change.Id id, boolean starred) {
- Gerrit.EVENT_BUS.fireEventFromSource(new ChangeStarEvent(id, starred), id);
- }
-
- /**
- * Set the starred status of a change. This method broadcasts to all interested UI widgets and
- * sends an RPC to the server to record the updated status.
- */
- public static void toggleStar(Change.Id changeId, boolean newValue) {
- pending.put(changeId, newValue);
- fireChangeStarEvent(changeId, newValue);
- if (!busy) {
- startRequest();
- }
- }
-
- private static boolean busy;
- private static final Map<Change.Id, Boolean> pending = new LinkedHashMap<>(4);
-
- private static void startRequest() {
- busy = true;
-
- final Change.Id id = pending.keySet().iterator().next();
- final boolean starred = pending.remove(id);
- RestApi call = AccountApi.self().view("starred.changes").id(id.get());
- AsyncCallback<JavaScriptObject> cb =
- new AsyncCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject none) {
- if (pending.isEmpty()) {
- busy = false;
- } else {
- startRequest();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (!starred && RestApi.isStatus(caught, 404)) {
- onSuccess(null);
- return;
- }
-
- fireChangeStarEvent(id, !starred);
- for (Map.Entry<Change.Id, Boolean> e : pending.entrySet()) {
- fireChangeStarEvent(e.getKey(), !e.getValue());
- }
- pending.clear();
- busy = false;
- }
- };
- if (starred) {
- call.put(cb);
- } else {
- call.delete(cb);
- }
- }
-
- public static class Icon extends Image implements ChangeStarHandler, ClickHandler {
- private final Change.Id changeId;
- private boolean starred;
- private HandlerRegistration handler;
-
- Icon(Change.Id changeId, boolean starred) {
- super(resource(starred));
- this.changeId = changeId;
- this.starred = starred;
- addClickHandler(this);
- }
-
- /**
- * Toggles the state of the star, as if the user clicked on the image. This will broadcast the
- * new star status to all interested UI widgets, and RPC to the server to store the changed
- * value.
- */
- public void toggleStar() {
- StarredChanges.toggleStar(changeId, !starred);
- }
-
- @Override
- protected void onLoad() {
- handler = StarredChanges.addHandler(changeId, this);
- }
-
- @Override
- protected void onUnload() {
- handler.removeHandler();
- handler = null;
- }
-
- @Override
- public void onChangeStar(ChangeStarEvent event) {
- setResource(resource(event.isStarred()));
- starred = event.isStarred();
- }
-
- @Override
- public void onClick(ClickEvent event) {
- toggleStar();
- }
-
- private static ImageResource resource(boolean starred) {
- return starred ? Gerrit.RESOURCES.starFilled() : Gerrit.RESOURCES.starOpen();
- }
- }
-
- private StarredChanges() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitInfo.java
deleted file mode 100644
index 9027c5ba75..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitInfo.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2012 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.client.changes;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class SubmitInfo extends JavaScriptObject {
- final Change.Status status() {
- return Change.Status.valueOf(statusRaw());
- }
-
- private native String statusRaw() /*-{ return this.status; }-*/;
-
- protected SubmitInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
deleted file mode 100644
index 8d949d136f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
+++ /dev/null
@@ -1,79 +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.client.changes;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.core.client.GWT;
-
-public class Util {
- public static final ChangeConstants C = GWT.create(ChangeConstants.class);
- public static final ChangeMessages M = GWT.create(ChangeMessages.class);
-
- private static final int SUBJECT_MAX_LENGTH = 80;
- private static final String SUBJECT_CROP_APPENDIX = "...";
- private static final int SUBJECT_CROP_RANGE = 10;
-
- public static String toLongString(Change.Status status) {
- if (status == null) {
- return "";
- }
- switch (status) {
- case NEW:
- return C.statusLongNew();
- case MERGED:
- return C.statusLongMerged();
- case ABANDONED:
- return C.statusLongAbandoned();
- default:
- return status.name();
- }
- }
-
- /**
- * Crops the given change subject if needed so that it has at most {@link #SUBJECT_MAX_LENGTH}
- * characters.
- *
- * <p>If the given subject is not longer than {@link #SUBJECT_MAX_LENGTH} characters it is
- * returned unchanged.
- *
- * <p>If the length of the given subject exceeds {@link #SUBJECT_MAX_LENGTH} characters it is
- * cropped. In this case {@link #SUBJECT_CROP_APPENDIX} is appended to the cropped subject, the
- * cropped subject including the appendix has at most {@link #SUBJECT_MAX_LENGTH} characters.
- *
- * <p>If cropping is needed, the subject will be cropped after the last space character that is
- * found within the last {@link #SUBJECT_CROP_RANGE} characters of the potentially visible
- * characters. If no such space is found, the subject will be cropped so that the cropped subject
- * including the appendix has exactly {@link #SUBJECT_MAX_LENGTH} characters.
- *
- * @return the subject, cropped if needed
- */
- @SuppressWarnings("deprecation")
- public static String cropSubject(String subject) {
- if (subject.length() > SUBJECT_MAX_LENGTH) {
- final int maxLength = SUBJECT_MAX_LENGTH - SUBJECT_CROP_APPENDIX.length();
- for (int cropPosition = maxLength;
- cropPosition > maxLength - SUBJECT_CROP_RANGE;
- cropPosition--) {
- // Character.isWhitespace(char) can't be used because this method is not supported by GWT,
- // see https://developers.google.com/web-toolkit/doc/1.6/RefJreEmulation#Package_java_lang
- if (Character.isSpace(subject.charAt(cropPosition - 1))) {
- return subject.substring(0, cropPosition) + SUBJECT_CROP_APPENDIX;
- }
- }
- return subject.substring(0, maxLength) + SUBJECT_CROP_APPENDIX;
- }
- return subject;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/CapabilityInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/CapabilityInfo.java
deleted file mode 100644
index 1d7f4abe08..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/CapabilityInfo.java
+++ /dev/null
@@ -1,25 +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.client.config;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class CapabilityInfo extends JavaScriptObject {
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String name() /*-{ return this.name; }-*/;
-
- protected CapabilityInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
deleted file mode 100644
index e71929c064..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ConfigServerApi.java
+++ /dev/null
@@ -1,60 +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.client.config;
-
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.info.ServerInfo;
-import com.google.gerrit.client.info.TopMenuList;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** A collection of static methods which work on the Gerrit REST API for server configuration. */
-public class ConfigServerApi {
- /** map of the server wide capabilities (core & plugins). */
- public static void capabilities(AsyncCallback<NativeMap<CapabilityInfo>> cb) {
- new RestApi("/config/server/capabilities/").get(cb);
- }
-
- public static void topMenus(AsyncCallback<TopMenuList> cb) {
- new RestApi("/config/server/top-menus").get(cb);
- }
-
- public static void defaultPreferences(AsyncCallback<GeneralPreferences> cb) {
- new RestApi("/config/server/preferences").get(cb);
- }
-
- public static void serverInfo(AsyncCallback<ServerInfo> cb) {
- new RestApi("/config/server/info").get(cb);
- }
-
- public static void confirmEmail(String token, AsyncCallback<VoidResult> cb) {
- EmailConfirmationInput input = EmailConfirmationInput.create();
- input.setToken(token);
- new RestApi("/config/server/email.confirm").put(input, cb);
- }
-
- private static class EmailConfirmationInput extends JavaScriptObject {
- final native void setToken(String token) /*-{ this.token = token; }-*/;
-
- static EmailConfirmationInput create() {
- return createObject().cast();
- }
-
- protected EmailConfirmationInput() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.java
deleted file mode 100644
index ecb2938307..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2012 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.client.dashboards;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface DashboardConstants extends Constants {
- String dashboardName();
-
- String dashboardTitle();
-
- String dashboardDescription();
-
- String dashboardInherited();
-
- String dashboardItem();
-
- String dashboardDefaultToolTip();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.properties
deleted file mode 100644
index ac4de7c66a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardConstants.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-dashboardName = Dashboard Name
-dashboardTitle = Dashboard Title
-dashboardDescription = Dashboard Description
-dashboardInherited = Inherited From
-dashboardItem = dashboard
-dashboardDefaultToolTip = Project Default Dashboard \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardInfo.java
deleted file mode 100644
index 5c6b51a8d8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardInfo.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2012 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.client.dashboards;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class DashboardInfo extends JavaScriptObject {
- public final native String id() /*-{ return this.id; }-*/;
-
- public final native String title() /*-{ return this.title; }-*/;
-
- public final native String project() /*-{ return this.project; }-*/;
-
- public final native String definingProject() /*-{ return this.defining_project; }-*/;
-
- public final native String ref() /*-{ return this.ref; }-*/;
-
- public final native String path() /*-{ return this.path; }-*/;
-
- public final native String description() /*-{ return this.description; }-*/;
-
- public final native String foreach() /*-{ return this.foreach; }-*/;
-
- public final native String url() /*-{ return this.url; }-*/;
-
- private final native boolean isDefaultLegacy() /*-{ return this['default'] ? true : false; }-*/;
-
- private final native boolean isDefaultNew() /*-{ return this.is_default ? true : false; }-*/;
-
- public final boolean isDefault() {
- return isDefaultLegacy() || isDefaultNew();
- }
-
- protected DashboardInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
deleted file mode 100644
index 7ba3580089..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardList.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2012 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.client.dashboards;
-
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.http.client.URL;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Project dashboards from {@code /projects/<name>/dashboards/}. */
-public class DashboardList extends JsArray<DashboardInfo> {
- public static void all(Project.NameKey project, AsyncCallback<JsArray<DashboardList>> callback) {
- base(project).addParameterTrue("inherited").get(callback);
- }
-
- public static void getDefault(Project.NameKey project, AsyncCallback<DashboardInfo> callback) {
- base(project).view("default").addParameterTrue("inherited").get(callback);
- }
-
- public static void get(
- Project.NameKey project, String id, AsyncCallback<DashboardInfo> callback) {
- base(project).idRaw(encodeDashboardId(id)).get(callback);
- }
-
- private static RestApi base(Project.NameKey project) {
- return new RestApi("/projects/").id(project.get()).view("dashboards");
- }
-
- private static String encodeDashboardId(String id) {
- int c = id.indexOf(':');
- if (0 <= c) {
- String ref = URL.encodeQueryString(id.substring(0, c));
- String path = URL.encodeQueryString(id.substring(c + 1));
- return ref + ':' + path;
- }
- return URL.encodeQueryString(id);
- }
-
- protected DashboardList() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
deleted file mode 100644
index dcb9c01861..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (C) 2012 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.client.dashboards;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.Image;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class DashboardsTable extends NavigationTable<DashboardInfo> {
- Project.NameKey project;
-
- public DashboardsTable(Project.NameKey project) {
- super(Util.C.dashboardItem());
- this.project = project;
- initColumnHeaders();
- }
-
- protected void initColumnHeaders() {
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setColSpan(0, 0, 2);
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
-
- table.setText(0, 1, Util.C.dashboardName());
- table.setText(0, 2, Util.C.dashboardTitle());
- table.setText(0, 3, Util.C.dashboardDescription());
- table.setText(0, 4, Util.C.dashboardInherited());
- }
-
- public void display(DashboardList dashes) {
- display(Natives.asList(dashes));
- }
-
- public void display(JsArray<DashboardList> in) {
- Map<String, DashboardInfo> map = new HashMap<>();
- for (DashboardList list : Natives.asList(in)) {
- for (DashboardInfo d : Natives.asList(list)) {
- if (!map.containsKey(d.id())) {
- map.put(d.id(), d);
- }
- }
- }
- display(new ArrayList<>(map.values()));
- }
-
- public void display(List<DashboardInfo> list) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- list.sort(comparing(DashboardInfo::id));
-
- String ref = null;
- for (DashboardInfo d : list) {
- if (!d.ref().equals(ref)) {
- ref = d.ref();
- insertTitleRow(table.getRowCount(), ref);
- }
- insert(table.getRowCount(), d);
- }
-
- finishDisplay();
- }
-
- protected void insertTitleRow(int row, String section) {
- table.insertRow(row);
-
- table.setText(row, 0, section);
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setColSpan(row, 0, 6);
- fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().sectionHeader());
- }
-
- protected void insert(int row, DashboardInfo k) {
- table.insertRow(row);
-
- applyDataRowStyle(row);
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 5, Gerrit.RESOURCES.css().dataCell());
-
- populate(row, k);
- }
-
- protected void populate(int row, DashboardInfo k) {
- if (k.isDefault()) {
- table.setWidget(row, 1, new Image(Gerrit.RESOURCES.greenCheck()));
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.getElement(row, 1).setTitle(Util.C.dashboardDefaultToolTip());
- }
- table.setWidget(
- row,
- 2,
- new Anchor(
- k.path(),
- "#" + PageLinks.toProjectDashboard(new Project.NameKey(k.project()), k.id())));
- table.setText(row, 3, k.title() != null ? k.title() : k.path());
- table.setText(row, 4, k.description());
- if (k.definingProject() != null && !k.definingProject().equals(k.project())) {
- table.setWidget(
- row,
- 5,
- new Anchor(
- k.definingProject(),
- "#" + PageLinks.toProjectDashboards(new Project.NameKey(k.definingProject()))));
- }
- setRowItem(row, k);
- }
-
- @Override
- protected Object getRowItemKey(DashboardInfo item) {
- return item.id();
- }
-
- @Override
- protected void onOpenRow(int row) {
- if (row > 0) {
- movePointerTo(row);
- }
- History.newItem(getRowItem(row).url());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/Util.java
deleted file mode 100644
index b15bf734ad..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/Util.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2012 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.client.dashboards;
-
-import com.google.gwt.core.client.GWT;
-
-public class Util {
- public static final DashboardConstants C = GWT.create(DashboardConstants.class);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
deleted file mode 100644
index 0091f53853..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ChunkManager.java
+++ /dev/null
@@ -1,137 +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.client.diff;
-
-import static com.google.gerrit.client.diff.DisplaySide.A;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.Element;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineClassWhere;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker;
-
-/** Colors modified regions for {@link SideBySide} and {@link Unified}. */
-abstract class ChunkManager {
- static final native void onClick(Element e, JavaScriptObject f) /*-{ e.onclick = f }-*/;
-
- final Scrollbar scrollbar;
- final LineMapper lineMapper;
-
- private List<TextMarker> markers;
- private List<Runnable> undo;
-
- ChunkManager(Scrollbar scrollbar) {
- this.scrollbar = scrollbar;
- this.lineMapper = new LineMapper();
- }
-
- abstract DiffChunkInfo getFirst();
-
- List<TextMarker> getMarkers() {
- return markers;
- }
-
- void reset() {
- lineMapper.reset();
- for (TextMarker m : markers) {
- m.clear();
- }
- for (Runnable r : undo) {
- r.run();
- }
- }
-
- abstract void render(DiffInfo diff);
-
- void render() {
- markers = new ArrayList<>();
- undo = new ArrayList<>();
- }
-
- void colorLines(CodeMirror cm, String color, int line, int cnt) {
- colorLines(cm, LineClassWhere.WRAP, color, line, line + cnt);
- }
-
- void colorLines(CodeMirror cm, LineClassWhere where, String className, int start, int end) {
- if (start < end) {
- for (int line = start; line < end; line++) {
- cm.addLineClass(line, where, className);
- }
- undo.add(
- () -> {
- for (int line = start; line < end; line++) {
- cm.removeLineClass(line, where, className);
- }
- });
- }
- }
-
- abstract Runnable diffChunkNav(CodeMirror cm, Direction dir);
-
- void diffChunkNavHelper(
- List<? extends DiffChunkInfo> chunks, DiffScreen host, int res, Direction dir) {
- if (res < 0) {
- res = -res - (dir == Direction.PREV ? 1 : 2);
- }
- res = res + (dir == Direction.PREV ? -1 : 1);
- if (res < 0 || chunks.size() <= res) {
- return;
- }
-
- DiffChunkInfo lookUp = chunks.get(res);
- // If edit, skip the deletion chunk and set focus on the insertion one.
- if (lookUp.isEdit() && lookUp.getSide() == A) {
- res = res + (dir == Direction.PREV ? -1 : 1);
- if (res < 0 || chunks.size() <= res) {
- return;
- }
- }
-
- DiffChunkInfo target = chunks.get(res);
- CodeMirror targetCm = host.getCmFromSide(target.getSide());
- int cmLine = getCmLine(target.getStart(), target.getSide());
- targetCm.setCursor(Pos.create(cmLine));
- targetCm.focus();
- targetCm.scrollToY(
- targetCm.heightAtLine(cmLine, "local") - 0.5 * targetCm.scrollbarV().getClientHeight());
- }
-
- Comparator<DiffChunkInfo> getDiffChunkComparator() {
- // Chunks are ordered by their starting line. If it's a deletion,
- // use its corresponding line on the revision side for comparison.
- // In the edit case, put the deletion chunk right before the
- // insertion chunk. This placement guarantees well-ordering.
- return new Comparator<DiffChunkInfo>() {
- @Override
- public int compare(DiffChunkInfo a, DiffChunkInfo b) {
- if (a.getSide() == b.getSide()) {
- return a.getStart() - b.getStart();
- } else if (a.getSide() == A) {
- int comp = lineMapper.lineOnOther(a.getSide(), a.getStart()).getLine() - b.getStart();
- return comp == 0 ? -1 : comp;
- } else {
- int comp = a.getStart() - lineMapper.lineOnOther(b.getSide(), b.getStart()).getLine();
- return comp == 0 ? 1 : comp;
- }
- }
- };
- }
-
- abstract int getCmLine(int line, DisplaySide side);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.css
deleted file mode 100644
index b4216eb888..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.css
+++ /dev/null
@@ -1,147 +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.
- */
-
-@external .cm-s-midnight;
-@external .cm-s-night;
-@external .cm-s-twilight;
-@external .com-google-gerrit-client-diff-PublishedBox_BinderImpl_GenCss_style-name;
-@external .com-google-gerrit-client-diff-CommentBox-Style-message;
-@external .com-google-gerrit-client-diff-CommentBox-Style-date;
-@external .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-range;
-@external .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-rangeHighlight;
-@external .net-codemirror-lib-CodeMirror-Style-activeLine;
-@external .CodeMirror-linenumber;
-
-.cm-s-midnight .com-google-gerrit-client-diff-PublishedBox_BinderImpl_GenCss_style-name { color: black }
-.cm-s-midnight .com-google-gerrit-client-diff-CommentBox-Style-message { color: black }
-.cm-s-midnight .com-google-gerrit-client-diff-CommentBox-Style-date { color: black }
-.cm-s-midnight .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-range { color: #777 }
-.cm-s-midnight .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-rangeHighlight { color: #777 }
-.cm-s-midnight .net-codemirror-lib-CodeMirror-Style-activeLine .CodeMirror-linenumber { color: black }
-
-.cm-s-night .com-google-gerrit-client-diff-PublishedBox_BinderImpl_GenCss_style-name { color: black }
-.cm-s-night .com-google-gerrit-client-diff-CommentBox-Style-message { color: black }
-.cm-s-night .com-google-gerrit-client-diff-CommentBox-Style-date { color: black }
-.cm-s-night .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-range { color: #777 }
-.cm-s-night .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-rangeHighlight { color: #777 }
-.cm-s-night .net-codemirror-lib-CodeMirror-Style-activeLine .CodeMirror-linenumber { color: black }
-
-.cm-s-twilight .com-google-gerrit-client-diff-PublishedBox_BinderImpl_GenCss_style-name { color: black }
-.cm-s-twilight .com-google-gerrit-client-diff-CommentBox-Style-message { color: black }
-.cm-s-twilight .com-google-gerrit-client-diff-CommentBox-Style-date { color: black }
-.cm-s-twilight .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-range { color: #777 }
-.cm-s-twilight .com-google-gerrit-client-diff-DiffTable_BinderImpl_GenCss_style-rangeHighlight { color: #777 }
-.cm-s-twilight .net-codemirror-lib-CodeMirror-Style-activeLine .CodeMirror-linenumber { color: black }
-
-.commentWidgets {
- max-width: 650px;
-
- font-family: sans-serif;
- background-color: #fcfa96;
- border: 1px solid black;
- -webkit-box-shadow: 3px 3px 3px #888888;
- -moz-box-shadow: 3px 3px 3px #888888;
- box-shadow: 3px 3px 3px #888888;
-
- /* margin-bottom is fixed in CommentGroup.computeHeight() */
- margin-bottom: 5px;
- margin-right: 5px;
-
- -webkit-touch-callout: initial;
- -webkit-user-select: text;
- -khtml-user-select: text;
- -moz-user-select: text;
- -ms-user-select: text;
- user-select: text;
-}
-
-.commentBox {
- position: relative;
- min-height: 16px;
-}
-
-.header {
- cursor: pointer;
-}
-
-.summary {
- color: #777;
- position: absolute;
- top: 1px;
- left: 120px;
- width: 408px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- padding-bottom: 0.1em;
-}
-
-.date {
- white-space: nowrap;
- position: absolute;
- top: 2px;
- right: 5px;
-}
-
-.contents {
- margin-left: 28px;
- padding-top: 2px;
- position: relative;
-}
-.message {
- overflow-x: auto;
-}
-.message p,
-.message ul,
-.message blockquote {
- -webkit-margin-before: 0.2em;
- -webkit-margin-after: 0.3em;
-}
-.message {
- white-space: pre-wrap;
-}
-.commentBox button {
- margin-right: 3px;
- margin-bottom: 1px;
- padding: 1px;
- text-align: center;
- font-size: 8px;
- font-weight: bold;
- border: 1px solid black;
- cursor: pointer;
- color: #fff;
- background-color: #4d90fe;
- background-image: -webkit-linear-gradient(top, #4d90fe, #4d90fe);
- -webkit-border-radius: 2px;
- -webkit-box-sizing: content-box;
-}
-.commentBox button div {
- width: 25px;
- white-space: nowrap;
- color: #fff;
-}
-
-@sprite .goPrev {
- gwt-image: "goPrev";
- display: inline-block;
-}
-@sprite .goNext {
- gwt-image: "goNext";
- display: inline-block;
-}
-@sprite .goUp {
- gwt-image: "goUp";
- display: inline-block;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
deleted file mode 100644
index 6f9e694dfd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
+++ /dev/null
@@ -1,158 +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.client.diff;
-
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.dom.client.MouseOverHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.user.client.ui.Composite;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/** An HtmlPanel for displaying a comment */
-abstract class CommentBox extends Composite {
- static {
- Resources.I.style().ensureInjected();
- }
-
- interface Style extends CssResource {
- String commentWidgets();
-
- String commentBox();
-
- String contents();
-
- String message();
-
- String header();
-
- String summary();
-
- String date();
-
- String goPrev();
-
- String goNext();
-
- String goUp();
- }
-
- private final CommentGroup group;
- private ScrollbarAnnotation annotation;
- private FromTo fromTo;
- private TextMarker rangeMarker;
- private TextMarker rangeHighlightMarker;
-
- CommentBox(CommentGroup group, CommentRange range) {
- this.group = group;
- if (range != null) {
- DiffScreen screen = group.getManager().host;
- int startCmLine = screen.getCmLine(range.startLine() - 1, group.getSide());
- int endCmLine = screen.getCmLine(range.endLine() - 1, group.getSide());
- fromTo =
- FromTo.create(
- Pos.create(startCmLine, range.startCharacter()),
- Pos.create(endCmLine, range.endCharacter()));
- rangeMarker =
- group
- .getCm()
- .markText(
- fromTo.from(),
- fromTo.to(),
- Configuration.create().set("className", Resources.I.diffTableStyle().range()));
- }
- addDomHandler(
- new MouseOverHandler() {
- @Override
- public void onMouseOver(MouseOverEvent event) {
- setRangeHighlight(true);
- }
- },
- MouseOverEvent.getType());
- addDomHandler(
- new MouseOutHandler() {
- @Override
- public void onMouseOut(MouseOutEvent event) {
- setRangeHighlight(isOpen());
- }
- },
- MouseOutEvent.getType());
- }
-
- abstract CommentInfo getCommentInfo();
-
- abstract boolean isOpen();
-
- void setOpen(boolean open) {
- group.resize();
- setRangeHighlight(open);
- getCm().focus();
- }
-
- CommentGroup getCommentGroup() {
- return group;
- }
-
- CommentManager getCommentManager() {
- return group.getCommentManager();
- }
-
- ScrollbarAnnotation getAnnotation() {
- return annotation;
- }
-
- void setAnnotation(ScrollbarAnnotation mh) {
- annotation = mh;
- }
-
- void setRangeHighlight(boolean highlight) {
- if (fromTo != null) {
- if (highlight && rangeHighlightMarker == null) {
- rangeHighlightMarker =
- group
- .getCm()
- .markText(
- fromTo.from(),
- fromTo.to(),
- Configuration.create()
- .set("className", Resources.I.diffTableStyle().rangeHighlight()));
- } else if (!highlight && rangeHighlightMarker != null) {
- rangeHighlightMarker.clear();
- rangeHighlightMarker = null;
- }
- }
- }
-
- void clearRange() {
- if (rangeMarker != null) {
- rangeMarker.clear();
- rangeMarker = null;
- }
- }
-
- CodeMirror getCm() {
- return group.getCm();
- }
-
- FromTo getFromTo() {
- return fromTo;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java
deleted file mode 100644
index 414e82e9bd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentGroup.java
+++ /dev/null
@@ -1,202 +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.client.diff;
-
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.LineWidget;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/**
- * LineWidget attached to a CodeMirror container.
- *
- * <p>When a comment is placed on a line a CommentWidget is created.
- */
-abstract class CommentGroup extends Composite {
-
- final DisplaySide side;
- final int line;
-
- private final CommentManager manager;
- private final CodeMirror cm;
- private final FlowPanel comments;
- private LineWidget lineWidget;
- private Timer resizeTimer;
-
- CommentGroup(CommentManager manager, CodeMirror cm, DisplaySide side, int line) {
- this.manager = manager;
- this.cm = cm;
- this.side = side;
- this.line = line;
-
- comments = new FlowPanel();
- comments.setStyleName(Resources.I.style().commentWidgets());
- comments.setVisible(false);
- initWidget(new SimplePanel(comments));
- }
-
- CommentManager getCommentManager() {
- return manager;
- }
-
- CodeMirror getCm() {
- return cm;
- }
-
- int getLine() {
- return line;
- }
-
- DisplaySide getSide() {
- return side;
- }
-
- void add(PublishedBox box) {
- comments.add(box);
- comments.setVisible(true);
- }
-
- void add(DraftBox box) {
- PublishedBox p = box.getReplyToBox();
- if (p != null) {
- for (int i = 0; i < getBoxCount(); i++) {
- if (p == getCommentBox(i)) {
- comments.insert(box, i + 1);
- comments.setVisible(true);
- resize();
- return;
- }
- }
- }
- comments.add(box);
- comments.setVisible(true);
- resize();
- }
-
- CommentBox getCommentBox(int i) {
- return (CommentBox) comments.getWidget(i);
- }
-
- int getBoxCount() {
- return comments.getWidgetCount();
- }
-
- void openCloseLast() {
- if (0 < getBoxCount()) {
- CommentBox box = getCommentBox(getBoxCount() - 1);
- box.setOpen(!box.isOpen());
- }
- }
-
- void openCloseAll() {
- boolean open = false;
- for (int i = 0; i < getBoxCount(); i++) {
- if (!getCommentBox(i).isOpen()) {
- open = true;
- break;
- }
- }
- setOpenAll(open);
- }
-
- void setOpenAll(boolean open) {
- for (int i = 0; i < getBoxCount(); i++) {
- getCommentBox(i).setOpen(open);
- }
- }
-
- void remove(DraftBox box) {
- comments.remove(box);
- comments.setVisible(0 < getBoxCount());
- }
-
- void detach() {
- if (lineWidget != null) {
- lineWidget.clear();
- lineWidget = null;
- updateSelection();
- }
- manager.clearLine(side, line, this);
- removeFromParent();
- }
-
- void attach(DiffTable parent) {
- parent.add(this);
- lineWidget =
- cm.addLineWidget(
- Math.max(0, line - 1),
- getElement(),
- Configuration.create()
- .set("coverGutter", true)
- .set("noHScroll", true)
- .set("above", line <= 0)
- .set("insertAt", 0));
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- if (resizeTimer != null) {
- resizeTimer.cancel();
- }
- }
-
- void updateSelection() {
- if (cm.somethingSelected()) {
- FromTo r = cm.getSelectedRange();
- if (r.to().line() >= line) {
- cm.setSelection(r.from(), r.to());
- }
- }
- }
-
- boolean canComputeHeight() {
- return !comments.isVisible() || comments.getOffsetHeight() > 0;
- }
-
- LineWidget getLineWidget() {
- return lineWidget;
- }
-
- void setLineWidget(LineWidget widget) {
- lineWidget = widget;
- }
-
- Timer getResizeTimer() {
- return resizeTimer;
- }
-
- void setResizeTimer(Timer timer) {
- resizeTimer = timer;
- }
-
- FlowPanel getComments() {
- return comments;
- }
-
- CommentManager getManager() {
- return manager;
- }
-
- abstract void init(DiffTable parent);
-
- abstract void handleRedraw();
-
- abstract void resize();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
deleted file mode 100644
index ef1ec1e795..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
+++ /dev/null
@@ -1,451 +0,0 @@
-// Copyright (C) 2014 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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.patches.SkippedLine;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.Side;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/** Tracks comment widgets for {@link DiffScreen}. */
-abstract class CommentManager {
- @Nullable private final Project.NameKey project;
- private final DiffObject base;
- private final PatchSet.Id revision;
- private final String path;
- private final CommentLinkProcessor commentLinkProcessor;
- final SortedMap<Integer, CommentGroup> sideA;
- final SortedMap<Integer, CommentGroup> sideB;
- private final Map<String, PublishedBox> published;
- private final Set<DraftBox> unsavedDrafts;
- final DiffScreen host;
- private boolean attached;
- private boolean expandAll;
- private boolean open;
-
- CommentManager(
- DiffScreen host,
- @Nullable Project.NameKey project,
- DiffObject base,
- PatchSet.Id revision,
- String path,
- CommentLinkProcessor clp,
- boolean open) {
- this.host = host;
- this.project = project;
- this.base = base;
- this.revision = revision;
- this.path = path;
- this.commentLinkProcessor = clp;
- this.open = open;
-
- published = new HashMap<>();
- unsavedDrafts = new HashSet<>();
- sideA = new TreeMap<>();
- sideB = new TreeMap<>();
- }
-
- void setAttached(boolean attached) {
- this.attached = attached;
- }
-
- boolean isAttached() {
- return attached;
- }
-
- void setExpandAll(boolean expandAll) {
- this.expandAll = expandAll;
- }
-
- boolean isExpandAll() {
- return expandAll;
- }
-
- boolean isOpen() {
- return open;
- }
-
- String getPath() {
- return path;
- }
-
- Map<String, PublishedBox> getPublished() {
- return published;
- }
-
- CommentLinkProcessor getCommentLinkProcessor() {
- return commentLinkProcessor;
- }
-
- void renderDrafts(DisplaySide forSide, JsArray<CommentInfo> in) {
- for (CommentInfo info : Natives.asList(in)) {
- DisplaySide side = displaySide(info, forSide);
- if (side != null) {
- addDraftBox(side, info);
- }
- }
- }
-
- void setUnsaved(DraftBox box, boolean isUnsaved) {
- if (isUnsaved) {
- unsavedDrafts.add(box);
- } else {
- unsavedDrafts.remove(box);
- }
- }
-
- void saveAllDrafts(CallbackGroup cb) {
- for (DraftBox box : unsavedDrafts) {
- box.save(cb);
- }
- }
-
- Side getStoredSideFromDisplaySide(DisplaySide side) {
- if (side == DisplaySide.A && (base.isBaseOrAutoMerge() || base.isParent())) {
- return Side.PARENT;
- }
- return Side.REVISION;
- }
-
- int getParentNumFromDisplaySide(DisplaySide side) {
- if (side == DisplaySide.A) {
- return base.getParentNum();
- }
- return 0;
- }
-
- PatchSet.Id getPatchSetIdFromSide(DisplaySide side) {
- if (side == DisplaySide.A && (base.isPatchSet() || base.isEdit())) {
- return base.asPatchSetId();
- }
- return revision;
- }
-
- DisplaySide displaySide(CommentInfo info, DisplaySide forSide) {
- if (info.side() == Side.PARENT) {
- return (base.isBaseOrAutoMerge() || base.isParent()) ? DisplaySide.A : null;
- }
- return forSide;
- }
-
- static FromTo adjustSelection(CodeMirror cm) {
- FromTo fromTo = cm.getSelectedRange();
- Pos to = fromTo.to();
- if (to.ch() == 0) {
- to.line(to.line() - 1);
- to.ch(cm.getLine(to.line()).length());
- }
- return fromTo;
- }
-
- abstract CommentGroup group(DisplaySide side, int cmLinePlusOne);
-
- /**
- * Create a new {@link DraftBox} at the specified line and focus it.
- *
- * @param side which side the draft will appear on.
- * @param line the line the draft will be at. Lines are 1-based. Line 0 is a special case creating
- * a file level comment.
- */
- void insertNewDraft(DisplaySide side, int line) {
- if (line == 0) {
- host.skipManager.ensureFirstLineIsVisible();
- }
-
- CommentGroup group = group(side, line);
- if (0 < group.getBoxCount()) {
- CommentBox last = group.getCommentBox(group.getBoxCount() - 1);
- if (last instanceof DraftBox) {
- ((DraftBox) last).setEdit(true);
- } else {
- ((PublishedBox) last).doReply();
- }
- } else {
- addDraftBox(
- side,
- CommentInfo.create(
- getPath(),
- getStoredSideFromDisplaySide(side),
- getParentNumFromDisplaySide(side),
- line,
- null,
- false))
- .setEdit(true);
- }
- }
-
- abstract String getTokenSuffixForActiveLine(CodeMirror cm);
-
- Runnable signInCallback(CodeMirror cm) {
- return () -> {
- String token = host.getToken();
- if (cm.extras().hasActiveLine()) {
- token += "@" + getTokenSuffixForActiveLine(cm);
- }
- Gerrit.doSignIn(token);
- };
- }
-
- abstract void newDraft(CodeMirror cm);
-
- Runnable newDraftCallback(CodeMirror cm) {
- if (!Gerrit.isSignedIn()) {
- return signInCallback(cm);
- }
-
- return () -> {
- if (cm.extras().hasActiveLine()) {
- newDraft(cm);
- }
- };
- }
-
- DraftBox addDraftBox(DisplaySide side, CommentInfo info) {
- int cmLinePlusOne = host.getCmLine(info.line() - 1, side) + 1;
- CommentGroup group = group(side, cmLinePlusOne);
- DraftBox box =
- new DraftBox(
- group,
- getCommentLinkProcessor(),
- project,
- getPatchSetIdFromSide(side),
- info,
- isExpandAll());
-
- if (info.inReplyTo() != null) {
- PublishedBox r = getPublished().get(info.inReplyTo());
- if (r != null) {
- r.setReplyBox(box);
- }
- }
-
- group.add(box);
- box.setAnnotation(
- host.getDiffTable()
- .scrollbar
- .draft(host.getCmFromSide(side), Math.max(0, cmLinePlusOne - 1)));
- return box;
- }
-
- void setExpandAllComments(boolean b) {
- setExpandAll(b);
- for (CommentGroup g : sideA.values()) {
- g.setOpenAll(b);
- }
- for (CommentGroup g : sideB.values()) {
- g.setOpenAll(b);
- }
- }
-
- abstract SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side);
-
- Runnable commentNav(CodeMirror src, Direction dir) {
- return () -> {
- // Every comment appears in both side maps as a linked pair.
- // It is only necessary to search one side to find a comment
- // on either side of the editor pair.
- SortedMap<Integer, CommentGroup> map = getMapForNav(src.side());
- int line =
- src.extras().hasActiveLine() ? src.getLineNumber(src.extras().activeLine()) + 1 : 0;
-
- CommentGroup g;
- if (dir == Direction.NEXT) {
- map = map.tailMap(line + 1);
- if (map.isEmpty()) {
- return;
- }
- g = map.get(map.firstKey());
- while (g.getBoxCount() == 0) {
- map = map.tailMap(map.firstKey() + 1);
- if (map.isEmpty()) {
- return;
- }
- g = map.get(map.firstKey());
- }
- } else {
- map = map.headMap(line);
- if (map.isEmpty()) {
- return;
- }
- g = map.get(map.lastKey());
- while (g.getBoxCount() == 0) {
- map = map.headMap(map.lastKey());
- if (map.isEmpty()) {
- return;
- }
- g = map.get(map.lastKey());
- }
- }
-
- CodeMirror cm = g.getCm();
- double y = cm.heightAtLine(g.getLine() - 1, "local");
- cm.setCursor(Pos.create(g.getLine() - 1));
- cm.scrollToY(y - 0.5 * cm.scrollbarV().getClientHeight());
- cm.focus();
- };
- }
-
- void clearLine(DisplaySide side, int line, CommentGroup group) {
- SortedMap<Integer, CommentGroup> map = map(side);
- if (map.get(line) == group) {
- map.remove(line);
- }
- }
-
- void render(CommentsCollections in, boolean expandAll) {
- if (in.publishedBase != null) {
- renderPublished(DisplaySide.A, in.publishedBase);
- }
- if (in.publishedRevision != null) {
- renderPublished(DisplaySide.B, in.publishedRevision);
- }
- if (in.draftsBase != null) {
- renderDrafts(DisplaySide.A, in.draftsBase);
- }
- if (in.draftsRevision != null) {
- renderDrafts(DisplaySide.B, in.draftsRevision);
- }
- if (expandAll) {
- setExpandAllComments(true);
- }
- for (CommentGroup g : sideA.values()) {
- g.init(host.getDiffTable());
- }
- for (CommentGroup g : sideB.values()) {
- g.init(host.getDiffTable());
- g.handleRedraw();
- }
- setAttached(true);
- }
-
- void renderPublished(DisplaySide forSide, JsArray<CommentInfo> in) {
- for (CommentInfo info : Natives.asList(in)) {
- DisplaySide side = displaySide(info, forSide);
- if (side != null) {
- int cmLinePlusOne = host.getCmLine(info.line() - 1, side) + 1;
- CommentGroup group = group(side, cmLinePlusOne);
- PublishedBox box =
- new PublishedBox(
- group,
- getCommentLinkProcessor(),
- project,
- getPatchSetIdFromSide(side),
- info,
- side,
- isOpen());
- group.add(box);
- box.setAnnotation(
- host.getDiffTable().scrollbar.comment(host.getCmFromSide(side), cmLinePlusOne - 1));
- getPublished().put(info.id(), box);
- }
- }
- }
-
- abstract Collection<Integer> getLinesWithCommentGroups();
-
- private static void checkAndAddSkip(List<SkippedLine> out, SkippedLine s) {
- if (s.getSize() > 1) {
- out.add(s);
- }
- }
-
- List<SkippedLine> splitSkips(int context, List<SkippedLine> skips) {
- if (sideA.containsKey(0) || sideB.containsKey(0)) {
- // Special case of file comment; cannot skip first line.
- for (SkippedLine skip : skips) {
- if (skip.getStartA() == 0) {
- skip.incrementStart(1);
- break;
- }
- }
- }
-
- for (int boxLine : getLinesWithCommentGroups()) {
- List<SkippedLine> temp = new ArrayList<>(skips.size() + 2);
- for (SkippedLine skip : skips) {
- int startLine = host.getCmLine(skip.getStartB(), DisplaySide.B);
- int deltaBefore = boxLine - startLine;
- int deltaAfter = startLine + skip.getSize() - boxLine;
- if (deltaBefore < -context || deltaAfter < -context) {
- temp.add(skip); // Size guaranteed to be greater than 1
- } else if (deltaBefore > context && deltaAfter > context) {
- SkippedLine before =
- new SkippedLine(
- skip.getStartA(), skip.getStartB(), skip.getSize() - deltaAfter - context);
- skip.incrementStart(deltaBefore + context);
- checkAndAddSkip(temp, before);
- checkAndAddSkip(temp, skip);
- } else if (deltaAfter > context) {
- skip.incrementStart(deltaBefore + context);
- checkAndAddSkip(temp, skip);
- } else if (deltaBefore > context) {
- skip.reduceSize(deltaAfter + context);
- checkAndAddSkip(temp, skip);
- }
- }
- if (temp.isEmpty()) {
- return temp;
- }
- skips = temp;
- }
- return skips;
- }
-
- abstract void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int line);
-
- abstract CommentGroup getCommentGroupOnActiveLine(CodeMirror cm);
-
- Runnable toggleOpenBox(CodeMirror cm) {
- return () -> {
- CommentGroup group = getCommentGroupOnActiveLine(cm);
- if (group != null) {
- group.openCloseLast();
- }
- };
- }
-
- Runnable openCloseAll(CodeMirror cm) {
- return () -> {
- CommentGroup group = getCommentGroupOnActiveLine(cm);
- if (group != null) {
- group.openCloseAll();
- }
- };
- }
-
- SortedMap<Integer, CommentGroup> map(DisplaySide side) {
- return side == DisplaySide.A ? sideA : sideB;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
deleted file mode 100644
index 0f357d599e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentRange.java
+++ /dev/null
@@ -1,56 +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.client.diff;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker.FromTo;
-
-public class CommentRange extends JavaScriptObject {
- public static CommentRange create(int sl, int sc, int el, int ec) {
- CommentRange r = createObject().cast();
- r.set(sl, sc, el, ec);
- return r;
- }
-
- public static CommentRange create(FromTo fromTo) {
- if (fromTo == null) {
- return null;
- }
-
- Pos from = fromTo.from();
- Pos to = fromTo.to();
- return create(
- from.line() + 1, from.ch(),
- to.line() + 1, to.ch());
- }
-
- public final native int startLine() /*-{ return this.start_line; }-*/;
-
- public final native int startCharacter() /*-{ return this.start_character; }-*/;
-
- public final native int endLine() /*-{ return this.end_line; }-*/;
-
- public final native int endCharacter() /*-{ return this.end_character; }-*/;
-
- private native void set(int sl, int sc, int el, int ec) /*-{
- this.start_line = sl;
- this.start_character = sc;
- this.end_line = el;
- this.end_character = ec;
- }-*/;
-
- protected CommentRange() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
deleted file mode 100644
index 2698584ed2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
+++ /dev/null
@@ -1,165 +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.client.diff;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.CommentApi;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.Side;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Collection of published and draft comments loaded from the server. */
-class CommentsCollections {
- @Nullable private final Project.NameKey project;
- private final String path;
- private final DiffObject base;
- private final PatchSet.Id revision;
- private NativeMap<JsArray<CommentInfo>> publishedBaseAll;
- private NativeMap<JsArray<CommentInfo>> publishedRevisionAll;
- JsArray<CommentInfo> publishedBase;
- JsArray<CommentInfo> publishedRevision;
- JsArray<CommentInfo> draftsBase;
- JsArray<CommentInfo> draftsRevision;
-
- CommentsCollections(
- @Nullable Project.NameKey project, DiffObject base, PatchSet.Id revision, String path) {
- this.project = project;
- this.path = path;
- this.base = base;
- this.revision = revision;
- }
-
- void load(CallbackGroup group) {
- if (base.isPatchSet()) {
- CommentApi.comments(
- Project.NameKey.asStringOrNull(project), base.asPatchSetId(), group.add(publishedBase()));
- }
- CommentApi.comments(
- Project.NameKey.asStringOrNull(project), revision, group.add(publishedRevision()));
-
- if (Gerrit.isSignedIn()) {
- if (base.isPatchSet()) {
- CommentApi.drafts(
- Project.NameKey.asStringOrNull(project), base.asPatchSetId(), group.add(draftsBase()));
- }
- CommentApi.drafts(
- Project.NameKey.asStringOrNull(project), revision, group.add(draftsRevision()));
- }
- }
-
- boolean hasCommentForPath(String filePath) {
- if (base.isPatchSet()) {
- JsArray<CommentInfo> forBase = publishedBaseAll.get(filePath);
- if (forBase != null && forBase.length() > 0) {
- return true;
- }
- }
- JsArray<CommentInfo> forRevision = publishedRevisionAll.get(filePath);
- if (forRevision != null && forRevision.length() > 0) {
- return true;
- }
- return false;
- }
-
- private AsyncCallback<NativeMap<JsArray<CommentInfo>>> publishedBase() {
- return new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- publishedBaseAll = result;
- publishedBase = sort(result.get(path));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- };
- }
-
- private AsyncCallback<NativeMap<JsArray<CommentInfo>>> publishedRevision() {
- return new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- for (String k : result.keySet()) {
- result.put(k, filterForParent(result.get(k)));
- }
- publishedRevisionAll = result;
- publishedRevision = sort(result.get(path));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- };
- }
-
- private JsArray<CommentInfo> filterForParent(JsArray<CommentInfo> list) {
- JsArray<CommentInfo> result = JsArray.createArray().cast();
- for (CommentInfo c : Natives.asList(list)) {
- if (c.side() == Side.REVISION) {
- result.push(c);
- } else if (base.isBaseOrAutoMerge() && !c.hasParent()) {
- result.push(c);
- } else if (base.isParent() && c.parent() == base.getParentNum()) {
- result.push(c);
- }
- }
- return result;
- }
-
- private AsyncCallback<NativeMap<JsArray<CommentInfo>>> draftsBase() {
- return new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- draftsBase = sort(result.get(path));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- };
- }
-
- private AsyncCallback<NativeMap<JsArray<CommentInfo>>> draftsRevision() {
- return new AsyncCallback<NativeMap<JsArray<CommentInfo>>>() {
- @Override
- public void onSuccess(NativeMap<JsArray<CommentInfo>> result) {
- for (String k : result.keySet()) {
- result.put(k, filterForParent(result.get(k)));
- }
- draftsRevision = sort(result.get(path));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- };
- }
-
- private JsArray<CommentInfo> sort(JsArray<CommentInfo> in) {
- if (in != null) {
- for (CommentInfo c : Natives.asList(in)) {
- c.path(path);
- }
- Natives.asList(in).sort(comparing(CommentInfo::updated));
- }
- return in;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
deleted file mode 100644
index 1815920969..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
+++ /dev/null
@@ -1,116 +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.client.diff;
-
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace.IGNORE_ALL;
-
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.info.FileInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-public class DiffApi {
- public static void list(
- @Nullable String project,
- int id,
- String revision,
- RevisionInfo base,
- AsyncCallback<NativeMap<FileInfo>> cb) {
- RestApi api = ChangeApi.revision(project, id, revision).view("files");
- if (base != null) {
- if (base._number() < 0) {
- api.addParameter("parent", -base._number());
- } else {
- api.addParameter("base", base.name());
- }
- }
- api.get(NativeMap.copyKeysIntoChildren("path", cb));
- }
-
- public static void list(
- @Nullable String project,
- PatchSet.Id id,
- PatchSet.Id base,
- AsyncCallback<NativeMap<FileInfo>> cb) {
- RestApi api = ChangeApi.revision(project, id).view("files");
- if (base != null) {
- if (base.get() < 0) {
- api.addParameter("parent", -base.get());
- } else {
- api.addParameter("base", base.get());
- }
- }
- api.get(NativeMap.copyKeysIntoChildren("path", cb));
- }
-
- public static DiffApi diff(@Nullable String project, PatchSet.Id id, String path) {
- return new DiffApi(ChangeApi.revision(project, id).view("files").id(path).view("diff"));
- }
-
- private final RestApi call;
-
- private DiffApi(RestApi call) {
- this.call = call;
- }
-
- public DiffApi base(PatchSet.Id id) {
- if (id != null) {
- if (id.get() < 0) {
- call.addParameter("parent", -id.get());
- } else {
- call.addParameter("base", id.get());
- }
- }
- return this;
- }
-
- public DiffApi webLinksOnly() {
- call.addParameterTrue("weblinks-only");
- return this;
- }
-
- public DiffApi ignoreWhitespace(DiffPreferencesInfo.Whitespace w) {
- if (w != null && w != IGNORE_ALL) {
- call.addParameter("whitespace", w);
- }
- return this;
- }
-
- public DiffApi intraline(boolean intraline) {
- if (intraline) {
- call.addParameterTrue("intraline");
- }
- return this;
- }
-
- public DiffApi wholeFile() {
- call.addParameter("context", "ALL");
- return this;
- }
-
- public DiffApi context(int lines) {
- call.addParameter("context", lines);
- return this;
- }
-
- public void get(AsyncCallback<DiffInfo> cb) {
- call.get(cb);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java
deleted file mode 100644
index 3b1b346e36..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffChunkInfo.java
+++ /dev/null
@@ -1,46 +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.client.diff;
-
-/** Object recording the position of a diff chunk and whether it's an edit */
-class DiffChunkInfo {
- private DisplaySide side;
- private int start;
- private int end;
- private boolean edit;
-
- DiffChunkInfo(DisplaySide side, int start, int end, boolean edit) {
- this.side = side;
- this.start = start;
- this.end = end;
- this.edit = edit;
- }
-
- DisplaySide getSide() {
- return side;
- }
-
- int getStart() {
- return start;
- }
-
- int getEnd() {
- return end;
- }
-
- boolean isEdit() {
- return edit;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
deleted file mode 100644
index cf40762bb7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffInfo.java
+++ /dev/null
@@ -1,190 +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.client.diff;
-
-import com.google.gerrit.client.DiffWebLinkInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import java.util.ArrayList;
-import java.util.List;
-
-public class DiffInfo extends JavaScriptObject {
- public final native FileMeta metaA() /*-{ return this.meta_a; }-*/;
-
- public final native FileMeta metaB() /*-{ return this.meta_b; }-*/;
-
- public final native JsArrayString diffHeader() /*-{ return this.diff_header; }-*/;
-
- public final native JsArray<Region> content() /*-{ return this.content; }-*/;
-
- public final native JsArray<DiffWebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- public final native boolean binary() /*-{ return this.binary || false; }-*/;
-
- public final List<WebLinkInfo> sideBySideWebLinks() {
- return filterWebLinks(DiffView.SIDE_BY_SIDE);
- }
-
- public final List<WebLinkInfo> unifiedWebLinks() {
- return filterWebLinks(DiffView.UNIFIED_DIFF);
- }
-
- private List<WebLinkInfo> filterWebLinks(DiffView diffView) {
- List<WebLinkInfo> filteredDiffWebLinks = new ArrayList<>();
- List<DiffWebLinkInfo> allDiffWebLinks = Natives.asList(webLinks());
- if (allDiffWebLinks != null) {
- for (DiffWebLinkInfo webLink : allDiffWebLinks) {
- if (diffView == DiffView.SIDE_BY_SIDE && webLink.showOnSideBySideDiffView()) {
- filteredDiffWebLinks.add(webLink);
- }
- if (diffView == DiffView.UNIFIED_DIFF && webLink.showOnUnifiedDiffView()) {
- filteredDiffWebLinks.add(webLink);
- }
- }
- }
- return filteredDiffWebLinks;
- }
-
- public final ChangeType changeType() {
- return ChangeType.valueOf(changeTypeRaw());
- }
-
- private native String changeTypeRaw() /*-{ return this.change_type }-*/;
-
- public final IntraLineStatus intralineStatus() {
- String s = intralineStatusRaw();
- return s != null ? IntraLineStatus.valueOf(s) : IntraLineStatus.OFF;
- }
-
- private native String intralineStatusRaw() /*-{ return this.intraline_status }-*/;
-
- public final boolean hasSkip() {
- JsArray<Region> c = content();
- for (int i = 0; i < c.length(); i++) {
- if (c.get(i).skip() != 0) {
- return true;
- }
- }
- return false;
- }
-
- public final String textA() {
- StringBuilder s = new StringBuilder();
- JsArray<Region> c = content();
- for (int i = 0; i < c.length(); i++) {
- Region r = c.get(i);
- if (r.ab() != null) {
- append(s, r.ab());
- } else if (r.a() != null) {
- append(s, r.a());
- }
- // TODO skip may need to be handled
- }
- return s.toString();
- }
-
- public final String textB() {
- StringBuilder s = new StringBuilder();
- JsArray<Region> c = content();
- for (int i = 0; i < c.length(); i++) {
- Region r = c.get(i);
- if (r.ab() != null) {
- append(s, r.ab());
- } else if (r.b() != null) {
- append(s, r.b());
- }
- // TODO skip may need to be handled
- }
- return s.toString();
- }
-
- public final String textUnified() {
- StringBuilder s = new StringBuilder();
- JsArray<Region> c = content();
- for (int i = 0; i < c.length(); i++) {
- Region r = c.get(i);
- if (r.ab() != null) {
- append(s, r.ab());
- } else {
- if (r.a() != null) {
- append(s, r.a());
- }
- if (r.b() != null) {
- append(s, r.b());
- }
- }
- // TODO skip may need to be handled
- }
- return s.toString();
- }
-
- private static void append(StringBuilder s, JsArrayString lines) {
- for (int i = 0; i < lines.length(); i++) {
- s.append(lines.get(i)).append('\n');
- }
- }
-
- protected DiffInfo() {}
-
- public enum IntraLineStatus {
- OFF,
- OK,
- TIMEOUT,
- FAILURE
- }
-
- public static class FileMeta extends JavaScriptObject {
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String contentType() /*-{ return this.content_type; }-*/;
-
- public final native int lines() /*-{ return this.lines || 0 }-*/;
-
- public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- protected FileMeta() {}
- }
-
- public static class Region extends JavaScriptObject {
- public final native JsArrayString ab() /*-{ return this.ab; }-*/;
-
- public final native JsArrayString a() /*-{ return this.a; }-*/;
-
- public final native JsArrayString b() /*-{ return this.b; }-*/;
-
- public final native int skip() /*-{ return this.skip || 0; }-*/;
-
- public final native boolean common() /*-{ return this.common || false; }-*/;
-
- public final native JsArray<Span> editA() /*-{ return this.edit_a }-*/;
-
- public final native JsArray<Span> editB() /*-{ return this.edit_b }-*/;
-
- protected Region() {}
- }
-
- public static class Span extends JavaScriptObject {
- public final native int skip() /*-{ return this[0]; }-*/;
-
- public final native int mark() /*-{ return this[1]; }-*/;
-
- protected Span() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java
deleted file mode 100644
index b4221cab84..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffScreen.java
+++ /dev/null
@@ -1,931 +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 impl ied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.client.diff;
-
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.WHOLE_FILE_CONTEXT;
-import static java.lang.Double.POSITIVE_INFINITY;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.DiffPreferences;
-import com.google.gerrit.client.change.ChangeScreen;
-import com.google.gerrit.client.change.FileTable;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeList;
-import com.google.gerrit.client.diff.DiffInfo.FileMeta;
-import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
-import com.google.gerrit.client.info.ChangeInfo.EditInfo;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.info.FileInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.projects.ConfigInfoCache;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.extensions.client.ListChangesOption;
-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.gwt.core.client.JsArray;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.RepeatingCommand;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtexpui.globalkey.client.ShowHelpCommand;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.BeforeSelectionChangeHandler;
-import net.codemirror.lib.CodeMirror.GutterClickHandler;
-import net.codemirror.lib.CodeMirror.LineHandle;
-import net.codemirror.lib.KeyMap;
-import net.codemirror.lib.Pos;
-import net.codemirror.mode.ModeInfo;
-import net.codemirror.mode.ModeInjector;
-import net.codemirror.theme.ThemeLoader;
-
-/** Base class for SideBySide and Unified */
-abstract class DiffScreen extends Screen {
- private static final KeyMap RENDER_ENTIRE_FILE_KEYMAP =
- KeyMap.create().propagate("Ctrl-F").propagate("Ctrl-G").propagate("Shift-Ctrl-G");
-
- enum FileSize {
- SMALL(0),
- LARGE(500),
- HUGE(4000);
-
- final int lines;
-
- FileSize(int n) {
- this.lines = n;
- }
- }
-
- @Nullable private Project.NameKey project;
- private final Change.Id changeId;
- final DiffObject base;
- final PatchSet.Id revision;
- final String path;
- final DiffPreferences prefs;
- final SkipManager skipManager;
-
- private DisplaySide startSide;
- private int startLine;
- private Change.Status changeStatus;
-
- private HandlerRegistration resizeHandler;
- private DiffInfo diff;
- private FileSize fileSize;
- private EditInfo edit;
-
- private KeyCommandSet keysNavigation;
- private KeyCommandSet keysAction;
- private KeyCommandSet keysComment;
- private List<HandlerRegistration> handlers;
- private PreferencesAction prefsAction;
- private int reloadVersionId;
- private int parents;
-
- @UiField(provided = true)
- Header header;
-
- DiffScreen(
- @Nullable Project.NameKey project,
- DiffObject base,
- DiffObject revision,
- String path,
- DisplaySide startSide,
- int startLine,
- DiffView diffScreenType) {
- this.project = project;
- this.base = base;
- this.revision = revision.asPatchSetId();
- this.changeId = revision.asPatchSetId().getParentKey();
- this.path = path;
- this.startSide = startSide;
- this.startLine = startLine;
-
- prefs = DiffPreferences.create(Gerrit.getDiffPreferences());
- handlers = new ArrayList<>(6);
- keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
- header = new Header(keysNavigation, project, base, revision, path, diffScreenType, prefs);
- skipManager = new SkipManager(this);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setHeaderVisible(false);
- setWindowTitle(FileInfo.getFileName(path));
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- CallbackGroup group1 = new CallbackGroup();
- final CallbackGroup group2 = new CallbackGroup();
-
- CodeMirror.initLibrary(
- group1.add(
- new AsyncCallback<Void>() {
- final AsyncCallback<Void> themeCallback = group2.addEmpty();
-
- @Override
- public void onSuccess(Void result) {
- // Load theme after CM library to ensure theme can override CSS.
- ThemeLoader.loadTheme(prefs.theme(), themeCallback);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
-
- DiffApi.diff(Project.NameKey.asStringOrNull(project), revision, path)
- .base(base.asPatchSetId())
- .wholeFile()
- .intraline(prefs.intralineDifference())
- .ignoreWhitespace(prefs.ignoreWhitespace())
- .get(
- group1.addFinal(
- new GerritCallback<DiffInfo>() {
- final AsyncCallback<Void> modeInjectorCb = group2.addEmpty();
-
- @Override
- public void onSuccess(DiffInfo diffInfo) {
- diff = diffInfo;
- fileSize = bucketFileSize(diffInfo);
-
- if (prefs.syntaxHighlighting()) {
- if (fileSize.compareTo(FileSize.SMALL) > 0) {
- modeInjectorCb.onSuccess(null);
- } else {
- injectMode(diffInfo, modeInjectorCb);
- }
- } else {
- modeInjectorCb.onSuccess(null);
- }
- }
- }));
-
- if (Gerrit.isSignedIn()) {
- ChangeApi.edit(
- Project.NameKey.asStringOrNull(project),
- changeId.get(),
- group2.add(
- new AsyncCallback<EditInfo>() {
- @Override
- public void onSuccess(EditInfo result) {
- edit = result;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- }
-
- final CommentsCollections comments = new CommentsCollections(project, base, revision, path);
- comments.load(group2);
-
- countParents(group2);
-
- RestApi call = ChangeApi.detail(Project.NameKey.asStringOrNull(project), changeId.get());
- ChangeList.addOptions(call, EnumSet.of(ListChangesOption.ALL_REVISIONS));
- call.get(
- group2.add(
- new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo info) {
- changeStatus = info.status();
- project = info.projectNameKey();
- info.revisions().copyKeysIntoChildren("name");
- if (edit != null) {
- edit.setName(edit.commit().commit());
- info.setEdit(edit);
- info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
- }
- String currentRevision = info.currentRevision();
- boolean current =
- currentRevision != null
- && revision.get() == info.revision(currentRevision)._number();
- JsArray<RevisionInfo> list = info.revisions().values();
- RevisionInfo.sortRevisionInfoByNumber(list);
- getDiffTable()
- .set(
- prefs,
- list,
- parents,
- diff,
- edit != null,
- current,
- changeStatus.isOpen(),
- diff.binary());
- header.setChangeInfo(info);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
-
- ConfigInfoCache.get(changeId, group2.addFinal(getScreenLoadCallback(comments)));
- }
-
- private void countParents(CallbackGroup cbg) {
- ChangeApi.revision(Project.NameKey.asStringOrNull(project), changeId.get(), revision.getId())
- .view("commit")
- .get(
- cbg.add(
- new AsyncCallback<CommitInfo>() {
- @Override
- public void onSuccess(CommitInfo info) {
- parents = info.parents().length();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- parents = 0;
- }
- }));
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
-
- Window.enableScrolling(false);
- if (prefs.hideTopMenu()) {
- Gerrit.setHeaderVisible(false);
- }
- resizeHandler =
- Window.addResizeHandler(
- new ResizeHandler() {
- @Override
- public void onResize(ResizeEvent event) {
- resizeCodeMirror();
- }
- });
- }
-
- KeyCommandSet getKeysNavigation() {
- return keysNavigation;
- }
-
- KeyCommandSet getKeysAction() {
- return keysAction;
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
-
- removeKeyHandlerRegistrations();
- if (getCommentManager() != null) {
- CallbackGroup group = new CallbackGroup();
- getCommentManager().saveAllDrafts(group);
- group.done();
- }
- if (resizeHandler != null) {
- resizeHandler.removeHandler();
- resizeHandler = null;
- }
- for (CodeMirror cm : getCms()) {
- if (cm != null) {
- cm.getWrapperElement().removeFromParent();
- }
- }
- if (prefsAction != null) {
- prefsAction.hide();
- }
-
- Window.enableScrolling(true);
- Gerrit.setHeaderVisible(true);
- }
-
- private void removeKeyHandlerRegistrations() {
- for (HandlerRegistration h : handlers) {
- h.removeHandler();
- }
- handlers.clear();
- }
-
- void registerCmEvents(CodeMirror cm) {
- cm.on("cursorActivity", updateActiveLine(cm));
- cm.on("focus", updateActiveLine(cm));
- KeyMap keyMap =
- KeyMap.create()
- .on("A", upToChange(true))
- .on("U", upToChange(false))
- .on("'['", header.navigate(Direction.PREV))
- .on("']'", header.navigate(Direction.NEXT))
- .on("R", header.toggleReviewed())
- .on("O", getCommentManager().toggleOpenBox(cm))
- .on("N", maybeNextVimSearch(cm))
- .on("Ctrl-Alt-E", openEditScreen(cm))
- .on("P", getChunkManager().diffChunkNav(cm, Direction.PREV))
- .on("Shift-M", header.reviewedAndNext())
- .on("Shift-N", maybePrevVimSearch(cm))
- .on("Shift-P", getCommentManager().commentNav(cm, Direction.PREV))
- .on("Shift-O", getCommentManager().openCloseAll(cm))
- .on(
- "I",
- () -> {
- switch (getIntraLineStatus()) {
- case OFF:
- case OK:
- toggleShowIntraline();
- break;
- case FAILURE:
- case TIMEOUT:
- default:
- break;
- }
- })
- .on("','", prefsAction::show)
- .on("Shift-/", () -> new ShowHelpCommand().onKeyPress(null))
- .on("Space", () -> cm.vim().handleKey("<C-d>"))
- .on("Shift-Space", () -> cm.vim().handleKey("<C-u>"))
- .on("Ctrl-F", () -> cm.execCommand("find"))
- .on("Ctrl-G", () -> cm.execCommand("findNext"))
- .on("Enter", maybeNextCmSearch(cm))
- .on("Shift-Ctrl-G", () -> cm.execCommand("findPrev"))
- .on("Shift-Enter", () -> cm.execCommand("findPrev"))
- .on(
- "Esc",
- () -> {
- cm.setCursor(cm.getCursor());
- cm.execCommand("clearSearch");
- cm.vim().handleEx("nohlsearch");
- })
- .on("Ctrl-A", () -> cm.execCommand("selectAll"))
- .on("G O", () -> Gerrit.display(PageLinks.toChangeQuery("status:open")))
- .on("G M", () -> Gerrit.display(PageLinks.toChangeQuery("status:merged")))
- .on("G A", () -> Gerrit.display(PageLinks.toChangeQuery("status:abandoned")));
- if (Gerrit.isSignedIn()) {
- keyMap
- .on("G I", () -> Gerrit.display(PageLinks.MINE))
- .on("G C", () -> Gerrit.display(PageLinks.toChangeQuery("has:draft")))
- .on("G W", () -> Gerrit.display(PageLinks.toChangeQuery("is:watched status:open")))
- .on("G S", () -> Gerrit.display(PageLinks.toChangeQuery("is:starred")));
- }
-
- if (revision.get() != 0) {
- cm.on("beforeSelectionChange", onSelectionChange(cm));
- cm.on("gutterClick", onGutterClick(cm));
- keyMap.on("C", getCommentManager().newDraftCallback(cm));
- }
- CodeMirror.normalizeKeyMap(keyMap); // Needed to for multi-stroke keymaps
- cm.addKeyMap(keyMap);
- }
-
- void maybeRegisterRenderEntireFileKeyMap(CodeMirror cm) {
- if (renderEntireFile()) {
- cm.addKeyMap(RENDER_ENTIRE_FILE_KEYMAP);
- }
- }
-
- private BeforeSelectionChangeHandler onSelectionChange(CodeMirror cm) {
- return new BeforeSelectionChangeHandler() {
- private InsertCommentBubble bubble;
-
- @Override
- public void handle(CodeMirror cm, Pos anchor, Pos head) {
- if (anchor.equals(head)) {
- if (bubble != null) {
- bubble.setVisible(false);
- }
- return;
- } else if (bubble == null) {
- init(anchor);
- } else {
- bubble.setVisible(true);
- }
- bubble.position(cm.charCoords(head, "local"));
- }
-
- private void init(Pos anchor) {
- bubble = new InsertCommentBubble(getCommentManager(), cm);
- add(bubble);
- cm.addWidget(anchor, bubble.getElement());
- }
- };
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
-
- keysNavigation.add(new UpToChangeCommand(project, revision, 0, 'u'));
- keysNavigation.add(
- new NoOpKeyCommand(0, 'j', PatchUtil.C.lineNext()),
- new NoOpKeyCommand(0, 'k', PatchUtil.C.linePrev()));
- keysNavigation.add(
- new NoOpKeyCommand(0, 'n', PatchUtil.C.chunkNext()),
- new NoOpKeyCommand(0, 'p', PatchUtil.C.chunkPrev()));
- keysNavigation.add(
- new NoOpKeyCommand(KeyCommand.M_SHIFT, 'n', PatchUtil.C.commentNext()),
- new NoOpKeyCommand(KeyCommand.M_SHIFT, 'p', PatchUtil.C.commentPrev()));
- keysNavigation.add(new NoOpKeyCommand(KeyCommand.M_CTRL, 'f', Gerrit.C.keySearch()));
-
- keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
- keysAction.add(new NoOpKeyCommand(0, KeyCodes.KEY_ENTER, PatchUtil.C.expandComment()));
- keysAction.add(new NoOpKeyCommand(0, 'o', PatchUtil.C.expandComment()));
- keysAction.add(
- new NoOpKeyCommand(KeyCommand.M_SHIFT, 'o', PatchUtil.C.expandAllCommentsOnCurrentLine()));
- if (Gerrit.isSignedIn()) {
- keysAction.add(
- new KeyCommand(0, 'r', PatchUtil.C.toggleReviewed()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- header.toggleReviewed().run();
- }
- });
- keysAction.add(
- new NoOpKeyCommand(KeyCommand.M_CTRL | KeyCommand.M_ALT, 'e', Gerrit.C.keyEditor()));
- }
- keysAction.add(
- new KeyCommand(KeyCommand.M_SHIFT, 'm', PatchUtil.C.markAsReviewedAndGoToNext()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- header.reviewedAndNext().run();
- }
- });
- keysAction.add(
- new KeyCommand(0, 'a', PatchUtil.C.openReply()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- upToChange(true).run();
- }
- });
- keysAction.add(
- new KeyCommand(0, ',', PatchUtil.C.showPreferences()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- prefsAction.show();
- }
- });
- if (getIntraLineStatus() == DiffInfo.IntraLineStatus.OFF
- || getIntraLineStatus() == DiffInfo.IntraLineStatus.OK) {
- keysAction.add(
- new KeyCommand(0, 'i', PatchUtil.C.toggleIntraline()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- toggleShowIntraline();
- }
- });
- }
-
- if (Gerrit.isSignedIn()) {
- keysAction.add(new NoOpKeyCommand(0, 'c', PatchUtil.C.commentInsert()));
- keysComment = new KeyCommandSet(PatchUtil.C.commentEditorSet());
- keysComment.add(new NoOpKeyCommand(KeyCommand.M_CTRL, 's', PatchUtil.C.commentSaveDraft()));
- keysComment.add(new NoOpKeyCommand(0, KeyCodes.KEY_ESCAPE, PatchUtil.C.commentCancelEdit()));
- } else {
- keysComment = null;
- }
- }
-
- @Nullable
- public Project.NameKey getProject() {
- return project;
- }
-
- void registerHandlers() {
- removeKeyHandlerRegistrations();
- handlers.add(GlobalKey.add(this, keysAction));
- handlers.add(GlobalKey.add(this, keysNavigation));
- if (keysComment != null) {
- handlers.add(GlobalKey.add(this, keysComment));
- }
- handlers.add(ShowHelpCommand.addFocusHandler(getFocusHandler()));
- }
-
- void setupSyntaxHighlighting() {
- if (prefs.syntaxHighlighting() && fileSize.compareTo(FileSize.SMALL) > 0) {
- Scheduler.get()
- .scheduleFixedDelay(
- new RepeatingCommand() {
- @Override
- public boolean execute() {
- if (prefs.syntaxHighlighting() && isAttached()) {
- setSyntaxHighlighting(prefs.syntaxHighlighting());
- }
- return false;
- }
- },
- 250);
- }
- }
-
- abstract CodeMirror newCm(DiffInfo.FileMeta meta, String contents, Element parent);
-
- void render(DiffInfo diff) {
- header.setNoDiff(diff);
- getChunkManager().render(diff);
- }
-
- void setShowLineNumbers(boolean b) {
- if (b) {
- getDiffTable().addStyleName(Resources.I.diffTableStyle().showLineNumbers());
- } else {
- getDiffTable().removeStyleName(Resources.I.diffTableStyle().showLineNumbers());
- }
- }
-
- void setShowIntraline(boolean b) {
- if (b && getIntraLineStatus() == DiffInfo.IntraLineStatus.OFF) {
- reloadDiffInfo();
- } else if (b) {
- getDiffTable().removeStyleName(Resources.I.diffTableStyle().noIntraline());
- } else {
- getDiffTable().addStyleName(Resources.I.diffTableStyle().noIntraline());
- }
- }
-
- private void toggleShowIntraline() {
- prefs.intralineDifference(!Boolean.valueOf(prefs.intralineDifference()));
- setShowIntraline(prefs.intralineDifference());
- prefsAction.update();
- }
-
- abstract void setSyntaxHighlighting(boolean b);
-
- void setContext(int context) {
- operation(
- () -> {
- skipManager.removeAll();
- skipManager.render(context, diff);
- updateRenderEntireFile();
- });
- }
-
- private int adjustCommitMessageLine(int line) {
- /* When commit messages are shown in the diff screen they include
- a header block that looks like this:
-
- 1 Parent: deadbeef (Parent commit title)
- 2 Author: A. U. Thor <author@example.com>
- 3 AuthorDate: 2015-02-27 19:20:52 +0900
- 4 Commit: A. U. Thor <author@example.com>
- 5 CommitDate: 2015-02-27 19:20:52 +0900
- 6 [blank line]
- 7 Commit message title
- 8
- 9 Commit message body
- 10 ...
- 11 ...
-
- If the commit is a merge commit, both parent commits are listed in the
- first two lines instead of a 'Parent' line:
-
- 1 Merge Of: deadbeef (Parent 1 commit title)
- 2 beefdead (Parent 2 commit title)
-
- */
-
- // Offset to compensate for header lines until the blank line
- // after 'CommitDate'
- int offset = 6;
-
- // Adjust for merge commits, which have two parent lines
- if (diff.textB().startsWith("Merge")) {
- offset += 1;
- }
-
- // If the cursor is inside the header line, reset to the first line of the
- // commit message. Otherwise if the cursor is on an actual line of the commit
- // message, adjust the line number to compensate for the header lines, so the
- // focus is on the correct line.
- if (line <= offset) {
- return 1;
- }
- return line - offset;
- }
-
- private Runnable openEditScreen(CodeMirror cm) {
- return () -> {
- LineHandle handle = cm.extras().activeLine();
- int line = cm.getLineNumber(handle) + 1;
- if (Patch.COMMIT_MSG.equals(path)) {
- line = adjustCommitMessageLine(line);
- }
- String token = Dispatcher.toEditScreen(project, revision, path, line);
- if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(token);
- } else {
- Gerrit.display(token);
- }
- };
- }
-
- void updateRenderEntireFile() {
- boolean entireFile = renderEntireFile();
- for (CodeMirror cm : getCms()) {
- cm.removeKeyMap(RENDER_ENTIRE_FILE_KEYMAP);
- if (entireFile) {
- cm.addKeyMap(RENDER_ENTIRE_FILE_KEYMAP);
- }
- cm.setOption("viewportMargin", entireFile ? POSITIVE_INFINITY : 10);
- }
- }
-
- void resizeCodeMirror() {
- int height = header.getOffsetHeight() + getDiffTable().getHeaderHeight();
- for (CodeMirror cm : getCms()) {
- cm.adjustHeight(height);
- }
- }
-
- abstract ChunkManager getChunkManager();
-
- abstract CommentManager getCommentManager();
-
- Change.Status getChangeStatus() {
- return changeStatus;
- }
-
- int getStartLine() {
- return startLine;
- }
-
- void setStartLine(int startLine) {
- this.startLine = startLine;
- }
-
- DisplaySide getStartSide() {
- return startSide;
- }
-
- void setStartSide(DisplaySide startSide) {
- this.startSide = startSide;
- }
-
- DiffInfo getDiff() {
- return diff;
- }
-
- FileSize getFileSize() {
- return fileSize;
- }
-
- PreferencesAction getPrefsAction() {
- return prefsAction;
- }
-
- void setPrefsAction(PreferencesAction prefsAction) {
- this.prefsAction = prefsAction;
- }
-
- abstract void operation(Runnable apply);
-
- private Runnable upToChange(boolean openReplyBox) {
- return () -> {
- CallbackGroup group = new CallbackGroup();
- getCommentManager().saveAllDrafts(group);
- group.done();
- group.addListener(
- new GerritCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- String rev = String.valueOf(revision.get());
- Gerrit.display(
- PageLinks.toChange(project, changeId, base.asString(), rev),
- new ChangeScreen(
- project, changeId, base, rev, openReplyBox, FileTable.Mode.REVIEW));
- }
- });
- };
- }
-
- private Runnable maybePrevVimSearch(CodeMirror cm) {
- return () -> {
- if (cm.vim().hasSearchHighlight()) {
- cm.vim().handleKey("N");
- } else {
- getCommentManager().commentNav(cm, Direction.NEXT).run();
- }
- };
- }
-
- private Runnable maybeNextVimSearch(CodeMirror cm) {
- return () -> {
- if (cm.vim().hasSearchHighlight()) {
- cm.vim().handleKey("n");
- } else {
- getChunkManager().diffChunkNav(cm, Direction.NEXT).run();
- }
- };
- }
-
- Runnable maybeNextCmSearch(CodeMirror cm) {
- return () -> {
- if (cm.hasSearchHighlight()) {
- cm.execCommand("findNext");
- } else {
- cm.execCommand("clearSearch");
- getCommentManager().toggleOpenBox(cm).run();
- }
- };
- }
-
- boolean renderEntireFile() {
- return prefs.renderEntireFile() && canRenderEntireFile(prefs);
- }
-
- boolean canRenderEntireFile(DiffPreferences prefs) {
- // CodeMirror is too slow to layout an entire huge file.
- return fileSize.compareTo(FileSize.HUGE) < 0
- || (prefs.context() != WHOLE_FILE_CONTEXT && prefs.context() < 100);
- }
-
- DiffInfo.IntraLineStatus getIntraLineStatus() {
- return diff.intralineStatus();
- }
-
- void setThemeStyles(boolean d) {
- if (d) {
- getDiffTable().addStyleName(Resources.I.diffTableStyle().dark());
- } else {
- getDiffTable().removeStyleName(Resources.I.diffTableStyle().dark());
- }
- }
-
- void setShowTabs(boolean show) {
- for (CodeMirror cm : getCms()) {
- cm.extras().showTabs(show);
- }
- }
-
- void setLineLength(int columns) {
- for (CodeMirror cm : getCms()) {
- cm.extras().lineLength(columns);
- }
- }
-
- String getContentType(DiffInfo.FileMeta meta) {
- if (prefs.syntaxHighlighting() && meta != null && meta.contentType() != null) {
- ModeInfo m = ModeInfo.findMode(meta.contentType(), path);
- return m != null ? m.mime() : null;
- }
- return null;
- }
-
- String getContentType() {
- return getContentType(diff.metaB());
- }
-
- void injectMode(DiffInfo diffInfo, AsyncCallback<Void> cb) {
- new ModeInjector()
- .add(getContentType(diffInfo.metaA()))
- .add(getContentType(diffInfo.metaB()))
- .inject(cb);
- }
-
- abstract void setAutoHideDiffHeader(boolean hide);
-
- void prefetchNextFile() {
- String nextPath = header.getNextPath();
- if (nextPath != null) {
- DiffApi.diff(Project.NameKey.asStringOrNull(project), revision, nextPath)
- .base(base.asPatchSetId())
- .wholeFile()
- .intraline(prefs.intralineDifference())
- .ignoreWhitespace(prefs.ignoreWhitespace())
- .get(
- new AsyncCallback<DiffInfo>() {
- @Override
- public void onSuccess(DiffInfo info) {
- new ModeInjector()
- .add(getContentType(info.metaA()))
- .add(getContentType(info.metaB()))
- .inject(CallbackGroup.<Void>emptyCallback());
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
-
- void reloadDiffInfo() {
- int id = ++reloadVersionId;
- DiffApi.diff(Project.NameKey.asStringOrNull(project), revision, path)
- .base(base.asPatchSetId())
- .wholeFile()
- .intraline(prefs.intralineDifference())
- .ignoreWhitespace(prefs.ignoreWhitespace())
- .get(
- new GerritCallback<DiffInfo>() {
- @Override
- public void onSuccess(DiffInfo diffInfo) {
- if (id == reloadVersionId && isAttached()) {
- diff = diffInfo;
- operation(
- () -> {
- skipManager.removeAll();
- getChunkManager().reset();
- getDiffTable().scrollbar.removeDiffAnnotations();
- setShowIntraline(prefs.intralineDifference());
- render(diff);
- skipManager.render(prefs.context(), diff);
- });
- }
- }
- });
- }
-
- private static FileSize bucketFileSize(DiffInfo diff) {
- FileMeta a = diff.metaA();
- FileMeta b = diff.metaB();
- FileSize[] sizes = FileSize.values();
- for (int i = sizes.length - 1; 0 <= i; i--) {
- FileSize s = sizes[i];
- if ((a != null && s.lines <= a.lines()) || (b != null && s.lines <= b.lines())) {
- return s;
- }
- }
- return FileSize.SMALL;
- }
-
- abstract Runnable updateActiveLine(CodeMirror cm);
-
- private GutterClickHandler onGutterClick(CodeMirror cm) {
- return new GutterClickHandler() {
- @Override
- public void handle(
- CodeMirror instance, int line, String gutterClass, NativeEvent clickEvent) {
- if (Element.as(clickEvent.getEventTarget()).hasClassName(getLineNumberClassName())
- && clickEvent.getButton() == NativeEvent.BUTTON_LEFT
- && !clickEvent.getMetaKey()
- && !clickEvent.getAltKey()
- && !clickEvent.getCtrlKey()
- && !clickEvent.getShiftKey()) {
- cm.setCursor(Pos.create(line));
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- getCommentManager().newDraftOnGutterClick(cm, gutterClass, line + 1);
- }
- });
- }
- }
- };
- }
-
- abstract FocusHandler getFocusHandler();
-
- abstract CodeMirror[] getCms();
-
- abstract CodeMirror getCmFromSide(DisplaySide side);
-
- abstract DiffTable getDiffTable();
-
- abstract int getCmLine(int line, DisplaySide side);
-
- abstract String getLineNumberClassName();
-
- LineOnOtherInfo lineOnOther(DisplaySide side, int line) {
- return getChunkManager().lineMapper.lineOnOther(side, line);
- }
-
- abstract ScreenLoadCallback<ConfigInfoCache.Entry> getScreenLoadCallback(
- CommentsCollections comments);
-
- abstract boolean isSideBySide();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.css
deleted file mode 100644
index 7569cf50c0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.css
+++ /dev/null
@@ -1,41 +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.
- */
-.range {
- background-color: #ffd500 !important;
-}
-.rangeHighlight {
- background-color: #ffff00 !important;
-}
-
-.fullscreen {
- background-color: #f7f7f7;
- border-bottom: 1px solid #ddd;
-}
-
-@external .diffHeader;
-.diffHeader {
- font-size: 12px;
- font-weight: bold;
- color: #5252ad;
-}
-
-.diffHeader pre {
- margin: 0 0 3px 0;
-}
-
-@external .dark, .noIntraline, .showLineNumbers;
-.dark {}
-.noIntraline {}
-.showLineNumbers {} \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
deleted file mode 100644
index a91f8e653e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
+++ /dev/null
@@ -1,184 +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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.account.DiffPreferences;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import net.codemirror.lib.CodeMirror;
-
-/** Base class for SideBySideTable2 and UnifiedTable2 */
-abstract class DiffTable extends Composite {
- static {
- Resources.I.diffTableStyle().ensureInjected();
- }
-
- interface Style extends CssResource {
- String fullscreen();
-
- String dark();
-
- String noIntraline();
-
- String range();
-
- String rangeHighlight();
-
- String diffHeader();
-
- String showLineNumbers();
- }
-
- @UiField Element patchSetNavRow;
- @UiField Element patchSetNavCellA;
- @UiField Element patchSetNavCellB;
- @UiField Element diffHeaderRow;
- @UiField Element diffHeaderText;
- @UiField FlowPanel widgets;
-
- @UiField(provided = true)
- PatchSetSelectBox patchSetSelectBoxA;
-
- @UiField(provided = true)
- PatchSetSelectBox patchSetSelectBoxB;
-
- private boolean header;
- private ChangeType changeType;
- Scrollbar scrollbar;
-
- DiffTable(DiffScreen parent, DiffObject base, DiffObject revision, String path) {
- patchSetSelectBoxA =
- new PatchSetSelectBox(
- parent,
- DisplaySide.A,
- parent.getProject(),
- revision.asPatchSetId().getParentKey(),
- base,
- path);
- patchSetSelectBoxB =
- new PatchSetSelectBox(
- parent,
- DisplaySide.B,
- parent.getProject(),
- revision.asPatchSetId().getParentKey(),
- revision,
- path);
- PatchSetSelectBox.link(patchSetSelectBoxA, patchSetSelectBoxB);
-
- this.scrollbar = new Scrollbar(this);
- }
-
- abstract boolean isVisibleA();
-
- void setHeaderVisible(boolean show) {
- DiffScreen parent = getDiffScreen();
- if (show != UIObject.isVisible(patchSetNavRow)) {
- UIObject.setVisible(patchSetNavRow, show);
- UIObject.setVisible(diffHeaderRow, show && header);
- if (show) {
- parent.header.removeStyleName(Resources.I.diffTableStyle().fullscreen());
- } else {
- parent.header.addStyleName(Resources.I.diffTableStyle().fullscreen());
- }
- parent.resizeCodeMirror();
- }
- }
-
- abstract int getHeaderHeight();
-
- ChangeType getChangeType() {
- return changeType;
- }
-
- void setUpBlameIconA(CodeMirror cm, boolean isBase, PatchSet.Id rev, String path) {
- patchSetSelectBoxA.setUpBlame(cm, isBase, rev, path);
- }
-
- void setUpBlameIconB(CodeMirror cm, PatchSet.Id rev, String path) {
- patchSetSelectBoxB.setUpBlame(cm, false, rev, path);
- }
-
- void set(
- DiffPreferences prefs,
- JsArray<RevisionInfo> list,
- int parents,
- DiffInfo info,
- boolean editExists,
- boolean current,
- boolean open,
- boolean binary) {
- this.changeType = info.changeType();
- patchSetSelectBoxA.setUpPatchSetNav(
- list, parents, info.metaA(), editExists, current, open, binary);
- patchSetSelectBoxB.setUpPatchSetNav(
- list, parents, info.metaB(), editExists, current, open, binary);
-
- JsArrayString hdr = info.diffHeader();
- if (hdr != null) {
- StringBuilder b = new StringBuilder();
- for (int i = 1; i < hdr.length(); i++) {
- String s = hdr.get(i);
- if (!info.binary()
- && (s.startsWith("diff --git ")
- || s.startsWith("index ")
- || s.startsWith("+++ ")
- || s.startsWith("--- "))) {
- continue;
- }
- b.append(s).append('\n');
- }
-
- String hdrTxt = b.toString().trim();
- header = !hdrTxt.isEmpty();
- diffHeaderText.setInnerText(hdrTxt);
- UIObject.setVisible(diffHeaderRow, header);
- } else {
- header = false;
- UIObject.setVisible(diffHeaderRow, false);
- }
- setHideEmptyPane(prefs.hideEmptyPane());
- }
-
- abstract void setHideEmptyPane(boolean hide);
-
- void refresh() {
- if (header) {
- CodeMirror cm = getDiffScreen().getCmFromSide(DisplaySide.A);
- diffHeaderText.getStyle().setMarginLeft(cm.getGutterElement().getOffsetWidth(), Unit.PX);
- }
- }
-
- void add(Widget widget) {
- widgets.add(widget);
- }
-
- abstract DiffScreen getDiffScreen();
-
- boolean hasHeader() {
- return header;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Direction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Direction.java
deleted file mode 100644
index b1dd87e1cf..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Direction.java
+++ /dev/null
@@ -1,21 +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.client.diff;
-
-/** Direction of traversal in an ordered list. */
-public enum Direction {
- PREV,
- NEXT
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
deleted file mode 100644
index 6cee17410a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
+++ /dev/null
@@ -1,25 +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.client.diff;
-
-/** Enum representing the side on a side-by-side view */
-public enum DisplaySide {
- A,
- B;
-
- DisplaySide otherSide() {
- return this == A ? B : A;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
deleted file mode 100644
index 33d1ac4524..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.java
+++ /dev/null
@@ -1,466 +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.client.diff;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.change.LocalComments;
-import com.google.gerrit.client.changes.CommentApi;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.MouseMoveEvent;
-import com.google.gwt.event.dom.client.MouseMoveHandler;
-import com.google.gwt.event.dom.client.MouseUpEvent;
-import com.google.gwt.event.dom.client.MouseUpHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import net.codemirror.lib.CodeMirror;
-
-/** An HtmlPanel for displaying and editing a draft */
-class DraftBox extends CommentBox {
- interface Binder extends UiBinder<HTMLPanel, DraftBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- private static final int INITIAL_LINES = 5;
- private static final int MAX_LINES = 30;
-
- private final CommentLinkProcessor linkProcessor;
- private final PatchSet.Id psId;
- @Nullable private final Project.NameKey project;
- private final boolean expandAll;
- private CommentInfo comment;
- private PublishedBox replyToBox;
- private Timer expandTimer;
- private Timer resizeTimer;
- private int editAreaHeight;
- private boolean autoClosed;
- private CallbackGroup pendingGroup;
-
- @UiField Widget header;
- @UiField Element summary;
- @UiField Element date;
-
- @UiField Element p_view;
- @UiField HTML message;
- @UiField Button edit;
- @UiField Button discard1;
-
- @UiField Element p_edit;
- @UiField NpTextArea editArea;
- @UiField Button save;
- @UiField Button cancel;
- @UiField Button discard2;
-
- DraftBox(
- CommentGroup group,
- CommentLinkProcessor clp,
- @Nullable Project.NameKey pj,
- PatchSet.Id id,
- CommentInfo info,
- boolean expandAllComments) {
- super(group, info.range());
-
- linkProcessor = clp;
- psId = id;
- project = pj;
- expandAll = expandAllComments;
- initWidget(uiBinder.createAndBindUi(this));
-
- expandTimer =
- new Timer() {
- @Override
- public void run() {
- expandText();
- }
- };
- set(info);
-
- header.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- if (!isEdit()) {
- if (autoClosed && !isOpen()) {
- setOpen(true);
- setEdit(true);
- } else {
- setOpen(!isOpen());
- }
- }
- }
- },
- ClickEvent.getType());
-
- addDomHandler(
- new DoubleClickHandler() {
- @Override
- public void onDoubleClick(DoubleClickEvent event) {
- if (isEdit()) {
- editArea.setFocus(true);
- } else {
- setOpen(true);
- setEdit(true);
- }
- }
- },
- DoubleClickEvent.getType());
-
- initResizeHandler();
- }
-
- private void set(CommentInfo info) {
- autoClosed = !expandAll && info.message() != null && info.message().length() < 70;
- date.setInnerText(FormatUtil.shortFormatDayTime(info.updated()));
- if (info.message() != null) {
- String msg = info.message().trim();
- summary.setInnerText(msg);
- message.setHTML(linkProcessor.apply(new SafeHtmlBuilder().append(msg).wikify()));
- }
- comment = info;
- }
-
- @Override
- CommentInfo getCommentInfo() {
- return comment;
- }
-
- @Override
- boolean isOpen() {
- return UIObject.isVisible(p_view);
- }
-
- @Override
- void setOpen(boolean open) {
- UIObject.setVisible(summary, !open);
- UIObject.setVisible(p_view, open);
- super.setOpen(open);
- }
-
- private void expandText() {
- double cols = editArea.getCharacterWidth();
- int rows = 2;
- for (String line : editArea.getValue().split("\n")) {
- rows += Math.ceil((1.0 + line.length()) / cols);
- }
- rows = Math.max(INITIAL_LINES, Math.min(rows, MAX_LINES));
- if (editArea.getVisibleLines() != rows) {
- editArea.setVisibleLines(rows);
- }
- editAreaHeight = editArea.getOffsetHeight();
- getCommentGroup().resize();
- }
-
- boolean isEdit() {
- return UIObject.isVisible(p_edit);
- }
-
- void setEdit(boolean edit) {
- UIObject.setVisible(summary, false);
- UIObject.setVisible(p_view, !edit);
- UIObject.setVisible(p_edit, edit);
-
- setRangeHighlight(edit);
- if (edit) {
- String msg = comment.message() != null ? comment.message() : "";
- editArea.setValue(msg);
- cancel.setVisible(!isNew());
- expandText();
- editAreaHeight = editArea.getOffsetHeight();
-
- final int len = msg.length();
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- editArea.setFocus(true);
- if (len > 0) {
- editArea.setCursorPos(len);
- }
- }
- });
- } else {
- expandTimer.cancel();
- resizeTimer.cancel();
- }
- getCommentManager().setUnsaved(this, edit);
- getCommentGroup().resize();
- }
-
- PublishedBox getReplyToBox() {
- return replyToBox;
- }
-
- void setReplyToBox(PublishedBox box) {
- replyToBox = box;
- }
-
- @Override
- protected void onUnload() {
- expandTimer.cancel();
- resizeTimer.cancel();
- super.onUnload();
- }
-
- private void removeUI() {
- if (replyToBox != null) {
- replyToBox.unregisterReplyBox();
- }
-
- getCommentManager().setUnsaved(this, false);
- setRangeHighlight(false);
- clearRange();
- getAnnotation().remove();
- getCommentGroup().remove(this);
- getCm().focus();
- }
-
- private void restoreSelection() {
- if (getFromTo() != null && comment.inReplyTo() == null) {
- getCm().setSelection(getFromTo().from(), getFromTo().to());
- }
- }
-
- @UiHandler("message")
- void onMessageClick(ClickEvent e) {
- e.stopPropagation();
- }
-
- @UiHandler("message")
- void onMessageDoubleClick(@SuppressWarnings("unused") DoubleClickEvent e) {
- setEdit(true);
- }
-
- @UiHandler("edit")
- void onEdit(ClickEvent e) {
- e.stopPropagation();
- setEdit(true);
- }
-
- @UiHandler("save")
- void onSave(ClickEvent e) {
- e.stopPropagation();
- CallbackGroup group = new CallbackGroup();
- save(group);
- group.done();
- }
-
- void save(CallbackGroup group) {
- if (pendingGroup != null) {
- pendingGroup.addListener(group);
- return;
- }
-
- String message = editArea.getValue().trim();
- if (message.length() == 0) {
- return;
- }
-
- CommentInfo input = CommentInfo.copy(comment);
- input.message(message);
- enableEdit(false);
-
- pendingGroup = group;
- final LocalComments lc = new LocalComments(project, psId);
- GerritCallback<CommentInfo> cb =
- new GerritCallback<CommentInfo>() {
- @Override
- public void onSuccess(CommentInfo result) {
- enableEdit(true);
- pendingGroup = null;
- set(result);
- setEdit(false);
- if (autoClosed) {
- setOpen(false);
- }
- getCommentManager().setUnsaved(DraftBox.this, false);
- }
-
- @Override
- public void onFailure(Throwable e) {
- enableEdit(true);
- pendingGroup = null;
- if (RestApi.isNotSignedIn(e)) {
- CommentInfo saved = CommentInfo.copy(comment);
- saved.message(editArea.getValue().trim());
- lc.setInlineComment(saved);
- }
- super.onFailure(e);
- }
- };
- if (input.id() == null) {
- CommentApi.createDraft(Project.NameKey.asStringOrNull(project), psId, input, group.add(cb));
- } else {
- CommentApi.updateDraft(
- Project.NameKey.asStringOrNull(project), psId, input.id(), input, group.add(cb));
- }
- CodeMirror cm = getCm();
- cm.vim().handleKey("<Esc>");
- cm.focus();
- }
-
- private void enableEdit(boolean on) {
- editArea.setEnabled(on);
- save.setEnabled(on);
- cancel.setEnabled(on);
- discard2.setEnabled(on);
- }
-
- @UiHandler("cancel")
- void onCancel(ClickEvent e) {
- e.stopPropagation();
- if (isNew() && !isDirty()) {
- removeUI();
- restoreSelection();
- } else {
- setEdit(false);
- if (autoClosed) {
- setOpen(false);
- }
- getCm().focus();
- }
- }
-
- @UiHandler({"discard1", "discard2"})
- void onDiscard(ClickEvent e) {
- e.stopPropagation();
- if (isNew()) {
- removeUI();
- restoreSelection();
- } else {
- setEdit(false);
- pendingGroup = new CallbackGroup();
- CommentApi.deleteDraft(
- Project.NameKey.asStringOrNull(project),
- psId,
- comment.id(),
- pendingGroup.addFinal(
- new GerritCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {
- pendingGroup = null;
- removeUI();
- }
- }));
- }
- }
-
- @UiHandler("editArea")
- void onKeyDown(KeyDownEvent e) {
- resizeTimer.cancel();
- if ((e.isControlKeyDown() || e.isMetaKeyDown()) && !e.isAltKeyDown() && !e.isShiftKeyDown()) {
- switch (e.getNativeKeyCode()) {
- case 's':
- case 'S':
- e.preventDefault();
- CallbackGroup group = new CallbackGroup();
- save(group);
- group.done();
- return;
- }
- } else if (e.getNativeKeyCode() == KeyCodes.KEY_ESCAPE && !isDirty()) {
- if (isNew()) {
- removeUI();
- restoreSelection();
- return;
- }
- setEdit(false);
- if (autoClosed) {
- setOpen(false);
- }
- getCm().focus();
- return;
- }
- expandTimer.schedule(250);
- }
-
- @UiHandler("editArea")
- void onBlur(@SuppressWarnings("unused") BlurEvent e) {
- resizeTimer.cancel();
- }
-
- private void initResizeHandler() {
- resizeTimer =
- new Timer() {
- @Override
- public void run() {
- getCommentGroup().resize();
- }
- };
-
- addDomHandler(
- new MouseMoveHandler() {
- @Override
- public void onMouseMove(MouseMoveEvent event) {
- int h = editArea.getOffsetHeight();
- if (isEdit() && h != editAreaHeight) {
- getCommentGroup().resize();
- resizeTimer.scheduleRepeating(50);
- editAreaHeight = h;
- }
- }
- },
- MouseMoveEvent.getType());
-
- addDomHandler(
- new MouseUpHandler() {
- @Override
- public void onMouseUp(MouseUpEvent event) {
- resizeTimer.cancel();
- getCommentGroup().resize();
- }
- },
- MouseUpEvent.getType());
- }
-
- private boolean isNew() {
- return comment.id() == null;
- }
-
- private boolean isDirty() {
- String msg = editArea.getValue().trim();
- if (isNew()) {
- return msg.length() > 0;
- }
- return !msg.equals(comment.message() != null ? comment.message().trim() : "");
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml
deleted file mode 100644
index a363c06dba..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DraftBox.ui.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gerrit.client'
- xmlns:e='urn:import:com.google.gwtexpui.globalkey.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/>
- <ui:style gss='false'>
- .draft {
- width: 45px;
- text-align: center;
- color: #fff;
- background-color: #aaa;
- -webkit-border-radius: 2px;
- }
- .editArea { max-width: 637px; }
- button.button div {
- width: 35px;
- }
- button.discard {
- color: #d14836;
- background-color: #d14836;
- background-image: -webkit-linear-gradient(top, #d14836, #d14836);
- position: absolute;
- left: 150px;
- }
- </ui:style>
-
- <g:HTMLPanel styleName='{res.style.commentBox}'>
- <div class='{res.style.contents}'>
- <g:HTMLPanel ui:field='header' styleName='{res.style.header}'>
- <div class='{style.draft}'>Draft</div>
- <div ui:field='summary' class='{res.style.summary}'/>
- <div ui:field='date' class='{res.style.date}'/>
- </g:HTMLPanel>
- <div ui:field='p_view' aria-hidden='true' style='display: NONE'>
- <g:HTML ui:field='message' styleName='{res.style.message}'/>
- <div style='position: relative'>
- <g:Button ui:field='edit'
- title='Edit this draft comment'
- styleName='{style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Edit</ui:msg></div>
- </g:Button>
- <g:Button ui:field='discard1'
- title='Discard this draft comment'
- styleName='{style.button}'
- addStyleNames='{style.discard}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Discard</ui:msg></div>
- </g:Button>
- </div>
- </div>
- <div ui:field='p_edit' aria-hidden='true' style='display: NONE'>
- <e:NpTextArea ui:field='editArea'
- characterWidth='60'
- visibleLines='5'
- spellCheck='true'
- styleName='{style.editArea}'/>
- <div style='position: relative'>
- <g:Button ui:field='save'
- title='Save this draft comment'
- styleName='{style.button}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Save</ui:msg></div>
- </g:Button>
- <g:Button ui:field='cancel' styleName='{style.button}'>
- <div><ui:msg>Cancel</ui:msg></div>
- </g:Button>
- <g:Button ui:field='discard2'
- title='Discard this draft comment'
- styleName='{style.button}'
- addStyleNames='{style.discard}'>
- <ui:attribute name='title'/>
- <div><ui:msg>Discard</ui:msg></div>
- </g:Button>
- </div>
- </div>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java
deleted file mode 100644
index 4cf78c79c2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/EditIterator.java
+++ /dev/null
@@ -1,71 +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.client.diff;
-
-import com.google.gwt.core.client.JsArrayString;
-import net.codemirror.lib.Pos;
-
-/** An iterator for intraline edits */
-class EditIterator {
- private final JsArrayString lines;
- private final int startLine;
- private int line;
- private int pos;
-
- EditIterator(JsArrayString lineArray, int start) {
- lines = lineArray;
- startLine = start;
- }
-
- Pos advance(int numOfChar) {
- numOfChar = adjustForNegativeDelta(numOfChar);
-
- while (line < lines.length()) {
- int len = lines.get(line).length() - pos + 1; // + 1 for LF
- if (numOfChar < len) {
- Pos at = Pos.create(startLine + line, numOfChar + pos);
- pos += numOfChar;
- return at;
- }
-
- numOfChar -= len;
- line++;
- pos = 0;
-
- if (numOfChar == 0) {
- return Pos.create(startLine + line, 0);
- }
- }
-
- throw new IllegalStateException("EditIterator index out of bounds");
- }
-
- private int adjustForNegativeDelta(int n) {
- while (n < 0) {
- if (-n <= pos) {
- pos += n;
- return 0;
- }
-
- n += pos;
- line--;
- if (line < 0) {
- throw new IllegalStateException("EditIterator index out of bounds");
- }
- pos = lines.get(line).length() + 1;
- }
- return n;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
deleted file mode 100644
index 7a97df123e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.java
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright (C) 2010 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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.DiffPreferences;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ReviewInfo;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.diff.DiffInfo.Region;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.FileInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-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.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Visibility;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.List;
-
-public class Header extends Composite {
- interface Binder extends UiBinder<HTMLPanel, Header> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- static {
- Resources.I.style().ensureInjected();
- }
-
- private enum ReviewedState {
- AUTO_REVIEW,
- LOADED
- }
-
- @UiField CheckBox reviewed;
- @UiField Element project;
- @UiField Element filePath;
- @UiField Element fileNumber;
- @UiField Element fileCount;
-
- @UiField Element noDiff;
- @UiField FlowPanel linkPanel;
-
- @UiField InlineHyperlink prev;
- @UiField InlineHyperlink up;
- @UiField InlineHyperlink next;
- @UiField Image preferences;
-
- private final KeyCommandSet keys;
- @Nullable private final Project.NameKey projectKey;
- private final DiffObject base;
- private final PatchSet.Id patchSetId;
- private final String path;
- private final DiffView diffScreenType;
- private final DiffPreferences prefs;
- private boolean hasPrev;
- private boolean hasNext;
- private String nextPath;
- private JsArray<FileInfo> files;
- private PreferencesAction prefsAction;
- private ReviewedState reviewedState;
-
- Header(
- KeyCommandSet keys,
- @Nullable Project.NameKey project,
- DiffObject base,
- DiffObject patchSetId,
- String path,
- DiffView diffSreenType,
- DiffPreferences prefs) {
- initWidget(uiBinder.createAndBindUi(this));
- this.keys = keys;
- this.projectKey = project;
- this.base = base;
- this.patchSetId = patchSetId.asPatchSetId();
- this.path = path;
- this.diffScreenType = diffSreenType;
- this.prefs = prefs;
-
- if (!Gerrit.isSignedIn()) {
- reviewed.getElement().getStyle().setVisibility(Visibility.HIDDEN);
- }
- SafeHtml.setInnerHTML(filePath, formatPath(path));
- up.setTargetHistoryToken(
- PageLinks.toChange(
- project,
- patchSetId.asPatchSetId().getParentKey(),
- base.asString(),
- patchSetId.asPatchSetId().getId()));
- }
-
- public static SafeHtml formatPath(String path) {
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- if (Patch.COMMIT_MSG.equals(path)) {
- return b.append(Util.C.commitMessage());
- } else if (Patch.MERGE_LIST.equals(path)) {
- return b.append(Util.C.mergeList());
- }
-
- int s = path.lastIndexOf('/') + 1;
- b.append(path.substring(0, s));
- b.openElement("b");
- b.append(path.substring(s));
- b.closeElement("b");
- return b;
- }
-
- private int findCurrentFileIndex(JsArray<FileInfo> files) {
- int currIndex = 0;
- for (int i = 0; i < files.length(); i++) {
- if (path.equals(files.get(i).path())) {
- currIndex = i;
- break;
- }
- }
- return currIndex;
- }
-
- @Override
- protected void onLoad() {
- DiffApi.list(
- Project.NameKey.asStringOrNull(projectKey),
- patchSetId,
- base.asPatchSetId(),
- new GerritCallback<NativeMap<FileInfo>>() {
- @Override
- public void onSuccess(NativeMap<FileInfo> result) {
- files = result.values();
- FileInfo.sortFileInfoByPath(files);
- fileNumber.setInnerText(
- Integer.toString(Natives.asList(files).indexOf(result.get(path)) + 1));
- fileCount.setInnerText(Integer.toString(files.length()));
- }
- });
-
- if (Gerrit.isSignedIn()) {
- ChangeApi.revision(Project.NameKey.asStringOrNull(projectKey), patchSetId)
- .view("files")
- .addParameterTrue("reviewed")
- .get(
- new AsyncCallback<JsArrayString>() {
- @Override
- public void onSuccess(JsArrayString result) {
- boolean b = Natives.asList(result).contains(path);
- reviewed.setValue(b, false);
- if (!b && reviewedState == ReviewedState.AUTO_REVIEW) {
- postAutoReviewed();
- }
- reviewedState = ReviewedState.LOADED;
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
-
- void autoReview() {
- if (reviewedState == ReviewedState.LOADED && !reviewed.getValue()) {
- postAutoReviewed();
- } else {
- reviewedState = ReviewedState.AUTO_REVIEW;
- }
- }
-
- void setChangeInfo(ChangeInfo info) {
- project.setInnerText(info.project());
- }
-
- void init(PreferencesAction pa, List<InlineHyperlink> links, List<WebLinkInfo> webLinks) {
- prefsAction = pa;
- prefsAction.setPartner(preferences);
-
- for (InlineHyperlink link : links) {
- linkPanel.add(link);
- }
- for (WebLinkInfo webLink : webLinks) {
- linkPanel.add(webLink.toAnchor());
- }
- }
-
- @UiHandler("reviewed")
- void onValueChange(ValueChangeEvent<Boolean> event) {
- if (event.getValue()) {
- reviewed().put(CallbackGroup.<ReviewInfo>emptyCallback());
- } else {
- reviewed().delete(CallbackGroup.<ReviewInfo>emptyCallback());
- }
- }
-
- private void postAutoReviewed() {
- reviewed()
- .background()
- .put(
- new AsyncCallback<ReviewInfo>() {
- @Override
- public void onSuccess(ReviewInfo result) {
- reviewed.setValue(true, false);
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- private RestApi reviewed() {
- return ChangeApi.revision(Project.NameKey.asStringOrNull(projectKey), patchSetId)
- .view("files")
- .id(path)
- .view("reviewed");
- }
-
- @UiHandler("preferences")
- void onPreferences(@SuppressWarnings("unused") ClickEvent e) {
- prefsAction.show();
- }
-
- private String url(FileInfo info) {
- return diffScreenType == DiffView.UNIFIED_DIFF
- ? Dispatcher.toUnified(projectKey, base, patchSetId, info.path())
- : Dispatcher.toSideBySide(projectKey, base, patchSetId, info.path());
- }
-
- private KeyCommand setupNav(InlineHyperlink link, char key, String help, FileInfo info) {
- if (info != null) {
- final String url = url(info);
- link.setTargetHistoryToken(url);
- link.setTitle(
- PatchUtil.M.fileNameWithShortcutKey(
- FileInfo.getFileName(info.path()), Character.toString(key)));
- KeyCommand k =
- new KeyCommand(0, key, help) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(url);
- }
- };
- keys.add(k);
- if (link == prev) {
- hasPrev = true;
- } else {
- hasNext = true;
- }
- return k;
- }
- link.getElement().getStyle().setVisibility(Visibility.HIDDEN);
- keys.add(new UpToChangeCommand(projectKey, patchSetId, 0, key));
- return null;
- }
-
- private boolean shouldSkipFile(FileInfo curr, CommentsCollections comments) {
- return prefs.skipDeleted() && ChangeType.DELETED.matches(curr.status())
- || prefs.skipUnchanged() && ChangeType.RENAMED.matches(curr.status())
- || prefs.skipUncommented() && !comments.hasCommentForPath(curr.path());
- }
-
- void setupPrevNextFiles(CommentsCollections comments) {
- FileInfo prevInfo = null;
- FileInfo nextInfo = null;
- int currIndex = findCurrentFileIndex(files);
- for (int i = currIndex - 1; i >= 0; i--) {
- FileInfo curr = files.get(i);
- if (shouldSkipFile(curr, comments)) {
- continue;
- }
- prevInfo = curr;
- break;
- }
- for (int i = currIndex + 1; i < files.length(); i++) {
- FileInfo curr = files.get(i);
- if (shouldSkipFile(curr, comments)) {
- continue;
- }
- nextInfo = curr;
- break;
- }
- KeyCommand p = setupNav(prev, '[', PatchUtil.C.previousFileHelp(), prevInfo);
- KeyCommand n = setupNav(next, ']', PatchUtil.C.nextFileHelp(), nextInfo);
- if (p != null && n != null) {
- keys.pair(p, n);
- }
- nextPath = nextInfo != null ? nextInfo.path() : null;
- }
-
- Runnable toggleReviewed() {
- return () -> reviewed.setValue(!reviewed.getValue(), true);
- }
-
- Runnable navigate(Direction dir) {
- switch (dir) {
- case PREV:
- return () -> (hasPrev ? prev : up).go();
- case NEXT:
- return () -> (hasNext ? next : up).go();
- default:
- return () -> {};
- }
- }
-
- Runnable reviewedAndNext() {
- return () -> {
- if (Gerrit.isSignedIn()) {
- reviewed.setValue(true, true);
- }
- navigate(Direction.NEXT).run();
- };
- }
-
- String getNextPath() {
- return nextPath;
- }
-
- void setNoDiff(DiffInfo diff) {
- if (diff.binary()) {
- UIObject.setVisible(noDiff, false); // Don't bother showing "No Differences"
- } else {
- JsArray<Region> regions = diff.content();
- boolean b = regions.length() == 0 || (regions.length() == 1 && regions.get(0).ab() != null);
- UIObject.setVisible(noDiff, b);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml
deleted file mode 100644
index 39eb6cbcf6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Header.ui.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/>
- <ui:style gss='false'>
- .header {
- position: relative;
- height: 16px;
- line-height: 16px;
- }
- .reviewed input {
- margin: 0;
- padding: 0;
- vertical-align: middle;
- }
- .path {
- white-space: nowrap;
- }
- .fileCount {
- white-space: nowrap;
- position: relative;
- bottom: 4px;
- }
- .navigation {
- position: absolute;
- top: 0;
- right: 10px;
- height: 16px;
- line-height: 16px;
- }
- .nodiff {
- white-space: nowrap;
- color: #B00000;
- vertical-align: top;
- font-weight: bold;
- margin-right: 1em;
- float: left;
- }
- .linkPanel {
- float: left;
- }
- .linkPanel img {
- padding-right: 3px;
- }
- .preferences {
- cursor: pointer;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.header}'>
- <g:CheckBox ui:field='reviewed'
- styleName='{style.reviewed}'
- title='Mark file as reviewed (Shortcut: r)'>
- <ui:attribute name='title'/>
- </g:CheckBox>
- <span class='{style.path}'><span ui:field='project'/> / <span ui:field='filePath'/></span>
- <div class='{style.navigation}'>
- <span ui:field='noDiff' class='{style.nodiff}'><ui:msg>No Differences</ui:msg></span>
- <g:FlowPanel ui:field='linkPanel' styleName='{style.linkPanel}'/>
- <span class='{style.fileCount}'>
- <ui:msg>File <span ui:field='fileNumber'/> of <span ui:field='fileCount'/></ui:msg>
- </span>
- <x:InlineHyperlink ui:field='prev' styleName='{res.style.goPrev}'/>
- <x:InlineHyperlink ui:field='up'
- styleName='{res.style.goUp}'
- title='Up to change (Shortcut: u)'>
- <ui:attribute name='title'/>
- </x:InlineHyperlink>
- <x:InlineHyperlink ui:field='next' styleName='{res.style.goNext}'/>
- <g:Image ui:field='preferences'
- styleName='{style.preferences}'
- resource='{ico.gear}'
- title='Diff preferences (Shortcut: ,)'>
- <ui:attribute name='title'/>
- </g:Image>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java
deleted file mode 100644
index f8eab91df5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2014 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.client.diff;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Style;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Rect;
-
-/** Bubble displayed near a selected region to create a comment. */
-class InsertCommentBubble extends Composite {
- interface Binder extends UiBinder<HTMLPanel, InsertCommentBubble> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField Image icon;
-
- InsertCommentBubble(CommentManager commentManager, CodeMirror cm) {
- initWidget(uiBinder.createAndBindUi(this));
- addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- setVisible(false);
- commentManager.newDraftCallback(cm).run();
- }
- },
- ClickEvent.getType());
- }
-
- void position(Rect r) {
- Style s = getElement().getStyle();
- int top = (int) (r.top() - (getOffsetHeight() - 8));
- if (top < 0) {
- s.setTop(-3, Unit.PX);
- s.setLeft(r.right() + 2, Unit.PX);
- } else {
- s.setTop(top, Unit.PX);
- s.setLeft((int) (r.right() - 14), Unit.PX);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml
deleted file mode 100644
index 6a18c4d0ec..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/InsertCommentBubble.ui.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.GerritResources'/>
- <ui:style gss='false'>
- .bubble {
- z-index: 150;
- white-space: nowrap;
- line-height: 16px;
- cursor: pointer;
- }
- .message {
- background: #fff1a8;
- padding-left: 5px;
- padding-right: 5px;
- border-radius: 5px;
- border: 1px solid #aaa;
- font-family: sans-serif;
- font-size: smaller;
- font-style: italic;
- vertical-align: top;
- }
- .message b {
- vertical-align: top;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.bubble}'>
- <g:Image ui:field='icon'
- styleName=''
- resource='{res.draftComments}'
- title='Create a new inline comment'>
- <ui:attribute name='title'/>
- </g:Image><span class='{style.message}'><ui:msg>press <b>c</b> to comment</ui:msg></span>
- </g:HTMLPanel>
-</ui:UiBinder> \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java
deleted file mode 100644
index fc83a144ea..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/LineMapper.java
+++ /dev/null
@@ -1,225 +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.client.diff;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/** Helper class to handle calculations involving line gaps. */
-class LineMapper {
- private int lineA;
- private int lineB;
- private List<LineGap> lineMapAtoB;
- private List<LineGap> lineMapBtoA;
-
- LineMapper() {
- reset();
- }
-
- void reset() {
- lineA = 0;
- lineB = 0;
- lineMapAtoB = new ArrayList<>();
- lineMapBtoA = new ArrayList<>();
- }
-
- int getLineA() {
- return lineA;
- }
-
- int getLineB() {
- return lineB;
- }
-
- void appendCommon(int numLines) {
- lineA += numLines;
- lineB += numLines;
- }
-
- void appendReplace(int aLen, int bLen) {
- appendCommon(Math.min(aLen, bLen));
- if (aLen < bLen) { // Edit with insertion
- appendInsert(bLen - aLen);
- } else if (aLen > bLen) { // Edit with deletion
- appendDelete(aLen - bLen);
- }
- }
-
- void appendInsert(int numLines) {
- int origLineB = lineB;
- lineB += numLines;
- int bAheadOfA = lineB - lineA;
- lineMapAtoB.add(new LineGap(lineA, -1, bAheadOfA));
- lineMapBtoA.add(new LineGap(origLineB, lineB - 1, -bAheadOfA));
- }
-
- void appendDelete(int numLines) {
- int origLineA = lineA;
- lineA += numLines;
- int aAheadOfB = lineA - lineB;
- lineMapAtoB.add(new LineGap(origLineA, lineA - 1, -aAheadOfB));
- lineMapBtoA.add(new LineGap(lineB, -1, aAheadOfB));
- }
-
- /**
- * Helper method to retrieve the line number on the other side.
- *
- * <p>Given a line number on one side, performs a binary search in the lineMap to find the
- * corresponding LineGap record.
- *
- * <p>A LineGap records gap information from the start of an actual gap up to the start of the
- * next gap. In the following example, lineMapAtoB will have LineGap: {start: 1, end: -1, delta:
- * 3} (end set to -1 to represent a dummy gap of length zero. The binary search only looks at
- * start so setting it to -1 has no effect here.) lineMapBtoA will have LineGap: {start: 1, end:
- * 3, delta: -3} These LineGaps control lines between 1 and 5.
- *
- * <p>The "delta" is computed as the number to add on our side to get the line number on the other
- * side given a line after the actual gap, so the result will be (line + delta). All lines within
- * the actual gap (1 to 3) are considered corresponding to the last line above the region on the
- * other side, which is 0 in this case. For these lines, we do (end + delta).
- *
- * <p>For example, to get the line number on the left corresponding to 1 on the right
- * (lineOnOther(REVISION, 1)), the method looks up in lineMapBtoA, finds the "delta" to be -3, and
- * returns 3 + (-3) = 0 since 1 falls in the actual gap. On the other hand, the line corresponding
- * to 5 on the right will be 5 + (-3) = 2, since 5 is in the region after the gap (but still
- * controlled by the current LineGap).
- *
- * <p>PARENT REVISION 0 | 0 - | 1 \ \ - | 2 | Actual insertion gap | - | 3 / | Region controlled
- * by one LineGap 1 | 4 <- delta = 4 - 1 = 3 | 2 | 5 / - | 6 ...
- */
- LineOnOtherInfo lineOnOther(DisplaySide mySide, int line) {
- List<LineGap> lineGaps = gapList(mySide);
- // Create a dummy LineGap for the search.
- int ret = Collections.binarySearch(lineGaps, new LineGap(line));
- if (ret == -1) {
- return new LineOnOtherInfo(line, true);
- }
- LineGap lookup = lineGaps.get(0 <= ret ? ret : -ret - 2);
- int start = lookup.start;
- int end = lookup.end;
- int delta = lookup.delta;
- if (start <= line && line <= end && end != -1) { // Line falls within gap
- return new LineOnOtherInfo(end + delta, false);
- }
- // Line after gap
- return new LineOnOtherInfo(line + delta, true);
- }
-
- AlignedPair align(DisplaySide mySide, int line) {
- List<LineGap> gaps = gapList(mySide);
- int idx = Collections.binarySearch(gaps, new LineGap(line));
- if (idx == -1) {
- return new AlignedPair(line, line);
- }
-
- LineGap g = gaps.get(0 <= idx ? idx : -idx - 2);
- if (g.start <= line && line <= g.end && g.end != -1) {
- if (0 < g.start) {
- // Line falls within this gap, use alignment before.
- return new AlignedPair(g.start - 1, g.end + g.delta);
- }
- return new AlignedPair(g.end, g.end + g.delta + 1);
- }
- return new AlignedPair(line, line + g.delta);
- }
-
- private List<LineGap> gapList(DisplaySide mySide) {
- return mySide == DisplaySide.A ? lineMapAtoB : lineMapBtoA;
- }
-
- static class AlignedPair {
- final int src;
- final int dst;
-
- AlignedPair(int s, int d) {
- src = s;
- dst = d;
- }
- }
-
- /**
- * @field line The line number on the other side.
- * @field aligned Whether the two lines are at the same height when displayed.
- */
- static class LineOnOtherInfo {
- private int line;
- private boolean aligned;
-
- LineOnOtherInfo(int line, boolean aligned) {
- this.line = line;
- this.aligned = aligned;
- }
-
- int getLine() {
- return line;
- }
-
- boolean isAligned() {
- return aligned;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof LineOnOtherInfo) {
- LineOnOtherInfo other = (LineOnOtherInfo) obj;
- return aligned == other.aligned && line == other.line;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(line, aligned);
- }
-
- @Override
- public String toString() {
- return line + " " + aligned;
- }
- }
-
- /**
- * Helper class to record line gap info and assist in calculation of line number on the other
- * side.
- *
- * <p>For a mapping from A to B, where A is the side with an insertion:
- *
- * @field start The start line of the insertion in A.
- * @field end The exclusive end line of the insertion in A.
- * @field delta The offset added to A to get the line number in B calculated from end.
- */
- private static class LineGap implements Comparable<LineGap> {
- private final int start;
- private final int end;
- private final int delta;
-
- private LineGap(int start, int end, int delta) {
- this.start = start;
- this.end = end;
- this.delta = delta;
- }
-
- private LineGap(int line) {
- this(line, 0, 0);
- }
-
- @Override
- public int compareTo(LineGap o) {
- return start - o.start;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/NoOpKeyCommand.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/NoOpKeyCommand.java
deleted file mode 100644
index 584232d1b5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/NoOpKeyCommand.java
+++ /dev/null
@@ -1,28 +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.client.diff;
-
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-
-/** A KeyCommand that does nothing, used to display a help message */
-class NoOpKeyCommand extends KeyCommand {
- NoOpKeyCommand(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
deleted file mode 100644
index 292773c7da..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.java
+++ /dev/null
@@ -1,240 +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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.blame.BlameInfo;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.Nullable;
-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.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtorm.client.KeyUtil;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-
-/** HTMLPanel to select among patch sets */
-class PatchSetSelectBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, PatchSetSelectBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface BoxStyle extends CssResource {
- String selected();
- }
-
- @UiField Image icon;
- @UiField HTMLPanel linkPanel;
- @UiField BoxStyle style;
-
- @Nullable private final Project.NameKey project;
- private final Change.Id changeId;
-
- private DiffScreen parent;
- private DisplaySide side;
- private boolean sideA;
- private String path;
- private PatchSet.Id revision;
- private DiffObject idActive;
- private PatchSetSelectBox other;
-
- PatchSetSelectBox(
- DiffScreen parent,
- DisplaySide side,
- @Nullable Project.NameKey project,
- Change.Id changeId,
- DiffObject diffObject,
- String path) {
- initWidget(uiBinder.createAndBindUi(this));
- icon.setTitle(PatchUtil.C.addFileCommentToolTip());
- icon.addStyleName(Gerrit.RESOURCES.css().link());
-
- this.parent = parent;
- this.side = side;
- this.sideA = side == DisplaySide.A;
- this.project = project;
- this.changeId = changeId;
- this.revision = diffObject.asPatchSetId();
- this.idActive = diffObject;
- this.path = path;
- }
-
- void setUpPatchSetNav(
- JsArray<RevisionInfo> list,
- int parents,
- DiffInfo.FileMeta meta,
- boolean editExists,
- boolean current,
- boolean open,
- boolean binary) {
- InlineHyperlink selectedLink = null;
- if (sideA) {
- if (parents <= 1) {
- InlineHyperlink link = createLink(PatchUtil.C.patchBase(), DiffObject.base());
- linkPanel.add(link);
- selectedLink = link;
- } else {
- for (int i = parents; i > 0; i--) {
- PatchSet.Id id = new PatchSet.Id(changeId, -i);
- InlineHyperlink link = createLink(Util.M.diffBaseParent(i), DiffObject.patchSet(id));
- linkPanel.add(link);
- if (revision != null && id.equals(revision)) {
- selectedLink = link;
- }
- }
- InlineHyperlink link = createLink(Util.C.autoMerge(), DiffObject.autoMerge());
- linkPanel.add(link);
- if (selectedLink == null) {
- selectedLink = link;
- }
- }
- }
- for (int i = 0; i < list.length(); i++) {
- RevisionInfo r = list.get(i);
- InlineHyperlink link =
- createLink(r.id(), DiffObject.patchSet(new PatchSet.Id(changeId, r._number())));
- linkPanel.add(link);
- if (revision != null && r.id().equals(revision.getId())) {
- selectedLink = link;
- }
- }
- if (selectedLink != null) {
- selectedLink.setStyleName(style.selected());
- }
-
- if (meta == null) {
- return;
- }
- if (!Patch.isMagic(path)) {
- linkPanel.add(createDownloadLink());
- }
- if (!binary && open && !idActive.isBaseOrAutoMerge() && Gerrit.isSignedIn()) {
- if ((editExists && idActive.isEdit()) || (!editExists && current)) {
- linkPanel.add(createEditIcon());
- }
- }
- List<WebLinkInfo> webLinks = Natives.asList(meta.webLinks());
- if (webLinks != null) {
- for (WebLinkInfo webLink : webLinks) {
- linkPanel.add(webLink.toAnchor());
- }
- }
- }
-
- void setUpBlame(final CodeMirror cm, boolean isBase, PatchSet.Id rev, String path) {
- if (!Patch.isMagic(path) && Gerrit.isSignedIn() && Gerrit.info().change().allowBlame()) {
- Anchor blameIcon = createBlameIcon();
- blameIcon.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent clickEvent) {
- if (cm.extras().getBlameInfo() != null) {
- cm.extras().toggleAnnotation();
- } else {
- ChangeApi.blame(Project.NameKey.asStringOrNull(project), rev, path, isBase)
- .get(
- new GerritCallback<JsArray<BlameInfo>>() {
-
- @Override
- public void onSuccess(JsArray<BlameInfo> lines) {
- cm.extras().toggleAnnotation(lines);
- }
- });
- }
- }
- });
- linkPanel.add(blameIcon);
- }
- }
-
- private Widget createEditIcon() {
- PatchSet.Id id =
- idActive.isBaseOrAutoMerge() ? other.idActive.asPatchSetId() : idActive.asPatchSetId();
- Anchor anchor =
- new Anchor(
- new ImageResourceRenderer().render(Gerrit.RESOURCES.edit()),
- "#" + Dispatcher.toEditScreen(project, id, path));
- anchor.setTitle(PatchUtil.C.edit());
- return anchor;
- }
-
- private Anchor createBlameIcon() {
- Anchor anchor = new Anchor(new ImageResourceRenderer().render(Gerrit.RESOURCES.blame()));
- anchor.setTitle(PatchUtil.C.blame());
- return anchor;
- }
-
- static void link(PatchSetSelectBox a, PatchSetSelectBox b) {
- a.other = b;
- b.other = a;
- }
-
- private InlineHyperlink createLink(String label, DiffObject id) {
- assert other != null;
- if (sideA) {
- assert !other.idActive.isBaseOrAutoMerge();
- }
- DiffObject diffBase = sideA ? id : other.idActive;
- DiffObject revision = sideA ? other.idActive : id;
-
- return new InlineHyperlink(
- label,
- parent.isSideBySide()
- ? Dispatcher.toSideBySide(project, diffBase, revision.asPatchSetId(), path)
- : Dispatcher.toUnified(project, diffBase, revision.asPatchSetId(), path));
- }
-
- private Anchor createDownloadLink() {
- DiffObject diffObject = idActive.isBaseOrAutoMerge() ? other.idActive : idActive;
- String sideURL = idActive.isBaseOrAutoMerge() ? "1" : "0";
- String base = GWT.getHostPageBaseURL() + "cat/";
- Anchor anchor =
- new Anchor(
- new ImageResourceRenderer().render(Gerrit.RESOURCES.downloadIcon()),
- base + KeyUtil.encode(diffObject.asPatchSetId() + "," + path) + "^" + sideURL);
- anchor.setTitle(PatchUtil.C.download());
- return anchor;
- }
-
- @UiHandler("icon")
- void onIconClick(@SuppressWarnings("unused") ClickEvent e) {
- parent.getCmFromSide(side).scrollToY(0);
- parent.getCommentManager().insertNewDraft(side, 0);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml
deleted file mode 100644
index 6e526ec017..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PatchSetSelectBox.ui.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.GerritResources'/>
- <ui:with field='patchConstants'
- type='com.google.gerrit.client.patches.PatchConstants'/>
- <ui:style gss='false' type='com.google.gerrit.client.diff.PatchSetSelectBox.BoxStyle'>
- @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
- @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
- .table {
- width: 100%;
- }
- .linkCell {
- text-align: center;
- font-size: 12px;
- white-space: normal;
- font-family: sans-serif;
- font-weight: bold;
- }
- .linkCell div {
- padding-left: 3px;
- padding-right: 3px;
- vertical-align: middle;
- display: inline-block;
- }
- .linkCell a {
- padding-left: 3px;
- padding-right: 3px;
- text-decoration: none;
- vertical-align: middle;
- display: inline-block;
- }
- .selected {
- font-weight: bold;
- background-color: selectionColor;
- }
- .hidden {
- visibility: hidden;
- }
- .iconCell {
- width: 16px;
- }
- </ui:style>
- <g:HTMLPanel>
- <table class='{style.table}'>
- <td class='{style.iconCell}'>
- <g:Image ui:field='icon' resource='{res.addFileComment}'/>
- </td>
- <td class='{style.linkCell}'>
- <g:HTMLPanel ui:field='linkPanel'>
- <g:Label>
- <ui:text from='{patchConstants.patchSet}'/>
- </g:Label>
- </g:HTMLPanel>
- </td>
- </table>
- </g:HTMLPanel>
-</ui:UiBinder> \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesAction.java
deleted file mode 100644
index 2d4a4c4c27..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesAction.java
+++ /dev/null
@@ -1,86 +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.client.diff;
-
-import com.google.gerrit.client.account.DiffPreferences;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
-import com.google.gwt.user.client.ui.Widget;
-
-class PreferencesAction {
- private final DiffScreen view;
- private final DiffPreferences prefs;
- private PopupPanel popup;
- private PreferencesBox current;
- private Widget partner;
-
- PreferencesAction(DiffScreen view, DiffPreferences prefs) {
- this.view = view;
- this.prefs = prefs;
- }
-
- void update() {
- if (current != null) {
- current.set(prefs);
- }
- }
-
- void show() {
- if (popup != null) {
- // Already open? Close the dialog.
- hide();
- return;
- }
-
- current = new PreferencesBox(view);
- current.set(prefs);
-
- popup = new PopupPanel(true, false);
- popup.setStyleName(current.style.dialog());
- popup.add(current);
- popup.addAutoHidePartner(partner.getElement());
- popup.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- view.getCmFromSide(DisplaySide.B).focus();
- popup = null;
- current = null;
- }
- });
- popup.setPopupPositionAndShow(
- new PositionCallback() {
- @Override
- public void setPosition(int offsetWidth, int offsetHeight) {
- popup.setPopupPosition(300, 120);
- }
- });
- current.setFocus(true);
- }
-
- void hide() {
- if (popup != null) {
- popup.hide();
- popup = null;
- current = null;
- }
- }
-
- void setPartner(Widget w) {
- partner = w;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
deleted file mode 100644
index fed001d0e9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
+++ /dev/null
@@ -1,644 +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.client.diff;
-
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.DEFAULT_CONTEXT;
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.WHOLE_FILE_CONTEXT;
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace.IGNORE_ALL;
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace.IGNORE_LEADING_AND_TRAILING;
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace.IGNORE_NONE;
-import static com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace.IGNORE_TRAILING;
-import static com.google.gwt.event.dom.client.KeyCodes.KEY_ESCAPE;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.account.DiffPreferences;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.NpIntTextBox;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Visibility;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.ToggleButton;
-import com.google.gwt.user.client.ui.UIObject;
-import java.util.Objects;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.mode.ModeInfo;
-import net.codemirror.mode.ModeInjector;
-import net.codemirror.theme.ThemeLoader;
-
-/** Displays current diff preferences. */
-public class PreferencesBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, PreferencesBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- public interface Style extends CssResource {
- String dialog();
- }
-
- private final DiffScreen view;
- private DiffPreferences prefs;
- private int contextLastValue;
- private Timer updateContextTimer;
-
- @UiField Style style;
- @UiField Element header;
- @UiField Anchor close;
- @UiField ListBox ignoreWhitespace;
- @UiField NpIntTextBox tabWidth;
- @UiField NpIntTextBox lineLength;
- @UiField NpIntTextBox context;
- @UiField NpIntTextBox cursorBlinkRate;
- @UiField CheckBox contextEntireFile;
- @UiField ToggleButton intralineDifference;
- @UiField ToggleButton syntaxHighlighting;
- @UiField ToggleButton whitespaceErrors;
- @UiField ToggleButton showTabs;
- @UiField ToggleButton lineNumbers;
- @UiField Element leftSideLabel;
- @UiField ToggleButton leftSide;
- @UiField ToggleButton emptyPane;
- @UiField ToggleButton topMenu;
- @UiField ToggleButton autoHideDiffTableHeader;
- @UiField ToggleButton manualReview;
- @UiField ToggleButton expandAllComments;
- @UiField ToggleButton renderEntireFile;
- @UiField ToggleButton matchBrackets;
- @UiField ToggleButton lineWrapping;
- @UiField ToggleButton skipDeleted;
- @UiField ToggleButton skipUnchanged;
- @UiField ToggleButton skipUncommented;
- @UiField ListBox theme;
- @UiField Element modeLabel;
- @UiField ListBox mode;
- @UiField Button apply;
- @UiField Button save;
-
- public PreferencesBox(DiffScreen view) {
- this.view = view;
-
- initWidget(uiBinder.createAndBindUi(this));
- initIgnoreWhitespace();
- initTheme();
-
- if (view != null) {
- initMode();
- } else {
- UIObject.setVisible(header, false);
- apply.getElement().getStyle().setVisibility(Visibility.HIDDEN);
- }
- }
-
- @Override
- public void onLoad() {
- super.onLoad();
-
- save.setVisible(Gerrit.isSignedIn());
-
- if (view != null) {
- addDomHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent event) {
- if (event.getNativeKeyCode() == KEY_ESCAPE || event.getNativeKeyCode() == ',') {
- close();
- }
- }
- },
- KeyDownEvent.getType());
-
- updateContextTimer =
- new Timer() {
- @Override
- public void run() {
- if (prefs.context() == WHOLE_FILE_CONTEXT) {
- contextEntireFile.setValue(true);
- }
- if (view.canRenderEntireFile(prefs)) {
- renderEntireFile.setEnabled(true);
- renderEntireFile.setValue(prefs.renderEntireFile());
- } else {
- renderEntireFile.setValue(false);
- renderEntireFile.setEnabled(false);
- }
- view.setContext(prefs.context());
- }
- };
- }
- }
-
- public Style getStyle() {
- return style;
- }
-
- public void set(DiffPreferences prefs) {
- this.prefs = prefs;
-
- setIgnoreWhitespace(prefs.ignoreWhitespace());
- tabWidth.setIntValue(prefs.tabSize());
- if (view != null && Patch.COMMIT_MSG.equals(view.path)) {
- lineLength.setEnabled(false);
- lineLength.setIntValue(72);
- } else {
- lineLength.setEnabled(true);
- lineLength.setIntValue(prefs.lineLength());
- }
- cursorBlinkRate.setIntValue(prefs.cursorBlinkRate());
- syntaxHighlighting.setValue(prefs.syntaxHighlighting());
- whitespaceErrors.setValue(prefs.showWhitespaceErrors());
- showTabs.setValue(prefs.showTabs());
- lineNumbers.setValue(prefs.showLineNumbers());
- emptyPane.setValue(!prefs.hideEmptyPane());
- if (view != null) {
- leftSide.setValue(view.getDiffTable().isVisibleA());
- leftSide.setEnabled(
- !(prefs.hideEmptyPane() && view.getDiffTable().getChangeType() == ChangeType.ADDED));
- } else {
- UIObject.setVisible(leftSideLabel, false);
- leftSide.setVisible(false);
- }
- topMenu.setValue(!prefs.hideTopMenu());
- autoHideDiffTableHeader.setValue(!prefs.autoHideDiffTableHeader());
- manualReview.setValue(prefs.manualReview());
- expandAllComments.setValue(prefs.expandAllComments());
- matchBrackets.setValue(prefs.matchBrackets());
- lineWrapping.setValue(prefs.lineWrapping());
- skipDeleted.setValue(!prefs.skipDeleted());
- skipUnchanged.setValue(!prefs.skipUnchanged());
- skipUncommented.setValue(!prefs.skipUncommented());
- setTheme(prefs.theme());
-
- if (view == null || view.canRenderEntireFile(prefs)) {
- renderEntireFile.setValue(prefs.renderEntireFile());
- renderEntireFile.setEnabled(true);
- } else {
- renderEntireFile.setValue(false);
- renderEntireFile.setEnabled(false);
- }
-
- if (view != null) {
- mode.setEnabled(prefs.syntaxHighlighting());
- if (prefs.syntaxHighlighting()) {
- setMode(view.getCmFromSide(DisplaySide.B).getStringOption("mode"));
- }
- } else {
- UIObject.setVisible(modeLabel, false);
- mode.setVisible(false);
- }
-
- if (view != null) {
- switch (view.getIntraLineStatus()) {
- case OFF:
- case OK:
- intralineDifference.setValue(prefs.intralineDifference());
- break;
-
- case TIMEOUT:
- case FAILURE:
- intralineDifference.setValue(false);
- intralineDifference.setEnabled(false);
- break;
- }
- } else {
- intralineDifference.setValue(prefs.intralineDifference());
- }
-
- if (prefs.context() == WHOLE_FILE_CONTEXT) {
- contextLastValue = DEFAULT_CONTEXT;
- context.setText("");
- contextEntireFile.setValue(true);
- } else {
- context.setIntValue(prefs.context());
- contextEntireFile.setValue(false);
- }
- }
-
- @UiHandler("ignoreWhitespace")
- void onIgnoreWhitespace(@SuppressWarnings("unused") ChangeEvent e) {
- prefs.ignoreWhitespace(
- Whitespace.valueOf(ignoreWhitespace.getValue(ignoreWhitespace.getSelectedIndex())));
- if (view != null) {
- view.reloadDiffInfo();
- }
- }
-
- @UiHandler("intralineDifference")
- void onIntralineDifference(ValueChangeEvent<Boolean> e) {
- prefs.intralineDifference(e.getValue());
- if (view != null) {
- view.setShowIntraline(prefs.intralineDifference());
- }
- }
-
- @UiHandler("context")
- void onContextKey(KeyPressEvent e) {
- if (contextEntireFile.getValue()) {
- char c = e.getCharCode();
- if ('0' <= c && c <= '9') {
- contextEntireFile.setValue(false);
- }
- }
- }
-
- @UiHandler("context")
- void onContext(ValueChangeEvent<String> e) {
- String v = e.getValue();
- int c;
- if (v != null && v.length() > 0) {
- c = Math.min(Math.max(0, Integer.parseInt(v)), 32767);
- contextEntireFile.setValue(false);
- } else if (v == null || v.isEmpty()) {
- c = WHOLE_FILE_CONTEXT;
- } else {
- return;
- }
- prefs.context(c);
- if (view != null) {
- updateContextTimer.schedule(200);
- }
- }
-
- @UiHandler("contextEntireFile")
- void onContextEntireFile(ValueChangeEvent<Boolean> e) {
- // If a click arrives too fast after onContext applied an update
- // the user committed the context line update by clicking on the
- // whole file checkmark. Drop this event, but transfer focus.
- if (e.getValue()) {
- contextLastValue = context.getIntValue();
- context.setText("");
- prefs.context(WHOLE_FILE_CONTEXT);
- } else {
- prefs.context(contextLastValue > 0 ? contextLastValue : DEFAULT_CONTEXT);
- context.setIntValue(prefs.context());
- context.setFocus(true);
- context.setSelectionRange(0, context.getText().length());
- }
- if (view != null) {
- updateContextTimer.schedule(200);
- }
- }
-
- @UiHandler("tabWidth")
- void onTabWidth(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- prefs.tabSize(Math.max(1, Integer.parseInt(v)));
- if (view != null) {
- view.operation(
- () -> {
- int size = prefs.tabSize();
- for (CodeMirror cm : view.getCms()) {
- cm.setOption("tabSize", size);
- }
- });
- }
- }
- }
-
- @UiHandler("lineLength")
- void onLineLength(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- prefs.lineLength(Math.max(1, Integer.parseInt(v)));
- if (view != null) {
- view.operation(() -> view.setLineLength(prefs.lineLength()));
- }
- }
- }
-
- @UiHandler("expandAllComments")
- void onExpandAllComments(ValueChangeEvent<Boolean> e) {
- prefs.expandAllComments(e.getValue());
- if (view != null) {
- view.getCommentManager().setExpandAllComments(prefs.expandAllComments());
- }
- }
-
- @UiHandler("cursorBlinkRate")
- void onCursoBlinkRate(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- // A negative value hides the cursor entirely:
- // don't let user shoot himself in the foot.
- prefs.cursorBlinkRate(Math.max(0, Integer.parseInt(v)));
- view.getCmFromSide(DisplaySide.A).setOption("cursorBlinkRate", prefs.cursorBlinkRate());
- view.getCmFromSide(DisplaySide.B).setOption("cursorBlinkRate", prefs.cursorBlinkRate());
- }
- }
-
- @UiHandler("showTabs")
- void onShowTabs(ValueChangeEvent<Boolean> e) {
- prefs.showTabs(e.getValue());
- if (view != null) {
- view.setShowTabs(prefs.showTabs());
- }
- }
-
- @UiHandler("lineNumbers")
- void onLineNumbers(ValueChangeEvent<Boolean> e) {
- prefs.showLineNumbers(e.getValue());
- if (view != null) {
- view.setShowLineNumbers(prefs.showLineNumbers());
- }
- }
-
- @UiHandler("leftSide")
- void onLeftSide(ValueChangeEvent<Boolean> e) {
- if (view.getDiffTable() instanceof SideBySideTable) {
- ((SideBySideTable) view.getDiffTable()).setVisibleA(e.getValue());
- }
- }
-
- @UiHandler("emptyPane")
- void onHideEmptyPane(ValueChangeEvent<Boolean> e) {
- prefs.hideEmptyPane(!e.getValue());
- if (view != null) {
- view.getDiffTable().setHideEmptyPane(prefs.hideEmptyPane());
- if (prefs.hideEmptyPane()) {
- if (view.getDiffTable().getChangeType() == ChangeType.ADDED) {
- leftSide.setValue(false);
- leftSide.setEnabled(false);
- }
- } else {
- leftSide.setValue(view.getDiffTable().isVisibleA());
- leftSide.setEnabled(true);
- }
- }
- }
-
- @UiHandler("topMenu")
- void onTopMenu(ValueChangeEvent<Boolean> e) {
- prefs.hideTopMenu(!e.getValue());
- if (view != null) {
- Gerrit.setHeaderVisible(!prefs.hideTopMenu());
- view.resizeCodeMirror();
- }
- }
-
- @UiHandler("autoHideDiffTableHeader")
- void onAutoHideDiffTableHeader(ValueChangeEvent<Boolean> e) {
- prefs.autoHideDiffTableHeader(!e.getValue());
- if (view != null) {
- view.setAutoHideDiffHeader(!e.getValue());
- }
- }
-
- @UiHandler("manualReview")
- void onManualReview(ValueChangeEvent<Boolean> e) {
- prefs.manualReview(e.getValue());
- }
-
- @UiHandler("syntaxHighlighting")
- void onSyntaxHighlighting(ValueChangeEvent<Boolean> e) {
- prefs.syntaxHighlighting(e.getValue());
- if (view != null) {
- mode.setEnabled(prefs.syntaxHighlighting());
- if (prefs.syntaxHighlighting()) {
- setMode(view.getContentType());
- }
- view.setSyntaxHighlighting(prefs.syntaxHighlighting());
- }
- }
-
- @UiHandler("mode")
- void onMode(@SuppressWarnings("unused") ChangeEvent e) {
- String mode = getSelectedMode();
- prefs.syntaxHighlighting(true);
- syntaxHighlighting.setValue(true, false);
- new ModeInjector()
- .add(mode)
- .inject(
- new GerritCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- if (prefs.syntaxHighlighting()
- && Objects.equals(mode, getSelectedMode())
- && view.isAttached()) {
- view.operation(
- () -> {
- view.getCmFromSide(DisplaySide.A).setOption("mode", mode);
- view.getCmFromSide(DisplaySide.B).setOption("mode", mode);
- });
- }
- }
- });
- }
-
- private String getSelectedMode() {
- String m = mode.getValue(mode.getSelectedIndex());
- return m != null && !m.isEmpty() ? m : null;
- }
-
- @UiHandler("whitespaceErrors")
- void onWhitespaceErrors(ValueChangeEvent<Boolean> e) {
- prefs.showWhitespaceErrors(e.getValue());
- if (view != null) {
- view.operation(
- () -> {
- boolean s = prefs.showWhitespaceErrors();
- for (CodeMirror cm : view.getCms()) {
- cm.setOption("showTrailingSpace", s);
- }
- });
- }
- }
-
- @UiHandler("renderEntireFile")
- void onRenderEntireFile(ValueChangeEvent<Boolean> e) {
- prefs.renderEntireFile(e.getValue());
- if (view != null) {
- view.updateRenderEntireFile();
- }
- }
-
- @UiHandler("matchBrackets")
- void onMatchBrackets(ValueChangeEvent<Boolean> e) {
- prefs.matchBrackets(e.getValue());
- view.getCmFromSide(DisplaySide.A).setOption("matchBrackets", prefs.matchBrackets());
- view.getCmFromSide(DisplaySide.B).setOption("matchBrackets", prefs.matchBrackets());
- }
-
- @UiHandler("lineWrapping")
- void onLineWrapping(ValueChangeEvent<Boolean> e) {
- prefs.lineWrapping(e.getValue());
- view.getCmFromSide(DisplaySide.A).setOption("lineWrapping", prefs.lineWrapping());
- view.getCmFromSide(DisplaySide.B).setOption("lineWrapping", prefs.lineWrapping());
- }
-
- @UiHandler("skipDeleted")
- void onSkipDeleted(ValueChangeEvent<Boolean> e) {
- prefs.skipDeleted(!e.getValue());
- // TODO: Update the navigation links on the current DiffScreen
- }
-
- @UiHandler("skipUnchanged")
- void onSkipUnchanged(ValueChangeEvent<Boolean> e) {
- prefs.skipUnchanged(!e.getValue());
- // TODO: Update the navigation links on the current DiffScreen
- }
-
- @UiHandler("skipUncommented")
- void onSkipUncommented(ValueChangeEvent<Boolean> e) {
- prefs.skipUncommented(!e.getValue());
- // TODO: Update the navigation links on the current DiffScreen
- }
-
- @UiHandler("theme")
- void onTheme(@SuppressWarnings("unused") ChangeEvent e) {
- Theme newTheme = getSelectedTheme();
- prefs.theme(newTheme);
- if (view != null) {
- ThemeLoader.loadTheme(
- newTheme,
- new GerritCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- view.operation(
- () -> {
- if (getSelectedTheme() == newTheme && isAttached()) {
- String t = newTheme.name().toLowerCase();
- view.getCmFromSide(DisplaySide.A).setOption("theme", t);
- view.getCmFromSide(DisplaySide.B).setOption("theme", t);
- view.setThemeStyles(newTheme.isDark());
- }
- });
- }
- });
- }
- }
-
- private Theme getSelectedTheme() {
- return Theme.valueOf(theme.getValue(theme.getSelectedIndex()));
- }
-
- @UiHandler("apply")
- void onApply(@SuppressWarnings("unused") ClickEvent e) {
- close();
- }
-
- @UiHandler("save")
- void onSave(@SuppressWarnings("unused") ClickEvent e) {
- AccountApi.putDiffPreferences(
- prefs,
- new GerritCallback<DiffPreferences>() {
- @Override
- public void onSuccess(DiffPreferences result) {
- DiffPreferencesInfo p = new DiffPreferencesInfo();
- result.copyTo(p);
- Gerrit.setDiffPreferences(p);
- }
- });
- if (view != null) {
- close();
- }
- }
-
- @UiHandler("close")
- void onClose(ClickEvent e) {
- e.preventDefault();
- close();
- }
-
- void setFocus(boolean focus) {
- ignoreWhitespace.setFocus(focus);
- }
-
- private void close() {
- ((PopupPanel) getParent()).hide();
- }
-
- private void setIgnoreWhitespace(Whitespace v) {
- String name = v != null ? v.name() : IGNORE_NONE.name();
- for (int i = 0; i < ignoreWhitespace.getItemCount(); i++) {
- if (ignoreWhitespace.getValue(i).equals(name)) {
- ignoreWhitespace.setSelectedIndex(i);
- return;
- }
- }
- ignoreWhitespace.setSelectedIndex(0);
- }
-
- private void initIgnoreWhitespace() {
- ignoreWhitespace.addItem(PatchUtil.C.whitespaceIGNORE_NONE(), IGNORE_NONE.name());
- ignoreWhitespace.addItem(PatchUtil.C.whitespaceIGNORE_TRAILING(), IGNORE_TRAILING.name());
- ignoreWhitespace.addItem(
- PatchUtil.C.whitespaceIGNORE_LEADING_AND_TRAILING(), IGNORE_LEADING_AND_TRAILING.name());
- ignoreWhitespace.addItem(PatchUtil.C.whitespaceIGNORE_ALL(), IGNORE_ALL.name());
- }
-
- private void initMode() {
- mode.addItem("", "");
- for (ModeInfo m : Natives.asList(ModeInfo.all())) {
- mode.addItem(m.name(), m.mime());
- }
- }
-
- private void setMode(String modeType) {
- if (modeType != null && !modeType.isEmpty()) {
- ModeInfo m = ModeInfo.findModeByMIME(modeType);
- if (m != null) {
- for (int i = 0; i < mode.getItemCount(); i++) {
- if (mode.getValue(i).equals(m.mime())) {
- mode.setSelectedIndex(i);
- return;
- }
- }
- }
- }
- mode.setSelectedIndex(0);
- }
-
- private void setTheme(Theme v) {
- String name = v != null ? v.name() : Theme.DEFAULT.name();
- for (int i = 0; i < theme.getItemCount(); i++) {
- if (theme.getValue(i).equals(name)) {
- theme.setSelectedIndex(i);
- return;
- }
- }
- theme.setSelectedIndex(0);
- }
-
- private void initTheme() {
- for (Theme t : Theme.values()) {
- theme.addItem(t.name().toLowerCase(), t.name());
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
deleted file mode 100644
index 4465d63c3d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
+++ /dev/null
@@ -1,341 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'>
- <ui:style gss='false' type='com.google.gerrit.client.diff.PreferencesBox.Style'>
- @external .gwt-TextBox;
- @external .gwt-ToggleButton .html-face;
- @external .gwt-ToggleButton-up;
- @external .gwt-ToggleButton-up-hovering;
- @external .gwt-ToggleButton-up-disabled;
- @external .gwt-ToggleButton-down;
- @external .gwt-ToggleButton-down-hovering;
- @external .gwt-ToggleButton-down-disabled;
-
- .dialog {
- background: rgba(0, 0, 0, 0.85) none repeat scroll 0 50%;
- color: #ffffff;
- font-family: arial,sans-serif;
- font-weight: bold;
- overflow: auto !important;
- bottom: 0;
- text-align: left;
- text-shadow: 1px 1px 7px #000000;
- min-width: 300px;
- z-index: 200;
- border-radius: 10px;
- }
-
- @if user.agent safari {
- .dialog {
- \-webkit-border-radius: 10px;
- }
- }
-
- @if user.agent gecko1_8 {
- .dialog {
- \-moz-border-radius: 10px;
- }
- }
-
- .box { margin: 10px; }
- .box .gwt-TextBox { padding: 0; }
- .context { vertical-align: bottom; }
-
- .table tr { min-height: 23px; }
- .table th,
- .table td {
- white-space: nowrap;
- color: #ffffff;
- }
- .table th {
- padding-right: 8px;
- text-align: right;
- }
-
- .box a,
- .box a:visited,
- .box a:hover {
- color: #dddd00;
- }
-
- .box input.gwt-TextBox:disabled {
- background-color: #cacaca;
- }
-
- .box .gwt-ToggleButton {
- position: relative;
- height: 19px;
- width: 140px;
- background: #fff;
- color: #000;
- text-shadow: none;
- }
- .box .gwt-ToggleButton .html-face {
- position: absolute;
- top: 0;
- width: 68px;
- height: 17px;
- line-height: 17px;
- text-align: center;
- border-width: 1px;
- }
-
- .box .gwt-ToggleButton-up,
- .box .gwt-ToggleButton-up-hovering,
- .box .gwt-ToggleButton-up-disabled,
- .box .gwt-ToggleButton-down,
- .box .gwt-ToggleButton-down-hovering,
- .box .gwt-ToggleButton-down-disabled {
- padding: 0;
- border: 0;
- }
- .box .gwt-ToggleButton-up .html-face,
- .box .gwt-ToggleButton-up-hovering .html-face {
- left: 0;
- background: #cacaca;
- border-style: outset;
- }
- .box .gwt-ToggleButton-down .html-face,
- .box .gwt-ToggleButton-down-hovering .html-face {
- right: 0;
- background: #bcf;
- border-style: inset;
- }
-
- .box button {
- margin: 6px 3px 0 0;
- border-color: rgba(0, 0, 0, 0.1);
- text-align: center;
- font-size: 8pt;
- font-weight: bold;
- border: 1px solid;
- cursor: pointer;
- color: #444;
- background-color: #f5f5f5;
- background-image: -webkit-linear-gradient(top, #f5f5f5, #f1f1f1);
- -webkit-border-radius: 2px;
- -webkit-box-sizing: content-box;
- }
- .box button div {
- color: #444;
- height: 10px;
- min-width: 54px;
- line-height: 10px;
- white-space: nowrap;
- }
-
- button.apply {
- background-color: #4d90fe;
- background-image: -webkit-linear-gradient(top, #4d90fe, #4d90fe);
- }
- button.apply div { color: #fff; }
-
- button.save {
- margin-left: 10px;
- color: #d14836;
- background-color: #d14836;
- background-image: -webkit-linear-gradient(top, #d14836, #d14836);
- }
- button.save div { color: #fff; }
- </ui:style>
-
- <g:HTMLPanel styleName='{style.box}'>
- <div ui:field='header'>
- <table style='width: 100%'>
- <tr>
- <td><ui:msg>Diff Preferences</ui:msg></td>
- <td style='text-align: right'>
- <g:Anchor ui:field='close' href='javascript:;'><ui:msg>Close</ui:msg></g:Anchor>
- </td>
- </tr>
- </table>
- <hr/>
- </div>
- <table class='{style.table}'>
- <tr>
- <th><ui:msg>Theme</ui:msg></th>
- <td><g:ListBox ui:field='theme'/></td>
- </tr>
- <tr>
- <th><ui:msg>Ignore Whitespace</ui:msg></th>
- <td><g:ListBox ui:field='ignoreWhitespace'/></td>
- </tr>
- <tr>
- <th><ui:msg>Tab Width</ui:msg></th>
- <td><x:NpIntTextBox ui:field='tabWidth'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Columns</ui:msg></th>
- <td><x:NpIntTextBox ui:field='lineLength'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Lines of Context</ui:msg></th>
- <td><ui:msg><x:NpIntTextBox ui:field='context'
- addStyleNames='{style.context}'
- visibleLength='4'
- alignment='RIGHT'/>
- or <g:CheckBox ui:field='contextEntireFile'>entire file</g:CheckBox></ui:msg></td>
- </tr>
- <tr>
- <th><ui:msg>Cursor Blink Rate</ui:msg></th>
- <td><x:NpIntTextBox ui:field='cursorBlinkRate'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Intraline Difference</ui:msg></th>
- <td><g:ToggleButton ui:field='intralineDifference'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Syntax Highlighting</ui:msg></th>
- <td><g:ToggleButton ui:field='syntaxHighlighting'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><div ui:field='modeLabel'><ui:msg>Language</ui:msg></div></th>
- <td><g:ListBox ui:field='mode'/></td>
- </tr>
- <tr>
- <th><ui:msg>Whitespace Errors</ui:msg></th>
- <td><g:ToggleButton ui:field='whitespaceErrors'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Show Tabs</ui:msg></th>
- <td><g:ToggleButton ui:field='showTabs'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Line Numbers</ui:msg></th>
- <td><g:ToggleButton ui:field='lineNumbers'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Empty Pane</ui:msg></th>
- <td><g:ToggleButton ui:field='emptyPane'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><div ui:field='leftSideLabel'><ui:msg>Left Side</ui:msg></div></th>
- <td><g:ToggleButton ui:field='leftSide'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Top Menu</ui:msg></th>
- <td><g:ToggleButton ui:field='topMenu'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Auto Hide Diff Table Header</ui:msg></th>
- <td><g:ToggleButton ui:field='autoHideDiffTableHeader'>
- <g:upFace><ui:msg>Yes</ui:msg></g:upFace>
- <g:downFace><ui:msg>No</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Mark Reviewed</ui:msg></th>
- <td><g:ToggleButton ui:field='manualReview'>
- <g:upFace><ui:msg>Automatic</ui:msg></g:upFace>
- <g:downFace><ui:msg>Manual</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Expand All Comments</ui:msg></th>
- <td><g:ToggleButton ui:field='expandAllComments'>
- <g:upFace><ui:msg>Collapse</ui:msg></g:upFace>
- <g:downFace><ui:msg>Expand</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Render</ui:msg></th>
- <td><g:ToggleButton ui:field='renderEntireFile'>
- <g:upFace><ui:msg>Fast</ui:msg></g:upFace>
- <g:downFace><ui:msg>Slow</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Match Brackets</ui:msg></th>
- <td><g:ToggleButton ui:field='matchBrackets'>
- <g:upFace><ui:msg>Off</ui:msg></g:upFace>
- <g:downFace><ui:msg>On</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Line Wrapping</ui:msg></th>
- <td><g:ToggleButton ui:field='lineWrapping'>
- <g:upFace><ui:msg>Off</ui:msg></g:upFace>
- <g:downFace><ui:msg>On</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Skip Deleted Files</ui:msg></th>
- <td><g:ToggleButton ui:field='skipDeleted'>
- <g:upFace><ui:msg>Yes</ui:msg></g:upFace>
- <g:downFace><ui:msg>No</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Skip Unchanged Files</ui:msg></th>
- <td><g:ToggleButton ui:field='skipUnchanged'>
- <g:upFace><ui:msg>Yes</ui:msg></g:upFace>
- <g:downFace><ui:msg>No</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Skip Uncommented Files</ui:msg></th>
- <td><g:ToggleButton ui:field='skipUncommented'>
- <g:upFace><ui:msg>Yes</ui:msg></g:upFace>
- <g:downFace><ui:msg>No</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <td></td>
- <td>
- <g:Button ui:field='apply' styleName='{style.apply}'>
- <div><ui:msg>Apply</ui:msg></div>
- </g:Button>
- <g:Button ui:field='save' styleName='{style.save}'>
- <div><ui:msg>Save</ui:msg></div>
- </g:Button>
- </td>
- </tr>
- </table>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
deleted file mode 100644
index 1ddf895125..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.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.client.diff;
-
-import com.google.gerrit.client.AvatarImage;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.api.ApiGlue;
-import com.google.gerrit.client.change.ReplyBox;
-import com.google.gerrit.client.changes.CommentApi;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-/** An HtmlPanel for displaying a published comment */
-class PublishedBox extends CommentBox {
- interface Binder extends UiBinder<HTMLPanel, PublishedBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String closed();
- }
-
- private final PatchSet.Id psId;
- @Nullable private final Project.NameKey project;
- private final CommentInfo comment;
- private final DisplaySide displaySide;
- private DraftBox replyBox;
-
- @UiField Style style;
- @UiField Widget header;
- @UiField Element name;
- @UiField Element summary;
- @UiField Element date;
- @UiField Element message;
- @UiField Element buttons;
- @UiField Button reply;
- @UiField Button done;
- @UiField Button fix;
-
- @UiField(provided = true)
- AvatarImage avatar;
-
- PublishedBox(
- CommentGroup group,
- CommentLinkProcessor clp,
- @Nullable Project.NameKey project,
- PatchSet.Id psId,
- CommentInfo info,
- DisplaySide displaySide,
- boolean open) {
- super(group, info.range());
-
- this.psId = psId;
- this.project = project;
- this.comment = info;
- this.displaySide = displaySide;
-
- if (info.author() != null) {
- avatar = new AvatarImage(info.author());
- avatar.setSize("", "");
- } else {
- avatar = new AvatarImage();
- }
-
- initWidget(uiBinder.createAndBindUi(this));
- header.addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- setOpen(!isOpen());
- }
- },
- ClickEvent.getType());
-
- name.setInnerText(authorName(info));
- date.setInnerText(FormatUtil.shortFormatDayTime(info.updated()));
- if (info.message() != null) {
- String msg = info.message().trim();
- summary.setInnerText(msg);
- message.setInnerSafeHtml(clp.apply(new SafeHtmlBuilder().append(msg).wikify()));
- ApiGlue.fireEvent("comment", message);
- }
-
- fix.setVisible(open);
- }
-
- @Override
- CommentInfo getCommentInfo() {
- return comment;
- }
-
- @Override
- boolean isOpen() {
- return UIObject.isVisible(message);
- }
-
- @Override
- void setOpen(boolean open) {
- UIObject.setVisible(summary, !open);
- UIObject.setVisible(message, open);
- UIObject.setVisible(buttons, open && replyBox == null);
- if (open) {
- removeStyleName(style.closed());
- } else {
- addStyleName(style.closed());
- }
- super.setOpen(open);
- }
-
- void setReplyBox(DraftBox box) {
- replyBox = box;
- UIObject.setVisible(buttons, false);
- box.setReplyToBox(this);
- }
-
- void unregisterReplyBox() {
- replyBox = null;
- UIObject.setVisible(buttons, isOpen());
- }
-
- private void openReplyBox() {
- replyBox.setOpen(true);
- replyBox.setEdit(true);
- }
-
- void addReplyBox(boolean quote) {
- CommentInfo commentReply = CommentInfo.createReply(comment);
- if (quote) {
- commentReply.message(ReplyBox.quote(comment.message()));
- }
- getCommentManager().addDraftBox(displaySide, commentReply).setEdit(true);
- }
-
- void doReply() {
- if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(getCommentManager().host.getToken());
- } else if (replyBox == null) {
- addReplyBox(false);
- } else {
- openReplyBox();
- }
- }
-
- @UiHandler("reply")
- void onReply(ClickEvent e) {
- e.stopPropagation();
- doReply();
- }
-
- @UiHandler("quote")
- void onQuote(ClickEvent e) {
- e.stopPropagation();
- if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(getCommentManager().host.getToken());
- }
- addReplyBox(true);
- }
-
- @UiHandler("done")
- void onReplyDone(ClickEvent e) {
- e.stopPropagation();
- if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(getCommentManager().host.getToken());
- } else if (replyBox == null) {
- done.setEnabled(false);
- CommentInfo input = CommentInfo.createReply(comment);
- input.message(PatchUtil.C.cannedReplyDone());
- CommentApi.createDraft(
- Project.NameKey.asStringOrNull(project),
- psId,
- input,
- new GerritCallback<CommentInfo>() {
- @Override
- public void onSuccess(CommentInfo result) {
- done.setEnabled(true);
- setOpen(false);
- getCommentManager().addDraftBox(displaySide, result);
- }
- });
- } else {
- openReplyBox();
- setOpen(false);
- }
- }
-
- @UiHandler("fix")
- void onFix(ClickEvent e) {
- e.stopPropagation();
- String t = Dispatcher.toEditScreen(project, psId, comment.path(), comment.line());
- if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(t);
- } else {
- Gerrit.display(t);
- }
- }
-
- private static String authorName(CommentInfo info) {
- if (info.author() != null) {
- if (info.author().name() != null) {
- return info.author().name();
- }
- return Gerrit.info().user().anonymousCowardName();
- }
- return Util.C.messageNoAuthor();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml
deleted file mode 100644
index cbea847ab3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.ui.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder
- xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:c='urn:import:com.google.gerrit.client'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.diff.PublishedBox.Style'>
- .avatar {
- position: absolute;
- width: 26px;
- height: 26px;
- }
- .closed .avatar {
- position: absolute;
- width: 16px;
- height: 16px;
- }
-
- .name {
- white-space: nowrap;
- font-weight: bold;
- }
- .closed .name {
- width: 120px;
- overflow: hidden;
- text-overflow: ellipsis;
- font-weight: normal;
- }
- </ui:style>
-
- <g:HTMLPanel
- styleName='{res.style.commentBox}'
- addStyleNames='{style.closed}'>
- <c:AvatarImage ui:field='avatar' styleName='{style.avatar}'/>
- <div class='{res.style.contents}'>
- <g:HTMLPanel ui:field='header' styleName='{res.style.header}'>
- <div ui:field='name' class='{style.name}'/>
- <div ui:field='summary' class='{res.style.summary}'/>
- <div ui:field='date' class='{res.style.date}'/>
- </g:HTMLPanel>
- <div ui:field='message' class='{res.style.message}'
- aria-hidden='true' style='display: NONE'/>
- <div ui:field='buttons' aria-hidden='true' style='display: NONE'>
- <g:Button ui:field='reply' styleName=''
- title='Reply to this comment'>
- <ui:attribute name='title'/>
- <div><ui:msg>Reply</ui:msg></div>
- </g:Button>
- <g:Button ui:field='quote' styleName=''
- title='Reply to this comment with quoting it'>
- <ui:attribute name='title'/>
- <div><ui:msg>Quote</ui:msg></div>
- </g:Button>
- <g:Button ui:field='done' styleName=''
- title='Reply "Done" to this comment'>
- <ui:attribute name='title'/>
- <div><ui:msg>Done</ui:msg></div>
- </g:Button>
- <g:Button ui:field='fix' styleName='' visible='false'
- title='Fix this comment in the inline editor'>
- <ui:attribute name='title'/>
- <div><ui:msg>Fix</ui:msg></div>
- </g:Button>
- </div>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java
deleted file mode 100644
index e5903335e6..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Resources.java
+++ /dev/null
@@ -1,43 +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.client.diff;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.ImageResource;
-
-/** Resources used by diff. */
-interface Resources extends ClientBundle {
- Resources I = GWT.create(Resources.class);
-
- @Source("CommentBox.css")
- CommentBox.Style style();
-
- @Source("Scrollbar.css")
- Scrollbar.Style scrollbarStyle();
-
- @Source("DiffTable.css")
- DiffTable.Style diffTableStyle();
-
- /** tango icon library (public domain): http://tango.freedesktop.org/Tango_Icon_Library */
- @Source("goPrev.png")
- ImageResource goPrev();
-
- @Source("goNext.png")
- ImageResource goNext();
-
- @Source("goUp.png")
- ImageResource goUp();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
deleted file mode 100644
index 35e3e7d199..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollSynchronizer.java
+++ /dev/null
@@ -1,141 +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.client.diff;
-
-import com.google.gwt.user.client.Timer;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.ScrollInfo;
-
-class ScrollSynchronizer {
- private SideBySideTable diffTable;
- private LineMapper mapper;
- private ScrollCallback active;
- private ScrollCallback callbackA;
- private ScrollCallback callbackB;
- private CodeMirror cmB;
- private boolean autoHideDiffTableHeader;
-
- ScrollSynchronizer(SideBySideTable diffTable, CodeMirror cmA, CodeMirror cmB, LineMapper mapper) {
- this.diffTable = diffTable;
- this.mapper = mapper;
- this.cmB = cmB;
-
- callbackA = new ScrollCallback(cmA, cmB, DisplaySide.A);
- callbackB = new ScrollCallback(cmB, cmA, DisplaySide.B);
- cmA.on("scroll", callbackA);
- cmB.on("scroll", callbackB);
- }
-
- void setAutoHideDiffTableHeader(boolean autoHide) {
- if (autoHide) {
- updateDiffTableHeader(cmB.getScrollInfo());
- } else {
- diffTable.setHeaderVisible(true);
- }
- autoHideDiffTableHeader = autoHide;
- }
-
- void syncScroll(DisplaySide masterSide) {
- (masterSide == DisplaySide.A ? callbackA : callbackB).sync();
- }
-
- private void updateDiffTableHeader(ScrollInfo si) {
- if (si.top() == 0) {
- diffTable.setHeaderVisible(true);
- } else if (si.top() > 0.5 * si.clientHeight()) {
- diffTable.setHeaderVisible(false);
- }
- }
-
- class ScrollCallback implements Runnable {
- private final CodeMirror src;
- private final CodeMirror dst;
- private final DisplaySide srcSide;
- private final Timer fixup;
- private int state;
-
- ScrollCallback(CodeMirror src, CodeMirror dst, DisplaySide srcSide) {
- this.src = src;
- this.dst = dst;
- this.srcSide = srcSide;
- this.fixup =
- new Timer() {
- @Override
- public void run() {
- if (active == ScrollCallback.this) {
- fixup();
- }
- }
- };
- }
-
- void sync() {
- dst.scrollToY(align(src.getScrollInfo().top()));
- }
-
- @Override
- public void run() {
- if (active == null) {
- active = this;
- fixup.scheduleRepeating(20);
- }
- if (active == this) {
- ScrollInfo si = src.getScrollInfo();
- if (autoHideDiffTableHeader) {
- updateDiffTableHeader(si);
- }
- dst.scrollTo(si.left(), align(si.top()));
- state = 0;
- }
- }
-
- private void fixup() {
- switch (state) {
- case 0:
- state = 1;
- dst.scrollToY(align(src.getScrollInfo().top()));
- break;
- case 1:
- state = 2;
- break;
- case 2:
- active = null;
- fixup.cancel();
- break;
- }
- }
-
- private double align(double srcTop) {
- // Since CM doesn't always take the height of line widgets into
- // account when calculating scrollInfo when scrolling too fast (e.g.
- // throw scrolling), simply setting scrollTop to be the same doesn't
- // guarantee alignment.
-
- int line = src.lineAtHeight(srcTop, "local");
- if (line == 0) {
- // Padding for insert at start of file occurs above line 0,
- // and CM3 doesn't always compute heightAtLine correctly.
- return srcTop;
- }
-
- // Find a pair of lines that are aligned and near the top of
- // the viewport. Use that distance to correct the Y coordinate.
- LineMapper.AlignedPair p = mapper.align(srcSide, line);
- double sy = src.heightAtLine(p.src, "local");
- double dy = dst.heightAtLine(p.dst, "local");
- return Math.max(0, dy + (srcTop - sy));
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css
deleted file mode 100644
index 26f8ff506b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.css
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (C) 2014 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.
- */
-
-.comment, .draft, .insert, .delete, .edit {
- min-height: 5px;
- position: absolute;
- right: 0;
- z-index: 7;
-}
-
-.comment, .draft {
- color: #0d0d0d;
- font-size: 9px;
-}
-
-.delete {
- background-color: #faa;
- min-width: 12px;
-}
-.insert {
- background-color: #9f9;
- min-width: 12px;
-}
-.edit {
- border-left: 6px solid #faa;
- width: 6px !important;
- background-color: #9f9;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java
deleted file mode 100644
index 83ada90112..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Scrollbar.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2014 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.client.diff;
-
-import com.google.gwt.resources.client.CssResource;
-import java.util.ArrayList;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Pos;
-
-/** Displays overview of all edits and comments in this file. */
-class Scrollbar {
- static {
- Resources.I.scrollbarStyle().ensureInjected();
- }
-
- interface Style extends CssResource {
- String comment();
-
- String draft();
-
- String insert();
-
- String delete();
-
- String edit();
- }
-
- private final List<ScrollbarAnnotation> diff = new ArrayList<>();
- private final DiffTable parent;
-
- Scrollbar(DiffTable d) {
- parent = d;
- }
-
- ScrollbarAnnotation comment(CodeMirror cm, int line) {
- ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
- a.setStyleName(Resources.I.scrollbarStyle().comment());
- a.at(line);
- a.getElement().setInnerText("\u2736"); // Six pointed black star
- parent.add(a);
- return a;
- }
-
- ScrollbarAnnotation draft(CodeMirror cm, int line) {
- ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
- a.setStyleName(Resources.I.scrollbarStyle().draft());
- a.at(line);
- a.getElement().setInnerText("\u270D"); // Writing hand
- parent.add(a);
- return a;
- }
-
- ScrollbarAnnotation insert(CodeMirror cm, int line, int len) {
- ScrollbarAnnotation a = diff(cm, line, len);
- a.setStyleName(Resources.I.scrollbarStyle().insert());
- parent.add(a);
- return a;
- }
-
- ScrollbarAnnotation delete(CodeMirror cmA, CodeMirror cmB, int line, int len) {
- ScrollbarAnnotation a = diff(cmA, line, len);
- a.setStyleName(Resources.I.scrollbarStyle().delete());
- a.renderOn(cmB);
- parent.add(a);
- return a;
- }
-
- ScrollbarAnnotation edit(CodeMirror cm, int line, int len) {
- ScrollbarAnnotation a = diff(cm, line, len);
- a.setStyleName(Resources.I.scrollbarStyle().edit());
- parent.add(a);
- return a;
- }
-
- private ScrollbarAnnotation diff(CodeMirror cm, int s, int n) {
- ScrollbarAnnotation a = new ScrollbarAnnotation(cm);
- a.at(Pos.create(s), Pos.create(s + n));
- diff.add(a);
- return a;
- }
-
- void removeDiffAnnotations() {
- for (ScrollbarAnnotation a : diff) {
- a.remove();
- }
- diff.clear();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java
deleted file mode 100644
index ecdac46730..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/ScrollbarAnnotation.java
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (C) 2014 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.client.diff;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Widget;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.RegisteredHandler;
-import net.codemirror.lib.Pos;
-
-/** Displayed on the vertical scrollbar to place a chunk or comment. */
-class ScrollbarAnnotation extends Widget implements ClickHandler {
- private final CodeMirror cm;
- private CodeMirror cmB;
- private RegisteredHandler refresh;
- private Pos from;
- private Pos to;
- private double scale;
-
- ScrollbarAnnotation(CodeMirror cm) {
- setElement((Element) DOM.createDiv());
- getElement().setAttribute("not-content", "true");
- addDomHandler(this, ClickEvent.getType());
- this.cm = cm;
- this.cmB = cm;
- }
-
- void remove() {
- removeFromParent();
- }
-
- void at(int line) {
- at(Pos.create(line), Pos.create(line + 1));
- }
-
- void at(Pos from, Pos to) {
- this.from = from;
- this.to = to;
- }
-
- void renderOn(CodeMirror cm) {
- this.cmB = cm;
- }
-
- @Override
- protected void onLoad() {
- cmB.getWrapperElement().appendChild(getElement());
- refresh =
- cmB.on(
- "refresh",
- () -> {
- if (updateScale()) {
- updatePosition();
- }
- });
- updateScale();
- updatePosition();
- }
-
- @Override
- protected void onUnload() {
- cmB.off("refresh", refresh);
- }
-
- private boolean updateScale() {
- double old = scale;
- double docHeight = cmB.getWrapperElement().getClientHeight();
- double lineHeight = cmB.heightAtLine(cmB.lastLine() + 1, "local");
- scale = (docHeight - cmB.barHeight()) / lineHeight;
- return old != scale;
- }
-
- private void updatePosition() {
- double top = cm.charCoords(from, "local").top() * scale;
- double bottom = cm.charCoords(to, "local").bottom() * scale;
-
- Element e = getElement();
- e.getStyle().setTop(top, Unit.PX);
- e.getStyle().setWidth(Math.max(2, cm.barWidth() - 1), Unit.PX);
- e.getStyle().setHeight(Math.max(3, bottom - top), Unit.PX);
- }
-
- @Override
- public void onClick(ClickEvent event) {
- event.stopPropagation();
-
- int line = from.line();
- int h = to.line() - line;
- if (h > 5) {
- // Map click inside of the annotation to the relative position
- // within the region covered by the annotation.
- double s = ((double) event.getY()) / getElement().getOffsetHeight();
- line += (int) (s * h);
- }
-
- double y = cm.heightAtLine(line, "local");
- double viewport = cm.getScrollInfo().clientHeight();
- cm.setCursor(from);
- cm.scrollTo(0, y - 0.5 * viewport);
- cm.focus();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
deleted file mode 100644
index d052323037..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
+++ /dev/null
@@ -1,414 +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.client.diff;
-
-import static java.lang.Double.POSITIVE_INFINITY;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.projects.ConfigInfoCache;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import java.util.Collections;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineHandle;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.KeyMap;
-import net.codemirror.lib.Pos;
-
-public class SideBySide extends DiffScreen {
- interface Binder extends UiBinder<FlowPanel, SideBySide> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
- private static final String LINE_NUMBER_CLASSNAME = "CodeMirror-linenumber";
-
- @UiField(provided = true)
- SideBySideTable diffTable;
-
- private CodeMirror cmA;
- private CodeMirror cmB;
-
- private ScrollSynchronizer scrollSynchronizer;
-
- private SideBySideChunkManager chunkManager;
- private SideBySideCommentManager commentManager;
-
- public SideBySide(
- @Nullable Project.NameKey project,
- DiffObject base,
- DiffObject revision,
- String path,
- DisplaySide startSide,
- int startLine) {
- super(project, base, revision, path, startSide, startLine, DiffView.SIDE_BY_SIDE);
-
- diffTable = new SideBySideTable(this, base, revision, path);
- add(uiBinder.createAndBindUi(this));
- addDomHandler(GlobalKey.STOP_PROPAGATION, KeyPressEvent.getType());
- }
-
- @Override
- ScreenLoadCallback<ConfigInfoCache.Entry> getScreenLoadCallback(
- final CommentsCollections comments) {
- return new ScreenLoadCallback<ConfigInfoCache.Entry>(SideBySide.this) {
- @Override
- protected void preDisplay(ConfigInfoCache.Entry result) {
- commentManager =
- new SideBySideCommentManager(
- SideBySide.this,
- getProject(),
- base,
- revision,
- path,
- result.getCommentLinkProcessor(),
- getChangeStatus().isOpen());
- setTheme(result.getTheme());
- display(comments);
- header.setupPrevNextFiles(comments);
- }
- };
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
-
- operation(
- () -> {
- resizeCodeMirror();
- chunkManager.adjustPadding();
- cmA.refresh();
- cmB.refresh();
- });
- setLineLength(Patch.COMMIT_MSG.equals(path) ? 72 : prefs.lineLength());
- diffTable.refresh();
-
- if (getStartLine() == 0) {
- DiffChunkInfo d = chunkManager.getFirst();
- if (d != null) {
- if (d.isEdit() && d.getSide() == DisplaySide.A) {
- setStartSide(DisplaySide.B);
- setStartLine(lineOnOther(d.getSide(), d.getStart()).getLine() + 1);
- } else {
- setStartSide(d.getSide());
- setStartLine(d.getStart() + 1);
- }
- }
- }
- if (getStartSide() != null && getStartLine() > 0) {
- CodeMirror cm = getCmFromSide(getStartSide());
- cm.scrollToLine(getStartLine() - 1);
- cm.focus();
- } else {
- cmA.setCursor(Pos.create(0));
- cmA.focus();
- }
- if (Gerrit.isSignedIn() && prefs.autoReview()) {
- header.autoReview();
- }
- prefetchNextFile();
- }
-
- @Override
- void registerCmEvents(CodeMirror cm) {
- super.registerCmEvents(cm);
-
- KeyMap keyMap =
- KeyMap.create()
- .on("Shift-A", diffTable.toggleA())
- .on("Shift-Left", moveCursorToSide(cm, DisplaySide.A))
- .on("Shift-Right", moveCursorToSide(cm, DisplaySide.B));
- cm.addKeyMap(keyMap);
- maybeRegisterRenderEntireFileKeyMap(cm);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
-
- getKeysNavigation()
- .add(
- new NoOpKeyCommand(KeyCommand.M_SHIFT, KeyCodes.KEY_LEFT, PatchUtil.C.focusSideA()),
- new NoOpKeyCommand(KeyCommand.M_SHIFT, KeyCodes.KEY_RIGHT, PatchUtil.C.focusSideB()));
- getKeysAction()
- .add(
- new KeyCommand(KeyCommand.M_SHIFT, 'a', PatchUtil.C.toggleSideA()) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- diffTable.toggleA().run();
- }
- });
-
- registerHandlers();
- }
-
- @Override
- FocusHandler getFocusHandler() {
- return new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- cmB.focus();
- }
- };
- }
-
- private void display(CommentsCollections comments) {
- DiffInfo diff = getDiff();
- setThemeStyles(prefs.theme().isDark());
- setShowIntraline(prefs.intralineDifference());
- if (prefs.showLineNumbers()) {
- diffTable.addStyleName(Resources.I.diffTableStyle().showLineNumbers());
- }
-
- cmA = newCm(diff.metaA(), diff.textA(), diffTable.cmA);
- cmB = newCm(diff.metaB(), diff.textB(), diffTable.cmB);
-
- getDiffTable()
- .setUpBlameIconA(
- cmA,
- base.isBaseOrAutoMerge(),
- base.isBaseOrAutoMerge() ? revision : base.asPatchSetId(),
- path);
- getDiffTable().setUpBlameIconB(cmB, revision, path);
-
- cmA.extras().side(DisplaySide.A);
- cmB.extras().side(DisplaySide.B);
- setShowTabs(prefs.showTabs());
-
- chunkManager = new SideBySideChunkManager(this, cmA, cmB, diffTable.scrollbar);
-
- operation(
- () -> {
- // Estimate initial CodeMirror height, fixed up in onShowView.
- int height = Window.getClientHeight() - (Gerrit.getHeaderFooterHeight() + 18);
- cmA.setHeight(height);
- cmB.setHeight(height);
-
- render(diff);
- commentManager.render(comments, prefs.expandAllComments());
- skipManager.render(prefs.context(), diff);
- });
-
- registerCmEvents(cmA);
- registerCmEvents(cmB);
- scrollSynchronizer = new ScrollSynchronizer(diffTable, cmA, cmB, chunkManager.lineMapper);
-
- setPrefsAction(new PreferencesAction(this, prefs));
- header.init(getPrefsAction(), getUnifiedDiffLink(), diff.sideBySideWebLinks());
- scrollSynchronizer.setAutoHideDiffTableHeader(prefs.autoHideDiffTableHeader());
-
- setupSyntaxHighlighting();
- }
-
- private List<InlineHyperlink> getUnifiedDiffLink() {
- InlineHyperlink toUnifiedDiffLink = new InlineHyperlink();
- toUnifiedDiffLink.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.unifiedDiff()));
- toUnifiedDiffLink.setTargetHistoryToken(
- Dispatcher.toUnified(getProject(), base, revision, path));
- toUnifiedDiffLink.setTitle(PatchUtil.C.unifiedDiff());
- return Collections.singletonList(toUnifiedDiffLink);
- }
-
- @Override
- CodeMirror newCm(DiffInfo.FileMeta meta, String contents, Element parent) {
- return CodeMirror.create(
- parent,
- Configuration.create()
- .set("cursorBlinkRate", prefs.cursorBlinkRate())
- .set("cursorHeight", 0.85)
- .set("inputStyle", "textarea")
- .set("keyMap", "vim_ro")
- .set("lineNumbers", prefs.showLineNumbers())
- .set("matchBrackets", prefs.matchBrackets())
- .set("lineWrapping", prefs.lineWrapping())
- .set("mode", getFileSize() == FileSize.SMALL ? getContentType(meta) : null)
- .set("readOnly", true)
- .set("scrollbarStyle", "overlay")
- .set("showTrailingSpace", prefs.showWhitespaceErrors())
- .set("styleSelectedText", true)
- .set("tabSize", prefs.tabSize())
- .set("theme", prefs.theme().name().toLowerCase())
- .set("value", meta != null ? contents : "")
- .set("viewportMargin", renderEntireFile() ? POSITIVE_INFINITY : 10));
- }
-
- @Override
- void setShowLineNumbers(boolean b) {
- super.setShowLineNumbers(b);
-
- cmA.setOption("lineNumbers", b);
- cmB.setOption("lineNumbers", b);
- }
-
- @Override
- void setSyntaxHighlighting(boolean b) {
- final DiffInfo diff = getDiff();
- if (b) {
- injectMode(
- diff,
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- if (prefs.syntaxHighlighting()) {
- cmA.setOption("mode", getContentType(diff.metaA()));
- cmB.setOption("mode", getContentType(diff.metaB()));
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- prefs.syntaxHighlighting(false);
- }
- });
- } else {
- cmA.setOption("mode", (String) null);
- cmB.setOption("mode", (String) null);
- }
- }
-
- @Override
- void setAutoHideDiffHeader(boolean hide) {
- scrollSynchronizer.setAutoHideDiffTableHeader(hide);
- }
-
- CodeMirror otherCm(CodeMirror me) {
- return me == cmA ? cmB : cmA;
- }
-
- @Override
- CodeMirror getCmFromSide(DisplaySide side) {
- return side == DisplaySide.A ? cmA : cmB;
- }
-
- @Override
- int getCmLine(int line, DisplaySide side) {
- return line;
- }
-
- @Override
- Runnable updateActiveLine(CodeMirror cm) {
- CodeMirror other = otherCm(cm);
- return () -> {
- // The rendering of active lines has to be deferred. Reflow
- // caused by adding and removing styles chokes Firefox when arrow
- // key (or j/k) is held down. Performance on Chrome is fine
- // without the deferral.
- //
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- operation(
- () -> {
- LineHandle handle = cm.getLineHandleVisualStart(cm.getCursor("end").line());
- if (!cm.extras().activeLine(handle)) {
- return;
- }
-
- LineOnOtherInfo info = lineOnOther(cm.side(), cm.getLineNumber(handle));
- if (info.isAligned()) {
- other.extras().activeLine(other.getLineHandle(info.getLine()));
- } else {
- other.extras().clearActiveLine();
- }
- });
- }
- });
- };
- }
-
- private Runnable moveCursorToSide(CodeMirror cmSrc, DisplaySide sideDst) {
- CodeMirror cmDst = getCmFromSide(sideDst);
- if (cmDst == cmSrc) {
- return () -> {};
- }
-
- DisplaySide sideSrc = cmSrc.side();
- return () -> {
- if (cmSrc.extras().hasActiveLine()) {
- cmDst.setCursor(
- Pos.create(
- lineOnOther(sideSrc, cmSrc.getLineNumber(cmSrc.extras().activeLine())).getLine()));
- }
- cmDst.focus();
- };
- }
-
- void syncScroll(DisplaySide masterSide) {
- if (scrollSynchronizer != null) {
- scrollSynchronizer.syncScroll(masterSide);
- }
- }
-
- @Override
- void operation(Runnable apply) {
- cmA.operation(() -> cmB.operation(apply::run));
- }
-
- @Override
- CodeMirror[] getCms() {
- return new CodeMirror[] {cmA, cmB};
- }
-
- @Override
- SideBySideTable getDiffTable() {
- return diffTable;
- }
-
- @Override
- SideBySideChunkManager getChunkManager() {
- return chunkManager;
- }
-
- @Override
- SideBySideCommentManager getCommentManager() {
- return commentManager;
- }
-
- @Override
- boolean isSideBySide() {
- return true;
- }
-
- @Override
- String getLineNumberClassName() {
- return LINE_NUMBER_CLASSNAME;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml
deleted file mode 100644
index 55c9de0d44..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.ui.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:d='urn:import:com.google.gerrit.client.diff'>
- <ui:style gss='false'>
- .sbs {
- margin-left: -5px;
- margin-right: -5px;
- }
- </ui:style>
- <g:FlowPanel styleName='{style.sbs}'>
- <d:Header ui:field='header'/>
- <d:SideBySideTable ui:field='diffTable'/>
- </g:FlowPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java
deleted file mode 100644
index 28777940ad..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideChunkManager.java
+++ /dev/null
@@ -1,262 +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.client.diff;
-
-import static com.google.gerrit.client.diff.DisplaySide.A;
-import static com.google.gerrit.client.diff.DisplaySide.B;
-
-import com.google.gerrit.client.diff.DiffInfo.Region;
-import com.google.gerrit.client.diff.DiffInfo.Span;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.EventListener;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineClassWhere;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.LineWidget;
-import net.codemirror.lib.Pos;
-
-/** Colors modified regions for {@link SideBySide}. */
-class SideBySideChunkManager extends ChunkManager {
- private static final String DATA_LINES = "_cs2h";
- private static double guessedLineHeightPx = 15;
- private static final JavaScriptObject focusA = initOnClick(A);
- private static final JavaScriptObject focusB = initOnClick(B);
-
- private static native JavaScriptObject initOnClick(DisplaySide s) /*-{
- return $entry(function(e){
- @com.google.gerrit.client.diff.SideBySideChunkManager::focus(
- Lcom/google/gwt/dom/client/NativeEvent;
- Lcom/google/gerrit/client/diff/DisplaySide;)(e,s)
- });
- }-*/;
-
- private static void focus(NativeEvent event, DisplaySide side) {
- Element e = Element.as(event.getEventTarget());
- for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
- EventListener l = DOM.getEventListener(e);
- if (l instanceof SideBySide) {
- ((SideBySide) l).getCmFromSide(side).focus();
- event.stopPropagation();
- }
- }
- }
-
- static void focusOnClick(Element e, DisplaySide side) {
- onClick(e, side == A ? focusA : focusB);
- }
-
- private final SideBySide host;
- private final CodeMirror cmA;
- private final CodeMirror cmB;
-
- private List<DiffChunkInfo> chunks;
- private List<LineWidget> padding;
- private List<Element> paddingDivs;
-
- SideBySideChunkManager(SideBySide host, CodeMirror cmA, CodeMirror cmB, Scrollbar scrollbar) {
- super(scrollbar);
-
- this.host = host;
- this.cmA = cmA;
- this.cmB = cmB;
- }
-
- @Override
- DiffChunkInfo getFirst() {
- return !chunks.isEmpty() ? chunks.get(0) : null;
- }
-
- @Override
- void reset() {
- super.reset();
-
- for (LineWidget w : padding) {
- w.clear();
- }
- }
-
- @Override
- void render(DiffInfo diff) {
- super.render();
-
- chunks = new ArrayList<>();
- padding = new ArrayList<>();
- paddingDivs = new ArrayList<>();
-
- String diffColor =
- diff.metaA() == null || diff.metaB() == null
- ? SideBySideTable.style.intralineBg()
- : SideBySideTable.style.diff();
-
- for (Region current : Natives.asList(diff.content())) {
- if (current.ab() != null) {
- lineMapper.appendCommon(current.ab().length());
- } else if (current.skip() > 0) {
- lineMapper.appendCommon(current.skip());
- } else if (current.common()) {
- lineMapper.appendCommon(current.b().length());
- } else {
- render(current, diffColor);
- }
- }
-
- if (paddingDivs.isEmpty()) {
- paddingDivs = null;
- }
- }
-
- void adjustPadding() {
- if (paddingDivs != null) {
- double h = cmB.extras().lineHeightPx();
- for (Element div : paddingDivs) {
- int lines = div.getPropertyInt(DATA_LINES);
- div.getStyle().setHeight(lines * h, Unit.PX);
- }
- for (LineWidget w : padding) {
- w.changed();
- }
- paddingDivs = null;
- guessedLineHeightPx = h;
- }
- }
-
- private void render(Region region, String diffColor) {
- int startA = lineMapper.getLineA();
- int startB = lineMapper.getLineB();
-
- JsArrayString a = region.a();
- JsArrayString b = region.b();
- int aLen = a != null ? a.length() : 0;
- int bLen = b != null ? b.length() : 0;
-
- String color = a == null || b == null ? diffColor : SideBySideTable.style.intralineBg();
-
- colorLines(cmA, color, startA, aLen);
- colorLines(cmB, color, startB, bLen);
- markEdit(cmA, startA, a, region.editA());
- markEdit(cmB, startB, b, region.editB());
- addPadding(cmA, startA + aLen - 1, bLen - aLen);
- addPadding(cmB, startB + bLen - 1, aLen - bLen);
- addGutterTag(region, startA, startB);
- lineMapper.appendReplace(aLen, bLen);
-
- int endA = lineMapper.getLineA() - 1;
- int endB = lineMapper.getLineB() - 1;
- if (aLen > 0) {
- addDiffChunk(cmB, endA, aLen, bLen > 0);
- }
- if (bLen > 0) {
- addDiffChunk(cmA, endB, bLen, aLen > 0);
- }
- }
-
- private void addGutterTag(Region region, int startA, int startB) {
- if (region.a() == null) {
- scrollbar.insert(cmB, startB, region.b().length());
- } else if (region.b() == null) {
- scrollbar.delete(cmA, cmB, startA, region.a().length());
- } else {
- scrollbar.edit(cmB, startB, region.b().length());
- }
- }
-
- private void markEdit(CodeMirror cm, int startLine, JsArrayString lines, JsArray<Span> edits) {
- if (lines == null || edits == null) {
- return;
- }
-
- EditIterator iter = new EditIterator(lines, startLine);
- Configuration bg =
- Configuration.create()
- .set("className", SideBySideTable.style.intralineBg())
- .set("readOnly", true);
-
- Configuration diff =
- Configuration.create().set("className", SideBySideTable.style.diff()).set("readOnly", true);
-
- Pos last = Pos.create(0, 0);
- for (Span span : Natives.asList(edits)) {
- Pos from = iter.advance(span.skip());
- Pos to = iter.advance(span.mark());
- if (from.line() == last.line()) {
- getMarkers().add(cm.markText(last, from, bg));
- } else {
- getMarkers().add(cm.markText(Pos.create(from.line(), 0), from, bg));
- }
- getMarkers().add(cm.markText(from, to, diff));
- last = to;
- colorLines(
- cm, LineClassWhere.BACKGROUND, SideBySideTable.style.diff(), from.line(), to.line());
- }
- }
-
- /**
- * Insert a new padding div below the given line.
- *
- * @param cm parent CodeMirror to add extra space into.
- * @param line line to put the padding below.
- * @param len number of lines to pad. Padding is inserted only if {@code len >= 1}.
- */
- private void addPadding(CodeMirror cm, int line, int len) {
- if (0 < len) {
- Element pad = DOM.createDiv();
- pad.setClassName(SideBySideTable.style.padding());
- pad.setPropertyInt(DATA_LINES, len);
- pad.getStyle().setHeight(guessedLineHeightPx * len, Unit.PX);
- focusOnClick(pad, cm.side());
- paddingDivs.add(pad);
- padding.add(
- cm.addLineWidget(
- line == -1 ? 0 : line,
- pad,
- Configuration.create()
- .set("coverGutter", true)
- .set("noHScroll", true)
- .set("above", line == -1)));
- }
- }
-
- private void addDiffChunk(CodeMirror cmToPad, int lineOnOther, int chunkSize, boolean edit) {
- chunks.add(
- new DiffChunkInfo(
- host.otherCm(cmToPad).side(), lineOnOther - chunkSize + 1, lineOnOther, edit));
- }
-
- @Override
- Runnable diffChunkNav(CodeMirror cm, Direction dir) {
- return () -> {
- int line = cm.extras().hasActiveLine() ? cm.getLineNumber(cm.extras().activeLine()) : 0;
- int res =
- Collections.binarySearch(
- chunks, new DiffChunkInfo(cm.side(), line, 0, false), getDiffChunkComparator());
- diffChunkNavHelper(chunks, host, res, dir);
- };
- }
-
- @Override
- int getCmLine(int line, DisplaySide side) {
- return line;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentGroup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentGroup.java
deleted file mode 100644
index c728f6fa5c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentGroup.java
+++ /dev/null
@@ -1,159 +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.client.diff;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Timer;
-import java.util.PriorityQueue;
-import net.codemirror.lib.CodeMirror;
-
-/**
- * LineWidget attached to a CodeMirror container.
- *
- * <p>When a comment is placed on a line a CommentWidget is created on both sides. The group tracks
- * all comment boxes on that same line, and also includes an empty padding element to keep
- * subsequent lines vertically aligned.
- */
-class SideBySideCommentGroup extends CommentGroup implements Comparable<SideBySideCommentGroup> {
- static void pair(SideBySideCommentGroup a, SideBySideCommentGroup b) {
- a.peers.add(b);
- b.peers.add(a);
- }
-
- private final Element padding;
- private final PriorityQueue<SideBySideCommentGroup> peers;
-
- SideBySideCommentGroup(
- SideBySideCommentManager manager, CodeMirror cm, DisplaySide side, int line) {
- super(manager, cm, side, line);
-
- padding = DOM.createDiv();
- padding.setClassName(SideBySideTable.style.padding());
- SideBySideChunkManager.focusOnClick(padding, cm.side());
- getElement().appendChild(padding);
- peers = new PriorityQueue<>();
- }
-
- SideBySideCommentGroup getPeer() {
- return peers.peek();
- }
-
- @Override
- void remove(DraftBox box) {
- super.remove(box);
-
- if (getBoxCount() == 0 && peers.size() == 1 && peers.peek().peers.size() > 1) {
- SideBySideCommentGroup peer = peers.peek();
- peer.peers.remove(this);
- detach();
- if (peer.getBoxCount() == 0
- && peer.peers.size() == 1
- && peer.peers.peek().getBoxCount() == 0) {
- peer.detach();
- } else {
- peer.resize();
- }
- } else {
- resize();
- }
- }
-
- @Override
- void init(DiffTable parent) {
- if (getLineWidget() == null) {
- attach(parent);
- }
- for (CommentGroup peer : peers) {
- if (peer.getLineWidget() == null) {
- peer.attach(parent);
- }
- }
- }
-
- @Override
- void handleRedraw() {
- getLineWidget()
- .onRedraw(
- () -> {
- if (canComputeHeight() && peers.peek().canComputeHeight()) {
- if (getResizeTimer() != null) {
- getResizeTimer().cancel();
- setResizeTimer(null);
- }
- adjustPadding(SideBySideCommentGroup.this, peers.peek());
- } else if (getResizeTimer() == null) {
- setResizeTimer(
- new Timer() {
- @Override
- public void run() {
- if (canComputeHeight() && peers.peek().canComputeHeight()) {
- cancel();
- setResizeTimer(null);
- adjustPadding(SideBySideCommentGroup.this, peers.peek());
- }
- }
- });
- getResizeTimer().scheduleRepeating(5);
- }
- });
- }
-
- @Override
- void resize() {
- if (getLineWidget() != null) {
- adjustPadding(this, peers.peek());
- }
- }
-
- private int computeHeight() {
- if (getComments().isVisible()) {
- // Include margin-bottom: 5px from CSS class.
- return getComments().getOffsetHeight() + 5;
- }
- return 0;
- }
-
- private static void adjustPadding(SideBySideCommentGroup a, SideBySideCommentGroup b) {
- int apx = a.computeHeight();
- int bpx = b.computeHeight();
- for (SideBySideCommentGroup otherPeer : a.peers) {
- if (otherPeer != b) {
- bpx += otherPeer.computeHeight();
- }
- }
- for (SideBySideCommentGroup otherPeer : b.peers) {
- if (otherPeer != a) {
- apx += otherPeer.computeHeight();
- }
- }
- int h = Math.max(apx, bpx);
- a.padding.getStyle().setHeight(Math.max(0, h - apx), Unit.PX);
- b.padding.getStyle().setHeight(Math.max(0, h - bpx), Unit.PX);
- a.getLineWidget().changed();
- b.getLineWidget().changed();
- a.updateSelection();
- b.updateSelection();
- }
-
- @Override
- public int compareTo(SideBySideCommentGroup o) {
- if (side == o.side) {
- return line - o.line;
- }
- throw new IllegalStateException("Cannot compare SideBySideCommentGroup with different sides");
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
deleted file mode 100644
index 09c5b072b3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
+++ /dev/null
@@ -1,136 +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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import java.util.Collection;
-import java.util.Map;
-import java.util.SortedMap;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/** Tracks comment widgets for {@link SideBySide}. */
-class SideBySideCommentManager extends CommentManager {
- SideBySideCommentManager(
- SideBySide host,
- @Nullable Project.NameKey project,
- DiffObject base,
- PatchSet.Id revision,
- String path,
- CommentLinkProcessor clp,
- boolean open) {
- super(host, project, base, revision, path, clp, open);
- }
-
- @Override
- SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side) {
- return map(side);
- }
-
- @Override
- void clearLine(DisplaySide side, int line, CommentGroup group) {
- super.clearLine(side, line, group);
- }
-
- @Override
- void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int line) {
- if (!Gerrit.isSignedIn()) {
- signInCallback(cm).run();
- } else {
- insertNewDraft(cm.side(), line);
- }
- }
-
- @Override
- CommentGroup getCommentGroupOnActiveLine(CodeMirror cm) {
- CommentGroup group = null;
- if (cm.extras().hasActiveLine()) {
- group = map(cm.side()).get(cm.getLineNumber(cm.extras().activeLine()) + 1);
- }
- return group;
- }
-
- @Override
- Collection<Integer> getLinesWithCommentGroups() {
- return sideB.tailMap(1).keySet();
- }
-
- @Override
- String getTokenSuffixForActiveLine(CodeMirror cm) {
- return (cm.side() == DisplaySide.A ? "a" : "")
- + (cm.getLineNumber(cm.extras().activeLine()) + 1);
- }
-
- @Override
- void newDraft(CodeMirror cm) {
- int line = cm.getLineNumber(cm.extras().activeLine()) + 1;
- if (cm.somethingSelected()) {
- FromTo fromTo = adjustSelection(cm);
- addDraftBox(
- cm.side(),
- CommentInfo.create(
- getPath(),
- getStoredSideFromDisplaySide(cm.side()),
- getParentNumFromDisplaySide(cm.side()),
- line,
- CommentRange.create(fromTo),
- false))
- .setEdit(true);
- cm.setCursor(fromTo.to());
- cm.setSelection(cm.getCursor());
- } else {
- insertNewDraft(cm.side(), line);
- }
- }
-
- @Override
- CommentGroup group(DisplaySide side, int line) {
- CommentGroup existing = map(side).get(line);
- if (existing != null) {
- return existing;
- }
-
- SideBySideCommentGroup newGroup = newGroup(side, line);
- Map<Integer, CommentGroup> map = side == DisplaySide.A ? sideA : sideB;
- Map<Integer, CommentGroup> otherMap = side == DisplaySide.A ? sideB : sideA;
- map.put(line, newGroup);
- int otherLine = host.lineOnOther(side, line - 1).getLine() + 1;
- existing = map(side.otherSide()).get(otherLine);
- CommentGroup otherGroup;
- if (existing != null) {
- otherGroup = existing;
- } else {
- otherGroup = newGroup(side.otherSide(), otherLine);
- otherMap.put(otherLine, otherGroup);
- }
- SideBySideCommentGroup.pair(newGroup, (SideBySideCommentGroup) otherGroup);
-
- if (isAttached()) {
- newGroup.init(host.getDiffTable());
- otherGroup.handleRedraw();
- }
- return newGroup;
- }
-
- private SideBySideCommentGroup newGroup(DisplaySide side, int line) {
- return new SideBySideCommentGroup(this, host.getCmFromSide(side), side, line);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.java
deleted file mode 100644
index c65dcf0428..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.java
+++ /dev/null
@@ -1,112 +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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.HTMLPanel;
-
-/**
- * A table with one row and two columns to hold the two CodeMirrors displaying the files to be
- * compared.
- */
-class SideBySideTable extends DiffTable {
- interface Binder extends UiBinder<HTMLPanel, SideBySideTable> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface DiffTableStyle extends CssResource {
- String intralineBg();
-
- String diff();
-
- String hideA();
-
- String hideB();
-
- String padding();
- }
-
- private SideBySide parent;
- @UiField Element cmA;
- @UiField Element cmB;
- @UiField static DiffTableStyle style;
-
- private boolean visibleA;
-
- SideBySideTable(SideBySide parent, DiffObject base, DiffObject revision, String path) {
- super(parent, base, revision, path);
-
- initWidget(uiBinder.createAndBindUi(this));
- this.visibleA = true;
- this.parent = parent;
- }
-
- @Override
- boolean isVisibleA() {
- return visibleA;
- }
-
- void setVisibleA(boolean show) {
- visibleA = show;
- if (show) {
- removeStyleName(style.hideA());
- parent.syncScroll(DisplaySide.B); // match B's viewport
- } else {
- addStyleName(style.hideA());
- }
- }
-
- Runnable toggleA() {
- return () -> setVisibleA(!isVisibleA());
- }
-
- void setVisibleB(boolean show) {
- if (show) {
- removeStyleName(style.hideB());
- parent.syncScroll(DisplaySide.A); // match A's viewport
- } else {
- addStyleName(style.hideB());
- }
- }
-
- @Override
- void setHideEmptyPane(boolean hide) {
- if (getChangeType() == ChangeType.ADDED) {
- setVisibleA(!hide);
- } else if (getChangeType() == ChangeType.DELETED) {
- setVisibleB(!hide);
- }
- }
-
- @Override
- SideBySide getDiffScreen() {
- return parent;
- }
-
- @Override
- int getHeaderHeight() {
- int h = patchSetSelectBoxA.getOffsetHeight();
- if (hasHeader()) {
- h += diffHeaderRow.getOffsetHeight();
- }
- return h;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml
deleted file mode 100644
index b2e3f437cd..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideTable.ui.xml
+++ /dev/null
@@ -1,147 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:d='urn:import:com.google.gerrit.client.diff'>
- <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.diff.SideBySideTable.DiffTableStyle'>
- @external .CodeMirror, .CodeMirror-selectedtext;
- @external .CodeMirror-linenumber;
- @external .CodeMirror-overlayscroll-vertical, .CodeMirror-scroll;
- @external .CodeMirror-dialog-bottom;
- @external .CodeMirror-cursor;
-
- @external .dark, .noIntraline, .showLineNumbers;
-
- .difftable .patchSetNav,
- .difftable .CodeMirror {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- }
-
- .difftable .CodeMirror pre {
- overflow: visible;
- border-right: 0;
- width: auto;
- }
-
- /* Preserve space for underscores. If this changes
- * see ChunkManager.addPadding() and adjust there.
- */
- .difftable .CodeMirror pre,
- .difftable .CodeMirror pre span {
- padding-bottom: 1px;
- }
-
- .hideA .psNavA,
- .hideA .a {
- display: none;
- }
-
- .hideB .psNavB,
- .hideB .b {
- display: none;
- }
-
- .table {
- width: 100%;
- table-layout: fixed;
- border-spacing: 0;
- }
- .table td { padding: 0 }
- .a, .b { width: 50% }
- .hideA .psNavB, .hideA .b { width: 100% }
- .hideB .psNavA, .hideB .a { width: 100% }
-
- /* Hide scrollbars on A, B controls both views. */
- .a .CodeMirror-scroll { margin-right: -36px; }
- .a .CodeMirror-overlayscroll-vertical { display: none !important; }
-
- .showLineNumbers .b { border-left: none; }
- .b { border-left: 1px solid #ddd; }
-
- .a .diff { background-color: #faa; }
- /* Set min-width for lineWrapping to make sure it gets enough width
- before lineWrapping and to make sure it dosent do a ugly line wrap */
- .b .diff { background-color: #9f9; min-width: 60em; }
- .a .intralineBg { background-color: #fee; }
- .b .intralineBg { background-color: #dfd; }
- .noIntraline .a .intralineBg { background-color: #faa; }
- .noIntraline .b .intralineBg { background-color: #9f9; }
-
- .dark .a .diff { background-color: #400; }
- .dark .b .diff { background-color: #444; }
-
- .dark .a .intralineBg { background-color: #888; }
- .dark .b .intralineBg { background-color: #bbb; }
- .dark .noIntraline .a .intralineBg { background-color: #400; }
- .dark .noIntraline .b .intralineBg { background-color: #444; }
-
- .patchSetNav, .diff_header {
- background-color: #f7f7f7;
- line-height: 1;
- }
-
- .difftable .CodeMirror-selectedtext {
- background-color: inherit !important;
- }
- .difftable .CodeMirror-linenumber {
- height: 1.11em;
- cursor: pointer;
- }
- .difftable .CodeMirror div.CodeMirror-cursor {
- border-left: 2px solid black;
- }
- .difftable .CodeMirror-dialog-bottom {
- border-top: 0;
- border-left: 1px solid #000;
- border-bottom: 1px solid #000;
- background-color: #f7f7f7;
- top: 0;
- right: 0;
- bottom: auto;
- left: auto;
- }
- .showLineNumbers .padding {
- margin-left: 21px;
- border-left: 2px solid #d64040;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.difftable}'>
- <table class='{style.table}'>
- <tr ui:field='patchSetNavRow' class='{style.patchSetNav}'>
- <td ui:field='patchSetNavCellA' class='{style.psNavA}'>
- <d:PatchSetSelectBox ui:field='patchSetSelectBoxA' />
- </td>
- <td ui:field='patchSetNavCellB' class='{style.psNavB}'>
- <d:PatchSetSelectBox ui:field='patchSetSelectBoxB' />
- </td>
- </tr>
- <tr ui:field='diffHeaderRow' class='{res.diffTableStyle.diffHeader}'>
- <td colspan='2'><pre ui:field='diffHeaderText' /></td>
- </tr>
- <tr>
- <td ui:field='cmA' class='{style.a}' />
- <td ui:field='cmB' class='{style.b}' />
- </tr>
- </table>
- <g:FlowPanel ui:field='widgets' visible='false'/>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
deleted file mode 100644
index c138f3770b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.java
+++ /dev/null
@@ -1,217 +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.client.diff;
-
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.LineWidget;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker;
-import net.codemirror.lib.TextMarker.FromTo;
-
-class SkipBar extends Composite {
- interface Binder extends UiBinder<HTMLPanel, SkipBar> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
- private static final int NUM_ROWS_TO_EXPAND = 10;
- private static final int UP_DOWN_THRESHOLD = 30;
-
- interface SkipBarStyle extends CssResource {
- String noExpand();
- }
-
- @UiField(provided = true)
- Anchor skipNum;
-
- @UiField(provided = true)
- Anchor upArrow;
-
- @UiField(provided = true)
- Anchor downArrow;
-
- @UiField SkipBarStyle style;
-
- private final SkipManager manager;
- private final CodeMirror cm;
-
- private LineWidget lineWidget;
- private TextMarker textMarker;
- private SkipBar otherBar;
-
- SkipBar(SkipManager manager, CodeMirror cm) {
- this.manager = manager;
- this.cm = cm;
-
- skipNum = new Anchor(true);
- upArrow = new Anchor(true);
- downArrow = new Anchor(true);
- initWidget(uiBinder.createAndBindUi(this));
- addDomHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- cm.focus();
- }
- },
- ClickEvent.getType());
- }
-
- void collapse(int start, int end, boolean attach) {
- if (attach) {
- boolean isNew = lineWidget == null;
- Configuration cfg = Configuration.create().set("coverGutter", true).set("noHScroll", true);
- if (start == 0) { // First line workaround
- lineWidget = cm.addLineWidget(end + 1, getElement(), cfg.set("above", true));
- } else {
- lineWidget = cm.addLineWidget(start - 1, getElement(), cfg);
- }
- if (isNew) {
- lineWidget.onFirstRedraw(
- () -> {
- int w = cm.getGutterElement().getOffsetWidth();
- getElement().getStyle().setPaddingLeft(w, Unit.PX);
- });
- }
- }
-
- textMarker =
- cm.markText(
- Pos.create(start, 0),
- Pos.create(end),
- Configuration.create()
- .set("collapsed", true)
- .set("inclusiveLeft", true)
- .set("inclusiveRight", true));
-
- textMarker.on("beforeCursorEnter", this::expandAll);
-
- int skipped = end - start + 1;
- if (skipped <= UP_DOWN_THRESHOLD) {
- addStyleName(style.noExpand());
- } else {
- upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND));
- downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND));
- }
- skipNum.setText(PatchUtil.M.patchSkipRegion(Integer.toString(skipped)));
- }
-
- static void link(SkipBar barA, SkipBar barB) {
- barA.otherBar = barB;
- barB.otherBar = barA;
- }
-
- private void clearMarkerAndWidget() {
- textMarker.clear();
- lineWidget.clear();
- }
-
- void expandBefore(int cnt) {
- expandSideBefore(cnt);
-
- if (otherBar != null) {
- otherBar.expandSideBefore(cnt);
- }
- }
-
- private void expandSideBefore(int cnt) {
- FromTo range = textMarker.find();
- int oldStart = range.from().line();
- int newStart = oldStart + cnt;
- int end = range.to().line();
- clearMarkerAndWidget();
- collapse(newStart, end, true);
- updateSelection();
- }
-
- void expandSideAll() {
- clearMarkerAndWidget();
- removeFromParent();
- }
-
- private void expandAfter() {
- FromTo range = textMarker.find();
- int start = range.from().line();
- int oldEnd = range.to().line();
- int newEnd = oldEnd - NUM_ROWS_TO_EXPAND;
- boolean attach = start == 0;
- if (attach) {
- clearMarkerAndWidget();
- } else {
- textMarker.clear();
- }
- collapse(start, newEnd, attach);
- updateSelection();
- }
-
- private void updateSelection() {
- if (cm.somethingSelected()) {
- FromTo sel = cm.getSelectedRange();
- cm.setSelection(sel.from(), sel.to());
- }
- }
-
- @UiHandler("skipNum")
- void onExpandAll(@SuppressWarnings("unused") ClickEvent e) {
- expandAll();
- updateSelection();
- if (otherBar != null) {
- otherBar.expandAll();
- otherBar.updateSelection();
- }
- cm.refresh();
- cm.focus();
- }
-
- private void expandAll() {
- expandSideAll();
- if (otherBar != null) {
- otherBar.expandSideAll();
- }
- manager.remove(this, otherBar);
- }
-
- @UiHandler("upArrow")
- void onExpandBefore(@SuppressWarnings("unused") ClickEvent e) {
- expandBefore(NUM_ROWS_TO_EXPAND);
- if (otherBar != null) {
- otherBar.expandBefore(NUM_ROWS_TO_EXPAND);
- }
- cm.refresh();
- cm.focus();
- }
-
- @UiHandler("downArrow")
- void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
- expandAfter();
-
- if (otherBar != null) {
- otherBar.expandAfter();
- }
- cm.refresh();
- cm.focus();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml
deleted file mode 100644
index bf3c425c03..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipBar.ui.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:style gss='false' type='com.google.gerrit.client.diff.SkipBar.SkipBarStyle'>
- .skipBar {
- background-color: #def;
- height: 1.3em;
- overflow: hidden;
- }
- .text {
- display: table;
- margin: 0 auto;
- color: #777;
- font-style: italic;
- overflow: hidden;
- }
- .anchor {
- color: inherit;
- text-decoration: none;
- }
- .noExpand .arrow {
- display: none;
- }
- .arrow {
- font-family: Arial Unicode MS, sans-serif;
- }
- </ui:style>
- <g:HTMLPanel addStyleNames='{style.skipBar}'>
- <div class='{style.text}'>
- <ui:msg>
- <g:Anchor ui:field='upArrow' addStyleNames='{style.arrow} {style.anchor}' />
- <g:Anchor ui:field='skipNum' addStyleNames='{style.anchor}' />
- <g:Anchor ui:field='downArrow' addStyleNames=' {style.arrow} {style.anchor}' />
- </ui:msg>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder> \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
deleted file mode 100644
index 533ba1fed5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SkipManager.java
+++ /dev/null
@@ -1,149 +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.client.diff;
-
-import com.google.gerrit.client.diff.DiffInfo.Region;
-import com.google.gerrit.client.patches.SkippedLine;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gwt.core.client.JsArray;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import net.codemirror.lib.CodeMirror;
-
-/** Collapses common regions with {@link SkipBar} for {@link SideBySide} and {@link Unified}. */
-class SkipManager {
- private final Set<SkipBar> skipBars;
- private final DiffScreen host;
- private SkipBar line0;
-
- SkipManager(DiffScreen host) {
- this.host = host;
- this.skipBars = new HashSet<>();
- }
-
- void render(int context, DiffInfo diff) {
- if (context == DiffPreferencesInfo.WHOLE_FILE_CONTEXT) {
- return;
- }
-
- List<SkippedLine> skips = new ArrayList<>();
- int lineA = 0;
- int lineB = 0;
- JsArray<Region> regions = diff.content();
- for (int i = 0; i < regions.length(); i++) {
- Region current = regions.get(i);
- if (current.ab() != null || current.common() || current.skip() > 0) {
- int len =
- current.skip() > 0
- ? current.skip()
- : (current.ab() != null ? current.ab() : current.b()).length();
- if (i == 0 && len > context + 1) {
- skips.add(new SkippedLine(0, 0, len - context));
- } else if (i == regions.length() - 1 && len > context + 1) {
- skips.add(new SkippedLine(lineA + context, lineB + context, len - context));
- } else if (len > 2 * context + 1) {
- skips.add(new SkippedLine(lineA + context, lineB + context, len - 2 * context));
- }
- lineA += len;
- lineB += len;
- } else {
- lineA += current.a() != null ? current.a().length() : 0;
- lineB += current.b() != null ? current.b().length() : 0;
- }
- }
- skips = host.getCommentManager().splitSkips(context, skips);
- renderSkips(skips, lineA, lineB);
- }
-
- private void renderSkips(List<SkippedLine> skips, int lineA, int lineB) {
- if (!skips.isEmpty()) {
- boolean isSideBySide = host.isSideBySide();
- CodeMirror cmA = null;
- if (isSideBySide) {
- cmA = host.getCmFromSide(DisplaySide.A);
- }
- CodeMirror cmB = host.getCmFromSide(DisplaySide.B);
-
- for (SkippedLine skip : skips) {
- SkipBar barA = null;
- SkipBar barB = newSkipBar(cmB, DisplaySide.B, skip);
- skipBars.add(barB);
- if (isSideBySide) {
- barA = newSkipBar(cmA, DisplaySide.A, skip);
- SkipBar.link(barA, barB);
- skipBars.add(barA);
- }
-
- if (skip.getStartA() == 0 || skip.getStartB() == 0) {
- if (isSideBySide) {
- barA.upArrow.setVisible(false);
- }
- barB.upArrow.setVisible(false);
- setLine0(barB);
- } else if (skip.getStartA() + skip.getSize() == lineA
- || skip.getStartB() + skip.getSize() == lineB) {
- if (isSideBySide) {
- barA.downArrow.setVisible(false);
- }
- barB.downArrow.setVisible(false);
- }
- }
- }
- }
-
- private SkipBar newSkipBar(CodeMirror cm, DisplaySide side, SkippedLine skip) {
- int start = host.getCmLine(side == DisplaySide.A ? skip.getStartA() : skip.getStartB(), side);
- int end = start + skip.getSize() - 1;
-
- SkipBar bar = new SkipBar(this, cm);
- host.getDiffTable().add(bar);
- bar.collapse(start, end, true);
- return bar;
- }
-
- void ensureFirstLineIsVisible() {
- if (line0 != null) {
- line0.expandBefore(1);
- line0 = null;
- }
- }
-
- void removeAll() {
- if (!skipBars.isEmpty()) {
- for (SkipBar bar : skipBars) {
- bar.expandSideAll();
- }
- line0 = null;
- }
- }
-
- void remove(SkipBar a, SkipBar b) {
- skipBars.remove(a);
- skipBars.remove(b);
- if (getLine0() == a || getLine0() == b) {
- setLine0(null);
- }
- }
-
- SkipBar getLine0() {
- return line0;
- }
-
- void setLine0(SkipBar bar) {
- line0 = bar;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java
deleted file mode 100644
index 7bd980415e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.java
+++ /dev/null
@@ -1,383 +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.client.diff;
-
-import static java.lang.Double.POSITIVE_INFINITY;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.diff.UnifiedChunkManager.LineRegionInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.projects.ConfigInfoCache;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwt.user.client.ui.InlineHTML;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import java.util.Collections;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineHandle;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.ScrollInfo;
-
-public class Unified extends DiffScreen {
- interface Binder extends UiBinder<FlowPanel, Unified> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- @UiField(provided = true)
- UnifiedTable diffTable;
-
- private CodeMirror cm;
-
- private UnifiedChunkManager chunkManager;
- private UnifiedCommentManager commentManager;
-
- private boolean autoHideDiffTableHeader;
-
- public Unified(
- @Nullable Project.NameKey project,
- DiffObject base,
- DiffObject revision,
- String path,
- DisplaySide startSide,
- int startLine) {
- super(project, base, revision, path, startSide, startLine, DiffView.UNIFIED_DIFF);
-
- diffTable = new UnifiedTable(this, base, revision, path);
- add(uiBinder.createAndBindUi(this));
- addDomHandler(GlobalKey.STOP_PROPAGATION, KeyPressEvent.getType());
- }
-
- @Override
- ScreenLoadCallback<ConfigInfoCache.Entry> getScreenLoadCallback(
- final CommentsCollections comments) {
- return new ScreenLoadCallback<ConfigInfoCache.Entry>(Unified.this) {
- @Override
- protected void preDisplay(ConfigInfoCache.Entry result) {
- commentManager =
- new UnifiedCommentManager(
- Unified.this,
- getProject(),
- base,
- revision,
- path,
- result.getCommentLinkProcessor(),
- getChangeStatus().isOpen());
- setTheme(result.getTheme());
- display(comments);
- header.setupPrevNextFiles(comments);
- }
- };
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
-
- operation(
- () -> {
- resizeCodeMirror();
- cm.refresh();
- });
- setLineLength(Patch.COMMIT_MSG.equals(path) ? 72 : prefs.lineLength());
- diffTable.refresh();
-
- if (getStartLine() == 0) {
- DiffChunkInfo d = chunkManager.getFirst();
- if (d != null) {
- if (d.isEdit() && d.getSide() == DisplaySide.A) {
- setStartSide(DisplaySide.B);
- } else {
- setStartSide(d.getSide());
- }
- setStartLine(chunkManager.getCmLine(d.getStart(), d.getSide()) + 1);
- }
- }
- if (getStartSide() != null && getStartLine() > 0) {
- cm.scrollToLine(chunkManager.getCmLine(getStartLine() - 1, getStartSide()));
- cm.focus();
- } else {
- cm.setCursor(Pos.create(0));
- cm.focus();
- }
- if (Gerrit.isSignedIn() && prefs.autoReview()) {
- header.autoReview();
- }
- prefetchNextFile();
- }
-
- @Override
- void registerCmEvents(CodeMirror cm) {
- super.registerCmEvents(cm);
-
- cm.on(
- "scroll",
- () -> {
- ScrollInfo si = cm.getScrollInfo();
- if (autoHideDiffTableHeader) {
- updateDiffTableHeader(si);
- }
- });
- maybeRegisterRenderEntireFileKeyMap(cm);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
-
- registerHandlers();
- }
-
- @Override
- FocusHandler getFocusHandler() {
- return new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- cm.focus();
- }
- };
- }
-
- private void display(CommentsCollections comments) {
- DiffInfo diff = getDiff();
- setThemeStyles(prefs.theme().isDark());
- setShowIntraline(prefs.intralineDifference());
- if (prefs.showLineNumbers()) {
- diffTable.addStyleName(Resources.I.diffTableStyle().showLineNumbers());
- }
-
- cm =
- newCm(diff.metaA() == null ? diff.metaB() : diff.metaA(), diff.textUnified(), diffTable.cm);
- setShowTabs(prefs.showTabs());
-
- chunkManager = new UnifiedChunkManager(this, cm, diffTable.scrollbar);
-
- operation(
- () -> {
- // Estimate initial CodeMirror height, fixed up in onShowView.
- int height = Window.getClientHeight() - (Gerrit.getHeaderFooterHeight() + 18);
- cm.setHeight(height);
-
- render(diff);
- commentManager.render(comments, prefs.expandAllComments());
- skipManager.render(prefs.context(), diff);
- });
-
- registerCmEvents(cm);
-
- setPrefsAction(new PreferencesAction(this, prefs));
- header.init(getPrefsAction(), getSideBySideDiffLink(), diff.unifiedWebLinks());
- setAutoHideDiffHeader(prefs.autoHideDiffTableHeader());
-
- setupSyntaxHighlighting();
- }
-
- private List<InlineHyperlink> getSideBySideDiffLink() {
- InlineHyperlink toSideBySideDiffLink = new InlineHyperlink();
- toSideBySideDiffLink.setHTML(
- new ImageResourceRenderer().render(Gerrit.RESOURCES.sideBySideDiff()));
- toSideBySideDiffLink.setTargetHistoryToken(
- Dispatcher.toSideBySide(getProject(), base, revision, path));
- toSideBySideDiffLink.setTitle(PatchUtil.C.sideBySideDiff());
- return Collections.singletonList(toSideBySideDiffLink);
- }
-
- @Override
- CodeMirror newCm(DiffInfo.FileMeta meta, String contents, Element parent) {
- JsArrayString gutters = JavaScriptObject.createArray().cast();
- gutters.push(UnifiedTable.style.lineNumbersLeft());
- gutters.push(UnifiedTable.style.lineNumbersRight());
-
- return CodeMirror.create(
- parent,
- Configuration.create()
- .set("cursorBlinkRate", prefs.cursorBlinkRate())
- .set("cursorHeight", 0.85)
- .set("gutters", gutters)
- .set("inputStyle", "textarea")
- .set("keyMap", "vim_ro")
- .set("lineNumbers", false)
- .set("lineWrapping", prefs.lineWrapping())
- .set("matchBrackets", prefs.matchBrackets())
- .set("mode", getFileSize() == FileSize.SMALL ? getContentType(meta) : null)
- .set("readOnly", true)
- .set("scrollbarStyle", "overlay")
- .set("styleSelectedText", true)
- .set("showTrailingSpace", prefs.showWhitespaceErrors())
- .set("tabSize", prefs.tabSize())
- .set("theme", prefs.theme().name().toLowerCase())
- .set("value", meta != null ? contents : "")
- .set("viewportMargin", renderEntireFile() ? POSITIVE_INFINITY : 10));
- }
-
- @Override
- void setShowLineNumbers(boolean b) {
- super.setShowLineNumbers(b);
-
- cm.refresh();
- }
-
- private void setLineNumber(DisplaySide side, int cmLine, Integer line, String styleName) {
- SafeHtml html = SafeHtml.asis(line != null ? line.toString() : "&nbsp;");
- InlineHTML gutter = new InlineHTML(html);
- diffTable.add(gutter);
- gutter.setStyleName(styleName);
- cm.setGutterMarker(
- cmLine,
- side == DisplaySide.A
- ? UnifiedTable.style.lineNumbersLeft()
- : UnifiedTable.style.lineNumbersRight(),
- gutter.getElement());
- }
-
- void setLineNumber(DisplaySide side, int cmLine, int line) {
- setLineNumber(side, cmLine, line, UnifiedTable.style.unifiedLineNumber());
- }
-
- void setLineNumberEmpty(DisplaySide side, int cmLine) {
- setLineNumber(side, cmLine, null, UnifiedTable.style.unifiedLineNumberEmpty());
- }
-
- @Override
- void setSyntaxHighlighting(boolean b) {
- final DiffInfo diff = getDiff();
- if (b) {
- injectMode(
- diff,
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- if (prefs.syntaxHighlighting()) {
- cm.setOption(
- "mode", getContentType(diff.metaA() == null ? diff.metaB() : diff.metaA()));
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- prefs.syntaxHighlighting(false);
- }
- });
- } else {
- cm.setOption("mode", (String) null);
- }
- }
-
- @Override
- void setAutoHideDiffHeader(boolean autoHide) {
- if (autoHide) {
- updateDiffTableHeader(cm.getScrollInfo());
- } else {
- diffTable.setHeaderVisible(true);
- }
- autoHideDiffTableHeader = autoHide;
- }
-
- private void updateDiffTableHeader(ScrollInfo si) {
- if (si.top() == 0) {
- diffTable.setHeaderVisible(true);
- } else if (si.top() > 0.5 * si.clientHeight()) {
- diffTable.setHeaderVisible(false);
- }
- }
-
- @Override
- Runnable updateActiveLine(CodeMirror cm) {
- return () -> {
- // The rendering of active lines has to be deferred. Reflow
- // caused by adding and removing styles chokes Firefox when arrow
- // key (or j/k) is held down. Performance on Chrome is fine
- // without the deferral.
- //
- Scheduler.get()
- .scheduleDeferred(
- () -> {
- LineHandle handle = cm.getLineHandleVisualStart(cm.getCursor("end").line());
- cm.extras().activeLine(handle);
- });
- };
- }
-
- @Override
- CodeMirror getCmFromSide(DisplaySide side) {
- return cm;
- }
-
- @Override
- int getCmLine(int line, DisplaySide side) {
- return chunkManager.getCmLine(line, side);
- }
-
- LineRegionInfo getLineRegionInfoFromCmLine(int cmLine) {
- return chunkManager.getLineRegionInfoFromCmLine(cmLine);
- }
-
- @Override
- void operation(Runnable apply) {
- cm.operation(apply::run);
- }
-
- @Override
- CodeMirror[] getCms() {
- return new CodeMirror[] {cm};
- }
-
- @Override
- UnifiedTable getDiffTable() {
- return diffTable;
- }
-
- @Override
- UnifiedChunkManager getChunkManager() {
- return chunkManager;
- }
-
- @Override
- UnifiedCommentManager getCommentManager() {
- return commentManager;
- }
-
- @Override
- boolean isSideBySide() {
- return false;
- }
-
- @Override
- String getLineNumberClassName() {
- return UnifiedTable.style.unifiedLineNumber();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.ui.xml
deleted file mode 100644
index 85f46a6610..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/Unified.ui.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:d='urn:import:com.google.gerrit.client.diff'>
- <ui:style>
- .unified {
- margin-left: -5px;
- margin-right: -5px;
- }
- </ui:style>
- <g:FlowPanel styleName='{style.unified}'>
- <d:Header ui:field='header'/>
- <d:UnifiedTable ui:field='diffTable'/>
- </g:FlowPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
deleted file mode 100644
index 98ad023c9d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
+++ /dev/null
@@ -1,330 +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.client.diff;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.diff.DiffInfo.Region;
-import com.google.gerrit.client.diff.DiffInfo.Span;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.EventListener;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineClassWhere;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.Pos;
-
-/** Colors modified regions for {@link Unified}. */
-class UnifiedChunkManager extends ChunkManager {
- private static final JavaScriptObject focus = initOnClick();
-
- private static native JavaScriptObject initOnClick() /*-{
- return $entry(function(e){
- @com.google.gerrit.client.diff.UnifiedChunkManager::focus(
- Lcom/google/gwt/dom/client/NativeEvent;)(e)
- });
- }-*/;
-
- private List<UnifiedDiffChunkInfo> chunks;
-
- @Override
- DiffChunkInfo getFirst() {
- return !chunks.isEmpty() ? chunks.get(0) : null;
- }
-
- private static void focus(NativeEvent event) {
- Element e = Element.as(event.getEventTarget());
- for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) {
- EventListener l = DOM.getEventListener(e);
- if (l instanceof Unified) {
- ((Unified) l).getCmFromSide(DisplaySide.A).focus();
- event.stopPropagation();
- }
- }
- }
-
- static void focusOnClick(Element e) {
- onClick(e, focus);
- }
-
- private final Unified host;
- private final CodeMirror cm;
-
- UnifiedChunkManager(Unified host, CodeMirror cm, Scrollbar scrollbar) {
- super(scrollbar);
-
- this.host = host;
- this.cm = cm;
- }
-
- @Override
- void render(DiffInfo diff) {
- super.render();
-
- chunks = new ArrayList<>();
-
- int cmLine = 0;
- boolean useIntralineBg = diff.metaA() == null || diff.metaB() == null;
-
- for (Region current : Natives.asList(diff.content())) {
- int origLineA = lineMapper.getLineA();
- int origLineB = lineMapper.getLineB();
- if (current.ab() != null) {
- int length = current.ab().length();
- lineMapper.appendCommon(length);
- for (int i = 0; i < length; i++) {
- host.setLineNumber(DisplaySide.A, cmLine + i, origLineA + i + 1);
- host.setLineNumber(DisplaySide.B, cmLine + i, origLineB + i + 1);
- }
- cmLine += length;
- } else if (current.skip() > 0) {
- lineMapper.appendCommon(current.skip());
- cmLine += current.skip(); // Maybe current.ab().length();
- } else if (current.common()) {
- lineMapper.appendCommon(current.b().length());
- cmLine += current.b().length();
- } else {
- cmLine += render(current, cmLine, useIntralineBg);
- }
- }
- host.setLineNumber(DisplaySide.A, cmLine, lineMapper.getLineA() + 1);
- host.setLineNumber(DisplaySide.B, cmLine, lineMapper.getLineB() + 1);
- }
-
- private int render(Region region, int cmLine, boolean useIntralineBg) {
- int startA = lineMapper.getLineA();
- int startB = lineMapper.getLineB();
-
- JsArrayString a = region.a();
- JsArrayString b = region.b();
- int aLen = a != null ? a.length() : 0;
- int bLen = b != null ? b.length() : 0;
- boolean insertOrDelete = a == null || b == null;
-
- colorLines(
- cm,
- insertOrDelete && !useIntralineBg
- ? UnifiedTable.style.diffDelete()
- : UnifiedTable.style.intralineDelete(),
- cmLine,
- aLen);
- colorLines(
- cm,
- insertOrDelete && !useIntralineBg
- ? UnifiedTable.style.diffInsert()
- : UnifiedTable.style.intralineInsert(),
- cmLine + aLen,
- bLen);
- markEdit(DisplaySide.A, cmLine, a, region.editA());
- markEdit(DisplaySide.B, cmLine + aLen, b, region.editB());
- addGutterTag(region, cmLine); // TODO: verify addGutterTag
- lineMapper.appendReplace(aLen, bLen);
-
- int endA = lineMapper.getLineA() - 1;
- int endB = lineMapper.getLineB() - 1;
- if (aLen > 0) {
- addDiffChunk(DisplaySide.A, endA, aLen, cmLine, bLen > 0);
- for (int j = 0; j < aLen; j++) {
- host.setLineNumber(DisplaySide.A, cmLine + j, startA + j + 1);
- host.setLineNumberEmpty(DisplaySide.B, cmLine + j);
- }
- }
- if (bLen > 0) {
- addDiffChunk(DisplaySide.B, endB, bLen, cmLine + aLen, aLen > 0);
- for (int j = 0; j < bLen; j++) {
- host.setLineNumberEmpty(DisplaySide.A, cmLine + aLen + j);
- host.setLineNumber(DisplaySide.B, cmLine + aLen + j, startB + j + 1);
- }
- }
- return aLen + bLen;
- }
-
- private void addGutterTag(Region region, int cmLine) {
- if (region.a() == null) {
- scrollbar.insert(cm, cmLine, region.b().length());
- } else if (region.b() == null) {
- scrollbar.delete(cm, cm, cmLine, region.a().length());
- } else {
- scrollbar.edit(cm, cmLine, region.b().length());
- }
- }
-
- private void markEdit(DisplaySide side, int startLine, JsArrayString lines, JsArray<Span> edits) {
- if (lines == null || edits == null) {
- return;
- }
-
- EditIterator iter = new EditIterator(lines, startLine);
- Configuration bg =
- Configuration.create().set("className", getIntralineBgFromSide(side)).set("readOnly", true);
-
- Configuration diff =
- Configuration.create().set("className", getDiffColorFromSide(side)).set("readOnly", true);
-
- Pos last = Pos.create(0, 0);
- for (Span span : Natives.asList(edits)) {
- Pos from = iter.advance(span.skip());
- Pos to = iter.advance(span.mark());
- if (from.line() == last.line()) {
- getMarkers().add(cm.markText(last, from, bg));
- } else {
- getMarkers().add(cm.markText(Pos.create(from.line(), 0), from, bg));
- }
- getMarkers().add(cm.markText(from, to, diff));
- last = to;
- colorLines(cm, LineClassWhere.BACKGROUND, getDiffColorFromSide(side), from.line(), to.line());
- }
- }
-
- private String getIntralineBgFromSide(DisplaySide side) {
- return side == DisplaySide.A
- ? UnifiedTable.style.intralineDelete()
- : UnifiedTable.style.intralineInsert();
- }
-
- private String getDiffColorFromSide(DisplaySide side) {
- return side == DisplaySide.A
- ? UnifiedTable.style.diffDelete()
- : UnifiedTable.style.diffInsert();
- }
-
- private void addDiffChunk(
- DisplaySide side, int chunkEnd, int chunkSize, int cmLine, boolean edit) {
- chunks.add(new UnifiedDiffChunkInfo(side, chunkEnd - chunkSize + 1, chunkEnd, cmLine, edit));
- }
-
- @Override
- Runnable diffChunkNav(CodeMirror cm, Direction dir) {
- return () -> {
- int line = cm.extras().hasActiveLine() ? cm.getLineNumber(cm.extras().activeLine()) : 0;
- int res =
- Collections.binarySearch(
- chunks,
- new UnifiedDiffChunkInfo(cm.side(), 0, 0, line, false),
- getDiffChunkComparatorCmLine());
- diffChunkNavHelper(chunks, host, res, dir);
- };
- }
-
- /** Diff chunks are ordered by their starting lines in CodeMirror */
- private Comparator<UnifiedDiffChunkInfo> getDiffChunkComparatorCmLine() {
- return comparing(UnifiedDiffChunkInfo::getCmLine);
- }
-
- @Override
- int getCmLine(int line, DisplaySide side) {
- int res =
- Collections.binarySearch(
- chunks,
- new UnifiedDiffChunkInfo(side, line, 0, 0, false), // Dummy DiffChunkInfo
- getDiffChunkComparator());
- if (res >= 0) {
- return chunks.get(res).getCmLine();
- }
- // The line might be within a DiffChunk
- res = -res - 1;
- if (res > 0) {
- UnifiedDiffChunkInfo info = chunks.get(res - 1);
- if (side == DisplaySide.A && info.isEdit() && info.getSide() == DisplaySide.B) {
- // Need to use the start and cmLine of the deletion chunk
- UnifiedDiffChunkInfo delete = chunks.get(res - 2);
- if (line <= delete.getEnd()) {
- return delete.getCmLine() + line - delete.getStart();
- }
- // Need to add the length of the insertion chunk
- return delete.getCmLine() + line - delete.getStart() + info.getEnd() - info.getStart() + 1;
- } else if (side == info.getSide()) {
- return info.getCmLine() + line - info.getStart();
- } else {
- return info.getCmLine() + lineMapper.lineOnOther(side, line).getLine() - info.getStart();
- }
- }
- return line;
- }
-
- LineRegionInfo getLineRegionInfoFromCmLine(int cmLine) {
- int res =
- Collections.binarySearch(
- chunks,
- new UnifiedDiffChunkInfo(DisplaySide.A, 0, 0, cmLine, false), // Dummy DiffChunkInfo
- getDiffChunkComparatorCmLine());
- if (res >= 0) { // The line is right at the start of a diff chunk.
- UnifiedDiffChunkInfo info = chunks.get(res);
- return new LineRegionInfo(info.getStart(), displaySideToRegionType(info.getSide()));
- }
- // The line might be within or after a diff chunk.
- res = -res - 1;
- if (res > 0) {
- UnifiedDiffChunkInfo info = chunks.get(res - 1);
- int lineOnInfoSide = info.getStart() + cmLine - info.getCmLine();
- if (lineOnInfoSide > info.getEnd()) { // After a diff chunk
- if (info.getSide() == DisplaySide.A) {
- // For the common region after a deletion chunk, associate the line
- // on side B with a common region.
- return new LineRegionInfo(
- lineMapper.lineOnOther(DisplaySide.A, lineOnInfoSide).getLine(), RegionType.COMMON);
- }
- return new LineRegionInfo(lineOnInfoSide, RegionType.COMMON);
- }
- // Within a diff chunk
- return new LineRegionInfo(lineOnInfoSide, displaySideToRegionType(info.getSide()));
- }
- // The line is before any diff chunk, so it always equals cmLine and
- // belongs to a common region.
- return new LineRegionInfo(cmLine, RegionType.COMMON);
- }
-
- enum RegionType {
- INSERT,
- DELETE,
- COMMON,
- }
-
- private static RegionType displaySideToRegionType(DisplaySide side) {
- return side == DisplaySide.A ? RegionType.DELETE : RegionType.INSERT;
- }
-
- /**
- * Helper class to associate a line in the original file with the type of the region it belongs
- * to.
- *
- * @field line The 0-based line number in the original file. Note that this might be different
- * from the line number shown in CodeMirror.
- * @field type The type of the region the line belongs to. Can be INSERT, DELETE or COMMON.
- */
- static class LineRegionInfo {
- final int line;
- final RegionType type;
-
- LineRegionInfo(int line, RegionType type) {
- this.line = line;
- this.type = type;
- }
-
- DisplaySide getSide() {
- // Always return DisplaySide.B for INSERT or COMMON
- return type == RegionType.DELETE ? DisplaySide.A : DisplaySide.B;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentGroup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentGroup.java
deleted file mode 100644
index 6d5fba3327..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentGroup.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2014 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.client.diff;
-
-import com.google.gwt.user.client.Timer;
-import net.codemirror.lib.CodeMirror;
-
-/**
- * LineWidget attached to a CodeMirror container.
- *
- * <p>When a comment is placed on a line a CommentWidget is created. The group tracks all comment
- * boxes on a line in unified diff view.
- */
-class UnifiedCommentGroup extends CommentGroup {
- UnifiedCommentGroup(UnifiedCommentManager manager, CodeMirror cm, DisplaySide side, int line) {
- super(manager, cm, side, line);
- }
-
- @Override
- void remove(DraftBox box) {
- super.remove(box);
-
- if (0 < getBoxCount()) {
- resize();
- } else {
- detach();
- }
- }
-
- @Override
- void init(DiffTable parent) {
- if (getLineWidget() == null) {
- attach(parent);
- }
- }
-
- @Override
- void handleRedraw() {
- getLineWidget()
- .onRedraw(
- () -> {
- if (canComputeHeight()) {
- if (getResizeTimer() != null) {
- getResizeTimer().cancel();
- setResizeTimer(null);
- }
- reportHeightChange();
- } else if (getResizeTimer() == null) {
- setResizeTimer(
- new Timer() {
- @Override
- public void run() {
- if (canComputeHeight()) {
- cancel();
- setResizeTimer(null);
- reportHeightChange();
- }
- }
- });
- getResizeTimer().scheduleRepeating(5);
- }
- });
- }
-
- @Override
- void resize() {
- if (getLineWidget() != null) {
- reportHeightChange();
- }
- }
-
- private void reportHeightChange() {
- getLineWidget().changed();
- updateSelection();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
deleted file mode 100644
index c92075ff89..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright (C) 2014 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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
-import com.google.gerrit.client.diff.UnifiedChunkManager.LineRegionInfo;
-import com.google.gerrit.client.diff.UnifiedChunkManager.RegionType;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.Pos;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/** Tracks comment widgets for {@link Unified}. */
-class UnifiedCommentManager extends CommentManager {
-
- private final SortedMap<Integer, CommentGroup> mergedMap;
-
- // In Unified, a CodeMirror line can have up to two CommentGroups - one for
- // the base side and one for the revision, so we need to keep track of the
- // duplicates and replace the entries in mergedMap on draft removal.
- private final Map<Integer, CommentGroup> duplicates;
-
- UnifiedCommentManager(
- Unified host,
- @Nullable Project.NameKey project,
- DiffObject base,
- PatchSet.Id revision,
- String path,
- CommentLinkProcessor clp,
- boolean open) {
- super(host, project, base, revision, path, clp, open);
- mergedMap = new TreeMap<>();
- duplicates = new HashMap<>();
- }
-
- @Override
- SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side) {
- return mergedMap;
- }
-
- @Override
- void clearLine(DisplaySide side, int line, CommentGroup group) {
- super.clearLine(side, line, group);
-
- if (mergedMap.get(line) == group) {
- mergedMap.remove(line);
- if (duplicates.containsKey(line)) {
- mergedMap.put(line, duplicates.remove(line));
- }
- }
- }
-
- @Override
- void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int cmLinePlusOne) {
- if (!Gerrit.isSignedIn()) {
- signInCallback(cm).run();
- } else {
- LineRegionInfo info = ((Unified) host).getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
- DisplaySide side =
- gutterClass.equals(UnifiedTable.style.lineNumbersLeft()) ? DisplaySide.A : DisplaySide.B;
- int line = info.line;
- if (info.getSide() != side) {
- line = host.lineOnOther(info.getSide(), line).getLine();
- }
- insertNewDraft(side, line + 1);
- }
- }
-
- @Override
- CommentGroup getCommentGroupOnActiveLine(CodeMirror cm) {
- CommentGroup group = null;
- if (cm.extras().hasActiveLine()) {
- int cmLinePlusOne = cm.getLineNumber(cm.extras().activeLine()) + 1;
- LineRegionInfo info = ((Unified) host).getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
- CommentGroup forSide = map(info.getSide()).get(cmLinePlusOne);
- group = forSide == null ? map(info.getSide().otherSide()).get(cmLinePlusOne) : forSide;
- }
- return group;
- }
-
- @Override
- Collection<Integer> getLinesWithCommentGroups() {
- return mergedMap.tailMap(1).keySet();
- }
-
- @Override
- String getTokenSuffixForActiveLine(CodeMirror cm) {
- int cmLinePlusOne = cm.getLineNumber(cm.extras().activeLine()) + 1;
- LineRegionInfo info = ((Unified) host).getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
- return (info.getSide() == DisplaySide.A ? "a" : "") + cmLinePlusOne;
- }
-
- @Override
- void newDraft(CodeMirror cm) {
- if (cm.somethingSelected()) {
- FromTo fromTo = adjustSelection(cm);
- Pos from = fromTo.from();
- Pos to = fromTo.to();
- Unified unified = (Unified) host;
- UnifiedChunkManager manager = unified.getChunkManager();
- LineRegionInfo fromInfo = unified.getLineRegionInfoFromCmLine(from.line());
- LineRegionInfo toInfo = unified.getLineRegionInfoFromCmLine(to.line());
- DisplaySide side = toInfo.getSide();
-
- // Handle special cases in selections that span multiple regions. Force
- // start line to be on the same side as the end line.
- if ((fromInfo.type == RegionType.INSERT || fromInfo.type == RegionType.COMMON)
- && toInfo.type == RegionType.DELETE) {
- LineOnOtherInfo infoOnSideA = manager.lineMapper.lineOnOther(DisplaySide.B, fromInfo.line);
- int startLineOnSideA = infoOnSideA.getLine();
- if (infoOnSideA.isAligned()) {
- from.line(startLineOnSideA);
- } else {
- from.line(startLineOnSideA + 1);
- }
- from.ch(0);
- to.line(toInfo.line);
- } else if (fromInfo.type == RegionType.DELETE && toInfo.type == RegionType.INSERT) {
- LineOnOtherInfo infoOnSideB = manager.lineMapper.lineOnOther(DisplaySide.A, fromInfo.line);
- int startLineOnSideB = infoOnSideB.getLine();
- if (infoOnSideB.isAligned()) {
- from.line(startLineOnSideB);
- } else {
- from.line(startLineOnSideB + 1);
- }
- from.ch(0);
- to.line(toInfo.line);
- } else if (fromInfo.type == RegionType.DELETE && toInfo.type == RegionType.COMMON) {
- int toLineOnSideA = manager.lineMapper.lineOnOther(DisplaySide.B, toInfo.line).getLine();
- from.line(fromInfo.line);
- // Force the end line to be on the same side as the start line.
- to.line(toLineOnSideA);
- side = DisplaySide.A;
- } else { // Common case
- from.line(fromInfo.line);
- to.line(toInfo.line);
- }
-
- addDraftBox(
- side,
- CommentInfo.create(
- getPath(),
- getStoredSideFromDisplaySide(side),
- to.line() + 1,
- CommentRange.create(fromTo),
- false))
- .setEdit(true);
- cm.setCursor(Pos.create(host.getCmLine(to.line(), side), to.ch()));
- cm.setSelection(cm.getCursor());
- } else {
- int cmLine = cm.getLineNumber(cm.extras().activeLine());
- LineRegionInfo info = ((Unified) host).getLineRegionInfoFromCmLine(cmLine);
- insertNewDraft(info.getSide(), cmLine + 1);
- }
- }
-
- @Override
- CommentGroup group(DisplaySide side, int cmLinePlusOne) {
- Map<Integer, CommentGroup> map = map(side);
- CommentGroup existing = map.get(cmLinePlusOne);
- if (existing != null) {
- return existing;
- }
-
- UnifiedCommentGroup g =
- new UnifiedCommentGroup(this, host.getCmFromSide(side), side, cmLinePlusOne);
- map.put(cmLinePlusOne, g);
- if (mergedMap.containsKey(cmLinePlusOne)) {
- duplicates.put(cmLinePlusOne, mergedMap.remove(cmLinePlusOne));
- }
- mergedMap.put(cmLinePlusOne, g);
-
- if (isAttached()) {
- g.init(host.getDiffTable());
- g.handleRedraw();
- }
-
- return g;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java
deleted file mode 100644
index dc827cba24..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedDiffChunkInfo.java
+++ /dev/null
@@ -1,29 +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.client.diff;
-
-public class UnifiedDiffChunkInfo extends DiffChunkInfo {
-
- private int cmLine;
-
- UnifiedDiffChunkInfo(DisplaySide side, int start, int end, int cmLine, boolean edit) {
- super(side, start, end, edit);
- this.cmLine = cmLine;
- }
-
- int getCmLine() {
- return cmLine;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.java
deleted file mode 100644
index 2d5df63a10..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.java
+++ /dev/null
@@ -1,84 +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.client.diff;
-
-import com.google.gerrit.client.DiffObject;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.ui.HTMLPanel;
-
-/**
- * A table with one row and one column to hold a unified CodeMirror displaying the files to be
- * compared.
- */
-class UnifiedTable extends DiffTable {
- interface Binder extends UiBinder<HTMLPanel, UnifiedTable> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface DiffTableStyle extends CssResource {
- String intralineInsert();
-
- String intralineDelete();
-
- String diffInsert();
-
- String diffDelete();
-
- String unifiedLineNumber();
-
- String unifiedLineNumberEmpty();
-
- String lineNumbersLeft();
-
- String lineNumbersRight();
- }
-
- private Unified parent;
- @UiField Element cm;
- @UiField static DiffTableStyle style;
-
- UnifiedTable(Unified parent, DiffObject base, DiffObject revision, String path) {
- super(parent, base, revision, path);
-
- initWidget(uiBinder.createAndBindUi(this));
- this.parent = parent;
- }
-
- @Override
- void setHideEmptyPane(boolean hide) {}
-
- @Override
- boolean isVisibleA() {
- return true;
- }
-
- @Override
- Unified getDiffScreen() {
- return parent;
- }
-
- @Override
- int getHeaderHeight() {
- int h = patchSetSelectBoxA.getOffsetHeight() + patchSetSelectBoxB.getOffsetHeight();
- if (hasHeader()) {
- h += diffHeaderRow.getOffsetHeight();
- }
- return h;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.ui.xml
deleted file mode 100644
index c2cefe499e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedTable.ui.xml
+++ /dev/null
@@ -1,152 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:d='urn:import:com.google.gerrit.client.diff'>
- <ui:with field='res' type='com.google.gerrit.client.diff.Resources'/>
- <ui:style gss='false' type='com.google.gerrit.client.diff.UnifiedTable.DiffTableStyle'>
- @external .CodeMirror, .CodeMirror-selectedtext;
- @external .CodeMirror-vscrollbar .CodeMirror-scroll;
- @external .CodeMirror-dialog-bottom;
- @external .CodeMirror-cursor;
-
- @external .dark, .unifiedLineNumber, .noIntraline, .showLineNumbers;
-
- .difftable .patchSetNav,
- .difftable .CodeMirror {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- }
-
- .difftable .CodeMirror pre {
- overflow: visible;
- border-right: 0;
- width: auto;
- }
-
- /* Preserve space for underscores. If this changes
- * see ChunkManager.addPadding() and adjust there.
- */
- .difftable .CodeMirror pre,
- .difftable .CodeMirror pre span {
- padding-bottom: 1px;
- }
- .table {
- width: 100%;
- table-layout: fixed;
- border-spacing: 0;
- }
- .table td { padding: 0 }
-
- /* Hide scrollbars. */
- .difftable .CodeMirror-scroll { padding-right: 0; }
- .difftable .CodeMirror-vscrollbar { display: none !important; }
-
- .diffDelete { background-color: #faa; }
- .diffInsert { background-color: #9f9; }
- .intralineDelete { background-color: #fee; }
- .intralineInsert { background-color: #dfd; }
- .noIntraline .intralineDelete { background-color: #faa; }
- .noIntraline .intralineInsert { background-color: #9f9; }
-
- .dark .diffDelete { background-color: #400; }
- .dark .diffInsert { background-color: #444; }
- .dark .intralineDelete { background-color: #888; }
- .dark .intralineInsert { background-color: #bbb; }
- .dark .noIntraline .intralineDelete { background-color: #400; }
- .dark .noIntraline .intralineInsert { background-color: #444; }
-
- .patchSetNav, .diff_header {
- background-color: #f7f7f7;
- line-height: 1;
- }
-
- .difftable .CodeMirror-selectedtext {
- background-color: inherit !important;
- }
- .difftable .CodeMirror div.CodeMirror-cursor {
- border-left: 2px solid black;
- }
- .difftable .CodeMirror-dialog-bottom {
- border-top: 0;
- border-left: 1px solid #000;
- border-bottom: 1px solid #000;
- background-color: #f7f7f7;
- top: 0;
- right: 0;
- bottom: auto;
- left: auto;
- }
- .showLineNumbers .lineNumbersLeft, .showLineNumbers .lineNumbersRight {
- min-width: 20px;
- width: 3em; /* TODO: This needs to be set based on number of lines */
- }
- .showLineNumbers .lineNumbersLeft {
- border-right: 1px solid #ddd;
- }
- .unifiedLineNumber {
- display: none;
- }
- .showLineNumbers .unifiedLineNumber {
- display: block;
- cursor: pointer;
- padding: 0 3px 0 5px;
- min-width: 20px;
- text-align: right;
- color: #999;
- }
- .unifiedLineNumberEmpty {
- display: none;
- }
- .showLineNumbers .unifiedLineNumberEmpty {
- display: block;
- margin-left: 28px;
- border-left: 2px solid #d64040;
- padding-bottom: 1px;
- }
- </ui:style>
- <g:HTMLPanel styleName='{style.difftable}'>
- <table class='{style.table}'>
- <tr ui:field='patchSetNavRow' class='{style.patchSetNav}'>
- <td>
- <table class='{style.table}'>
- <tr>
- <td ui:field='patchSetNavCellA'>
- <d:PatchSetSelectBox ui:field='patchSetSelectBoxA' />
- </td>
- </tr>
- <tr>
- <td ui:field='patchSetNavCellB'>
- <d:PatchSetSelectBox ui:field='patchSetSelectBoxB' />
- </td>
- </tr>
- </table>
- </td>
- </tr>
- <tr ui:field='diffHeaderRow' class='{res.diffTableStyle.diffHeader}'>
- <td><pre ui:field='diffHeaderText' /></td>
- </tr>
- <tr>
- <td ui:field='cm'/>
- </tr>
- </table>
- <g:FlowPanel ui:field='widgets' visible='false'/>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand.java
deleted file mode 100644
index 50ef0d71a2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UpToChangeCommand.java
+++ /dev/null
@@ -1,40 +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.client.diff;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-
-class UpToChangeCommand extends KeyCommand {
- private final PatchSet.Id revision;
- @Nullable private final Project.NameKey project;
-
- UpToChangeCommand(@Nullable Project.NameKey project, PatchSet.Id revision, int mask, int key) {
- super(mask, key, PatchUtil.C.upToChange());
- this.revision = revision;
- this.project = project;
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- Gerrit.display(PageLinks.toChange(project, revision.getParentKey(), revision.getId()));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goNext.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goNext.png
deleted file mode 100644
index 872c1979e0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goNext.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goPrev.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goPrev.png
deleted file mode 100644
index d68f29b34c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goPrev.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goUp.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goUp.png
deleted file mode 100644
index f75bed4478..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/goUp.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java
deleted file mode 100644
index 2958783705..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.java
+++ /dev/null
@@ -1,27 +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.client.documentation;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface DocConstants extends Constants {
- String keyReloadSearch();
-
- String docItemHelp();
-
- String docTableColumnTitle();
-
- String docTableNone();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties
deleted file mode 100644
index b48c507cd7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocConstants.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-keyReloadSearch = Reload documentation list
-
-docItemHelp = documentation
-docTableColumnTitle = Title
-docTableNone = (None)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java
deleted file mode 100644
index 5fcb6b059b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocInfo.java
+++ /dev/null
@@ -1,35 +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.client.documentation;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class DocInfo extends JavaScriptObject {
-
- public final native String title() /*-{ return this.title; }-*/;
-
- public final native String url() /*-{ return this.url; }-*/;
-
- public static DocInfo create() {
- return (DocInfo) createObject();
- }
-
- protected DocInfo() {}
-
- public final String getFullUrl() {
- return GWT.getHostPageBaseURL() + url();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java
deleted file mode 100644
index 7d76f7bca2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.java
+++ /dev/null
@@ -1,23 +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.client.documentation;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface DocMessages extends Messages {
- String docQueryWindowTitle(String query);
-
- String docQueryPageTitle(String query);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties
deleted file mode 100644
index 8810a4a509..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocMessages.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-docQueryWindowTitle = {0}
-docQueryPageTitle = Search for {0} in documentation
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java
deleted file mode 100644
index 0a87d29875..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocScreen.java
+++ /dev/null
@@ -1,78 +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.client.documentation;
-
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.KeyUtil;
-
-public class DocScreen extends Screen {
- private static final String URI = "/Documentation/";
-
- private DocTable table;
- private final String query;
-
- public DocScreen(String query) {
- this.query = KeyUtil.decode(query);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- table = new DocTable();
- table.setSavePointerId(query);
- add(table);
-
- setWindowTitle(Util.M.docQueryWindowTitle(query));
- setPageTitle(Util.M.docQueryPageTitle(query));
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- doQuery();
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-
- private AsyncCallback<JsArray<DocInfo>> loadCallback() {
- return new GerritCallback<JsArray<DocInfo>>() {
- @Override
- public void onSuccess(JsArray<DocInfo> result) {
- displayResults(result);
- display();
- }
- };
- }
-
- private void displayResults(JsArray<DocInfo> result) {
- table.display(result);
- table.finishDisplay();
- }
-
- private void doQuery() {
- RestApi call = new RestApi(URI);
- call.addParameterRaw("q", KeyUtil.encode(query));
- call.get(loadCallback());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java
deleted file mode 100644
index 677c2bf388..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/DocTable.java
+++ /dev/null
@@ -1,126 +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.client.documentation;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-
-class DocTable extends NavigationTable<DocInfo> {
- private static final int C_TITLE = 1;
-
- private int rows;
- private int dataBeginRow;
-
- DocTable() {
- super(Util.C.docItemHelp());
-
- table.setText(0, C_TITLE, Util.C.docTableColumnTitle());
-
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, C_TITLE, Gerrit.RESOURCES.css().dataHeader());
-
- table.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- Cell cell = table.getCellForEvent(event);
- if (cell == null) {
- return;
- }
- if (getRowItem(cell.getRowIndex()) != null) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
- }
-
- @Override
- protected Object getRowItemKey(DocInfo item) {
- return item.url();
- }
-
- @Override
- protected void onOpenRow(int row) {
- DocInfo d = getRowItem(row);
- Window.Location.assign(d.getFullUrl());
- }
-
- private void insertNoneRow(int row) {
- table.insertRow(row);
- table.setText(row, 0, Util.C.docTableNone());
- FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setStyleName(row, 0, Gerrit.RESOURCES.css().emptySection());
- }
-
- private void insertDocRow(int row) {
- table.insertRow(row);
- applyDataRowStyle(row);
- }
-
- @Override
- protected void applyDataRowStyle(int row) {
- super.applyDataRowStyle(row);
- CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(row, C_TITLE, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, C_TITLE, Gerrit.RESOURCES.css().cSUBJECT());
- }
-
- private void populateDocRow(int row, DocInfo d) {
- table.setWidget(row, C_TITLE, new DocLink(d));
- setRowItem(row, d);
- }
-
- public void display(JsArray<DocInfo> docList) {
- int sz = docList != null ? docList.length() : 0;
- boolean hadData = rows > 0;
-
- if (hadData) {
- while (sz < rows) {
- table.removeRow(dataBeginRow);
- rows--;
- }
- } else {
- table.removeRow(dataBeginRow);
- }
-
- if (sz == 0) {
- insertNoneRow(dataBeginRow);
- return;
- }
-
- while (rows < sz) {
- insertDocRow(dataBeginRow + rows);
- rows++;
- }
- for (int i = 0; i < sz; i++) {
- populateDocRow(dataBeginRow + i, docList.get(i));
- }
- }
-
- public static class DocLink extends Anchor {
- DocLink(DocInfo d) {
- super(com.google.gerrit.client.changes.Util.cropSubject(d.title()));
- setHref(d.getFullUrl());
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java
deleted file mode 100644
index 273ead8c6d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/documentation/Util.java
+++ /dev/null
@@ -1,22 +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.client.documentation;
-
-import com.google.gwt.core.client.GWT;
-
-public class Util {
- public static final DocConstants C = GWT.create(DocConstants.class);
- public static final DocMessages M = GWT.create(DocMessages.class);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
deleted file mode 100644
index 2b09175e52..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandLink.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2010 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.client.download;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.DownloadInfo.DownloadCommandInfo;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-
-public class DownloadCommandLink extends Anchor implements ClickHandler {
- private final CopyableLabel copyLabel;
- private final String command;
-
- public DownloadCommandLink(CopyableLabel copyLabel, DownloadCommandInfo commandInfo) {
- super(commandInfo.name());
- this.copyLabel = copyLabel;
- this.command = commandInfo.command();
-
- setStyleName(Gerrit.RESOURCES.css().downloadLink());
- Roles.getTabRole().set(getElement());
- addClickHandler(this);
- }
-
- @Override
- public void onClick(ClickEvent event) {
- event.preventDefault();
- event.stopPropagation();
-
- select();
- }
-
- void select() {
- copyLabel.setText(command);
-
- DownloadCommandPanel parent = (DownloadCommandPanel) getParent();
- for (Widget w : parent) {
- if (w != this && w instanceof DownloadCommandLink) {
- w.removeStyleName(Gerrit.RESOURCES.css().downloadLink_Active());
- }
- }
- parent.setCurrentCommand(this);
- addStyleName(Gerrit.RESOURCES.css().downloadLink_Active());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java
deleted file mode 100644
index 20cf3f3a0c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadCommandPanel.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2010 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.client.download;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-public class DownloadCommandPanel extends FlowPanel {
- private DownloadCommandLink currentCommand;
-
- public DownloadCommandPanel() {
- setStyleName(Gerrit.RESOURCES.css().downloadLinkList());
- Roles.getTablistRole().set(getElement());
- }
-
- public boolean isEmpty() {
- return getWidgetCount() == 0;
- }
-
- public void select() {
- DownloadCommandLink first = null;
-
- for (Widget w : this) {
- if (w instanceof DownloadCommandLink) {
- DownloadCommandLink d = (DownloadCommandLink) w;
- if (currentCommand != null && d.getText().equals(currentCommand.getText())) {
- d.select();
- return;
- }
- if (first == null) {
- first = d;
- }
- }
- }
-
- // If none matched the requested type, select the first in the
- // group as that will at least give us an initial baseline.
- if (first != null) {
- first.select();
- }
- }
-
- void setCurrentCommand(DownloadCommandLink cmd) {
- currentCommand = cmd;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java
deleted file mode 100644
index b881505cb5..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadPanel.java
+++ /dev/null
@@ -1,66 +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.client.download;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.DownloadInfo.DownloadCommandInfo;
-import com.google.gerrit.client.info.DownloadInfo.DownloadSchemeInfo;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import java.util.List;
-
-public abstract class DownloadPanel extends FlowPanel {
- protected final String project;
-
- private final DownloadCommandPanel commands = new DownloadCommandPanel();
- private final DownloadUrlPanel urls = new DownloadUrlPanel();
- private final CopyableLabel copyLabel = new CopyableLabel("");
-
- public DownloadPanel(String project, boolean allowAnonymous) {
- this.project = project;
- copyLabel.setStyleName(Gerrit.RESOURCES.css().downloadLinkCopyLabel());
- urls.add(DownloadUrlLink.createDownloadUrlLinks(allowAnonymous, this));
-
- setupWidgets();
- }
-
- private void setupWidgets() {
- if (!urls.isEmpty()) {
- urls.select(Gerrit.getUserPreferences().downloadScheme());
-
- FlowPanel p = new FlowPanel();
- p.setStyleName(Gerrit.RESOURCES.css().downloadLinkHeader());
- p.add(commands);
- final InlineLabel glue = new InlineLabel();
- glue.setStyleName(Gerrit.RESOURCES.css().downloadLinkHeaderGap());
- p.add(glue);
- p.add(urls);
-
- add(p);
- add(copyLabel);
- }
- }
-
- void populateDownloadCommandLinks(DownloadSchemeInfo schemeInfo) {
- commands.clear();
- for (DownloadCommandInfo cmd : getCommands(schemeInfo)) {
- commands.add(new DownloadCommandLink(copyLabel, cmd));
- }
- commands.select();
- }
-
- protected abstract List<DownloadCommandInfo> getCommands(DownloadSchemeInfo schemeInfo);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java
deleted file mode 100644
index 76e7d7ca25..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlLink.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2010 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.client.download;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.info.DownloadInfo.DownloadSchemeInfo;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Widget;
-import java.util.ArrayList;
-import java.util.List;
-
-public class DownloadUrlLink extends Anchor implements ClickHandler {
- public static List<DownloadUrlLink> createDownloadUrlLinks(
- boolean allowAnonymous, DownloadPanel downloadPanel) {
- List<DownloadUrlLink> urls = new ArrayList<>();
- for (String s : Gerrit.info().download().schemes()) {
- DownloadSchemeInfo scheme = Gerrit.info().download().scheme(s);
- if (scheme.isAuthRequired() && !allowAnonymous) {
- continue;
- }
- urls.add(new DownloadUrlLink(downloadPanel, scheme, s));
- }
- return urls;
- }
-
- private final DownloadPanel downloadPanel;
- private final DownloadSchemeInfo schemeInfo;
- private final String schemeName;
-
- public DownloadUrlLink(
- DownloadPanel downloadPanel, DownloadSchemeInfo schemeInfo, String schemeName) {
- super(schemeName);
- setStyleName(Gerrit.RESOURCES.css().downloadLink());
- Roles.getTabRole().set(getElement());
- addClickHandler(this);
-
- this.downloadPanel = downloadPanel;
- this.schemeInfo = schemeInfo;
- this.schemeName = schemeName;
- }
-
- public String getSchemeName() {
- return schemeName;
- }
-
- @Override
- public void onClick(ClickEvent event) {
- event.preventDefault();
- event.stopPropagation();
-
- select();
-
- GeneralPreferences prefs = Gerrit.getUserPreferences();
- if (Gerrit.isSignedIn() && !schemeName.equals(prefs.downloadScheme())) {
- prefs.downloadScheme(schemeName);
- GeneralPreferences in = GeneralPreferences.create();
- in.downloadScheme(schemeName);
- AccountApi.self()
- .view("preferences")
- .put(
- in,
- new AsyncCallback<JavaScriptObject>() {
- @Override
- public void onSuccess(JavaScriptObject result) {}
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
- }
-
- void select() {
- downloadPanel.populateDownloadCommandLinks(schemeInfo);
-
- DownloadUrlPanel parent = (DownloadUrlPanel) getParent();
- for (Widget w : parent) {
- if (w != this && w instanceof DownloadUrlLink) {
- w.removeStyleName(Gerrit.RESOURCES.css().downloadLink_Active());
- }
- }
- addStyleName(Gerrit.RESOURCES.css().downloadLink_Active());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlPanel.java
deleted file mode 100644
index 6a5fbe4f8b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/download/DownloadUrlPanel.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2010 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.client.download;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
-import java.util.Collection;
-
-public class DownloadUrlPanel extends FlowPanel {
-
- public DownloadUrlPanel() {
- setStyleName(Gerrit.RESOURCES.css().downloadLinkList());
- Roles.getTablistRole().set(getElement());
- }
-
- public boolean isEmpty() {
- return getWidgetCount() == 0;
- }
-
- public void select(String schemeName) {
- DownloadUrlLink first = null;
-
- for (Widget w : this) {
- if (w instanceof DownloadUrlLink) {
- final DownloadUrlLink d = (DownloadUrlLink) w;
- if (first == null) {
- first = d;
- }
- if (d.getSchemeName().equals(schemeName)) {
- d.select();
- return;
- }
- }
- }
-
- // If none matched the requested type, select the first in the
- // group as that will at least give us an initial baseline.
- if (first != null) {
- first.select();
- }
- }
-
- public void add(Collection<DownloadUrlLink> links) {
- for (Widget link : links) {
- add(link);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java
deleted file mode 100644
index b70c209ea0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2015 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.client.editor;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Constants;
-
-interface EditConstants extends Constants {
- EditConstants I = GWT.create(EditConstants.class);
-
- String closeUnsavedChanges();
-
- String cancelUnsavedChanges();
-
- String gotoLineNumber();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties
deleted file mode 100644
index 2e8a08708f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditConstants.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-closeUnsavedChanges = Unsaved changes were made to this file.
-
-cancelUnsavedChanges = Unsaved changes were made to this file.\n\
- \n\
- Discard unsaved changes?
-
-gotoLineNumber = Go to Line: \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
deleted file mode 100644
index 3f9d73282f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditFileInfo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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.client.editor;
-
-import com.google.gerrit.client.DiffWebLinkInfo;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-
-public class EditFileInfo extends JavaScriptObject {
- public final native JsArray<DiffWebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- protected EditFileInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesAction.java
deleted file mode 100644
index e11ded0d64..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesAction.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2014 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.client.editor;
-
-import com.google.gerrit.client.account.EditPreferences;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
-
-class EditPreferencesAction {
- private final EditScreen view;
- private final EditPreferences prefs;
- private PopupPanel popup;
- private EditPreferencesBox current;
-
- EditPreferencesAction(EditScreen view, EditPreferences prefs) {
- this.view = view;
- this.prefs = prefs;
- }
-
- void show() {
- if (popup != null) {
- hide();
- return;
- }
-
- current = new EditPreferencesBox(view);
- current.set(prefs);
-
- popup = new PopupPanel(true, false);
- popup.setStyleName(current.style.dialog());
- popup.add(current);
- popup.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- view.getEditor().focus();
- popup = null;
- current = null;
- }
- });
- popup.setPopupPositionAndShow(
- new PositionCallback() {
- @Override
- public void setPosition(int offsetWidth, int offsetHeight) {
- popup.setPopupPosition(300, 120);
- }
- });
- }
-
- void hide() {
- if (popup != null) {
- popup.hide();
- popup = null;
- current = null;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
deleted file mode 100644
index 5157123fdb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (C) 2014 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.client.editor;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.account.EditPreferences;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.NpIntTextBox;
-import com.google.gerrit.extensions.client.EditPreferencesInfo;
-import com.google.gerrit.extensions.client.KeyMapType;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Visibility;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.ToggleButton;
-import com.google.gwt.user.client.ui.UIObject;
-import net.codemirror.theme.ThemeLoader;
-
-/** Displays current edit preferences. */
-public class EditPreferencesBox extends Composite {
- interface Binder extends UiBinder<HTMLPanel, EditPreferencesBox> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- public interface Style extends CssResource {
- String dialog();
- }
-
- private final EditScreen view;
- private EditPreferences prefs;
-
- @UiField Style style;
- @UiField Element header;
- @UiField Anchor close;
- @UiField NpIntTextBox tabWidth;
- @UiField NpIntTextBox lineLength;
- @UiField NpIntTextBox indentUnit;
- @UiField NpIntTextBox cursorBlinkRate;
- @UiField ToggleButton topMenu;
- @UiField ToggleButton syntaxHighlighting;
- @UiField ToggleButton showTabs;
- @UiField ToggleButton whitespaceErrors;
- @UiField ToggleButton lineNumbers;
- @UiField ToggleButton matchBrackets;
- @UiField ToggleButton lineWrapping;
- @UiField ToggleButton indentWithTabs;
- @UiField ToggleButton autoCloseBrackets;
- @UiField ToggleButton showBase;
- @UiField ListBox theme;
- @UiField ListBox keyMap;
- @UiField Button apply;
- @UiField Button save;
-
- public EditPreferencesBox(EditScreen view) {
- this.view = view;
- initWidget(uiBinder.createAndBindUi(this));
- initTheme();
- initKeyMapType();
-
- if (view == null) {
- UIObject.setVisible(header, false);
- apply.getElement().getStyle().setVisibility(Visibility.HIDDEN);
- }
- }
-
- public Style getStyle() {
- return style;
- }
-
- public void set(EditPreferences prefs) {
- this.prefs = prefs;
-
- tabWidth.setIntValue(prefs.tabSize());
- lineLength.setIntValue(prefs.lineLength());
- indentUnit.setIntValue(prefs.indentUnit());
- cursorBlinkRate.setIntValue(prefs.cursorBlinkRate());
- topMenu.setValue(!prefs.hideTopMenu());
- syntaxHighlighting.setValue(prefs.syntaxHighlighting());
- showTabs.setValue(prefs.showTabs());
- whitespaceErrors.setValue(prefs.showWhitespaceErrors());
- lineNumbers.setValue(prefs.hideLineNumbers());
- matchBrackets.setValue(prefs.matchBrackets());
- lineWrapping.setValue(prefs.lineWrapping());
- indentWithTabs.setValue(prefs.indentWithTabs());
- autoCloseBrackets.setValue(prefs.autoCloseBrackets());
- showBase.setValue(prefs.showBase());
- setTheme(prefs.theme());
- setKeyMapType(prefs.keyMapType());
- }
-
- @UiHandler("tabWidth")
- void onTabWidth(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- prefs.tabSize(Math.max(1, Integer.parseInt(v)));
- if (view != null) {
- view.setOption("tabSize", v);
- }
- }
- }
-
- @UiHandler("lineLength")
- void onLineLength(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- prefs.lineLength(Math.max(1, Integer.parseInt(v)));
- if (view != null) {
- view.setLineLength(prefs.lineLength());
- }
- }
- }
-
- @UiHandler("indentUnit")
- void onIndentUnit(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- prefs.indentUnit(Math.max(0, Integer.parseInt(v)));
- if (view != null) {
- view.setIndentUnit(prefs.indentUnit());
- }
- }
- }
-
- @UiHandler("cursorBlinkRate")
- void onCursoBlinkRate(ValueChangeEvent<String> e) {
- String v = e.getValue();
- if (v != null && v.length() > 0) {
- // A negative value hides the cursor entirely:
- // don't let user shoot himself in the foot.
- prefs.cursorBlinkRate(Math.max(0, Integer.parseInt(v)));
- if (view != null) {
- view.setOption("cursorBlinkRate", prefs.cursorBlinkRate());
- }
- }
- }
-
- @UiHandler("topMenu")
- void onTopMenu(ValueChangeEvent<Boolean> e) {
- prefs.hideTopMenu(!e.getValue());
- if (view != null) {
- Gerrit.setHeaderVisible(!prefs.hideTopMenu());
- view.adjustHeight();
- }
- }
-
- @UiHandler("showTabs")
- void onShowTabs(ValueChangeEvent<Boolean> e) {
- prefs.showTabs(e.getValue());
- if (view != null) {
- view.setShowTabs(prefs.showTabs());
- }
- }
-
- @UiHandler("whitespaceErrors")
- void onshowTrailingSpace(ValueChangeEvent<Boolean> e) {
- prefs.showWhitespaceErrors(e.getValue());
- if (view != null) {
- view.setShowWhitespaceErrors(prefs.showWhitespaceErrors());
- }
- }
-
- @UiHandler("lineNumbers")
- void onLineNumbers(ValueChangeEvent<Boolean> e) {
- prefs.hideLineNumbers(e.getValue());
- if (view != null) {
- view.setShowLineNumbers(prefs.hideLineNumbers());
- }
- }
-
- @UiHandler("syntaxHighlighting")
- void onSyntaxHighlighting(ValueChangeEvent<Boolean> e) {
- prefs.syntaxHighlighting(e.getValue());
- if (view != null) {
- view.setSyntaxHighlighting(prefs.syntaxHighlighting());
- }
- }
-
- @UiHandler("matchBrackets")
- void onMatchBrackets(ValueChangeEvent<Boolean> e) {
- prefs.matchBrackets(e.getValue());
- if (view != null) {
- view.setOption("matchBrackets", prefs.matchBrackets());
- }
- }
-
- @UiHandler("lineWrapping")
- void onLineWrapping(ValueChangeEvent<Boolean> e) {
- prefs.lineWrapping(e.getValue());
- if (view != null) {
- view.getEditor().setOption("lineWrapping", prefs.lineWrapping());
- }
- }
-
- @UiHandler("indentWithTabs")
- void onIndentWithTabs(ValueChangeEvent<Boolean> e) {
- prefs.indentWithTabs(e.getValue());
- if (view != null) {
- view.getEditor().setOption("indentWithTabs", prefs.indentWithTabs());
- }
- }
-
- @UiHandler("autoCloseBrackets")
- void onCloseBrackets(ValueChangeEvent<Boolean> e) {
- prefs.autoCloseBrackets(e.getValue());
- if (view != null) {
- view.getEditor().setOption("autoCloseBrackets", prefs.autoCloseBrackets());
- }
- }
-
- @UiHandler("showBase")
- void onShowBase(ValueChangeEvent<Boolean> e) {
- Boolean value = e.getValue();
- prefs.showBase(value);
- if (view != null) {
- view.showBase.setValue(value, true);
- }
- }
-
- @UiHandler("theme")
- void onTheme(@SuppressWarnings("unused") ChangeEvent e) {
- final Theme newTheme = Theme.valueOf(theme.getValue(theme.getSelectedIndex()));
- prefs.theme(newTheme);
- if (view != null) {
- ThemeLoader.loadTheme(
- newTheme,
- new GerritCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- view.setTheme(newTheme);
- }
- });
- }
- }
-
- @UiHandler("keyMap")
- void onKeyMap(@SuppressWarnings("unused") ChangeEvent e) {
- KeyMapType keyMapType = KeyMapType.valueOf(keyMap.getValue(keyMap.getSelectedIndex()));
- prefs.keyMapType(keyMapType);
- if (view != null) {
- view.setOption("keyMap", keyMapType.name().toLowerCase());
- }
- }
-
- @UiHandler("apply")
- void onApply(@SuppressWarnings("unused") ClickEvent e) {
- close();
- }
-
- @UiHandler("save")
- void onSave(@SuppressWarnings("unused") ClickEvent e) {
- AccountApi.putEditPreferences(
- prefs,
- new GerritCallback<EditPreferences>() {
- @Override
- public void onSuccess(EditPreferences p) {
- Gerrit.setEditPreferences(p.copyTo(new EditPreferencesInfo()));
- }
- });
- if (view != null) {
- close();
- }
- }
-
- @UiHandler("close")
- void onClose(ClickEvent e) {
- e.preventDefault();
- close();
- }
-
- private void close() {
- ((PopupPanel) getParent()).hide();
- }
-
- private void setTheme(Theme v) {
- String name = v != null ? v.name() : Theme.DEFAULT.name();
- for (int i = 0; i < theme.getItemCount(); i++) {
- if (theme.getValue(i).equals(name)) {
- theme.setSelectedIndex(i);
- return;
- }
- }
- theme.setSelectedIndex(0);
- }
-
- private void initTheme() {
- for (Theme t : Theme.values()) {
- theme.addItem(t.name().toLowerCase(), t.name());
- }
- }
-
- private void setKeyMapType(KeyMapType v) {
- String name = v != null ? v.name() : KeyMapType.DEFAULT.name();
- for (int i = 0; i < keyMap.getItemCount(); i++) {
- if (keyMap.getValue(i).equals(name)) {
- keyMap.setSelectedIndex(i);
- return;
- }
- }
- keyMap.setSelectedIndex(0);
- }
-
- private void initKeyMapType() {
- for (KeyMapType t : KeyMapType.values()) {
- keyMap.addItem(t.name().toLowerCase(), t.name());
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
deleted file mode 100644
index f5ec71e04c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
+++ /dev/null
@@ -1,282 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'>
- <ui:style type='com.google.gerrit.client.editor.EditPreferencesBox.Style'>
- @external .gwt-TextBox;
- @external .gwt-ToggleButton .html-face;
- @external .gwt-ToggleButton-up;
- @external .gwt-ToggleButton-up-hovering;
- @external .gwt-ToggleButton-up-disabled;
- @external .gwt-ToggleButton-down;
- @external .gwt-ToggleButton-down-hovering;
- @external .gwt-ToggleButton-down-disabled;
-
- .dialog {
- background: rgba(0, 0, 0, 0.85) none repeat scroll 0 50%;
- color: #ffffff;
- font-family: arial,sans-serif;
- font-weight: bold;
- overflow: auto !important;
- bottom: 0;
- text-align: left;
- text-shadow: 1px 1px 7px #000000;
- min-width: 300px;
- z-index: 200;
- border-radius: 10px;
- }
-
- @if user.agent safari {
- .dialog {
- \-webkit-border-radius: 10px;
- }
- }
-
- @if user.agent gecko1_8 {
- .dialog {
- \-moz-border-radius: 10px;
- }
- }
-
- .box { margin: 10px; }
- .box .gwt-TextBox { padding: 0; }
- .context { vertical-align: bottom; }
-
- .table tr { min-height: 23px; }
- .table th,
- .table td {
- white-space: nowrap;
- color: #ffffff;
- }
- .table th {
- padding-right: 8px;
- text-align: right;
- }
-
- .box a,
- .box a:visited,
- .box a:hover {
- color: #dddd00;
- }
-
- .box .gwt-ToggleButton {
- position: relative;
- height: 19px;
- width: 140px;
- background: #fff;
- color: #000;
- text-shadow: none;
- }
- .box .gwt-ToggleButton .html-face {
- position: absolute;
- top: 0;
- width: 68px;
- height: 17px;
- line-height: 17px;
- text-align: center;
- border-width: 1px;
- }
-
- .box .gwt-ToggleButton-up,
- .box .gwt-ToggleButton-up-hovering,
- .box .gwt-ToggleButton-up-disabled,
- .box .gwt-ToggleButton-down,
- .box .gwt-ToggleButton-down-hovering,
- .box .gwt-ToggleButton-down-disabled {
- padding: 0;
- border: 0;
- }
- .box .gwt-ToggleButton-up .html-face,
- .box .gwt-ToggleButton-up-hovering .html-face {
- left: 0;
- background: #cacaca;
- border-style: outset;
- }
- .box .gwt-ToggleButton-down .html-face,
- .box .gwt-ToggleButton-down-hovering .html-face {
- right: 0;
- background: #bcf;
- border-style: inset;
- }
-
- .box button {
- margin: 6px 3px 0 0;
- border-color: rgba(0, 0, 0, 0.1);
- text-align: center;
- font-size: 8pt;
- font-weight: bold;
- border: 1px solid;
- cursor: pointer;
- color: #444;
- background-color: #f5f5f5;
- background-image: -webkit-linear-gradient(top, #f5f5f5, #f1f1f1);
- -webkit-border-radius: 2px;
- -webkit-box-sizing: content-box;
- }
- .box button div {
- color: #444;
- height: 10px;
- min-width: 54px;
- line-height: 10px;
- white-space: nowrap;
- }
-
- button.apply {
- background-color: #4d90fe;
- background-image: -webkit-linear-gradient(top, #4d90fe, #4d90fe);
- }
- button.apply div { color: #fff; }
-
- button.save {
- margin-left: 10px;
- color: #d14836;
- background-color: #d14836;
- background-image: -webkit-linear-gradient(top, #d14836, #d14836);
- }
- button.save div { color: #fff; }
- </ui:style>
-
- <g:HTMLPanel styleName='{style.box}'>
- <div ui:field='header'>
- <table style='width: 100%'>
- <tr>
- <td><ui:msg>Edit Preferences</ui:msg></td>
- <td style='text-align: right'>
- <g:Anchor ui:field='close' href='javascript:;'><ui:msg>Close</ui:msg></g:Anchor>
- </td>
- </tr>
- </table>
- <hr/>
- </div>
- <table class='{style.table}'>
- <tr>
- <th><ui:msg>Theme</ui:msg></th>
- <td><g:ListBox ui:field='theme'/></td>
- </tr>
- <tr>
- <th><ui:msg>Key Map</ui:msg></th>
- <td><g:ListBox ui:field='keyMap'/></td>
- </tr>
- <tr>
- <th><ui:msg>Tab Width</ui:msg></th>
- <td><x:NpIntTextBox ui:field='tabWidth'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Columns</ui:msg></th>
- <td><x:NpIntTextBox ui:field='lineLength'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Indent Unit</ui:msg></th>
- <td><x:NpIntTextBox ui:field='indentUnit'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Cursor Blink Rate</ui:msg></th>
- <td><x:NpIntTextBox ui:field='cursorBlinkRate'
- visibleLength='4'
- alignment='RIGHT'/></td>
- </tr>
- <tr>
- <th><ui:msg>Top Menu</ui:msg></th>
- <td><g:ToggleButton ui:field='topMenu'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Syntax Highlighting</ui:msg></th>
- <td><g:ToggleButton ui:field='syntaxHighlighting'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Show Tabs</ui:msg></th>
- <td><g:ToggleButton ui:field='showTabs'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Whitespace Errors</ui:msg></th>
- <td><g:ToggleButton ui:field='whitespaceErrors'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Line Numbers</ui:msg></th>
- <td><g:ToggleButton ui:field='lineNumbers'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Match Brackets</ui:msg></th>
- <td><g:ToggleButton ui:field='matchBrackets'>
- <g:upFace><ui:msg>Off</ui:msg></g:upFace>
- <g:downFace><ui:msg>On</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Line Wrapping</ui:msg></th>
- <td><g:ToggleButton ui:field='lineWrapping'>
- <g:upFace><ui:msg>Off</ui:msg></g:upFace>
- <g:downFace><ui:msg>On</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Indent With Tabs</ui:msg></th>
- <td><g:ToggleButton ui:field='indentWithTabs'>
- <g:upFace><ui:msg>Off</ui:msg></g:upFace>
- <g:downFace><ui:msg>On</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Auto Close Brackets</ui:msg></th>
- <td><g:ToggleButton ui:field='autoCloseBrackets'>
- <g:upFace><ui:msg>Off</ui:msg></g:upFace>
- <g:downFace><ui:msg>On</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <th><ui:msg>Show Base Version</ui:msg></th>
- <td><g:ToggleButton ui:field='showBase'>
- <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
- <g:downFace><ui:msg>Show</ui:msg></g:downFace>
- </g:ToggleButton></td>
- </tr>
- <tr>
- <td></td>
- <td>
- <g:Button ui:field='apply' styleName='{style.apply}'>
- <div><ui:msg>Apply</ui:msg></div>
- </g:Button>
- <g:Button ui:field='save' styleName='{style.save}'>
- <div><ui:msg>Save</ui:msg></div>
- </g:Button>
- </td>
- </tr>
- </table>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
deleted file mode 100644
index cbf12a3407..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
+++ /dev/null
@@ -1,699 +0,0 @@
-// Copyright (C) 2014 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.client.editor;
-
-import static com.google.gwt.dom.client.Style.Visibility.HIDDEN;
-import static com.google.gwt.dom.client.Style.Visibility.VISIBLE;
-
-import com.google.gerrit.client.DiffWebLinkInfo;
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.JumpKeys;
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.account.EditPreferences;
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.changes.ChangeEditApi;
-import com.google.gerrit.client.diff.DiffApi;
-import com.google.gerrit.client.diff.DiffInfo;
-import com.google.gerrit.client.diff.Header;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.FileInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.HttpCallback;
-import com.google.gerrit.client.rpc.HttpResponse;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.InlineHyperlink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.KeyMapType;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.ClosingEvent;
-import com.google.gwt.user.client.Window.ClosingHandler;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTMLPanel;
-import com.google.gwt.user.client.ui.ImageResourceRenderer;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import java.util.List;
-import net.codemirror.addon.AddonInjector;
-import net.codemirror.addon.Addons;
-import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.ChangesHandler;
-import net.codemirror.lib.CodeMirror.CommandRunner;
-import net.codemirror.lib.Configuration;
-import net.codemirror.lib.KeyMap;
-import net.codemirror.lib.MergeView;
-import net.codemirror.lib.Pos;
-import net.codemirror.mode.ModeInfo;
-import net.codemirror.mode.ModeInjector;
-import net.codemirror.theme.ThemeLoader;
-
-public class EditScreen extends Screen {
- interface Binder extends UiBinder<HTMLPanel, EditScreen> {}
-
- private static final Binder uiBinder = GWT.create(Binder.class);
-
- interface Style extends CssResource {
- String fullWidth();
-
- String base();
-
- String hideBase();
- }
-
- @Nullable private Project.NameKey projectKey;
- private final PatchSet.Id revision;
- private final String path;
- private final int startLine;
- private EditPreferences prefs;
- private EditPreferencesAction editPrefsAction;
- private MergeView mv;
- private CodeMirror cmBase;
- private CodeMirror cmEdit;
- private HttpResponse<NativeString> content;
- private HttpResponse<NativeString> baseContent;
- private EditFileInfo editFileInfo;
- private JsArray<DiffWebLinkInfo> diffLinks;
-
- @UiField Element header;
- @UiField Element project;
- @UiField Element filePath;
- @UiField FlowPanel linkPanel;
- @UiField Element cursLine;
- @UiField Element cursCol;
- @UiField Element dirty;
- @UiField CheckBox showBase;
- @UiField Button close;
- @UiField Button save;
- @UiField Element editor;
- @UiField Style style;
-
- private HandlerRegistration resizeHandler;
- private HandlerRegistration closeHandler;
- private int generation;
-
- public EditScreen(@Nullable Project.NameKey projectKey, Patch.Key patch, int startLine) {
- this.projectKey = projectKey;
- this.revision = patch.getParentKey();
- this.path = patch.get();
- this.startLine = startLine - 1;
- setRequiresSignIn(true);
- add(uiBinder.createAndBindUi(this));
- addDomHandler(GlobalKey.STOP_PROPAGATION, KeyPressEvent.getType());
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setHeaderVisible(false);
- setWindowTitle(FileInfo.getFileName(path));
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- prefs = EditPreferences.create(Gerrit.getEditPreferences());
-
- CallbackGroup group1 = new CallbackGroup();
- final CallbackGroup group2 = new CallbackGroup();
- final CallbackGroup group3 = new CallbackGroup();
-
- CodeMirror.initLibrary(
- group1.add(
- new AsyncCallback<Void>() {
- final AsyncCallback<Void> themeCallback = group3.addEmpty();
-
- @Override
- public void onSuccess(Void result) {
- // Load theme after CM library to ensure theme can override CSS.
- ThemeLoader.loadTheme(prefs.theme(), themeCallback);
- group2.done();
-
- new AddonInjector()
- .add(Addons.I.merge_bundled().getName())
- .inject(
- new AsyncCallback<Void>() {
- @Override
- public void onFailure(Throwable caught) {}
-
- @Override
- public void onSuccess(Void result) {
- if (!prefs.showBase() || revision.get() > 0) {
- group3.done();
- }
- }
- });
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
-
- ChangeApi.detail(
- Project.NameKey.asStringOrNull(projectKey),
- revision.getParentKey().get(),
- group1.add(
- new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo c) {
- projectKey = c.projectNameKey();
- project.setInnerText(c.project());
- SafeHtml.setInnerHTML(filePath, Header.formatPath(path));
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
-
- if (revision.get() == 0) {
- ChangeEditApi.getMeta(
- Project.NameKey.asStringOrNull(projectKey),
- revision,
- path,
- group1.add(
- new AsyncCallback<EditFileInfo>() {
- @Override
- public void onSuccess(EditFileInfo editInfo) {
- editFileInfo = editInfo;
- }
-
- @Override
- public void onFailure(Throwable e) {}
- }));
-
- if (prefs.showBase()) {
- ChangeEditApi.get(
- projectKey,
- revision,
- path,
- true /* base */,
- group1.addFinal(
- new HttpCallback<NativeString>() {
- @Override
- public void onSuccess(HttpResponse<NativeString> fc) {
- baseContent = fc;
- group3.done();
- }
-
- @Override
- public void onFailure(Throwable e) {}
- }));
- } else {
- group1.done();
- }
- } else {
- // TODO(davido): We probably want to create dedicated GET EditScreenMeta
- // REST endpoint. Abuse GET diff for now, as it retrieves links we need.
- DiffApi.diff(Project.NameKey.asStringOrNull(projectKey), revision, path)
- .webLinksOnly()
- .get(
- group1.addFinal(
- new AsyncCallback<DiffInfo>() {
- @Override
- public void onSuccess(DiffInfo diffInfo) {
- diffLinks = diffInfo.webLinks();
- }
-
- @Override
- public void onFailure(Throwable e) {}
- }));
- }
-
- ChangeEditApi.get(
- projectKey,
- revision,
- path,
- group2.add(
- new HttpCallback<NativeString>() {
- final AsyncCallback<Void> modeCallback = group3.addEmpty();
-
- @Override
- public void onSuccess(HttpResponse<NativeString> fc) {
- content = fc;
- if (revision.get() > 0) {
- baseContent = fc;
- }
-
- if (prefs.syntaxHighlighting()) {
- injectMode(fc.getContentType(), modeCallback);
- } else {
- modeCallback.onSuccess(null);
- }
- }
-
- @Override
- public void onFailure(Throwable e) {
- // "Not Found" means it's a new file.
- if (RestApi.isNotFound(e)) {
- content = null;
- modeCallback.onSuccess(null);
- } else {
- GerritCallback.showFailure(e);
- }
- }
- }));
-
- group3.addListener(
- new ScreenLoadCallback<Void>(this) {
- @Override
- protected void preDisplay(Void result) {
- initEditor();
-
- renderLinks(editFileInfo, diffLinks);
- editFileInfo = null;
- diffLinks = null;
-
- showBase.setValue(prefs.showBase(), true);
- cmBase.refresh();
- }
- });
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- KeyMap localKeyMap = KeyMap.create();
- localKeyMap.on("Ctrl-L", gotoLine()).on("Cmd-L", gotoLine()).on("Cmd-S", save());
-
- // TODO(davido): Find a better way to prevent key maps collisions
- if (prefs.keyMapType() != KeyMapType.EMACS) {
- localKeyMap.on("Ctrl-S", save());
- }
-
- cmBase.addKeyMap(localKeyMap);
- cmEdit.addKeyMap(localKeyMap);
- }
-
- private Runnable gotoLine() {
- return () -> cmEdit.execCommand("jumpToLine");
- }
-
- @Override
- public void onShowView() {
- super.onShowView();
- Window.enableScrolling(false);
- JumpKeys.enable(false);
- if (prefs.hideTopMenu()) {
- Gerrit.setHeaderVisible(false);
- }
- resizeHandler =
- Window.addResizeHandler(
- new ResizeHandler() {
- @Override
- public void onResize(ResizeEvent event) {
- adjustHeight();
- }
- });
- closeHandler =
- Window.addWindowClosingHandler(
- new ClosingHandler() {
- @Override
- public void onWindowClosing(ClosingEvent event) {
- if (!cmEdit.isClean(generation)) {
- event.setMessage(EditConstants.I.closeUnsavedChanges());
- }
- }
- });
-
- generation = cmEdit.changeGeneration(true);
- setClean(true);
- cmEdit.on(
- new ChangesHandler() {
- @Override
- public void handle(CodeMirror cm) {
- setClean(cm.isClean(generation));
- }
- });
-
- adjustHeight();
- cmEdit.on("cursorActivity", updateCursorPosition());
- setShowTabs(prefs.showTabs());
- setLineLength(prefs.lineLength());
- cmEdit.refresh();
- cmEdit.focus();
-
- if (startLine > 0) {
- cmEdit.scrollToLine(startLine);
- }
- updateActiveLine();
- editPrefsAction = new EditPreferencesAction(this, prefs);
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- if (cmBase != null) {
- cmBase.getWrapperElement().removeFromParent();
- }
- if (cmEdit != null) {
- cmEdit.getWrapperElement().removeFromParent();
- }
- if (resizeHandler != null) {
- resizeHandler.removeHandler();
- }
- if (closeHandler != null) {
- closeHandler.removeHandler();
- }
- Window.enableScrolling(true);
- Gerrit.setHeaderVisible(true);
- JumpKeys.enable(true);
- }
-
- CodeMirror getEditor() {
- return cmEdit;
- }
-
- @UiHandler("editSettings")
- void onEditSetting(@SuppressWarnings("unused") ClickEvent e) {
- editPrefsAction.show();
- }
-
- @UiHandler("save")
- void onSave(@SuppressWarnings("unused") ClickEvent e) {
- save().run();
- }
-
- @UiHandler("close")
- void onClose(@SuppressWarnings("unused") ClickEvent e) {
- if (cmEdit.isClean(generation) || Window.confirm(EditConstants.I.cancelUnsavedChanges())) {
- upToChange();
- }
- }
-
- private void displayBase() {
- cmBase.getWrapperElement().getParentElement().removeClassName(style.hideBase());
- cmEdit.getWrapperElement().getParentElement().removeClassName(style.fullWidth());
- mv.getGapElement().removeClassName(style.hideBase());
- setCmBaseValue();
- setLineLength(prefs.lineLength());
- cmBase.refresh();
- }
-
- @UiHandler("showBase")
- void onShowBase(ValueChangeEvent<Boolean> e) {
- boolean shouldShow = e.getValue();
- if (shouldShow) {
- if (baseContent == null) {
- ChangeEditApi.get(
- projectKey,
- revision,
- path,
- true /* base */,
- new HttpCallback<NativeString>() {
- @Override
- public void onSuccess(HttpResponse<NativeString> fc) {
- baseContent = fc;
- displayBase();
- }
-
- @Override
- public void onFailure(Throwable e) {}
- });
- } else {
- displayBase();
- }
- } else {
- cmBase.getWrapperElement().getParentElement().addClassName(style.hideBase());
- cmEdit.getWrapperElement().getParentElement().addClassName(style.fullWidth());
- mv.getGapElement().addClassName(style.hideBase());
- }
- mv.setShowDifferences(shouldShow);
- }
-
- void setOption(String option, String value) {
- cmBase.setOption(option, value);
- cmEdit.setOption(option, value);
- }
-
- void setOption(String option, boolean value) {
- cmBase.setOption(option, value);
- cmEdit.setOption(option, value);
- }
-
- void setOption(String option, double value) {
- cmBase.setOption(option, value);
- cmEdit.setOption(option, value);
- }
-
- void setTheme(Theme newTheme) {
- cmBase.operation(() -> cmBase.setOption("theme", newTheme.name().toLowerCase()));
- cmEdit.operation(() -> cmEdit.setOption("theme", newTheme.name().toLowerCase()));
- }
-
- void setLineLength(int length) {
- int adjustedLength = Patch.COMMIT_MSG.equals(path) ? 72 : length;
- cmBase.extras().lineLength(adjustedLength);
- cmEdit.extras().lineLength(adjustedLength);
- }
-
- void setIndentUnit(int indent) {
- cmEdit.setOption("indentUnit", Patch.COMMIT_MSG.equals(path) ? 2 : indent);
- }
-
- void setShowLineNumbers(boolean show) {
- cmBase.setOption("lineNumbers", show);
- cmEdit.setOption("lineNumbers", show);
- }
-
- void setShowWhitespaceErrors(boolean show) {
- cmBase.operation(() -> cmBase.setOption("showTrailingSpace", show));
- cmEdit.operation(() -> cmEdit.setOption("showTrailingSpace", show));
- }
-
- void setShowTabs(boolean show) {
- cmBase.extras().showTabs(show);
- cmEdit.extras().showTabs(show);
- }
-
- void adjustHeight() {
- int height = header.getOffsetHeight();
- int rest = Gerrit.getHeaderFooterHeight() + height + 5; // Estimate
- mv.getGapElement().getStyle().setHeight(Window.getClientHeight() - rest, Unit.PX);
- cmBase.adjustHeight(height);
- cmEdit.adjustHeight(height);
- }
-
- void setSyntaxHighlighting(boolean b) {
- ModeInfo modeInfo = ModeInfo.findMode(content.getContentType(), path);
- final String mode = modeInfo != null ? modeInfo.mime() : null;
- if (b && mode != null && !mode.isEmpty()) {
- injectMode(
- mode,
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- cmBase.setOption("mode", mode);
- cmEdit.setOption("mode", mode);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- prefs.syntaxHighlighting(false);
- }
- });
- } else {
- cmBase.setOption("mode", (String) null);
- cmEdit.setOption("mode", (String) null);
- }
- }
-
- private void upToChange() {
- Gerrit.display(PageLinks.toChangeInEditMode(projectKey, revision.getParentKey()));
- }
-
- private void initEditor() {
- ModeInfo mode = null;
- String editContent = "";
- if (content != null && content.getResult() != null) {
- editContent = content.getResult().asString();
- if (prefs.syntaxHighlighting()) {
- mode = ModeInfo.findMode(content.getContentType(), path);
- }
- }
-
- Configuration cfg =
- Configuration.create()
- .set("autoCloseBrackets", prefs.autoCloseBrackets())
- .set("cursorBlinkRate", prefs.cursorBlinkRate())
- .set("cursorHeight", 0.85)
- .set("indentUnit", prefs.indentUnit())
- .set("keyMap", prefs.keyMapType().name().toLowerCase())
- .set("lineNumbers", prefs.hideLineNumbers())
- .set("lineWrapping", prefs.lineWrapping())
- .set("indentWithTabs", prefs.indentWithTabs())
- .set("matchBrackets", prefs.matchBrackets())
- .set("mode", mode != null ? mode.mime() : null)
- .set("origLeft", editContent)
- .set("scrollbarStyle", "overlay")
- .set("showTrailingSpace", prefs.showWhitespaceErrors())
- .set("styleSelectedText", true)
- .set("tabSize", prefs.tabSize())
- .set("theme", prefs.theme().name().toLowerCase())
- .set("value", "");
-
- if (editContent.contains("\r\n")) {
- cfg.set("lineSeparator", "\r\n");
- }
-
- mv = MergeView.create(editor, cfg);
-
- cmBase = mv.leftOriginal();
- cmBase.getWrapperElement().addClassName(style.base());
- cmEdit = mv.editor();
- setCmBaseValue();
- cmEdit.setValue(editContent);
-
- CodeMirror.addCommand(
- "save",
- new CommandRunner() {
- @Override
- public void run(CodeMirror instance) {
- save().run();
- }
- });
- }
-
- private void renderLinks(EditFileInfo editInfo, JsArray<DiffWebLinkInfo> diffLinks) {
- renderLinksToDiff();
-
- if (editInfo != null) {
- renderLinks(Natives.asList(editInfo.webLinks()));
- } else if (diffLinks != null) {
- renderLinks(Natives.asList(diffLinks));
- }
- }
-
- private void renderLinks(List<DiffWebLinkInfo> links) {
- if (links != null) {
- for (DiffWebLinkInfo webLink : links) {
- linkPanel.add(webLink.toAnchor());
- }
- }
- }
-
- private void renderLinksToDiff() {
- InlineHyperlink sbs = new InlineHyperlink();
- sbs.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.sideBySideDiff()));
- sbs.setTargetHistoryToken(
- Dispatcher.toPatch(projectKey, "sidebyside", null, new Patch.Key(revision, path)));
- sbs.setTitle(PatchUtil.C.sideBySideDiff());
- linkPanel.add(sbs);
-
- InlineHyperlink unified = new InlineHyperlink();
- unified.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.unifiedDiff()));
- unified.setTargetHistoryToken(
- Dispatcher.toPatch(projectKey, "unified", null, new Patch.Key(revision, path)));
- unified.setTitle(PatchUtil.C.unifiedDiff());
- linkPanel.add(unified);
- }
-
- private Runnable updateCursorPosition() {
- return () -> {
- // The rendering of active lines has to be deferred. Reflow
- // caused by adding and removing styles chokes Firefox when arrow
- // key (or j/k) is held down. Performance on Chrome is fine
- // without the deferral.
- //
- Scheduler.get().scheduleDeferred(() -> cmEdit.operation(this::updateActiveLine));
- };
- }
-
- private void updateActiveLine() {
- Pos p = cmEdit.getCursor("end");
- cursLine.setInnerText(Integer.toString(p.line() + 1));
- cursCol.setInnerText(Integer.toString(p.ch() + 1));
- cmEdit.extras().activeLine(cmEdit.getLineHandleVisualStart(p.line()));
- }
-
- private void setClean(boolean clean) {
- save.setEnabled(!clean);
- close.setEnabled(true);
- dirty.getStyle().setVisibility(!clean ? VISIBLE : HIDDEN);
- }
-
- private Runnable save() {
- return () -> {
- if (!cmEdit.isClean(generation)) {
- close.setEnabled(false);
- String text = cmEdit.getValue();
- if (Patch.COMMIT_MSG.equals(path)) {
- String trimmed = text.trim() + "\r";
- if (!trimmed.equals(text)) {
- text = trimmed;
- cmEdit.setValue(text);
- }
- }
- final int g = cmEdit.changeGeneration(false);
- ChangeEditApi.put(
- Project.NameKey.asStringOrNull(projectKey),
- revision.getParentKey().get(),
- path,
- text,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- generation = g;
- setClean(cmEdit.isClean(g));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- close.setEnabled(true);
- }
- });
- }
- };
- }
-
- private void injectMode(String type, AsyncCallback<Void> cb) {
- new ModeInjector().add(type).inject(cb);
- }
-
- private void setCmBaseValue() {
- cmBase.setValue(
- baseContent != null && baseContent.getResult() != null
- ? baseContent.getResult().asString()
- : "");
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml
deleted file mode 100644
index 34282c836d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.ui.xml
+++ /dev/null
@@ -1,185 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2014 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.
--->
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
- xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
- <ui:style gss='false' type='com.google.gerrit.client.editor.EditScreen.Style'>
- @external .CodeMirror, .CodeMirror-cursor;
- @external .CodeMirror-merge-2pane, .CodeMirror-merge-pane;
- @external .CodeMirror-merge-gap;
- @external .CodeMirror-scroll, .CodeMirror-overlayscroll-vertical;
-
- .header {
- position: relative;
- height: 16px;
- line-height: 16px;
- }
-
- .header .CodeMirror div.CodeMirror-cursor {
- border-left: 2px solid black;
- }
-
- .headerLine {
- background-color: #f7f7f7;
- border-bottom: 1px solid #ddd;
- padding-left: 30px;
- }
-
- .headerButtons {
- display: inline-block;
- padding-right: 5px;
- border-right: 1px inset #ddd;
- margin-right: 5px;
- }
-
- .headerButtons button:disabled {
- background-color: #ddd;
- font-weight: normal;
- cursor: default;
- }
-
- .headerButtons button {
- margin: 2px 0 2px 0;
- text-align: center;
- font-size: 8pt;
- cursor: pointer;
- border: 1px solid;
- color: rgba(0, 0, 0, 0.15);
- background-color: #f5f5f5;
- -webkit-border-radius: 1px;
- -webkit-box-sizing: content-box;
- }
-
- .headerButtons button div {
- color: #444;
- min-width: 54px;
- white-space: nowrap;
- line-height: 8pt;
- }
-
- .save {
- font-weight: bold;
- }
-
- .path {
- white-space: nowrap;
- }
-
- .statusLine {
- position: fixed;
- bottom: 0;
- left: 0;
- width: 175px;
- height: 19px;
- background-color: #f7f7f7;
- border-top: 1px solid #ddd;
- border-right: 1px solid #ddd;
- }
- .statusLine div {
- height: inherit;
- }
-
- .cursorPosition {
- display: inline-block;
- margin: 0 5px 0 35px;
- white-space: nowrap;
- }
-
- .dirty {
- display: inline-block;
- margin: 0 5px 0 5px;
- padding: 0 0 0 5px;
- border-left: 1px solid #ddd;
- font-weight: bold;
- }
-
- .navigation {
- position: absolute;
- top: 0;
- right: 10px;
- }
- .linkPanel {
- float: left;
- }
- .linkPanel img {
- padding-top: 2px;
- padding-right: 3px;
- }
-
- .preferences {
- position: relative;
- top: 2px;
- cursor: pointer;
- outline: none;
- }
-
- .hideBase.CodeMirror-merge-pane {
- display: none;
- }
-
- .hideBase.CodeMirror-merge-gap {
- display: none;
- }
-
- .CodeMirror-merge-2pane .fullWidth.CodeMirror-merge-pane {
- width: 100%;
- }
-
- /* Hide the vertical scrollbar on the base side. The edit side controls
- both views */
- .base .CodeMirror-scroll { margin-right: -42px; }
- .base .CodeMirror-overlayscroll-vertical { display: none !important; }
- </ui:style>
- <g:HTMLPanel styleName='{style.header}'>
- <div class='{style.headerLine}' ui:field='header'>
- <div class='{style.headerButtons}'>
- <g:Button ui:field='close'
- title='Close file and return to change'>
- <ui:attribute name='title'/>
- <div><ui:msg>Close</ui:msg></div>
- </g:Button>
- <g:Button ui:field='save'
- styleName='{style.save}'
- title='Save'>
- <ui:attribute name='title'/>
- <div><ui:msg>Save</ui:msg></div>
- </g:Button>
- </div>
- <span class='{style.path}'><span ui:field='project'/> / <span ui:field='filePath'/></span>
- <div class='{style.navigation}'>
- <g:Label text='Show Base' styleName='{style.linkPanel}'></g:Label>
- <g:CheckBox ui:field='showBase' checked='true' styleName='{style.linkPanel}'
- title='Show Base Version'>
- <ui:attribute name='title'/>
- </g:CheckBox>
- <g:FlowPanel ui:field='linkPanel' styleName='{style.linkPanel}'/>
- <g:Image
- ui:field='editSettings'
- styleName='{style.preferences}'
- resource='{ico.gear}'
- title='Edit screen preferences'>
- <ui:attribute name='title'/>
- </g:Image>
- </div>
- </div>
- <div ui:field='editor' />
- <div class='{style.statusLine}'>
- <div class='{style.cursorPosition}'><span ui:field='cursLine'/> : <span ui:field='cursCol'/></div>
- <div class='{style.dirty}' ui:field='dirty'>Unsaved</div>
- </div>
- </g:HTMLPanel>
-</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
deleted file mode 100644
index 4076296f64..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ /dev/null
@@ -1,1060 +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.
- */
-
-/**
- * Make every single class external so users can rely on their names
- */
-@external .*;
-
-@def black #000000;
-@def white #ffffff;
-@def norm-font sans-serif;
-@def mono-font monospace;
-
-@eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
-@eval topMenuColor com.google.gerrit.client.Gerrit.getTheme().topMenuColor;
-@eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
-@eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-@eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-@eval changeTableOutdatedColor com.google.gerrit.client.Gerrit.getTheme().changeTableOutdatedColor;
-@eval tableOddRowColor com.google.gerrit.client.Gerrit.getTheme().tableOddRowColor;
-@eval tableEvenRowColor com.google.gerrit.client.Gerrit.getTheme().tableEvenRowColor;
-
-@sprite .greenCheckClass {
- gwt-image: "greenCheck";
-}
-
-/** Override various GWT defaults */
-.gerritTopMenu {
- font-size: 9pt;
- padding-left: 5px;
- padding-right: 5px;
- background: transparent;
-}
-
-body, table td, select {
- font-family: norm-font;
-}
-
-button {
- padding: 1px 6px;
-}
-
-.gerritBody {
- font-size: small;
- padding-left: 5px;
- padding-right: 5px;
-}
-
-a,
-a:visited {
- color: #0654ac;
- text-decoration: none;
-}
-
-a:hover {
- color: #0654ac;
- text-decoration: underline;
-}
-
-#gerrit_btmmenu {
- clear: both;
- color: #a0adcc;
- text-align: right;
- padding-right: 10px;
-}
-
-.version a,
-.version a:visited,
-.version a:hover {
- color: #2a5db0;
-}
-
-
-/** Widgets **/
-.gwt-Button {
- color: black;
-}
-
-.accountLinkPanel {
- display: inline;
-}
-
-.accountLinkPanel img {
- margin-right: 0.2em;
- position: relative;
- top: 2px;
- height: 16px !important;
- width: 16px;
-}
-
-.accountLinkPanel a {
- position: relative;
- top: -1px;
-}
-
-.inputFieldTypeHint {
- color: grey;
-}
-
-.smallHeading {
- margin-top: 5px;
- font-weight: bold;
-}
-
-.link {
- cursor: pointer;
-}
-
-.extensionPanel {
- padding-top: 10px;
-}
-
-/** MenuScreen **/
-.menuScreenMenuBar {
- background: topMenuColor;
- padding-top: 0.5em;
- padding-bottom: 10em;
- padding-left: 0.5em;
- padding-right: 0.5em;
- border-right: 1px solid black;
- margin-right: 0.5em;
-}
-
-.menuScreenMenuBar .menuItem {
- white-space: nowrap;
- display: block;
- border-right: none;
- padding: 0.2em;
-}
-
-.menuScreenMenuBar .menuItem.activeRow {
- background: selectionColor;
-}
-
-.menuItem.activeRow {
- background: selectionColor;
-}
-
-/** Menu **/
-.linkMenuBar {
- font-size: 9pt;
- display: inline;
- white-space: nowrap;
- padding-left: 6px;
-}
-.menuItem {
- padding-left: 5px;
- padding-right: 5px;
-}
-.linkMenuItemNotLast {
- border-right: 1px solid black;
-}
-
-.topmenu {
- width: 100%;
-}
-.topmenuTDmenu {
- vertical-align: top;
-}
-.topmenuTDglue {
- width: 100%;
-}
-
-.topmenuMenuLeft {
- width: 300px;
- font-size: 9pt;
- padding-top: 5px;
- padding-left: 5px;
- padding-right: 5px;
- background: none;
- position: relative;
- top: 0;
-}
-.topmenuMenuLeft tbody tr td table {
- border: 0;
-}
-.topmenuMenuLeft tbody tr td table.gwt-TabBar {
- border-bottom: 1px solid #DDD;
-}
-.topmenuMenuLeft .gwt-TextBox {
- width: 250px;
-}
-.topmenuMenuLeft .gwt-Button {
- padding: 3px 6px;
-}
-.topmenuMenuLeft .gwt-TabBarFirst {
- display: none;
-}
-.topmenuMenuLeft .gwt-TabBarItem {
- margin: 0px;
- background: transparent;
- padding-top: 0px;
- padding-bottom: 1px;
- padding-left: 1em;
- padding-right: 1em;
-}
-.topmenuMenuLeft .gwt-TabBarRest {
- background: transparent;
- padding-top: 0px;
-}
-.topmenuMenuLeft .gwt-TabPanelBottom {
- background: transparent;
- border-top: none;
- border-left: none;
- border-right: none;
- border-bottom: none;
- padding: 1px;
-}
-.topmenuMenuLeft .menuItem {
- padding-left: 1em;
- padding-right: 1em;
- border-right: none;
-}
-
-.topmenuMenuRight {
- float: right;
- text-align: right;
-}
-.menuBarUserName {
- padding-left: 5px;
- padding-right: 5px;
- white-space: nowrap;
-}
-.menuBarUserNameAvatar {
- vertical-align: middle;
-}
-.menuBarUserNameFocusPanel {
- display: inline;
-}
-.menuBarUserNamePanel {
- display: inline;
- cursor: pointer;
- font-weight: bold;
-}
-.userInfoPopup {
- border: 1px solid black;
- background: white;
- box-shadow: 3px 3px 5px #888;
- z-index: 200;
-}
-.searchPanel {
- white-space: nowrap;
- display: inline;
-}
-.searchPanel .searchTextBox {
- font-size: 9pt;
- margin: 8.286px 3px 0 0;
-}
-.searchPanel .searchDropdown {
- font-size: 8pt;
- border: 2px solid;
- border-color: rgba(0, 0, 0, 0.15);
- height: 16px;
- border-radius: 2px;
- box-sizing: content-box;
-}
-.searchPanel .searchButton {
- text-align: center;
- font-size: 8pt;
- font-weight: bold;
- cursor: pointer;
- border: 2px solid;
- color: #FFF;
- border-color: rgba(0, 0, 0, 0.15);
- height: 14px;
- background-color: #53A93F;
- border-radius: 2px;
- box-sizing: content-box;
-}
-.suggestBoxPopup {
- z-index: 200;
-}
-
-/** RPC Status **/
-.rpcStatus {
- position: fixed;
- top: 6px;
- left: 50%;
- padding-top: 4px;
- padding-bottom: 4px;
- padding-left: 10px;
- padding-right: 10px;
- text-align: center;
- font-weight: bold;
- background: #FFF1A8;
- z-index: 200;
-}
-
-
-/** Error Dialog **/
-.errorDialog {
- background: none;
- border: none;
- padding: 10px;
- width: 600px;
- color: backgroundColor;
- font-size: 15px;
- font-family: verdana;
- z-index: 200;
-}
-.errorDialogGlass {
- opacity: 0.75;
- z-index: 200;
-}
-@if user.agent safari {
- .errorDialogGlass {
- opacity: 0.80;
- }
-}
-@if user.agent ie8 {
- /* IE just doesn't do opacity the way we want, make our dialog
- * stand out in a way that it can't be missed against the page
- */
- .errorDialog {
- color: black;
- background: darkgray;
- border: 10px groove lightgrey;
- }
-}
-.errorDialogTitle {
- font-size: 30px;
- font-weight: bold;
- margin-bottom: 15px;
-}
-.errorDialogErrorType {
- font-weight: bold;
- white-space: nowrap;
- margin-bottom: 15px;
-}
-.errorDialogButtons {
- width: 100%;
- margin-top: 15px;
-}
-.errorDialog a,
-.errorDialog a:visited,
-.errorDialog a:hover {
- color: white;
- font-weight: bold;
- font-size: 15px;
- font-family: verdana;
-}
-.loadingPluginsDialog {
- background: #fff;
- color: #000;
- width: auto;
-}
-
-
-/** Screen **/
-.screen {
-}
-
-.screenHeader {
- white-space: nowrap;
- font-size: 16pt;
- margin: 3px 0 8px;
- text-overflow: ellipsis;
- overflow: hidden;
-}
-
-/** ChangeTable **/
-.changeTable {
- border-collapse: separate;
- border-spacing: 0;
-}
-
-.changeTable tr:nth-child\(even\) {
- background: tableEvenRowColor;
-}
-
-.changeTable tr:nth-child\(odd\) {
- background: tableOddRowColor;
-}
-
-.changeTable .iconCell {
- width: 1px;
- padding: 0px;
- vertical-align: middle;
- border-bottom: 1px solid trimColor;
-}
-
-.changeTable .leftMostCell {
- border-left: 1px solid trimColor;
-}
-
-.changeTable .dataCell {
- padding-left: 5px;
- padding-right: 5px;
- border-right: 1px solid trimColor;
- border-bottom: 1px solid trimColor;
- vertical-align: middle;
- height: 20px;
-}
-
-.changeTable .dataCellHidden {
- display: none;
-}
-
-.changeTable a.gwt-InlineHyperlink,
-.changeTable a.gwt-Anchor {
- color: #222 !important;
-}
-
-.changeTable .changeSize {
- height: 10px;
- display: inline-block;
- opacity: 0.6;
-}
-
-.accountDashboard.changeTable tr {
- color: #444444;
-}
-.accountDashboard.changeTable tr a {
- color: #444444;
- text-decoration: none;
-}
-.accountDashboard.changeTable .needsReview,
-.accountDashboard.changeTable .needsReview a {
- font-weight: bold;
- color: textColor;
-}
-
-.changeTable .activeRow,
-.accountDashboard.changeTable .activeRow,
-.accountDashboard.changeTable .activeRow a {
- background: selectionColor !important;
-}
-
-.changeTable .cSIZE {
- width: 70px;
- text-align: right;
-}
-
-.changeTable .cSUBJECT div {
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
-}
-
-.changeTable .cASSIGNEDTOME {
- background: #ffe9d6 !important;
-}
-
-.changeTable .cASSIGNEE,
-.changeTable .cOWNER,
-.changeTable .cSTATUS {
- white-space: nowrap;
-}
-
-.changeTable .cLastUpdate {
- white-space: nowrap;
- text-align: right;
- width: 1em;
-}
-
-.changeTable .groupName {
- white-space: nowrap;
-}
-
-.changeTable .cAPPROVAL {
- width: 0.5em;
- text-align: center;
-}
-.changeTable .dataCell.negscore {
- color: red;
-}
-.changeTable .dataCell.posscore {
- color: #08a400;
-}
-.changeTable .dataCell.singleLine {
- white-space: nowrap;
-}
-.changeTable .dataCell.labelNotApplicable {
- background: #F5F5F5;
-}
-.changeTable .iconHeader {
- border-top: 1px solid backgroundColor;
- border-bottom: 1px solid backgroundColor;
- background-color: trimColor;
-}
-
-.changeTable .dataHeader {
- border: 1px solid backgroundColor;
- padding: 2px 6px 1px;
- background-color: trimColor;
- font-style: italic;
- white-space: nowrap;
- color: textColor;
-}
-
-.changeTable .dataHeaderHidden {
- display: none;
-}
-
-.changeTable .sectionHeader {
- border-top: 8px solid backgroundColor;
- padding: 2px 6px 1px;
- background-color: trimColor;
- white-space: nowrap;
- font-weight: bold;
- color: textColor;
-}
-
-.changeTable .emptySection {
- border-left: 1px solid trimColor;
- border-right: 1px solid trimColor;
- border-bottom: 1px solid trimColor;
- font-style: italic;
- padding-left: 25px;
-}
-
-.changeTablePrevNextLinks {
- float: right;
- padding-right: 5px;
-}
-.changeTablePrevNextLinks td {
- width: 5em;
- text-align: right;
-}
-.changeTablePrevNextLinks .gwt-Hyperlink {
- font-size: 9pt;
- color: #2a5db0;
-}
-
-/** Change **/
-.avatarInfoPanel {
- margin-right: 10px;
-}
-.avatarInfoPanel td {
- text-align: center;
-}
-
-.infoBlock {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-.infoBlock td {
- padding: 2px 4px 2px 6px;
- border-right: 1px solid trimColor;
- border-bottom: 1px solid trimColor;
- text-align: left;
- white-space: nowrap;
-}
-
-.infoBlock td td {
- padding-left: 0px;
- border-right: 0px;
-}
-
-.infoBlock td.topmost {
- border-top: 1px solid trimColor;
-}
-
-.infoBlock td.header {
- background-color: trimColor;
- font-style: italic;
- text-align: right;
-}
-
-.infoBlock td.bottomheader {
- border-bottom: 1px solid trimColor;
-}
-
-
-.patchSetActions {
- margin-bottom: 10px;
-}
-.patchSetActions .gwt-Button {
- margin-right: 30px;
- font-size: 8pt;
-}
-
-.downloadBox {
- min-width: 580px;
- margin: 5px;
- margin-right: 15px;
-}
-.downloadBoxTable {
- border-spacing: 0;
- width: 100%;
-}
-.downloadBoxTableCommandColumn {
- text-align: left;
- font-weight: normal;
- white-space: nowrap;
- max-height: 18px;
- width: 80px;
- padding-right: 5px;
-}
-.downloadBoxSpacer {
- margin-left: 5px;
- margin-right: 5px;
-}
-.downloadBoxScheme {
- float: right;
-}
-.downloadBoxCopyLabel {
- font-size: smaller;
- font-family: monospace;
-}
-.downloadBoxCopyLabel span {
- width: 500px;
- white-space: nowrap;
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.downloadBoxCopyLabel .gwt-TextBox {
- padding: 0;
- margin: 0;
- border: 0;
- max-height: 18px;
- width: 500px;
-}
-.downloadBoxCopyLabel div {
- float: right;
-}
-.downloadLinkHeader {
- background: trimColor;
- white-space: nowrap;
- border-bottom: 1px solid black;
-}
-.downloadLinkHeaderGap {
- margin-left: 5em;
-}
-.downloadLinkList {
- display: inline;
- white-space: nowrap;
-}
-.downloadLink {
- color: black;
- text-decoration: none;
- white-space: nowrap;
- background: trimColor;
- border-right: 1px solid black;
- padding-left: 0.5em;
- padding-right: 0.5em;
-}
-a:hover.downloadLink {
- color: black;
-}
-.downloadLink_Active {
- background: selectionColor;
-}
-.downloadLinkCopyLabel {
- white-space: pre;
- font-family: mono-font;
- font-size: 12px;
- margin-left: 0.5em;
- margin-right: 0.5em;
-}
-.downloadLinkCopyLabel .gwt-TextBox {
- width: 40em;
-}
-.downloadLinkCopyLabel span {
- width: 40em;
- white-space: nowrap;
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-/** AccountSettings **/
-.usernameField {
- white-space: nowrap;
-}
-.accountUsername {
- font-family: mono-font;
- font-size: small;
-}
-.accountPassword {
- font-family: mono-font;
- font-size: small;
-}
-.sshKeyPanelEncodedKey {
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
- font-family: mono-font;
- font-size: small;
-}
-.sshKeyPanelInvalid {
- white-space: nowrap;
- color: red;
- font-weight: bold;
-}
-.identityUntrustedExternalId {
- white-space: nowrap;
- color: red;
- font-weight: bold;
-}
-
-.accountInfoBlock {
- margin-bottom: 10px;
-}
-.accountInfoBlock .gwt-Button {
- margin-left: 10px;
-}
-
-.addWatchPanel {
- margin-top: 10px;
- padding: 5px 5px 5px 5px;
-}
-.watchedProjectFilter {
- margin-left: 1em;
- color: grey;
-}
-
-.addBranch {
- margin-top: 10px;
- background-color: trimColor;
- padding: 5px 5px 5px 5px;
-}
-
-.addSshKeyPanel {
- margin-top: 10px;
- background-color: trimColor;
- padding: 5px 5px 5px 5px;
-}
-
-.addSshKeyPanel ol {
- margin-top: 0px;
- margin-bottom: 5px;
-}
-
-.addSshKeyPanel td {
- width: 100%;
-}
-
-.sshKeyTable td.dataCell, .sshKeyTable td.iconCell {
- vertical-align: top;
-}
-
-.createProjectPanel {
- margin-bottom: 10px;
- background-color: trimColor;
- padding: 5px 5px 5px 5px;
-}
-
-.sshHostKeyPanel {
- margin-top: 10px;
- border: 1px solid trimColor;
- padding: 5px 5px 5px 5px;
-}
-.sshHostKeyPanelHeading {
- white-space: nowrap;
- margin-top: 5px;
- margin-left: 1em;
-}
-.sshHostKeyPanelFingerprintData {
- margin-left: 2em;
- white-space: nowrap;
- font-family: mono-font;
- font-size: small;
-}
-.sshHostKeyPanelKnownHostEntry {
- margin-left: 2em;
- white-space: nowrap;
- font-family: mono-font;
- font-size: small;
- width: 80em;
-}
-
-.contributorAgreementButton {
- font-weight: bold;
-}
-
-.contributorAgreementShortDescription {
- margin-left: 20px;
- margin-right: 20px;
- margin-bottom: 10px;
- padding: 5px 5px 5px 5px;
- border: 1px solid #b0bdcc;
-}
-
-.contributorAgreementAlreadySubmitted {
- margin-left: 20px;
- margin-right: 20px;
- padding: 5px 5px 5px 5px;
- color: red;
-}
-
-.contributorAgreementLegal {
- margin-left: 20px;
- margin-right: 20px;
- padding: 5px 5px 5px 5px;
- border: 1px solid #b0bdcc;
-}
-
-.registerScreenSection {
- margin-top: 2em;
-}
-.registerScreenExplain {
- margin-left: 10px;
- margin-top: 5px;
- margin-bottom: 5px;
- width: 45em;
-}
-.registerScreenNextLinks {
- margin-top: 2em;
-}
-.registerScreenNextLinks .gwt-InlineHyperlink {
- margin-left: 2em;
- white-space: nowrap;
-}
-.registerScreenSection .changeTable {
- width: 45em;
-}
-.registerScreenSection .addSshKeyPanel {
- background: none;
-}
-.registerScreenSection .sshHostKeyPanel {
- border: none;
-}
-.registerScreenSection .sshHostKeyPanel .sshHostKeyPanelKnownHostEntry {
- width: 45em;
-}
-
-.projectActions {
- margin-bottom: 10px;
-}
-
-.oauthInfoBlock {
- margin-bottom: 10px;
-}
-.oauthToken {
- font-family: monospace;
- font-size: small;
- width: 40em;
-}
-.oauthToken span {
- white-space: nowrap;
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 38em;
-}
-.oauthExpires {
- font-family: monospace;
- font-size: small;
- width: 40em;
-}
-.oauthPanel {
- margin-top: 10px;
- border: 1px solid trimColor;
- padding: 5px 5px 5px 5px;
-}
-.oauthPanelNetRCHeading {
- margin-top: 5px;
- margin-left: 1em;
- white-space: nowrap;
-}
-.oauthPanelNetRCEntry {
- margin-top: 5px;
- margin-left: 2em;
- font-family: monospace;
- font-size: small;
- width: 80em;
-}
-.oauthPanelNetRCEntry span {
- white-space: nowrap;
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 78em;
-}
-.oauthPanelCookieHeading {
- margin-top: 15px;
- margin-left: 1em;
- white-space: nowrap;
-}
-.oauthPanelCookieEntry {
- margin-top: 5px;
- margin-left: 2em;
- font-family: monospace;
- font-size: small;
- width: 80em;
-}
-.oauthPanelCookieEntry span {
- white-space: nowrap;
- display: inline-block;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 78em;
-}
-
-
-/** CommentedActionDialog **/
-.commentedActionDialog .gwt-DisclosurePanel .header td {
- font-weight: bold;
- white-space: nowrap;
-}
-.commentedActionDialog .smallHeading {
- font-size: small;
- font-weight: bold;
- white-space: nowrap;
-}
-.commentedActionDialog .commentedActionMessage {
- margin-left: 10px;
- background: trimColor;
- padding: 5px 5px 5px 5px;
-}
-.commentedActionDialog .commentedActionMessage textarea {
- font-size: small;
-}
-.commentedActionDialog .gwt-Hyperlink {
- white-space: nowrap;
- font-size: small;
-}
-.commentedActionDialog .rebaseContentPanel {
- margin-left: 10px;
- background: trimColor;
- padding: 5px 5px 5px 5px;
- width: 300px;
-}
-.commentedActionDialog .rebaseContentPanel .rebaseSuggestBox {
- font-size: small;
- width: 100%;
-}
-
-/** AccountGroupInfoScreen **/
-.groupUUIDPanel {
- margin-bottom: 10px;
-}
-.groupDescriptionPanel {
- margin-bottom: 3px;
-}
-.groupNamePanel {
- margin-bottom: 3px;
-}
-.groupNameTextBox {
- margin-bottom: 2px;
-}
-.groupOptionsPanel {
- margin-bottom: 5px;
-}
-.groupOwnerPanel {
- margin-bottom: 3px;
-}
-.groupOwnerTextBox {
- margin-bottom: 2px;
-}
-
-
-/** AccountGroupMembersScreen **/
-.groupMembersTable {
- margin-bottom: 2px;
-}
-.groupIncludesTable {
- margin-bottom: 2px;
-}
-
-
-/** AddMemberBox **/
-.addMemberTextBox {
- margin-right: 2px;
- margin-bottom: 2px;
-}
-
-
-/** ProjectBranchesScreen **/
-.specialBranchIconCell {
- background: #ECECEC;
- border-bottom: 1px solid #FFFFFF;
- border-top: 1px solid #FFFFFF;
-}
-.specialBranchDataCell {
- background: #ECECEC;
- border: 1px solid white;
- font-style: italic;
- padding: 2px 6px 1px;
-}
-
-.editHeadButton {
- float: right;
- cursor: pointer;
-}
-
-.branchTableDeleteButton {
- margin-top: 5px;
-}
-
-.branchTablePrevNextLinks {
- position: relative;
-}
-.branchTablePrevNextLinks td {
- float: left;
- width: 5em;
- text-align: left;
- padding-right: 10px;
-}
-.branchTablePrevNextLinks .gwt-Hyperlink {
- font-size: 9pt;
- color: #2a5db0;
-}
-
-/** PluginListScreen **/
-.pluginsTable {
-}
-
-/** ProjectListScreen **/
-.projectFilterPanel {
- margin-bottom: 10px;
-}
-.projectFilterPanel input {
- width: 200px;
-}
-.projectFilterLabel {
- margin-right: 5px;
-}
-.projectNameColumn {
- min-width: 300px;
-}
-
-.queryIcon {
- position: relative;
- top: 2px;
- margin-right: 3px;
-}
-
-/** ProjectSettings */
-.maxObjectSizeLimitEffectiveLabel {
- padding-top: 5px;
- padding-left: 5px;
-}
-
-.pluginProjectConfigInheritedValue {
- padding-top: 5px;
- padding-left: 5px;
-}
-
-/* StringListPanel */
-.stringListPanelButtons {
- margin-left: 0.5em;
-}
-.stringListPanelButtons .gwt-Button {
- margin-right: 2em;
- font-size: 7pt;
- padding: 1px;
-}
-
-/* List Screens */
-.pagingLink {
- font-size: 18px;
- margin-top: 5px;
- margin-bottom: 15px;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
deleted file mode 100644
index 01c4d265c9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
+++ /dev/null
@@ -1,270 +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.client.groups;
-
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.Set;
-
-/** A collection of static methods which work on the Gerrit REST API for specific groups. */
-public class GroupApi {
- /** Create a new group */
- public static void createGroup(String groupName, AsyncCallback<GroupInfo> cb) {
- JavaScriptObject in = JavaScriptObject.createObject();
- new RestApi("/groups/").id(groupName).ifNoneMatch().put(in, cb);
- }
-
- public static void getGroupDetail(String group, AsyncCallback<GroupInfo> cb) {
- group(group).view("detail").get(cb);
- }
-
- /** Get the name of a group */
- public static void getGroupName(AccountGroup.UUID group, AsyncCallback<NativeString> cb) {
- group(group).view("name").get(cb);
- }
-
- /** Check if the current user is owner of a group */
- public static void isGroupOwner(String groupName, AsyncCallback<Boolean> cb) {
- GroupMap.myOwned(
- groupName,
- new AsyncCallback<GroupMap>() {
- @Override
- public void onSuccess(GroupMap result) {
- cb.onSuccess(!result.isEmpty());
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- }
-
- /** Rename a group */
- public static void renameGroup(
- AccountGroup.UUID group, String newName, AsyncCallback<VoidResult> cb) {
- GroupInput in = GroupInput.create();
- in.name(newName);
- group(group).view("name").put(in, cb);
- }
-
- /** Set description for a group */
- public static void setGroupDescription(
- AccountGroup.UUID group, String description, AsyncCallback<VoidResult> cb) {
- RestApi call = group(group).view("description");
- if (description != null && !description.isEmpty()) {
- GroupInput in = GroupInput.create();
- in.description(description);
- call.put(in, cb);
- } else {
- call.delete(cb);
- }
- }
-
- /** Set owner for a group */
- public static void setGroupOwner(
- AccountGroup.UUID group, String owner, AsyncCallback<GroupInfo> cb) {
- GroupInput in = GroupInput.create();
- in.owner(owner);
- group(group).view("owner").put(in, cb);
- }
-
- /** Set the options for a group */
- public static void setGroupOptions(
- AccountGroup.UUID group, boolean isVisibleToAll, AsyncCallback<VoidResult> cb) {
- GroupOptionsInput in = GroupOptionsInput.create();
- in.visibleToAll(isVisibleToAll);
- group(group).view("options").put(in, cb);
- }
-
- /** Add member to a group. */
- public static void addMember(
- AccountGroup.UUID group, String member, AsyncCallback<AccountInfo> cb) {
- members(group).id(member).put(cb);
- }
-
- /** Add members to a group. */
- public static void addMembers(
- AccountGroup.UUID group, Set<String> members, AsyncCallback<JsArray<AccountInfo>> cb) {
- if (members.size() == 1) {
- addMember(
- group,
- members.iterator().next(),
- new AsyncCallback<AccountInfo>() {
- @Override
- public void onSuccess(AccountInfo result) {
- cb.onSuccess(Natives.arrayOf(result));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- } else {
- MemberInput input = MemberInput.create();
- for (String member : members) {
- input.addMember(member);
- }
- members(group).post(input, cb);
- }
- }
-
- /** Remove members from a group. */
- public static void removeMembers(
- AccountGroup.UUID group, Set<Integer> ids, AsyncCallback<VoidResult> cb) {
- if (ids.size() == 1) {
- members(group).id(ids.iterator().next().toString()).delete(cb);
- } else {
- MemberInput in = MemberInput.create();
- for (Integer id : ids) {
- in.addMember(id.toString());
- }
- group(group).view("members.delete").post(in, cb);
- }
- }
-
- /** Include a group into a group. */
- public static void addIncludedGroup(
- AccountGroup.UUID group, String include, AsyncCallback<GroupInfo> cb) {
- groups(group).id(include).put(cb);
- }
-
- /** Include groups into a group. */
- public static void addIncludedGroups(
- AccountGroup.UUID group,
- Set<String> includedGroups,
- final AsyncCallback<JsArray<GroupInfo>> cb) {
- if (includedGroups.size() == 1) {
- addIncludedGroup(
- group,
- includedGroups.iterator().next(),
- new AsyncCallback<GroupInfo>() {
- @Override
- public void onSuccess(GroupInfo result) {
- cb.onSuccess(Natives.arrayOf(result));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- } else {
- IncludedGroupInput input = IncludedGroupInput.create();
- for (String includedGroup : includedGroups) {
- input.addGroup(includedGroup);
- }
- groups(group).post(input, cb);
- }
- }
-
- /** Remove included groups from a group. */
- public static void removeIncludedGroups(
- AccountGroup.UUID group, Set<AccountGroup.UUID> ids, AsyncCallback<VoidResult> cb) {
- if (ids.size() == 1) {
- AccountGroup.UUID g = ids.iterator().next();
- groups(group).id(g.get()).delete(cb);
- } else {
- IncludedGroupInput in = IncludedGroupInput.create();
- for (AccountGroup.UUID g : ids) {
- in.addGroup(g.get());
- }
- group(group).view("groups.delete").post(in, cb);
- }
- }
-
- /** Get audit log of a group. */
- public static void getAuditLog(
- AccountGroup.UUID group, AsyncCallback<JsArray<GroupAuditEventInfo>> cb) {
- group(group).view("log.audit").get(cb);
- }
-
- private static RestApi members(AccountGroup.UUID group) {
- return group(group).view("members");
- }
-
- private static RestApi groups(AccountGroup.UUID group) {
- return group(group).view("groups");
- }
-
- private static RestApi group(AccountGroup.UUID group) {
- return group(group.get());
- }
-
- private static RestApi group(String group) {
- return new RestApi("/groups/").id(group);
- }
-
- private static class GroupInput extends JavaScriptObject {
- final native void description(String d) /*-{ if(d)this.description=d; }-*/;
-
- final native void name(String n) /*-{ if(n)this.name=n; }-*/;
-
- final native void owner(String o) /*-{ if(o)this.owner=o; }-*/;
-
- static GroupInput create() {
- return (GroupInput) createObject();
- }
-
- protected GroupInput() {}
- }
-
- private static class GroupOptionsInput extends JavaScriptObject {
- final native void visibleToAll(boolean v) /*-{ if(v)this.visible_to_all=v; }-*/;
-
- static GroupOptionsInput create() {
- return (GroupOptionsInput) createObject();
- }
-
- protected GroupOptionsInput() {}
- }
-
- private static class MemberInput extends JavaScriptObject {
- final native void init() /*-{ this.members = []; }-*/;
-
- final native void addMember(String n) /*-{ this.members.push(n); }-*/;
-
- static MemberInput create() {
- MemberInput m = (MemberInput) createObject();
- m.init();
- return m;
- }
-
- protected MemberInput() {}
- }
-
- private static class IncludedGroupInput extends JavaScriptObject {
- final native void init() /*-{ this.groups = []; }-*/;
-
- final native void addGroup(String n) /*-{ this.groups.push(n); }-*/;
-
- static IncludedGroupInput create() {
- IncludedGroupInput g = (IncludedGroupInput) createObject();
- g.init();
- return g;
- }
-
- protected IncludedGroupInput() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupAuditEventInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupAuditEventInfo.java
deleted file mode 100644
index 255c6e8e20..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupAuditEventInfo.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 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.client.groups;
-
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.client.impl.ser.JavaSqlTimestamp_JsonSerializer;
-import java.sql.Timestamp;
-
-public class GroupAuditEventInfo extends JavaScriptObject {
- public enum Type {
- ADD_USER,
- REMOVE_USER,
- ADD_GROUP,
- REMOVE_GROUP
- }
-
- public final Timestamp date() {
- return JavaSqlTimestamp_JsonSerializer.parseTimestamp(dateRaw());
- }
-
- public final Type type() {
- return Type.valueOf(typeRaw());
- }
-
- public final native AccountInfo user() /*-{ return this.user; }-*/;
-
- public final native AccountInfo memberAsUser() /*-{ return this.member; }-*/;
-
- public final native GroupInfo memberAsGroup() /*-{ return this.member; }-*/;
-
- private native String dateRaw() /*-{ return this.date; }-*/;
-
- private native String typeRaw() /*-{ return this.type; }-*/;
-
- protected GroupAuditEventInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupList.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupList.java
deleted file mode 100644
index db966b1d98..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupList.java
+++ /dev/null
@@ -1,34 +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.client.groups;
-
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Groups available from {@code /groups/} or {@code /accounts/[id]/groups}. */
-public class GroupList extends JsArray<GroupInfo> {
- public static void my(AsyncCallback<GroupList> callback) {
- new RestApi("/accounts/self/groups").get(callback);
- }
-
- public static void included(AccountGroup.UUID group, AsyncCallback<GroupList> callback) {
- new RestApi("/groups/").id(group.get()).view("groups").get(callback);
- }
-
- protected GroupList() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupMap.java
deleted file mode 100644
index 73ac183326..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupMap.java
+++ /dev/null
@@ -1,78 +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.client.groups;
-
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Groups available from {@code /groups/}. */
-public class GroupMap extends NativeMap<GroupInfo> {
- public static void all(AsyncCallback<GroupMap> callback) {
- groups().get(NativeMap.copyKeysIntoChildren(callback));
- }
-
- public static void match(String match, int limit, int start, AsyncCallback<GroupMap> cb) {
- RestApi call = groups();
- if (match != null) {
- if (match.startsWith("^")) {
- call.addParameter("r", match);
- } else {
- call.addParameter("m", match);
- }
- }
- if (limit > 0) {
- call.addParameter("n", limit);
- }
- if (start > 0) {
- call.addParameter("S", start);
- }
- call.get(NativeMap.copyKeysIntoChildren(cb));
- }
-
- public static void suggestAccountGroupForProject(
- String project, String query, int limit, AsyncCallback<GroupMap> cb) {
- RestApi call = groups();
- if (project != null) {
- call.addParameter("p", project);
- }
- if (query != null) {
- call.addParameter("s", query);
- }
- if (limit > 0) {
- call.addParameter("n", limit);
- }
- call.get(NativeMap.copyKeysIntoChildren(cb));
- }
-
- public static void myOwned(AsyncCallback<GroupMap> cb) {
- myOwnedGroups().get(NativeMap.copyKeysIntoChildren(cb));
- }
-
- public static void myOwned(String groupName, AsyncCallback<GroupMap> cb) {
- myOwnedGroups().addParameter("g", groupName).get(NativeMap.copyKeysIntoChildren(cb));
- }
-
- private static RestApi myOwnedGroups() {
- return groups().addParameterTrue("owned");
- }
-
- private static RestApi groups() {
- return new RestApi("/groups/");
- }
-
- protected GroupMap() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css
deleted file mode 100644
index c92117c337..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gwt_override.css
+++ /dev/null
@@ -1,88 +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.
- */
-
-@external .gwt-Button;
-@external .gwt-DialogBox .dialogMiddleCenter;
-@external .gwt-TabBar;
-@external .gwt-TabBarFirst;
-@external .gwt-TabBarItem;
-@external .gwt-TabBarItem-selected;
-@external .gwt-TabBarRest;
-@external .gwt-TabPanel;
-@external .gwt-TabPanelBottom;
-
-@eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
-@eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
-@eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
-@eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-
-body {
- background: backgroundColor;
- color: textColor;
-}
-
-.gwt-DialogBox .dialogMiddleCenter {
- background: backgroundColor;
- color: textColor;
-}
-
-.gwt-Button {
- white-space: nowrap;
-}
-
-.gwt-TabBar .gwt-TabBarItem,
-.gwt-TabBar .gwt-TabBarRest,
-.gwt-TabPanelBottom {
- background: transparent;
-}
-
-.gwt-TabBar {
- border-bottom: 1px solid black;
-}
-.gwt-TabBar .gwt-TabBarFirst {
- display: none;
-}
-.gwt-TabBar .gwt-TabBarItem {
- color: #353535;
- margin: 0;
- background: trimColor;
- padding-top: 0.5em;
- padding-bottom: 1px;
- padding-left: 1em;
- padding-right: 1em;
- border-bottom: 3px solid transparent;
- border-right: 0;
-}
-.gwt-TabBar .gwt-TabBarItem-selected {
- color: #990000;
- background: selectionColor;
- border-bottom-color: #990000;
-}
-.gwt-TabBar .gwt-TabBarRest {
- background: trimColor;
- padding-top: 0.5em;
- padding-bottom: 1px;
-}
-.gwt-TabBar .gwt-TabPanelBottom {
- background: trimColor;
- border-top: 1px solid black;
- border-left: none;
- border-right: none;
- border-bottom: none;
- padding: 1px;
-}
-.gwt-TabPanel .gwt-TabPanelBottom {
- border: none;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
deleted file mode 100644
index 4c4c8da9d3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
+++ /dev/null
@@ -1,91 +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.client.patches;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface PatchConstants extends Constants {
- String patchBase();
-
- String patchSet();
-
- String upToChange();
-
- String openReply();
-
- String linePrev();
-
- String lineNext();
-
- String chunkPrev();
-
- String chunkNext();
-
- String commentPrev();
-
- String commentNext();
-
- String focusSideA();
-
- String focusSideB();
-
- String expandComment();
-
- String expandAllCommentsOnCurrentLine();
-
- String toggleSideA();
-
- String toggleIntraline();
-
- String showPreferences();
-
- String toggleReviewed();
-
- String markAsReviewedAndGoToNext();
-
- String commentEditorSet();
-
- String commentInsert();
-
- String commentSaveDraft();
-
- String commentCancelEdit();
-
- String whitespaceIGNORE_NONE();
-
- String whitespaceIGNORE_TRAILING();
-
- String whitespaceIGNORE_LEADING_AND_TRAILING();
-
- String whitespaceIGNORE_ALL();
-
- String previousFileHelp();
-
- String nextFileHelp();
-
- String download();
-
- String edit();
-
- String blame();
-
- String addFileCommentToolTip();
-
- String cannedReplyDone();
-
- String sideBySideDiff();
-
- String unifiedDiff();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
deleted file mode 100644
index 13f0afac89..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
+++ /dev/null
@@ -1,44 +0,0 @@
-cannedReplyDone = Done
-
-patchBase = Base
-patchSet = Patch Set
-
-upToChange = Up to change
-openReply = Reply and score
-linePrev = Previous line
-lineNext = Next line
-chunkPrev = Previous diff chunk
-chunkNext = Next diff chunk or search result
-commentPrev = Previous comment
-commentNext = Next comment
-focusSideA = Focus left side
-focusSideB = Focus right side
-expandComment = Expand or collapse comment
-expandAllCommentsOnCurrentLine = Expand or collapse all comments on current line
-toggleSideA = Toggle left side
-toggleIntraline = Toggle intraline difference
-showPreferences = Show diff preferences
-
-toggleReviewed = Toggle the reviewed flag
-markAsReviewedAndGoToNext = Mark patch as reviewed and go to next unreviewed patch
-
-commentEditorSet = Comment Editing
-commentInsert = Create a new inline comment
-commentSaveDraft = Save draft comment
-commentCancelEdit = Cancel comment edit
-
-whitespaceIGNORE_NONE=None
-whitespaceIGNORE_TRAILING=At Line End
-whitespaceIGNORE_LEADING_AND_TRAILING=Leading, At Line End
-whitespaceIGNORE_ALL=All
-
-previousFileHelp = Previous file
-nextFileHelp = Next file
-
-download = Download
-edit = Edit
-blame = Blame
-addFileCommentToolTip = Click to add file comment
-
-sideBySideDiff = Side-by-side diff
-unifiedDiff = Unified diff
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java
deleted file mode 100644
index 358ccd3674..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java
+++ /dev/null
@@ -1,27 +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.client.patches;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface PatchMessages extends Messages {
- String expandBefore(int cnt);
-
- String expandAfter(int cnt);
-
- String patchSkipRegion(String lineNumber);
-
- String fileNameWithShortcutKey(String file, String key);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties
deleted file mode 100644
index 8dcebdc022..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-expandBefore = +{0}&#x21e7;
-expandAfter = +{0}&#x21e9;
-patchSkipRegion = ... skipped {0} common lines ...
-fileNameWithShortcutKey = {0} (Shortcut: {1})
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties
deleted file mode 100644
index 8dcebdc022..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-expandBefore = +{0}&#x21e7;
-expandAfter = +{0}&#x21e9;
-patchSkipRegion = ... skipped {0} common lines ...
-fileNameWithShortcutKey = {0} (Shortcut: {1})
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
deleted file mode 100644
index d599756b39..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
+++ /dev/null
@@ -1,22 +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.client.patches;
-
-import com.google.gwt.core.client.GWT;
-
-public class PatchUtil {
- public static final PatchConstants C = GWT.create(PatchConstants.class);
- public static final PatchMessages M = GWT.create(PatchMessages.class);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SkippedLine.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SkippedLine.java
deleted file mode 100644
index 486e7b8466..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SkippedLine.java
+++ /dev/null
@@ -1,50 +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.client.patches;
-
-public class SkippedLine {
-
- private int a;
- private int b;
- private int sz;
-
- public SkippedLine(int startA, int startB, int size) {
- a = startA;
- b = startB;
- sz = size;
- }
-
- public int getStartA() {
- return a;
- }
-
- public int getStartB() {
- return b;
- }
-
- public int getSize() {
- return sz;
- }
-
- public void incrementStart(int n) {
- a += n;
- b += n;
- reduceSize(n);
- }
-
- public void reduceSize(int n) {
- sz -= n;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java
deleted file mode 100644
index ac073de4c8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2012 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.client.plugins;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class PluginInfo extends JavaScriptObject {
- public final native String name() /*-{ return this.name }-*/;
-
- public final native String version() /*-{ return this.version }-*/;
-
- public final native String indexUrl() /*-{ return this.index_url }-*/;
-
- public final native boolean disabled() /*-{ return this.disabled || false }-*/;
-
- protected PluginInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
deleted file mode 100644
index cea27b93e8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2012 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.client.plugins;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Plugins available from {@code /plugins/}. */
-public class PluginMap extends NativeMap<PluginInfo> {
- public static void all(AsyncCallback<PluginMap> callback) {
- new RestApi("/plugins/").addParameterTrue("all").get(NativeMap.copyKeysIntoChildren(callback));
- }
-
- protected PluginMap() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java
deleted file mode 100644
index 097f26ad43..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/BranchInfo.java
+++ /dev/null
@@ -1,30 +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.client.projects;
-
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gwt.core.client.JsArray;
-
-public class BranchInfo extends RefInfo {
- public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;
-
- public final native NativeMap<ActionInfo> actions() /*-{ return this.actions }-*/;
-
- public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- protected BranchInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
deleted file mode 100644
index 684f8e6f6f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
+++ /dev/null
@@ -1,262 +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.client.projects;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.info.ActionInfo;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.extensions.client.ProjectState;
-import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwtexpui.safehtml.client.FindReplace;
-import com.google.gwtexpui.safehtml.client.LinkFindReplace;
-import com.google.gwtexpui.safehtml.client.RawFindReplace;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ConfigInfo extends JavaScriptObject {
-
- public final native String description() /*-{ return this.description }-*/;
-
- public final native InheritedBooleanInfo requireChangeId()
- /*-{ return this.require_change_id; }-*/ ;
-
- public final native InheritedBooleanInfo useContentMerge()
- /*-{ return this.use_content_merge; }-*/ ;
-
- public final native InheritedBooleanInfo useContributorAgreements()
- /*-{ return this.use_contributor_agreements; }-*/ ;
-
- public final native InheritedBooleanInfo createNewChangeForAllNotInTarget()
- /*-{ return this.create_new_change_for_all_not_in_target; }-*/ ;
-
- public final native InheritedBooleanInfo useSignedOffBy()
- /*-{ return this.use_signed_off_by; }-*/ ;
-
- public final native InheritedBooleanInfo enableSignedPush()
- /*-{ return this.enable_signed_push; }-*/ ;
-
- public final native InheritedBooleanInfo requireSignedPush()
- /*-{ return this.require_signed_push; }-*/ ;
-
- public final native InheritedBooleanInfo rejectImplicitMerges()
- /*-{ return this.reject_implicit_merges; }-*/ ;
-
- public final native InheritedBooleanInfo privateByDefault()
- /*-{ return this.private_by_default; }-*/ ;
-
- public final native InheritedBooleanInfo workInProgressByDefault()
- /*-{ return this.work_in_progress_by_default; }-*/ ;
-
- public final native InheritedBooleanInfo enableReviewerByEmail()
- /*-{ return this.enable_reviewer_by_email; }-*/ ;
-
- public final native InheritedBooleanInfo matchAuthorToCommitterDate()
- /*-{ return this.match_author_to_committer_date; }-*/ ;
-
- public final SubmitType submitType() {
- return SubmitType.valueOf(submitTypeRaw());
- }
-
- public final native SubmitTypeInfo defaultSubmitType() /*-{ return this.default_submit_type; }-*/;
-
- public final native NativeMap<NativeMap<ConfigParameterInfo>> pluginConfig()
- /*-{ return this.plugin_config || {}; }-*/ ;
-
- public final native NativeMap<ConfigParameterInfo> pluginConfig(String p)
- /*-{ return this.plugin_config[p]; }-*/ ;
-
- public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
-
- private native String submitTypeRaw() /*-{ return this.submit_type }-*/;
-
- public final ProjectState state() {
- if (stateRaw() == null) {
- return ProjectState.ACTIVE;
- }
- return ProjectState.valueOf(stateRaw());
- }
-
- private native String stateRaw() /*-{ return this.state }-*/;
-
- public final native MaxObjectSizeLimitInfo maxObjectSizeLimit()
- /*-{ return this.max_object_size_limit; }-*/ ;
-
- private native NativeMap<CommentLinkInfo> commentlinks0() /*-{ return this.commentlinks; }-*/;
-
- final List<FindReplace> commentlinks() {
- JsArray<CommentLinkInfo> cls = commentlinks0().values();
- List<FindReplace> commentLinks = new ArrayList<>(cls.length());
- for (int i = 0; i < cls.length(); i++) {
- CommentLinkInfo cl = cls.get(i);
- if (!cl.enabled()) {
- continue;
- }
- if (cl.link() != null) {
- commentLinks.add(new LinkFindReplace(cl.match(), cl.link()));
- } else {
- try {
- FindReplace fr = new RawFindReplace(cl.match(), cl.html());
- commentLinks.add(fr);
- } catch (RuntimeException e) {
- int index = e.getMessage().indexOf("at Object");
- new ErrorDialog(
- "Invalid commentlink configuration: "
- + (index == -1 ? e.getMessage() : e.getMessage().substring(0, index)))
- .center();
- }
- }
- }
- return commentLinks;
- }
-
- final native ThemeInfo theme() /*-{ return this.theme; }-*/;
-
- final native NativeMap<JsArrayString>
- extensionPanelNames() /*-{ return this.extension_panel_names; }-*/;
-
- protected ConfigInfo() {}
-
- static class CommentLinkInfo extends JavaScriptObject {
- final native String match() /*-{ return this.match; }-*/;
-
- final native String link() /*-{ return this.link; }-*/;
-
- final native String html() /*-{ return this.html; }-*/;
-
- final native boolean enabled() /*-{
- return !this.hasOwnProperty('enabled') || this.enabled;
- }-*/;
-
- protected CommentLinkInfo() {}
- }
-
- public static class InheritedBooleanInfo extends JavaScriptObject {
- public static InheritedBooleanInfo create() {
- return (InheritedBooleanInfo) createObject();
- }
-
- public final native boolean value() /*-{ return this.value ? true : false; }-*/;
-
- public final native boolean inheritedValue()
- /*-{ return this.inherited_value ? true : false; }-*/ ;
-
- public final InheritableBoolean configuredValue() {
- return InheritableBoolean.valueOf(configuredValueRaw());
- }
-
- private native String configuredValueRaw() /*-{ return this.configured_value }-*/;
-
- public final void setConfiguredValue(InheritableBoolean v) {
- setConfiguredValueRaw(v.name());
- }
-
- public final native void setConfiguredValueRaw(String v)
- /*-{ if(v)this.configured_value=v; }-*/ ;
-
- protected InheritedBooleanInfo() {}
- }
-
- public static class MaxObjectSizeLimitInfo extends JavaScriptObject {
- public final native String value() /*-{ return this.value; }-*/;
-
- public final native String configuredValue() /*-{ return this.configured_value }-*/;
-
- public final native String summary() /*-{ return this.summary; }-*/;
-
- protected MaxObjectSizeLimitInfo() {}
- }
-
- public static class ConfigParameterInfo extends JavaScriptObject {
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String displayName() /*-{ return this.display_name; }-*/;
-
- public final native String description() /*-{ return this.description; }-*/;
-
- public final native String warning() /*-{ return this.warning; }-*/;
-
- public final native String type() /*-{ return this.type; }-*/;
-
- public final native String value() /*-{ return this.value; }-*/;
-
- public final native boolean editable() /*-{ return this.editable ? true : false; }-*/;
-
- public final native boolean inheritable() /*-{ return this.inheritable ? true : false; }-*/;
-
- public final native String configuredValue() /*-{ return this.configured_value; }-*/;
-
- public final native String inheritedValue() /*-{ return this.inherited_value; }-*/;
-
- public final native JsArrayString permittedValues() /*-{ return this.permitted_values; }-*/;
-
- public final native JsArrayString values() /*-{ return this.values; }-*/;
-
- protected ConfigParameterInfo() {}
- }
-
- public static class ConfigParameterValue extends JavaScriptObject {
- final native void init() /*-{ this.values = []; }-*/;
-
- final native void addValue(String v) /*-{ this.values.push(v); }-*/;
-
- final native void setValue(String v) /*-{ if(v)this.value = v; }-*/;
-
- public static ConfigParameterValue create() {
- ConfigParameterValue v = createObject().cast();
- return v;
- }
-
- public final ConfigParameterValue values(String[] values) {
- init();
- for (String v : values) {
- addValue(v);
- }
- return this;
- }
-
- public final ConfigParameterValue value(String v) {
- setValue(v);
- return this;
- }
-
- protected ConfigParameterValue() {}
- }
-
- public static class SubmitTypeInfo extends JavaScriptObject {
- public final SubmitType value() {
- return SubmitType.valueOf(valueRaw());
- }
-
- public final SubmitType configuredValue() {
- return SubmitType.valueOf(configuredValueRaw());
- }
-
- public final SubmitType inheritedValue() {
- return SubmitType.valueOf(inheritedValueRaw());
- }
-
- private final native String valueRaw() /*-{ return this.value; }-*/;
-
- private final native String configuredValueRaw() /*-{ return this.configured_value; }-*/;
-
- private final native String inheritedValueRaw() /*-{ return this.inherited_value; }-*/;
-
- protected SubmitTypeInfo() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java
deleted file mode 100644
index 7262b3a044..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfoCache.java
+++ /dev/null
@@ -1,141 +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.client.projects;
-
-import com.google.gerrit.client.changes.ChangeApi;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.client.ui.CommentLinkProcessor;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Cache of {@link ConfigInfo} objects by project name. */
-public class ConfigInfoCache {
- private static final int PROJECT_LIMIT = 25;
- private static final int CHANGE_LIMIT = 100;
- private static final ConfigInfoCache instance = GWT.create(ConfigInfoCache.class);
-
- public static class Entry {
- private final ConfigInfo info;
- private CommentLinkProcessor commentLinkProcessor;
-
- private Entry(ConfigInfo info) {
- this.info = info;
- }
-
- public CommentLinkProcessor getCommentLinkProcessor() {
- if (commentLinkProcessor == null) {
- commentLinkProcessor = new CommentLinkProcessor(info.commentlinks());
- }
- return commentLinkProcessor;
- }
-
- public ThemeInfo getTheme() {
- return info.theme();
- }
-
- public List<String> getExtensionPanelNames(String extensionPoint) {
- return Natives.asList(info.extensionPanelNames().get(extensionPoint));
- }
- }
-
- public static void get(Project.NameKey name, AsyncCallback<Entry> cb) {
- instance.getImpl(name.get(), cb);
- }
-
- public static void get(Change.Id changeId, AsyncCallback<Entry> cb) {
- instance.getImpl(changeId.get(), cb);
- }
-
- public static void add(ChangeInfo info) {
- instance.changeToProject.put(info.legacyId().get(), info.project());
- }
-
- private final LinkedHashMap<String, Entry> cache;
- private final LinkedHashMap<Integer, String> changeToProject;
-
- protected ConfigInfoCache() {
- cache =
- new LinkedHashMap<String, Entry>(PROJECT_LIMIT) {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected boolean removeEldestEntry(Map.Entry<String, ConfigInfoCache.Entry> e) {
- return size() > PROJECT_LIMIT;
- }
- };
-
- changeToProject =
- new LinkedHashMap<Integer, String>(CHANGE_LIMIT) {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected boolean removeEldestEntry(Map.Entry<Integer, String> e) {
- return size() > CHANGE_LIMIT;
- }
- };
- }
-
- private void getImpl(String name, AsyncCallback<Entry> cb) {
- Entry e = cache.get(name);
- if (e != null) {
- cb.onSuccess(e);
- return;
- }
- ProjectApi.getConfig(
- new Project.NameKey(name),
- new AsyncCallback<ConfigInfo>() {
- @Override
- public void onSuccess(ConfigInfo result) {
- Entry e = new Entry(result);
- cache.put(name, e);
- cb.onSuccess(e);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- }
-
- private void getImpl(Integer id, AsyncCallback<Entry> cb) {
- String name = changeToProject.get(id);
- if (name != null) {
- getImpl(name, cb);
- return;
- }
- // TODO(hiesel) Make a preflight request to get project before we deprecate the numeric changeId
- ChangeApi.change(null, id)
- .get(
- new AsyncCallback<ChangeInfo>() {
- @Override
- public void onSuccess(ChangeInfo result) {
- changeToProject.put(id, result.project());
- getImpl(result.project(), cb);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
deleted file mode 100644
index 3be52e6722..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ /dev/null
@@ -1,461 +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.client.projects;
-
-import com.google.gerrit.client.VoidResult;
-import com.google.gerrit.client.projects.ConfigInfo.ConfigParameterValue;
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gerrit.client.rpc.RestApi;
-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.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-public class ProjectApi {
- /** Create a new project */
- public static void createProject(
- String projectName,
- String parent,
- Boolean createEmptyCcommit,
- Boolean permissionsOnly,
- AsyncCallback<VoidResult> cb) {
- ProjectInput input = ProjectInput.create();
- input.setName(projectName);
- input.setParent(parent);
- input.setPermissionsOnly(permissionsOnly);
- input.setCreateEmptyCommit(createEmptyCcommit);
- new RestApi("/projects/").id(projectName).ifNoneMatch().put(input, cb);
- }
-
- private static RestApi getRestApi(
- Project.NameKey name, String viewName, int limit, int start, String match) {
- RestApi call = project(name).view(viewName);
- call.addParameter("n", limit);
- call.addParameter("S", start);
- if (match != null) {
- if (match.startsWith("^")) {
- call.addParameter("r", match);
- } else {
- call.addParameter("m", match);
- }
- }
- return call;
- }
-
- /** Create a new tag */
- public static void createTag(
- Project.NameKey name,
- String ref,
- String revision,
- String annotation,
- AsyncCallback<TagInfo> cb) {
- TagInput input = TagInput.create();
- input.setRevision(revision);
- input.setMessage(annotation);
- project(name).view("tags").id(ref).ifNoneMatch().put(input, cb);
- }
-
- /** Retrieve all visible tags of the project */
- public static void getTags(Project.NameKey name, AsyncCallback<JsArray<TagInfo>> cb) {
- project(name).view("tags").get(cb);
- }
-
- public static void getTags(
- Project.NameKey name,
- int limit,
- int start,
- String match,
- AsyncCallback<JsArray<TagInfo>> cb) {
- getRestApi(name, "tags", limit, start, match).get(cb);
- }
-
- /** Delete tags. One call is fired to the server to delete all the tags. */
- public static void deleteTags(
- Project.NameKey name, Set<String> refs, AsyncCallback<VoidResult> cb) {
- if (refs.size() == 1) {
- project(name).view("tags").id(refs.iterator().next()).delete(cb);
- } else {
- DeleteTagsInput d = DeleteTagsInput.create();
- for (String ref : refs) {
- d.addTag(ref);
- }
- project(name).view("tags:delete").post(d, cb);
- }
- }
-
- /** Create a new branch */
- public static void createBranch(
- Project.NameKey name, String ref, String revision, AsyncCallback<BranchInfo> cb) {
- BranchInput input = BranchInput.create();
- input.setRevision(revision);
- project(name).view("branches").id(ref).ifNoneMatch().put(input, cb);
- }
-
- /** Retrieve all visible branches of the project */
- public static void getBranches(Project.NameKey name, AsyncCallback<JsArray<BranchInfo>> cb) {
- project(name).view("branches").get(cb);
- }
-
- public static void getBranches(
- Project.NameKey name,
- int limit,
- int start,
- String match,
- AsyncCallback<JsArray<BranchInfo>> cb) {
- getRestApi(name, "branches", limit, start, match).get(cb);
- }
-
- /** Delete branches. One call is fired to the server to delete all the branches. */
- public static void deleteBranches(
- Project.NameKey name, Set<String> refs, AsyncCallback<VoidResult> cb) {
- if (refs.size() == 1) {
- project(name).view("branches").id(refs.iterator().next()).delete(cb);
- } else {
- DeleteBranchesInput d = DeleteBranchesInput.create();
- for (String ref : refs) {
- d.addBranch(ref);
- }
- project(name).view("branches:delete").post(d, cb);
- }
- }
-
- public static void getConfig(Project.NameKey name, AsyncCallback<ConfigInfo> cb) {
- project(name).view("config").get(cb);
- }
-
- public static void setConfig(
- Project.NameKey name,
- String description,
- InheritableBoolean useContributorAgreements,
- InheritableBoolean useContentMerge,
- InheritableBoolean useSignedOffBy,
- InheritableBoolean createNewChangeForAllNotInTarget,
- InheritableBoolean requireChangeId,
- InheritableBoolean enableSignedPush,
- InheritableBoolean requireSignedPush,
- InheritableBoolean rejectImplicitMerges,
- InheritableBoolean privateByDefault,
- InheritableBoolean workInProgressByDefault,
- InheritableBoolean enableReviewerByEmail,
- InheritableBoolean matchAuthorToCommitterDate,
- String maxObjectSizeLimit,
- SubmitType submitType,
- ProjectState state,
- Map<String, Map<String, ConfigParameterValue>> pluginConfigValues,
- AsyncCallback<ConfigInfo> cb) {
- ConfigInput in = ConfigInput.create();
- in.setDescription(description);
- in.setUseContributorAgreements(useContributorAgreements);
- in.setUseContentMerge(useContentMerge);
- in.setUseSignedOffBy(useSignedOffBy);
- in.setRequireChangeId(requireChangeId);
- in.setCreateNewChangeForAllNotInTarget(createNewChangeForAllNotInTarget);
- if (enableSignedPush != null) {
- in.setEnableSignedPush(enableSignedPush);
- }
- if (requireSignedPush != null) {
- in.setRequireSignedPush(requireSignedPush);
- }
- in.setRejectImplicitMerges(rejectImplicitMerges);
- in.setPrivateByDefault(privateByDefault);
- in.setWorkInProgressByDefault(workInProgressByDefault);
- in.setMaxObjectSizeLimit(maxObjectSizeLimit);
- if (submitType != null) {
- in.setSubmitType(submitType);
- }
- in.setState(state);
- in.setPluginConfigValues(pluginConfigValues);
- in.setEnableReviewerByEmail(enableReviewerByEmail);
- in.setMatchAuthorToCommitterDate(matchAuthorToCommitterDate);
-
- project(name).view("config").put(in, cb);
- }
-
- public static void getParent(Project.NameKey name, AsyncCallback<Project.NameKey> cb) {
- project(name)
- .view("parent")
- .get(
- new AsyncCallback<NativeString>() {
- @Override
- public void onSuccess(NativeString result) {
- cb.onSuccess(new Project.NameKey(result.asString()));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- cb.onFailure(caught);
- }
- });
- }
-
- public static void getChildren(
- Project.NameKey name, boolean recursive, AsyncCallback<JsArray<ProjectInfo>> cb) {
- RestApi view = project(name).view("children");
- if (recursive) {
- view.addParameterTrue("recursive");
- }
- view.get(cb);
- }
-
- public static void getDescription(Project.NameKey name, AsyncCallback<NativeString> cb) {
- project(name).view("description").get(cb);
- }
-
- public static void setDescription(
- Project.NameKey name, String description, AsyncCallback<NativeString> cb) {
- RestApi call = project(name).view("description");
- if (description != null && !description.isEmpty()) {
- DescriptionInput input = DescriptionInput.create();
- input.setDescription(description);
- call.put(input, cb);
- } else {
- call.delete(cb);
- }
- }
-
- public static void setHead(Project.NameKey name, String ref, AsyncCallback<NativeString> cb) {
- RestApi call = project(name).view("HEAD");
- HeadInput input = HeadInput.create();
- input.setRef(ref);
- call.put(input, cb);
- }
-
- public static RestApi project(Project.NameKey name) {
- return new RestApi("/projects/").id(name.get());
- }
-
- private static class ProjectInput extends JavaScriptObject {
- static ProjectInput create() {
- return (ProjectInput) createObject();
- }
-
- protected ProjectInput() {}
-
- final native void setName(String n) /*-{ if(n)this.name=n; }-*/;
-
- final native void setParent(String p) /*-{ if(p)this.parent=p; }-*/;
-
- final native void setPermissionsOnly(boolean po) /*-{ if(po)this.permissions_only=po; }-*/;
-
- final native void setCreateEmptyCommit(boolean cc) /*-{ if(cc)this.create_empty_commit=cc; }-*/;
- }
-
- private static class ConfigInput extends JavaScriptObject {
- static ConfigInput create() {
- return (ConfigInput) createObject();
- }
-
- protected ConfigInput() {}
-
- final native void setDescription(String d) /*-{ if(d)this.description=d; }-*/;
-
- final void setUseContributorAgreements(InheritableBoolean v) {
- setUseContributorAgreementsRaw(v.name());
- }
-
- private native void setUseContributorAgreementsRaw(String v)
- /*-{ if(v)this.use_contributor_agreements=v; }-*/ ;
-
- final void setUseContentMerge(InheritableBoolean v) {
- setUseContentMergeRaw(v.name());
- }
-
- private native void setUseContentMergeRaw(String v) /*-{ if(v)this.use_content_merge=v; }-*/;
-
- final void setUseSignedOffBy(InheritableBoolean v) {
- setUseSignedOffByRaw(v.name());
- }
-
- private native void setUseSignedOffByRaw(String v) /*-{ if(v)this.use_signed_off_by=v; }-*/;
-
- final void setRequireChangeId(InheritableBoolean v) {
- setRequireChangeIdRaw(v.name());
- }
-
- private native void setRequireChangeIdRaw(String v) /*-{ if(v)this.require_change_id=v; }-*/;
-
- final void setCreateNewChangeForAllNotInTarget(InheritableBoolean v) {
- setCreateNewChangeForAllNotInTargetRaw(v.name());
- }
-
- private native void setCreateNewChangeForAllNotInTargetRaw(String v)
- /*-{ if(v)this.create_new_change_for_all_not_in_target=v; }-*/ ;
-
- final void setEnableSignedPush(InheritableBoolean v) {
- setEnableSignedPushRaw(v.name());
- }
-
- private native void setEnableSignedPushRaw(String v) /*-{ if(v)this.enable_signed_push=v; }-*/;
-
- final void setRequireSignedPush(InheritableBoolean v) {
- setRequireSignedPushRaw(v.name());
- }
-
- final void setPrivateByDefault(InheritableBoolean v) {
- setPrivateByDefault(v.name());
- }
-
- private native void setPrivateByDefault(String v) /*-{ if(v)this.private_by_default=v; }-*/;
-
- final void setWorkInProgressByDefault(InheritableBoolean v) {
- setWorkInProgressByDefault(v.name());
- }
-
- private native void setWorkInProgressByDefault(
- String v) /*-{ if(v)this.work_in_progress_by_default=v; }-*/;
-
- final void setEnableReviewerByEmail(InheritableBoolean v) {
- setEnableReviewerByEmailRaw(v.name());
- }
-
- final void setMatchAuthorToCommitterDate(InheritableBoolean v) {
- setMatchAuthorToCommitterDateRaw(v.name());
- }
-
- private native void setMatchAuthorToCommitterDateRaw(String v)
- /*-{ if(v)this.match_author_to_committer_date=v; }-*/ ;
-
- private native void setEnableReviewerByEmailRaw(String v)
- /*-{ if(v)this.enable_reviewer_by_email=v; }-*/ ;
-
- private native void setRequireSignedPushRaw(String v)
- /*-{ if(v)this.require_signed_push=v; }-*/ ;
-
- final void setRejectImplicitMerges(InheritableBoolean v) {
- setRejectImplicitMergesRaw(v.name());
- }
-
- private native void setRejectImplicitMergesRaw(String v)
- /*-{ if(v)this.reject_implicit_merges=v; }-*/ ;
-
- final native void setMaxObjectSizeLimit(String l) /*-{ if(l)this.max_object_size_limit=l; }-*/;
-
- final void setSubmitType(SubmitType t) {
- setSubmitTypeRaw(t.name());
- }
-
- private native void setSubmitTypeRaw(String t) /*-{ if(t)this.submit_type=t; }-*/;
-
- final void setState(ProjectState s) {
- setStateRaw(s.name());
- }
-
- private native void setStateRaw(String s) /*-{ if(s)this.state=s; }-*/;
-
- final void setPluginConfigValues(
- Map<String, Map<String, ConfigParameterValue>> pluginConfigValues) {
- if (!pluginConfigValues.isEmpty()) {
- NativeMap<ConfigParameterValueMap> configValues = NativeMap.create().cast();
- for (Entry<String, Map<String, ConfigParameterValue>> e : pluginConfigValues.entrySet()) {
- ConfigParameterValueMap values = ConfigParameterValueMap.create();
- configValues.put(e.getKey(), values);
- for (Entry<String, ConfigParameterValue> e2 : e.getValue().entrySet()) {
- values.put(e2.getKey(), e2.getValue());
- }
- }
- setPluginConfigValuesRaw(configValues);
- }
- }
-
- private native void setPluginConfigValuesRaw(NativeMap<ConfigParameterValueMap> v)
- /*-{ this.plugin_config_values=v; }-*/ ;
- }
-
- private static class ConfigParameterValueMap extends JavaScriptObject {
- static ConfigParameterValueMap create() {
- return createObject().cast();
- }
-
- protected ConfigParameterValueMap() {}
-
- public final native void put(String n, ConfigParameterValue v) /*-{ this[n] = v; }-*/;
- }
-
- private static class TagInput extends JavaScriptObject {
- static TagInput create() {
- return (TagInput) createObject();
- }
-
- protected TagInput() {}
-
- final native void setRevision(String r) /*-{ if(r)this.revision=r; }-*/;
-
- final native void setMessage(String m) /*-{ if(m)this.message=m; }-*/;
- }
-
- private static class BranchInput extends JavaScriptObject {
- static BranchInput create() {
- return (BranchInput) createObject();
- }
-
- protected BranchInput() {}
-
- final native void setRevision(String r) /*-{ if(r)this.revision=r; }-*/;
- }
-
- private static class DescriptionInput extends JavaScriptObject {
- static DescriptionInput create() {
- return (DescriptionInput) createObject();
- }
-
- protected DescriptionInput() {}
-
- final native void setDescription(String d) /*-{ if(d)this.description=d; }-*/;
- }
-
- private static class HeadInput extends JavaScriptObject {
- static HeadInput create() {
- return createObject().cast();
- }
-
- protected HeadInput() {}
-
- final native void setRef(String r) /*-{ if(r)this.ref=r; }-*/;
- }
-
- private static class DeleteTagsInput extends JavaScriptObject {
- static DeleteTagsInput create() {
- DeleteTagsInput d = createObject().cast();
- d.init();
- return d;
- }
-
- protected DeleteTagsInput() {}
-
- final native void init() /*-{ this.tags = []; }-*/;
-
- final native void addTag(String b) /*-{ this.tags.push(b); }-*/;
- }
-
- private static class DeleteBranchesInput extends JavaScriptObject {
- static DeleteBranchesInput create() {
- DeleteBranchesInput d = createObject().cast();
- d.init();
- return d;
- }
-
- protected DeleteBranchesInput() {}
-
- final native void init() /*-{ this.branches = []; }-*/;
-
- final native void addBranch(String b) /*-{ this.branches.push(b); }-*/;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
deleted file mode 100644
index 1ff568f8a9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectInfo.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2012 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.client.projects;
-
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gerrit.extensions.client.ProjectState;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.SuggestOracle;
-
-public class ProjectInfo extends JavaScriptObject implements SuggestOracle.Suggestion {
- public final Project.NameKey name_key() {
- return new Project.NameKey(name());
- }
-
- public final native String name() /*-{ return this.name; }-*/;
-
- public final native String description() /*-{ return this.description; }-*/;
-
- public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- public final ProjectState state() {
- return ProjectState.valueOf(getStringState());
- }
-
- private native String getStringState() /*-{ return this.state; }-*/;
-
- @Override
- public final String getDisplayString() {
- if (description() != null) {
- return name() + " (" + description() + ")";
- }
- return name();
- }
-
- @Override
- public final String getReplacementString() {
- return name();
- }
-
- protected ProjectInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java
deleted file mode 100644
index 5ff300d2fa..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectMap.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2012 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.client.projects;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.RestApi;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-/** Projects available from {@code /projects/}. */
-public class ProjectMap extends NativeMap<ProjectInfo> {
- public static void all(AsyncCallback<ProjectMap> callback) {
- new RestApi("/projects/")
- .addParameterRaw("type", "ALL")
- .addParameterTrue("all")
- .addParameterTrue("d") // description
- .get(NativeMap.copyKeysIntoChildren(callback));
- }
-
- public static void permissions(AsyncCallback<ProjectMap> callback) {
- new RestApi("/projects/")
- .addParameterRaw("type", "PERMISSIONS")
- .addParameterTrue("all")
- .addParameterTrue("d") // description
- .get(NativeMap.copyKeysIntoChildren(callback));
- }
-
- public static void parentCandidates(AsyncCallback<ProjectMap> callback) {
- new RestApi("/projects/")
- .addParameterRaw("type", "PARENT_CANDIDATES")
- .addParameterTrue("all")
- .addParameterTrue("d") // description
- .get(NativeMap.copyKeysIntoChildren(callback));
- }
-
- public static void suggest(String match, int limit, AsyncCallback<ProjectMap> cb) {
- new RestApi("/projects/")
- .addParameter("m", match)
- .addParameter("n", limit)
- .addParameterRaw("type", "ALL")
- .addParameterTrue("d") // description
- .background()
- .get(NativeMap.copyKeysIntoChildren(cb));
- }
-
- public static void match(String match, int limit, int start, AsyncCallback<ProjectMap> cb) {
- RestApi call = new RestApi("/projects/");
- if (match != null) {
- if (match.startsWith("^")) {
- call.addParameter("r", match);
- } else {
- call.addParameter("m", match);
- }
- }
- if (limit > 0) {
- call.addParameter("n", limit);
- }
- if (start > 0) {
- call.addParameter("S", start);
- }
- call.addParameterRaw("type", "ALL");
- call.addParameterTrue("d"); // description
- call.get(NativeMap.copyKeysIntoChildren(cb));
- }
-
- public static void match(String match, AsyncCallback<ProjectMap> cb) {
- match(match, 0, 0, cb);
- }
-
- protected ProjectMap() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/RefInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/RefInfo.java
deleted file mode 100644
index 90c862fce7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/RefInfo.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2015 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.client.projects;
-
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class RefInfo extends JavaScriptObject {
- public final String getShortName() {
- return RefNames.shortName(ref());
- }
-
- public final native String ref() /*-{ return this.ref; }-*/;
-
- public final native String revision() /*-{ return this.revision; }-*/;
-
- protected RefInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java
deleted file mode 100644
index fc13fe1c9b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/TagInfo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2015 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.client.projects;
-
-import com.google.gerrit.client.info.WebLinkInfo;
-import com.google.gwt.core.client.JsArray;
-
-public class TagInfo extends RefInfo {
- public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;
-
- public final native JsArray<WebLinkInfo> webLinks() /*-{ return this.web_links; }-*/;
-
- // TODO(dpursehouse) add extra tag-related fields (message, tagger, etc)
- protected TagInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ThemeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ThemeInfo.java
deleted file mode 100644
index 7584e14516..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ThemeInfo.java
+++ /dev/null
@@ -1,27 +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.client.projects;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class ThemeInfo extends JavaScriptObject {
- public final native String css() /*-{ return this.css; }-*/;
-
- public final native String header() /*-{ return this.header; }-*/;
-
- public final native String footer() /*-{ return this.footer; }-*/;
-
- protected ThemeInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java
deleted file mode 100644
index af32d01f91..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/CallbackGroup.java
+++ /dev/null
@@ -1,260 +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.client.rpc;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Class for grouping together callbacks and calling them in order.
- *
- * <p>Callbacks are added to the group with {@link #add(AsyncCallback)}, which returns a wrapped
- * callback suitable for passing to an asynchronous RPC call. The last callback must be added using
- * {@link #addFinal(AsyncCallback)} or {@link #done()} must be invoked.
- *
- * <p>The enclosing group buffers returned results and ensures that {@code onSuccess} is called
- * exactly once for each callback in the group, in the same order that callbacks were added. This
- * allows callers to, for example, use a {@link ScreenLoadCallback} as the last callback in the list
- * and only display the screen once all callbacks have succeeded.
- *
- * <p>In the event of a failure, the <em>first</em> caught exception is sent to <em>all</em>
- * callbacks' {@code onFailure} methods, in order; subsequent successes or failures are all ignored.
- * Note that this means {@code onFailure} may be called with an exception unrelated to the callback
- * processing it.
- */
-public class CallbackGroup {
- private final List<CallbackGlue> callbacks;
- private final Set<CallbackGlue> remaining;
- private boolean finalAdded;
-
- private boolean failed;
- private Throwable failedThrowable;
-
- public static <T> Callback<T> emptyCallback() {
- return new Callback<T>() {
- @Override
- public void onSuccess(T result) {}
-
- @Override
- public void onFailure(Throwable err) {}
- };
- }
-
- public CallbackGroup() {
- callbacks = new ArrayList<>();
- remaining = new HashSet<>();
- }
-
- public <T> Callback<T> addEmpty() {
- Callback<T> cb = emptyCallback();
- return add(cb);
- }
-
- public <T> Callback<T> add(AsyncCallback<T> cb) {
- checkFinalAdded();
- return handleAdd(cb);
- }
-
- public <T> HttpCallback<T> add(HttpCallback<T> cb) {
- checkFinalAdded();
- return handleAdd(cb);
- }
-
- public <T> Callback<T> addFinal(AsyncCallback<T> cb) {
- checkFinalAdded();
- finalAdded = true;
- return handleAdd(cb);
- }
-
- public <T> HttpCallback<T> addFinal(HttpCallback<T> cb) {
- checkFinalAdded();
- finalAdded = true;
- return handleAdd(cb);
- }
-
- public void done() {
- finalAdded = true;
- apply();
- }
-
- public void addListener(AsyncCallback<Void> cb) {
- if (!failed && finalAdded && remaining.isEmpty()) {
- cb.onSuccess(null);
- } else {
- handleAdd(cb).onSuccess(null);
- }
- }
-
- public void addListener(CallbackGroup group) {
- addListener(group.<Void>addEmpty());
- }
-
- private void success(CallbackGlue cb) {
- remaining.remove(cb);
- apply();
- }
-
- private <T> void failure(CallbackGlue w, Throwable caught) {
- if (!failed) {
- failed = true;
- failedThrowable = caught;
- }
- remaining.remove(w);
- apply();
- }
-
- private void apply() {
- if (finalAdded && remaining.isEmpty()) {
- if (failed) {
- for (CallbackGlue cb : callbacks) {
- cb.applyFailed();
- }
- } else {
- for (CallbackGlue cb : callbacks) {
- cb.applySuccess();
- }
- }
- callbacks.clear();
- }
- }
-
- private <T> Callback<T> handleAdd(AsyncCallback<T> cb) {
- if (failed) {
- cb.onFailure(failedThrowable);
- return emptyCallback();
- }
-
- CallbackImpl<T> wrapper = new CallbackImpl<>(cb);
- callbacks.add(wrapper);
- remaining.add(wrapper);
- return wrapper;
- }
-
- private <T> HttpCallback<T> handleAdd(HttpCallback<T> cb) {
- if (failed) {
- cb.onFailure(failedThrowable);
- return new HttpCallback<T>() {
- @Override
- public void onSuccess(HttpResponse<T> result) {}
-
- @Override
- public void onFailure(Throwable caught) {}
- };
- }
-
- HttpCallbackImpl<T> w = new HttpCallbackImpl<>(cb);
- callbacks.add(w);
- remaining.add(w);
- return w;
- }
-
- private void checkFinalAdded() {
- if (finalAdded) {
- throw new IllegalStateException("final callback already added");
- }
- }
-
- public interface Callback<T>
- extends AsyncCallback<T>, com.google.gwtjsonrpc.common.AsyncCallback<T> {}
-
- private interface CallbackGlue {
- void applySuccess();
-
- void applyFailed();
- }
-
- private class CallbackImpl<T> implements Callback<T>, CallbackGlue {
- AsyncCallback<T> delegate;
- T result;
-
- CallbackImpl(AsyncCallback<T> delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public void onSuccess(T value) {
- this.result = value;
- success(this);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- failure(this, caught);
- }
-
- @Override
- public void applySuccess() {
- AsyncCallback<T> cb = delegate;
- if (cb != null) {
- delegate = null;
- cb.onSuccess(result);
- result = null;
- }
- }
-
- @Override
- public void applyFailed() {
- AsyncCallback<T> cb = delegate;
- if (cb != null) {
- delegate = null;
- result = null;
- cb.onFailure(failedThrowable);
- }
- }
- }
-
- private class HttpCallbackImpl<T> implements HttpCallback<T>, CallbackGlue {
- private HttpCallback<T> delegate;
- private HttpResponse<T> result;
-
- HttpCallbackImpl(HttpCallback<T> delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public void onSuccess(HttpResponse<T> result) {
- this.result = result;
- success(this);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- failure(this, caught);
- }
-
- @Override
- public void applySuccess() {
- HttpCallback<T> cb = delegate;
- if (cb != null) {
- delegate = null;
- cb.onSuccess(result);
- result = null;
- }
- }
-
- @Override
- public void applyFailed() {
- HttpCallback<T> cb = delegate;
- if (cb != null) {
- delegate = null;
- result = null;
- cb.onFailure(failedThrowable);
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
deleted file mode 100644
index 2d6723a1e9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
+++ /dev/null
@@ -1,111 +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.client.rpc;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotSignedInDialog;
-import com.google.gerrit.common.errors.NameAlreadyUsedException;
-import com.google.gerrit.common.errors.NoSuchAccountException;
-import com.google.gerrit.common.errors.NoSuchEntityException;
-import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.common.errors.NotSignedInException;
-import com.google.gwt.user.client.rpc.InvocationException;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-import com.google.gwtjsonrpc.client.ServerUnavailableException;
-import com.google.gwtjsonrpc.common.JsonConstants;
-
-/** Abstract callback handling generic error conditions automatically */
-public abstract class GerritCallback<T>
- implements com.google.gwtjsonrpc.common.AsyncCallback<T>,
- com.google.gwt.user.client.rpc.AsyncCallback<T> {
- @Override
- public void onFailure(Throwable caught) {
- showFailure(caught);
- }
-
- public static void showFailure(Throwable caught) {
- if (isSigninFailure(caught)) {
- new NotSignedInDialog().center();
- } else if (isNoSuchEntity(caught)) {
- new ErrorDialog(Gerrit.C.notFoundBody()).center();
- } else if (isNoSuchAccount(caught)) {
- final String msg = caught.getMessage();
- final String who = msg.substring(NoSuchAccountException.MESSAGE.length());
- final ErrorDialog d = new ErrorDialog(Gerrit.M.noSuchAccountMessage(who));
- d.setText(Gerrit.C.noSuchAccountTitle());
- d.center();
-
- } else if (isNameAlreadyUsed(caught)) {
- final String msg = caught.getMessage();
- final String alreadyUsedName = msg.substring(NameAlreadyUsedException.MESSAGE.length());
- new ErrorDialog(Gerrit.M.nameAlreadyUsedBody(alreadyUsedName)).center();
-
- } else if (isNoSuchGroup(caught)) {
- final String msg = caught.getMessage();
- final String group = msg.substring(NoSuchGroupException.MESSAGE.length());
- final ErrorDialog d = new ErrorDialog(Gerrit.M.noSuchGroupMessage(group));
- d.setText(Gerrit.C.noSuchGroupTitle());
- d.center();
-
- } else if (caught instanceof ServerUnavailableException) {
- new ErrorDialog(RpcConstants.C.errorServerUnavailable()).center();
-
- } else {
- new ErrorDialog(caught).center();
- }
- }
-
- public static boolean isSigninFailure(Throwable caught) {
- if (isNotSignedIn(caught)
- || isInvalidXSRF(caught)
- || (isNoSuchEntity(caught) && !Gerrit.isSignedIn())) {
- return true;
- }
- return false;
- }
-
- protected static boolean isInvalidXSRF(Throwable caught) {
- return caught instanceof InvocationException
- && caught.getMessage().equals(JsonConstants.ERROR_INVALID_XSRF);
- }
-
- protected static boolean isNotSignedIn(Throwable caught) {
- return RestApi.isNotSignedIn(caught)
- || (caught instanceof RemoteJsonException
- && caught.getMessage().equals(NotSignedInException.MESSAGE));
- }
-
- protected static boolean isNoSuchEntity(Throwable caught) {
- return RestApi.isNotFound(caught)
- || (caught instanceof RemoteJsonException
- && caught.getMessage().equals(NoSuchEntityException.MESSAGE));
- }
-
- protected static boolean isNoSuchAccount(Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().startsWith(NoSuchAccountException.MESSAGE);
- }
-
- protected static boolean isNameAlreadyUsed(Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().startsWith(NameAlreadyUsedException.MESSAGE);
- }
-
- protected static boolean isNoSuchGroup(Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().startsWith(NoSuchGroupException.MESSAGE);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpCallback.java
deleted file mode 100644
index 2de2980c97..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpCallback.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2015 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.client.rpc;
-
-/** AsyncCallback supplied with HTTP response headers. */
-public interface HttpCallback<T> {
- void onSuccess(HttpResponse<T> result);
-
- void onFailure(Throwable caught);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpResponse.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpResponse.java
deleted file mode 100644
index 22d62fb1e3..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/HttpResponse.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2015 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.client.rpc;
-
-import com.google.gwt.http.client.Response;
-
-/** Wraps decoded server reply with HTTP headers. */
-public class HttpResponse<T> {
- private final Response httpResponse;
- private final String contentType;
- private final T result;
-
- HttpResponse(Response httpResponse, String contentType, T result) {
- this.httpResponse = httpResponse;
- this.contentType = contentType;
- this.result = result;
- }
-
- /** HTTP status code, always in the 2xx family. */
- public int getStatusCode() {
- return httpResponse.getStatusCode();
- }
-
- /**
- * Content type supplied by the server.
- *
- * <p>This helper simplifies the common {@code getHeader("Content-Type")} case.
- */
- public String getContentType() {
- return contentType;
- }
-
- /** Lookup an arbitrary reply header. */
- public String getHeader(String header) {
- if ("Content-Type".equals(header)) {
- return contentType;
- }
- return httpResponse.getHeader(header);
- }
-
- public T getResult() {
- return result;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
deleted file mode 100644
index e2a9ffbe0c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
+++ /dev/null
@@ -1,519 +0,0 @@
-// Copyright (C) 2012 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.client.rpc;
-
-import static com.google.gwt.http.client.RequestBuilder.DELETE;
-import static com.google.gwt.http.client.RequestBuilder.GET;
-import static com.google.gwt.http.client.RequestBuilder.POST;
-import static com.google.gwt.http.client.RequestBuilder.PUT;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.RpcStatus;
-import com.google.gerrit.common.data.HostPageData;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.http.client.Request;
-import com.google.gwt.http.client.RequestBuilder;
-import com.google.gwt.http.client.RequestBuilder.Method;
-import com.google.gwt.http.client.RequestCallback;
-import com.google.gwt.http.client.RequestException;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.http.client.URL;
-import com.google.gwt.json.client.JSONException;
-import com.google.gwt.json.client.JSONParser;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-
-/** Makes a REST API call to the server. */
-public class RestApi {
- private static final int SC_UNAVAILABLE = 2;
- private static final int SC_BAD_TRANSPORT = 3;
- private static final int SC_BAD_RESPONSE = 4;
- private static final String JSON_TYPE = "application/json";
- private static final String JSON_UTF8 = JSON_TYPE + "; charset=utf-8";
- private static final String TEXT_TYPE = "text/plain";
- private static final String TEXT_UTF8 = TEXT_TYPE + "; charset=utf-8";
-
- /**
- * Expected JSON content body prefix that prevents XSSI.
- *
- * <p>The server always includes this line as the first line of the response content body when the
- * response body is formatted as JSON. It gets inserted by the server to prevent the resource from
- * being imported into another domain's page using a &lt;script&gt; tag. This line must be removed
- * before the JSON can be parsed.
- */
- private static final String JSON_MAGIC = ")]}'\n";
-
- /** True if err is a StatusCodeException reporting Not Found. */
- public static boolean isNotFound(Throwable err) {
- return isStatus(err, Response.SC_NOT_FOUND);
- }
-
- /** True if err is describing a user that is currently anonymous. */
- public static boolean isNotSignedIn(Throwable err) {
- if (err instanceof StatusCodeException) {
- StatusCodeException sce = (StatusCodeException) err;
- if (sce.getStatusCode() == Response.SC_UNAUTHORIZED) {
- return true;
- }
- return sce.getStatusCode() == Response.SC_FORBIDDEN
- && (sce.getEncodedResponse().equals("Authentication required")
- || sce.getEncodedResponse().startsWith("Must be signed-in")
- || sce.getEncodedResponse().startsWith("Invalid authentication"));
- }
- return false;
- }
-
- /** True if err is a StatusCodeException with a specific HTTP code. */
- public static boolean isStatus(Throwable err, int status) {
- return err instanceof StatusCodeException
- && ((StatusCodeException) err).getStatusCode() == status;
- }
-
- /** Is the Gerrit Code Review server likely to return this status? */
- public static boolean isExpected(int statusCode) {
- switch (statusCode) {
- case SC_UNAVAILABLE:
- case Response.SC_BAD_REQUEST:
- case Response.SC_UNAUTHORIZED:
- case Response.SC_FORBIDDEN:
- case Response.SC_NOT_FOUND:
- case Response.SC_METHOD_NOT_ALLOWED:
- case Response.SC_CONFLICT:
- case Response.SC_PRECONDITION_FAILED:
- case 422: // Unprocessable Entity
- case 429: // Too Many Requests (RFC 6585)
- return true;
-
- default:
- // Assume any other code is not expected. These may be
- // local proxy server errors outside of our control.
- return false;
- }
- }
-
- private static class HttpImpl<T extends JavaScriptObject> implements RequestCallback {
- private final boolean background;
- private final HttpCallback<T> cb;
-
- HttpImpl(boolean bg, HttpCallback<T> cb) {
- this.background = bg;
- this.cb = cb;
- }
-
- @Override
- public void onResponseReceived(Request req, Response res) {
- int status = res.getStatusCode();
- if (status == Response.SC_NO_CONTENT) {
- cb.onSuccess(new HttpResponse<T>(res, null, null));
- if (!background) {
- RpcStatus.INSTANCE.onRpcComplete();
- }
-
- } else if (200 <= status && status < 300) {
- long start = System.currentTimeMillis();
- final T data;
- final String type;
- if (isJsonBody(res)) {
- try {
- JSONValue val = parseJson(res);
- if (isJsonEncoded(res) && val.isString() != null) {
- data = NativeString.wrap(val.isString().stringValue()).cast();
- type = simpleType(res.getHeader("X-FYI-Content-Type"));
- } else {
- data = RestApi.<T>cast(val);
- type = JSON_TYPE;
- }
- } catch (JSONException e) {
- if (!background) {
- RpcStatus.INSTANCE.onRpcComplete();
- }
- cb.onFailure(
- new StatusCodeException(SC_BAD_RESPONSE, "Invalid JSON: " + e.getMessage()));
- return;
- }
- } else if (isTextBody(res)) {
- data = NativeString.wrap(res.getText()).cast();
- type = TEXT_TYPE;
- } else {
- if (!background) {
- RpcStatus.INSTANCE.onRpcComplete();
- }
- cb.onFailure(
- new StatusCodeException(
- SC_BAD_RESPONSE,
- "Expected "
- + JSON_TYPE
- + " or "
- + TEXT_TYPE
- + "; received Content-Type: "
- + res.getHeader("Content-Type")));
- return;
- }
-
- Scheduler.ScheduledCommand cmd =
- new Scheduler.ScheduledCommand() {
- @Override
- public void execute() {
- try {
- cb.onSuccess(new HttpResponse<>(res, type, data));
- } finally {
- if (!background) {
- RpcStatus.INSTANCE.onRpcComplete();
- }
- }
- }
- };
-
- // Defer handling the response if the create took a while.
- if ((System.currentTimeMillis() - start) > 75) {
- Scheduler.get().scheduleDeferred(cmd);
- } else {
- cmd.execute();
- }
- } else {
- String msg;
- if (isTextBody(res)) {
- msg = res.getText().trim();
- } else if (isJsonBody(res)) {
- JSONValue v;
- try {
- v = parseJson(res);
- } catch (JSONException e) {
- v = null;
- }
- if (v != null && v.isString() != null) {
- msg = v.isString().stringValue();
- } else {
- msg = trimJsonMagic(res.getText()).trim();
- }
- } else {
- msg = res.getStatusText();
- }
-
- if (!background) {
- RpcStatus.INSTANCE.onRpcComplete();
- }
- cb.onFailure(new StatusCodeException(status, msg));
- }
- }
-
- @Override
- public void onError(Request req, Throwable err) {
- if (!background) {
- RpcStatus.INSTANCE.onRpcComplete();
- }
- if (err.getMessage().contains("XmlHttpRequest.status")) {
- cb.onFailure(
- new StatusCodeException(SC_UNAVAILABLE, RpcConstants.C.errorServerUnavailable()));
- } else {
- cb.onFailure(new StatusCodeException(SC_BAD_TRANSPORT, err.getMessage()));
- }
- }
- }
-
- private StringBuilder url;
- private boolean hasQueryParams;
- private boolean background;
- private String ifNoneMatch;
-
- /**
- * Initialize a new API call.
- *
- * <p>By default the JSON format will be selected by including an HTTP Accept header in the
- * request.
- *
- * @param name URL of the REST resource to access, e.g. {@code "/projects/"} to list accessible
- * projects from the server.
- */
- public RestApi(String name) {
- if (name.startsWith("/")) {
- name = name.substring(1);
- }
-
- url = new StringBuilder();
- url.append(GWT.getHostPageBaseURL());
- url.append(name);
- }
-
- public RestApi view(String name) {
- return idRaw(name);
- }
-
- public RestApi id(String id) {
- return idRaw(URL.encodePathSegment(id));
- }
-
- public RestApi id(String project, int id) {
- return idRaw(URL.encodePathSegment(project) + "~" + id);
- }
-
- public RestApi id(int id) {
- return idRaw(Integer.toString(id));
- }
-
- public RestApi idRaw(String name) {
- if (hasQueryParams) {
- throw new IllegalStateException();
- }
- if (url.charAt(url.length() - 1) != '/') {
- url.append('/');
- }
- url.append(name);
- return this;
- }
-
- public RestApi addParameter(String name, String value) {
- return addParameterRaw(name, URL.encodeQueryString(value));
- }
-
- public RestApi addParameter(String name, String... value) {
- for (String val : value) {
- addParameter(name, val);
- }
- return this;
- }
-
- public RestApi addParameterTrue(String name) {
- return addParameterRaw(name, null);
- }
-
- public RestApi addParameter(String name, boolean value) {
- return addParameterRaw(name, value ? "t" : "f");
- }
-
- public RestApi addParameter(String name, int value) {
- return addParameterRaw(name, String.valueOf(value));
- }
-
- public RestApi addParameter(String name, Enum<?> value) {
- return addParameterRaw(name, value.name());
- }
-
- public RestApi addParameterRaw(String name, String value) {
- if (hasQueryParams) {
- url.append("&");
- } else {
- url.append("?");
- hasQueryParams = true;
- }
- url.append(name);
- if (value != null) {
- url.append("=").append(value);
- }
- return this;
- }
-
- public RestApi ifNoneMatch() {
- return ifNoneMatch("*");
- }
-
- public RestApi ifNoneMatch(String etag) {
- ifNoneMatch = etag;
- return this;
- }
-
- public RestApi background() {
- background = true;
- return this;
- }
-
- public String url() {
- return url.toString();
- }
-
- public <T extends JavaScriptObject> void get(AsyncCallback<T> cb) {
- get(wrap(cb));
- }
-
- public <T extends JavaScriptObject> void get(HttpCallback<T> cb) {
- send(GET, cb);
- }
-
- public <T extends JavaScriptObject> void delete(AsyncCallback<T> cb) {
- delete(wrap(cb));
- }
-
- public <T extends JavaScriptObject> void delete(HttpCallback<T> cb) {
- send(DELETE, cb);
- }
-
- private <T extends JavaScriptObject> void send(Method method, HttpCallback<T> cb) {
- HttpImpl<T> httpCallback = new HttpImpl<>(background, cb);
- try {
- if (!background) {
- RpcStatus.INSTANCE.onRpcStart();
- }
- request(method).sendRequest(null, httpCallback);
- } catch (RequestException e) {
- httpCallback.onError(null, e);
- }
- }
-
- public <T extends JavaScriptObject> void post(JavaScriptObject content, AsyncCallback<T> cb) {
- post(content, wrap(cb));
- }
-
- public <T extends JavaScriptObject> void post(JavaScriptObject content, HttpCallback<T> cb) {
- sendJSON(POST, content, cb);
- }
-
- public <T extends JavaScriptObject> void post(String content, AsyncCallback<T> cb) {
- post(content, wrap(cb));
- }
-
- public <T extends JavaScriptObject> void post(String content, HttpCallback<T> cb) {
- sendText(POST, content, cb);
- }
-
- public <T extends JavaScriptObject> void put(AsyncCallback<T> cb) {
- put(wrap(cb));
- }
-
- public <T extends JavaScriptObject> void put(HttpCallback<T> cb) {
- send(PUT, cb);
- }
-
- public <T extends JavaScriptObject> void put(String content, AsyncCallback<T> cb) {
- put(content, wrap(cb));
- }
-
- public <T extends JavaScriptObject> void put(String content, HttpCallback<T> cb) {
- sendText(PUT, content, cb);
- }
-
- public <T extends JavaScriptObject> void put(JavaScriptObject content, AsyncCallback<T> cb) {
- put(content, wrap(cb));
- }
-
- public <T extends JavaScriptObject> void put(JavaScriptObject content, HttpCallback<T> cb) {
- sendJSON(PUT, content, cb);
- }
-
- private <T extends JavaScriptObject> void sendJSON(
- Method method, JavaScriptObject content, HttpCallback<T> cb) {
- HttpImpl<T> httpCallback = new HttpImpl<>(background, cb);
- try {
- if (!background) {
- RpcStatus.INSTANCE.onRpcStart();
- }
- RequestBuilder req = request(method);
- req.setHeader("Content-Type", JSON_UTF8);
- req.sendRequest(str(content), httpCallback);
- } catch (RequestException e) {
- httpCallback.onError(null, e);
- }
- }
-
- private static native String str(JavaScriptObject jso) /*-{ return JSON.stringify(jso) }-*/;
-
- private <T extends JavaScriptObject> void sendText(
- Method method, String body, HttpCallback<T> cb) {
- HttpImpl<T> httpCallback = new HttpImpl<>(background, cb);
- try {
- if (!background) {
- RpcStatus.INSTANCE.onRpcStart();
- }
- RequestBuilder req = request(method);
- req.setHeader("Content-Type", TEXT_UTF8);
- req.sendRequest(body, httpCallback);
- } catch (RequestException e) {
- httpCallback.onError(null, e);
- }
- }
-
- private RequestBuilder request(Method method) {
- RequestBuilder req = new RequestBuilder(method, url());
- if (ifNoneMatch != null) {
- req.setHeader("If-None-Match", ifNoneMatch);
- }
- req.setHeader("Accept", JSON_TYPE);
- if (Gerrit.getXGerritAuth() != null) {
- req.setHeader(HostPageData.XSRF_HEADER_NAME, Gerrit.getXGerritAuth());
- }
- return req;
- }
-
- private static boolean isJsonBody(Response res) {
- return isContentType(res, JSON_TYPE);
- }
-
- private static boolean isTextBody(Response res) {
- return isContentType(res, TEXT_TYPE);
- }
-
- private static boolean isJsonEncoded(Response res) {
- return "json".equals(res.getHeader("X-FYI-Content-Encoding"));
- }
-
- private static boolean isContentType(Response res, String want) {
- String type = res.getHeader("Content-Type");
- return type != null && want.equals(simpleType(type));
- }
-
- private static String simpleType(String type) {
- int semi = type.indexOf(';');
- if (semi >= 0) {
- return type.substring(0, semi).trim();
- }
- return type;
- }
-
- private static JSONValue parseJson(Response res) throws JSONException {
- String json = trimJsonMagic(res.getText());
- if (json.isEmpty()) {
- throw new JSONException("response was empty");
- }
- return JSONParser.parseStrict(json);
- }
-
- private static String trimJsonMagic(String json) {
- if (json.startsWith(JSON_MAGIC)) {
- json = json.substring(JSON_MAGIC.length());
- }
- return json;
- }
-
- @SuppressWarnings("unchecked")
- private static <T extends JavaScriptObject> T cast(JSONValue val) {
- if (val.isObject() != null) {
- return (T) val.isObject().getJavaScriptObject();
- } else if (val.isArray() != null) {
- return (T) val.isArray().getJavaScriptObject();
- } else if (val.isString() != null) {
- return (T) NativeString.wrap(val.isString().stringValue());
- } else if (val.isNull() != null) {
- return null;
- } else {
- throw new JSONException("unsupported JSON type");
- }
- }
-
- private static <T extends JavaScriptObject> HttpCallback<T> wrap(AsyncCallback<T> cb) {
- return new HttpCallback<T>() {
- @Override
- public void onSuccess(HttpResponse<T> r) {
- cb.onSuccess(r.getResult());
- }
-
- @Override
- public void onFailure(Throwable e) {
- cb.onFailure(e);
- }
- };
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java
deleted file mode 100644
index 56d536d874..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java
+++ /dev/null
@@ -1,26 +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.client.rpc;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Constants;
-
-public interface RpcConstants extends Constants {
- RpcConstants C = GWT.create(RpcConstants.class);
-
- String errorServerUnavailable();
-
- String errorRemoteJsonException();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties
deleted file mode 100644
index e8695b1846..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-errorServerUnavailable = Server Unavailable
-errorRemoteJsonException = Server Error
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java
deleted file mode 100644
index 3aae04ab27..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java
+++ /dev/null
@@ -1,54 +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.client.rpc;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotFoundScreen;
-import com.google.gerrit.client.NotSignedInDialog;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gerrit.common.errors.NoSuchEntityException;
-
-/** Callback switching {@link NoSuchEntityException} to {@link NotFoundScreen} */
-public abstract class ScreenLoadCallback<T> extends GerritCallback<T> {
- private final Screen screen;
-
- public ScreenLoadCallback(Screen s) {
- screen = s;
- }
-
- @Override
- public final void onSuccess(T result) {
- if (screen.isAttached()) {
- preDisplay(result);
- screen.display();
- postDisplay();
- }
- }
-
- protected abstract void preDisplay(T result);
-
- protected void postDisplay() {}
-
- @Override
- public void onFailure(Throwable caught) {
- if (isSigninFailure(caught)) {
- new NotSignedInDialog().center();
- } else if (isNoSuchEntity(caught)) {
- Gerrit.display(screen.getToken(), new NotFoundScreen());
- } else {
- super.onFailure(caught);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
deleted file mode 100644
index bdebd684dc..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
+++ /dev/null
@@ -1,80 +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.client.ui;
-
-import com.google.gerrit.client.groups.GroupMap;
-import com.google.gerrit.client.info.GroupInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Suggestion Oracle for AccountGroup entities. */
-public class AccountGroupSuggestOracle extends SuggestAfterTypingNCharsOracle {
- private Map<String, AccountGroup.UUID> priorResults = new HashMap<>();
-
- private Project.NameKey projectName;
-
- @Override
- public void _onRequestSuggestions(Request req, Callback callback) {
- GroupMap.suggestAccountGroupForProject(
- projectName == null ? null : projectName.get(),
- req.getQuery(),
- req.getLimit(),
- new GerritCallback<GroupMap>() {
- @Override
- public void onSuccess(GroupMap result) {
- priorResults.clear();
- ArrayList<AccountGroupSuggestion> r = new ArrayList<>(result.size());
- for (GroupInfo group : Natives.asList(result.values())) {
- r.add(new AccountGroupSuggestion(group));
- priorResults.put(group.name(), group.getGroupUUID());
- }
- callback.onSuggestionsReady(req, new Response(r));
- }
- });
- }
-
- public void setProject(Project.NameKey projectName) {
- this.projectName = projectName;
- }
-
- private static class AccountGroupSuggestion implements SuggestOracle.Suggestion {
- private final GroupInfo info;
-
- AccountGroupSuggestion(GroupInfo k) {
- info = k;
- }
-
- @Override
- public String getDisplayString() {
- return info.name();
- }
-
- @Override
- public String getReplacementString() {
- return info.name();
- }
- }
-
- /** @return the group UUID, or null if it cannot be found. */
- public AccountGroup.UUID getUUID(String name) {
- return priorResults.get(name);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
deleted file mode 100644
index c44f35789b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountLinkPanel.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gerrit.client.AvatarImage;
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.user.client.ui.FlowPanel;
-import java.util.function.Function;
-
-/** Link to any user's account dashboard. */
-public class AccountLinkPanel extends FlowPanel {
- public static AccountLinkPanel create(AccountInfo ai) {
- return withStatus(ai, Change.Status.NEW);
- }
-
- public static AccountLinkPanel withStatus(AccountInfo ai, Change.Status status) {
- return new AccountLinkPanel(ai, name -> PageLinks.toAccountQuery(name, status));
- }
-
- public static AccountLinkPanel forAssignee(AccountInfo ai) {
- return new AccountLinkPanel(ai, PageLinks::toAssigneeQuery);
- }
-
- private AccountLinkPanel(AccountInfo ai, Function<String, String> nameToQuery) {
- addStyleName(Gerrit.RESOURCES.css().accountLinkPanel());
-
- InlineHyperlink l =
- new InlineHyperlink(FormatUtil.name(ai), nameToQuery.apply(name(ai))) {
- @Override
- public void go() {
- Gerrit.display(getTargetHistoryToken());
- }
- };
- l.setTitle(FormatUtil.nameEmail(ai));
-
- add(new AvatarImage(ai));
- add(l);
- }
-
- private static String name(AccountInfo ai) {
- if (ai.email() != null) {
- return ai.email();
- } else if (ai.name() != null) {
- return ai.name();
- } else if (ai._accountId() != 0) {
- return "" + ai._accountId();
- } else {
- return "";
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java
deleted file mode 100644
index 59ff9fe3cb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java
+++ /dev/null
@@ -1,22 +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.client.ui;
-
-/** A screen that requires the user to be signed-into their account. */
-public abstract class AccountScreen extends Screen {
- protected AccountScreen() {
- setRequiresSignIn(true);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java
deleted file mode 100644
index 5038ad9e8b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java
+++ /dev/null
@@ -1,88 +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.client.ui;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.account.AccountApi;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Suggestion Oracle for Account entities. */
-public class AccountSuggestOracle extends SuggestAfterTypingNCharsOracle {
- @Override
- public void _onRequestSuggestions(Request req, Callback cb) {
- AccountApi.suggest(
- req.getQuery(),
- req.getLimit(),
- new GerritCallback<JsArray<AccountInfo>>() {
- @Override
- public void onSuccess(JsArray<AccountInfo> in) {
- List<AccountSuggestion> r = new ArrayList<>(in.length());
- for (AccountInfo p : Natives.asList(in)) {
- r.add(new AccountSuggestion(p, req.getQuery()));
- }
- cb.onSuggestionsReady(req, new Response(r));
- }
- });
- }
-
- public static class AccountSuggestion implements SuggestOracle.Suggestion {
- private final String suggestion;
-
- public AccountSuggestion(AccountInfo info, String query) {
- this.suggestion = format(info, query);
- }
-
- @Override
- public String getDisplayString() {
- return suggestion;
- }
-
- @Override
- public String getReplacementString() {
- return suggestion;
- }
-
- public static String format(AccountInfo info, String query) {
- String s = FormatUtil.nameEmail(info);
- if (query != null && !containsQuery(s, query) && info.secondaryEmails() != null) {
- for (String email : Natives.asList(info.secondaryEmails())) {
- AccountInfo info2 =
- AccountInfo.create(info._accountId(), info.name(), email, info.username());
- String s2 = FormatUtil.nameEmail(info2);
- if (containsQuery(s2, query)) {
- s = s2;
- break;
- }
- }
- }
- return s;
- }
-
- private static boolean containsQuery(String s, String query) {
- for (String qterm : query.split("\\s+")) {
- if (!s.toLowerCase().contains(qterm.toLowerCase())) {
- return false;
- }
- }
- return true;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java
deleted file mode 100644
index a1d22298df..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java
+++ /dev/null
@@ -1,70 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SuggestOracle;
-
-public class AddMemberBox extends Composite {
- private final FlowPanel addPanel;
- private final Button addMember;
- private final RemoteSuggestBox suggestBox;
-
- public AddMemberBox(final String buttonLabel, String hint, SuggestOracle suggestOracle) {
- addPanel = new FlowPanel();
- addMember = new Button(buttonLabel);
-
- suggestBox = new RemoteSuggestBox(suggestOracle);
- suggestBox.setStyleName(Gerrit.RESOURCES.css().addMemberTextBox());
- suggestBox.setVisibleLength(50);
- suggestBox.setHintText(hint);
- suggestBox.addSelectionHandler(
- new SelectionHandler<String>() {
- @Override
- public void onSelection(SelectionEvent<String> event) {
- addMember.fireEvent(new ClickEvent() {});
- }
- });
-
- addPanel.add(suggestBox);
- addPanel.add(addMember);
-
- initWidget(addPanel);
- }
-
- public void addClickHandler(ClickHandler handler) {
- addMember.addClickHandler(handler);
- }
-
- public String getText() {
- return suggestBox.getText();
- }
-
- public void setEnabled(boolean enabled) {
- addMember.setEnabled(enabled);
- suggestBox.setEnabled(enabled);
- }
-
- public void setText(String text) {
- suggestBox.setText(text);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java
deleted file mode 100644
index 6e05f830f2..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/BranchLink.java
+++ /dev/null
@@ -1,85 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.QueryScreen;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-
-/** Link to the open changes of a project. */
-public class BranchLink extends InlineHyperlink {
- private final String query;
-
- public BranchLink(Project.NameKey project, Change.Status status, String branch, String topic) {
- this(text(branch, topic), query(project, status, branch, topic));
- }
-
- public BranchLink(
- String text, Project.NameKey project, Change.Status status, String branch, String topic) {
- this(text, query(project, status, branch, topic));
- }
-
- private BranchLink(String text, String query) {
- super(text, PageLinks.toChangeQuery(query));
- this.query = query;
- }
-
- @Override
- public void go() {
- Gerrit.display(getTargetHistoryToken(), createScreen());
- }
-
- private Screen createScreen() {
- return QueryScreen.forQuery(query);
- }
-
- private static String text(String branch, String topic) {
- if (topic != null && !topic.isEmpty()) {
- return branch + " (" + topic + ")";
- }
- return branch;
- }
-
- public static String query(
- Project.NameKey project, Change.Status status, String branch, String topic) {
- String query = PageLinks.projectQuery(project, status);
-
- if (branch.startsWith(RefNames.REFS)) {
- if (branch.startsWith(RefNames.REFS_HEADS)) {
- query +=
- " "
- + PageLinks.op(
- "branch", //
- branch.substring(RefNames.REFS_HEADS.length()));
- } else {
- query += " " + PageLinks.op("ref", branch);
- }
- } else {
- // Assume it was clipped already by the caller. This
- // happens for example inside of the ChangeInfo object.
- //
- query += " " + PageLinks.op("branch", branch);
- }
-
- if (topic != null && !topic.isEmpty()) {
- query += " " + PageLinks.op("topic", topic);
- }
-
- return query;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java
deleted file mode 100644
index b54d752a31..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java
+++ /dev/null
@@ -1,40 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.GWT;
-
-public class ChangeLink extends InlineHyperlink {
- public static String permalink(Change.Id c) {
- return GWT.getHostPageBaseURL() + c.get();
- }
-
- protected Change.Id cid;
-
- public ChangeLink(Project.NameKey project, Change.Id c, String text) {
- super(text, PageLinks.toChange(project, c));
- getElement().setPropertyString("href", permalink(c));
- cid = c;
- }
-
- @Override
- public void go() {
- Gerrit.display(getTargetHistoryToken());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java
deleted file mode 100644
index 0a0c14a78e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CherryPickDialog.java
+++ /dev/null
@@ -1,107 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class CherryPickDialog extends TextAreaActionDialog {
- private SuggestBox newBranch;
- private List<BranchInfo> branches;
-
- public CherryPickDialog(Project.NameKey project) {
- super(Util.C.cherryPickTitle(), Util.C.cherryPickCommitMessage());
- ProjectApi.getBranches(
- project,
- new GerritCallback<JsArray<BranchInfo>>() {
- @Override
- public void onSuccess(JsArray<BranchInfo> result) {
- branches = Natives.asList(result);
- }
- });
-
- newBranch =
- new SuggestBox(
- new HighlightSuggestOracle() {
- @Override
- protected void onRequestSuggestions(Request request, Callback done) {
- List<BranchSuggestion> suggestions = new ArrayList<>();
- for (BranchInfo b : branches) {
- if (b.ref().contains(request.getQuery())) {
- suggestions.add(new BranchSuggestion(b));
- }
- }
- done.onSuggestionsReady(request, new Response(suggestions));
- }
- });
-
- newBranch.setWidth("100%");
- newBranch.getElement().getStyle().setProperty("boxSizing", "border-box");
- message.setCharacterWidth(70);
-
- final FlowPanel mwrap = new FlowPanel();
- mwrap.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
- mwrap.add(newBranch);
-
- panel.insert(mwrap, 0);
- panel.insert(new SmallHeading(Util.C.headingCherryPickBranch()), 0);
- }
-
- @Override
- public void center() {
- super.center();
- GlobalKey.dialog(this);
- newBranch.setFocus(true);
- }
-
- public String getDestinationBranch() {
- return newBranch.getText();
- }
-
- static class BranchSuggestion implements Suggestion {
- private BranchInfo branch;
-
- BranchSuggestion(BranchInfo branch) {
- this.branch = branch;
- }
-
- @Override
- public String getDisplayString() {
- final String refsHeads = "refs/heads/";
- if (branch.ref().startsWith(refsHeads)) {
- return branch.ref().substring(refsHeads.length());
- }
- return branch.ref();
- }
-
- @Override
- public String getReplacementString() {
- return branch.getShortName();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
deleted file mode 100644
index c5ee34f7c9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
+++ /dev/null
@@ -1,40 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.ui.Anchor;
-
-public class CommandMenuItem extends Anchor implements ClickHandler {
- private final Command command;
-
- public CommandMenuItem(String text, Command cmd) {
- super(text);
- setStyleName(Gerrit.RESOURCES.css().menuItem());
- Roles.getMenuitemRole().set(getElement());
- addClickHandler(this);
- command = cmd;
- }
-
- @Override
- public void onClick(ClickEvent event) {
- setFocus(false);
- command.execute();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentLinkProcessor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentLinkProcessor.java
deleted file mode 100644
index 1753ade074..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentLinkProcessor.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwtexpui.safehtml.client.FindReplace;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.VoidResult;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class CommentLinkProcessor {
- private List<FindReplace> commentLinks;
-
- public CommentLinkProcessor(List<FindReplace> commentLinks) {
- this.commentLinks = commentLinks;
- }
-
- public SafeHtml apply(SafeHtml buf) {
- try {
- return buf.replaceAll(commentLinks);
- } catch (RuntimeException err) {
- // One or more of the patterns isn't valid on this browser.
- // Try to filter the list down and remove the invalid ones.
-
- List<FindReplace> safe = new ArrayList<>(commentLinks.size());
-
- List<PatternError> bad = new ArrayList<>();
- for (FindReplace r : commentLinks) {
- try {
- buf.replaceAll(Collections.singletonList(r));
- safe.add(r);
- } catch (RuntimeException why) {
- bad.add(new PatternError(r, why.getMessage()));
- }
- }
-
- if (!bad.isEmpty()) {
- StringBuilder msg = new StringBuilder();
- msg.append("Invalid commentlink pattern(s):");
- for (PatternError e : bad) {
- msg.append("\n");
- msg.append("\"");
- msg.append(e.pattern.pattern().getSource());
- msg.append("\": ");
- msg.append(e.errorMessage);
- }
- Gerrit.SYSTEM_SVC.clientError(
- msg.toString(),
- new AsyncCallback<VoidResult>() {
- @Override
- public void onFailure(Throwable caught) {}
-
- @Override
- public void onSuccess(VoidResult result) {}
- });
- }
-
- try {
- commentLinks = safe;
- return buf.replaceAll(safe);
- } catch (RuntimeException err2) {
- // To heck with it. The patterns passed individually above but
- // failed as a group? Just render without.
- //
- commentLinks = null;
- return buf;
- }
- }
- }
-
- private static class PatternError {
- FindReplace pattern;
- String errorMessage;
-
- PatternError(FindReplace r, String w) {
- pattern = r;
- errorMessage = w;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentedActionDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentedActionDialog.java
deleted file mode 100644
index b68f3296b7..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentedActionDialog.java
+++ /dev/null
@@ -1,110 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FocusWidget;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-
-public abstract class CommentedActionDialog extends AutoCenterDialogBox
- implements CloseHandler<PopupPanel> {
- protected final FlowPanel panel;
- protected final Button sendButton;
- protected final Button cancelButton;
- protected final FlowPanel buttonPanel;
- protected final FlowPanel contentPanel;
- protected FocusWidget focusOn;
-
- protected boolean sent;
-
- public CommentedActionDialog(String title, String heading) {
- super(/* auto hide */ false, /* modal */ true);
- setGlassEnabled(true);
- setText(title);
-
- addStyleName(Gerrit.RESOURCES.css().commentedActionDialog());
-
- sendButton = new Button(Util.C.commentedActionButtonSend());
- sendButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- enableButtons(false);
- onSend();
- }
- });
-
- cancelButton = new Button(Util.C.commentedActionButtonCancel());
- cancelButton.getElement().getStyle().setProperty("float", "right");
- cancelButton.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- }
- });
-
- contentPanel = new FlowPanel();
- contentPanel.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
-
- buttonPanel = new FlowPanel();
- buttonPanel.add(sendButton);
- buttonPanel.add(cancelButton);
- buttonPanel.getElement().getStyle().setProperty("marginTop", "4px");
-
- panel = new FlowPanel();
- if (heading != null) {
- panel.add(new SmallHeading(heading));
- }
- panel.add(contentPanel);
- panel.add(buttonPanel);
- add(panel);
-
- addCloseHandler(this);
- }
-
- public void setFocusOn(FocusWidget focusWidget) {
- focusOn = focusWidget;
- }
-
- public void enableButtons(boolean enable) {
- sendButton.setEnabled(enable);
- cancelButton.setEnabled(enable);
- }
-
- @Override
- public void center() {
- super.center();
- GlobalKey.dialog(this);
- if (focusOn != null) {
- focusOn.setFocus(true);
- }
- }
-
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- sent = false;
- }
-
- public abstract void onSend();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java
deleted file mode 100644
index c0b662a286..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.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.client.ui;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.HasCloseHandlers;
-import com.google.gwt.event.logical.shared.HasOpenHandlers;
-import com.google.gwt.event.logical.shared.OpenHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.ComplexPanel;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.DisclosurePanel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-
-public class ComplexDisclosurePanel extends Composite
- implements HasOpenHandlers<DisclosurePanel>, HasCloseHandlers<DisclosurePanel> {
- private final DisclosurePanel main;
- private final Panel header;
-
- public ComplexDisclosurePanel(String text, boolean isOpen) {
- // Ick. GWT's DisclosurePanel won't let us subclass it, or do any
- // other modification of its header. We're stuck with injecting
- // into the DOM directly.
- //
- main = new DisclosurePanel(text);
- main.setOpen(isOpen);
- final Element headerParent;
- {
- final Element table = main.getElement();
- final Element tbody = DOM.getFirstChild(table);
- final Element tr1 = DOM.getChild(tbody, 0);
- final Element tr2 = DOM.getChild(tbody, 1);
-
- DOM.getChild(tr1, 0).setPropertyString("width", "20px");
- DOM.getChild(tr2, 0).setPropertyInt("colSpan", 2);
- headerParent = tr1;
- }
-
- header =
- new ComplexPanel() {
- {
- setElement((Element) (DOM.createTD()));
- getElement().setInnerHTML("&nbsp;");
- }
-
- @Override
- public void add(Widget w) {
- add(w, (Element) getElement());
- }
- };
-
- initWidget(
- new ComplexPanel() {
- {
- final DisclosurePanel main = ComplexDisclosurePanel.this.main;
- setElement((Element) (main.getElement()));
- getChildren().add(main);
- adopt(main);
-
- add(ComplexDisclosurePanel.this.header, headerParent);
- }
- });
- }
-
- public Panel getHeader() {
- return header;
- }
-
- public void setContent(Widget w) {
- main.setContent(w);
- }
-
- public Widget getContent() {
- return main.getContent();
- }
-
- @Override
- public HandlerRegistration addOpenHandler(OpenHandler<DisclosurePanel> h) {
- return main.addOpenHandler(h);
- }
-
- @Override
- public HandlerRegistration addCloseHandler(CloseHandler<DisclosurePanel> h) {
- return main.addCloseHandler(h);
- }
-
- /** @return true if the panel's content is visible. */
- public boolean isOpen() {
- return main.isOpen();
- }
-
- /**
- * Changes the visible state of this panel's content.
- *
- * @param isOpen {@code true} to open, {@code false} to close
- */
- public void setOpen(boolean isOpen) {
- main.setOpen(isOpen);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java
deleted file mode 100644
index 2d00281bdf..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CreateChangeDialog.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2014 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class CreateChangeDialog extends TextAreaActionDialog {
- private SuggestBox newChange;
- private List<BranchInfo> branches;
- private TextBox topic;
-
- public CreateChangeDialog(Project.NameKey project) {
- super(Util.C.dialogCreateChangeTitle(), Util.C.dialogCreateChangeHeading());
- ProjectApi.getBranches(
- project,
- new GerritCallback<JsArray<BranchInfo>>() {
- @Override
- public void onSuccess(JsArray<BranchInfo> result) {
- branches = Natives.asList(result);
- }
- });
-
- topic = new TextBox();
- topic.setWidth("100%");
- topic.getElement().getStyle().setProperty("boxSizing", "border-box");
- FlowPanel newTopicPanel = new FlowPanel();
- newTopicPanel.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
- newTopicPanel.add(topic);
- panel.insert(newTopicPanel, 0);
- panel.insert(new SmallHeading(Util.C.newChangeTopicSuggestion()), 0);
-
- newChange =
- new SuggestBox(
- new HighlightSuggestOracle() {
- @Override
- protected void onRequestSuggestions(Request request, Callback done) {
- List<BranchSuggestion> suggestions = new ArrayList<>();
- for (BranchInfo b : branches) {
- if (b.ref().contains(request.getQuery())) {
- suggestions.add(new BranchSuggestion(b));
- }
- }
- done.onSuggestionsReady(request, new Response(suggestions));
- }
- });
-
- newChange.setWidth("100%");
- newChange.getElement().getStyle().setProperty("boxSizing", "border-box");
- FlowPanel newChangePanel = new FlowPanel();
- newChangePanel.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
- newChangePanel.add(newChange);
- panel.insert(newChangePanel, 0);
- panel.insert(new SmallHeading(Util.C.newChangeBranchSuggestion()), 0);
-
- message.setCharacterWidth(70);
- }
-
- @Override
- public void center() {
- super.center();
- GlobalKey.dialog(this);
- newChange.setFocus(true);
- }
-
- public String getDestinationBranch() {
- return newChange.getText();
- }
-
- public String getDestinationTopic() {
- return topic.getText();
- }
-
- static class BranchSuggestion implements Suggestion {
- private BranchInfo branch;
-
- BranchSuggestion(BranchInfo branch) {
- this.branch = branch;
- }
-
- @Override
- public String getDisplayString() {
- return branch.getShortName();
- }
-
- @Override
- public String getReplacementString() {
- return branch.getShortName();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
deleted file mode 100644
index 045e0ae279..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
+++ /dev/null
@@ -1,222 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import java.util.Comparator;
-import java.util.Iterator;
-
-public abstract class FancyFlexTable<RowItem> extends Composite {
- private static final FancyFlexTableImpl impl = GWT.create(FancyFlexTableImpl.class);
-
- protected static final int C_ARROW = 0;
-
- protected final MyFlexTable table;
-
- protected FancyFlexTable() {
- table = createFlexTable();
- table.addStyleName(Gerrit.RESOURCES.css().changeTable());
- table.setWidth("100%");
- initWidget(table);
-
- table.setText(0, C_ARROW, "");
- table.getCellFormatter().addStyleName(0, C_ARROW, Gerrit.RESOURCES.css().iconHeader());
- }
-
- protected MyFlexTable createFlexTable() {
- return new MyFlexTable();
- }
-
- protected RowItem getRowItem(int row) {
- return FancyFlexTable.<RowItem>getRowItem(table.getCellFormatter().getElement(row, 0));
- }
-
- protected void setRowItem(int row, RowItem item) {
- setRowItem(table.getCellFormatter().getElement(row, 0), item);
- }
-
- /**
- * Finds an item in the table.
- *
- * @param comparator comparator by which the items in the table are sorted
- * @param item the item that should be found
- * @return if the item is found the number of the row that contains the item; if the item is not
- * found {@code -1}
- */
- protected int findRowItem(Comparator<RowItem> comparator, RowItem item) {
- int row = lookupRowItem(comparator, item);
- if (row < table.getRowCount() && comparator.compare(item, getRowItem(row)) == 0) {
- return row;
- }
- return -1;
- }
-
- /**
- * Finds the number of the row where a new item should be inserted into the table.
- *
- * @param comparator comparator by which the items in the table are sorted
- * @param item the new item that should be inserted
- * @return if the item is not yet contained in the table, the number of the row where the new item
- * should be inserted; if the item is already contained in the table {@code -1}
- */
- protected int getInsertRow(Comparator<RowItem> comparator, RowItem item) {
- int row = lookupRowItem(comparator, item);
- if (row >= table.getRowCount() || comparator.compare(item, getRowItem(row)) != 0) {
- return row;
- }
- return -1;
- }
-
- /**
- * Makes a binary search for the given row item over the table.
- *
- * @param comparator comparator by which the items in the table are sorted
- * @param item the item that should be looked up
- * @return if the item is found the number of the row that contains the item; if the item is not
- * found the number of the row where the item should be inserted according to the given
- * comparator.
- */
- private int lookupRowItem(Comparator<RowItem> comparator, RowItem item) {
- int left = 1;
- int right = table.getRowCount() - 1;
- while (left <= right) {
- int middle = (left + right) >>> 1; // (left+right)/2
- RowItem i = getRowItem(middle);
- int cmp = comparator.compare(i, item);
-
- if (cmp < 0) {
- left = middle + 1;
- } else if (cmp > 0) {
- right = middle - 1;
- } else {
- // item is already contained in the table
- return middle;
- }
- }
- return left;
- }
-
- protected void resetHtml(SafeHtml body) {
- for (Iterator<Widget> i = table.iterator(); i.hasNext(); ) {
- i.next();
- i.remove();
- }
- impl.resetHtml(table, body);
- }
-
- protected void scrollIntoView(int topRow, int endRow) {
- final CellFormatter fmt = table.getCellFormatter();
- final Element top = fmt.getElement(topRow, C_ARROW).getParentElement();
- final Element end = fmt.getElement(endRow, C_ARROW).getParentElement();
-
- final int rTop = top.getAbsoluteTop();
- final int rEnd = end.getAbsoluteTop() + end.getOffsetHeight();
- final int rHeight = rEnd - rTop;
-
- final int sTop = Document.get().getScrollTop();
- final int sHeight = Document.get().getClientHeight();
- final int sEnd = sTop + sHeight;
-
- final int nTop;
- if (sHeight <= rHeight) {
- // The region is larger than the visible area, make the top
- // exactly the top of the region, its the most visible area.
- //
- nTop = rTop;
- } else if (sTop <= rTop && rTop <= sEnd) {
- // At least part of the region is already visible.
- //
- if (rEnd <= sEnd) {
- // ... actually its all visible. Don't scroll.
- //
- return;
- }
-
- // Move only enough to make the end visible.
- //
- nTop = sTop + (rHeight - (sEnd - rTop));
- } else {
- // None of the region is visible. Make it visible.
- //
- nTop = rTop;
- }
- Document.get().setScrollTop(nTop);
- }
-
- protected void applyDataRowStyle(int newRow) {
- table.getCellFormatter().addStyleName(newRow, C_ARROW, Gerrit.RESOURCES.css().iconCell());
- table.getCellFormatter().addStyleName(newRow, C_ARROW, Gerrit.RESOURCES.css().leftMostCell());
- }
-
- /**
- * Get the td element that contains another element.
- *
- * @param target the child element whose parent td is required.
- * @return the td containing element {@code target}; null if {@code target} is not a member of
- * this table.
- */
- protected Element getParentCell(Element target) {
- final Element body = FancyFlexTableImpl.getBodyElement(table);
- for (Element td = target; td != null && td != body; td = DOM.getParent(td)) {
- // If it's a TD, it might be the one we're looking for.
- if ("td".equalsIgnoreCase(td.getTagName())) {
- // Make sure it's directly a part of this table.
- Element tr = DOM.getParent(td);
- if (DOM.getParent(tr) == body) {
- return td;
- }
- }
- }
- return null;
- }
-
- /** @return the row of the child element; -1 if the child is not in the table. */
- protected int rowOf(Element target) {
- final Element td = getParentCell(target);
- if (td == null) {
- return -1;
- }
- final Element tr = DOM.getParent(td);
- final Element body = DOM.getParent(tr);
- return DOM.getChildIndex(body, tr);
- }
-
- /** @return the cell of the child element; -1 if the child is not in the table. */
- protected int columnOf(Element target) {
- final Element td = getParentCell(target);
- if (td == null) {
- return -1;
- }
- final Element tr = DOM.getParent(td);
- return DOM.getChildIndex(tr, td);
- }
-
- protected static class MyFlexTable extends FlexTable {}
-
- private static native <ItemType> void setRowItem(Element td, ItemType c)
- /*-{ td['__gerritRowItem'] = c; }-*/ ;
-
- private static native <ItemType> ItemType getRowItem(Element td)
- /*-{ return td['__gerritRowItem']; }-*/ ;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
deleted file mode 100644
index a3a2a7a4ad..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
+++ /dev/null
@@ -1,29 +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.client.ui;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HTMLTable;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-
-public class FancyFlexTableImpl {
- public void resetHtml(FlexTable myTable, SafeHtml body) {
- SafeHtml.setInnerHTML(getBodyElement(myTable), body);
- }
-
- protected static native Element getBodyElement(HTMLTable myTable)
- /*-{ return myTable.@com.google.gwt.user.client.ui.HTMLTable::bodyElem; }-*/ ;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java
deleted file mode 100644
index 3eae0f8c1e..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE8.java
+++ /dev/null
@@ -1,54 +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.client.ui;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HTMLTable;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-public class FancyFlexTableImplIE8 extends FancyFlexTableImpl {
- @Override
- public void resetHtml(FlexTable myTable, SafeHtml bodyHtml) {
- final Element oldBody = getBodyElement(myTable);
- final Element newBody = parseBody(bodyHtml);
- assert newBody != null;
-
- final Element tableElem = DOM.getParent(oldBody);
- tableElem.removeChild(oldBody);
- setBodyElement(myTable, newBody);
- DOM.appendChild(tableElem, newBody);
- }
-
- private static Element parseBody(SafeHtml body) {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.openElement("table");
- b.append(body);
- b.closeElement("table");
-
- final Element newTable = SafeHtml.parse(b);
- for (Element e = DOM.getFirstChild(newTable); e != null; e = DOM.getNextSibling(e)) {
- if ("tbody".equals(e.getTagName().toLowerCase())) {
- return e;
- }
- }
- return null;
- }
-
- private static native void setBodyElement(HTMLTable myTable, Element newBody)
- /*-{ myTable.@com.google.gwt.user.client.ui.HTMLTable::bodyElem = newBody; }-*/ ;
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingInlineHyperlink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingInlineHyperlink.java
deleted file mode 100644
index f8e382ace8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingInlineHyperlink.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-public class HighlightingInlineHyperlink extends InlineHyperlink {
-
- private String toHighlight;
-
- public HighlightingInlineHyperlink(final String text, String token, String toHighlight) {
- super(text, token);
- this.toHighlight = toHighlight;
- highlight(text, toHighlight);
- }
-
- @Override
- public void setText(String text) {
- super.setText(text);
- highlight(text, toHighlight);
- }
-
- private void highlight(String text, String toHighlight) {
- setHTML(Util.highlight(text, toHighlight));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingProjectsTable.java
deleted file mode 100644
index 1e3be3fb33..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HighlightingProjectsTable.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gerrit.client.projects.ProjectInfo;
-import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gwt.user.client.ui.InlineHTML;
-
-public class HighlightingProjectsTable extends ProjectsTable {
- private String toHighlight;
-
- public void display(ProjectMap projects, String toHighlight) {
- this.toHighlight = toHighlight;
- super.display(projects);
- }
-
- @Override
- protected void populate(int row, ProjectInfo k) {
- populateState(row, k);
- table.setWidget(
- row, ProjectsTable.C_NAME, new InlineHTML(Util.highlight(k.name(), toHighlight)));
- table.setText(row, ProjectsTable.C_DESCRIPTION, k.description());
-
- setRowItem(row, k);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HintTextBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HintTextBox.java
deleted file mode 100644
index 4ccfe9df65..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/HintTextBox.java
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-public class HintTextBox extends NpTextBox {
- private HandlerRegistration hintFocusHandler;
- private HandlerRegistration hintBlurHandler;
- private HandlerRegistration keyDownHandler;
-
- private String hintText;
- private String hintStyleName = Gerrit.RESOURCES.css().inputFieldTypeHint();
-
- private String prevText;
-
- private boolean hintOn;
- private boolean isFocused;
-
- @Override
- public String getText() {
- if (hintOn) {
- return "";
- }
- return super.getText();
- }
-
- @Override
- public void setText(String text) {
- focusHint();
-
- super.setText(text);
- prevText = text;
-
- if (!isFocused) {
- blurHint();
- }
- }
-
- public String getHintText() {
- return hintText;
- }
-
- public void setHintText(String text) {
- if (text == null) {
- if (hintText == null) { // was not set, still not set, no change.
- return;
- }
-
- // Clearing a previously set Hint
- hintFocusHandler.removeHandler();
- hintFocusHandler = null;
- hintBlurHandler.removeHandler();
- hintBlurHandler = null;
- keyDownHandler.removeHandler();
- keyDownHandler = null;
- hintText = null;
- focusHint();
-
- return;
- }
-
- // Setting Hints
-
- if (hintText == null) { // first time (was not already set)
- hintText = text;
-
- hintFocusHandler =
- addFocusHandler(
- new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- focusHint();
- prevText = getText();
- isFocused = true;
- }
- });
-
- hintBlurHandler =
- addBlurHandler(
- new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- blurHint();
- isFocused = false;
- }
- });
-
- /*
- * There seems to be a strange bug (at least on firefox 3.5.9 ubuntu) with
- * the textbox under the following circumstances:
- * 1) The field is not focused with BText in it.
- * 2) The field receives focus and a focus listener changes the text to FText
- * 3) The ESC key is pressed and the value of the field has not changed
- * (ever) from FText
- * 4) BUG: The text value gets reset to BText!
- *
- * A counter to this bug seems to be to force setFocus(false) on ESC.
- */
-
- /* Chrome does not create a KeyPressEvent on ESC, so use KeyDownEvents */
- keyDownHandler =
- addKeyDownHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent event) {
- onKey(event.getNativeKeyCode());
- }
- });
-
- } else { // Changing an already set Hint
-
- focusHint();
- hintText = text;
- }
-
- if (!isFocused) {
- blurHint();
- }
- }
-
- private void onKey(int key) {
- if (key == KeyCodes.KEY_ESCAPE) {
- setText(prevText);
-
- Widget p = getParent();
- if (p instanceof SuggestBox) {
- // Since the text was changed, ensure that the SuggestBox is
- // aware of this change so that it will refresh properly on
- // the next keystroke. Without this, if the first keystroke
- // recreates the same string as before ESC was pressed, the
- // SuggestBox will think that the string has not changed, and
- // it will not yet provide any Suggestions.
- ((SuggestBox) p).showSuggestionList();
-
- // The suggestion list lingers if we don't hide it.
- ((DefaultSuggestionDisplay) ((SuggestBox) p).getSuggestionDisplay()).hideSuggestions();
- }
-
- setFocus(false);
- }
- }
-
- public void setHintStyleName(String styleName) {
- if (hintStyleName != null && hintOn) {
- removeStyleName(hintStyleName);
- }
-
- hintStyleName = styleName;
-
- if (styleName != null && hintOn) {
- addStyleName(styleName);
- }
- }
-
- public String getHintStyleName() {
- return hintStyleName;
- }
-
- protected void blurHint() {
- if (!hintOn && getHintText() != null && "".equals(super.getText())) {
- hintOn = true;
- super.setText(getHintText());
- if (getHintStyleName() != null) {
- addStyleName(getHintStyleName());
- }
- }
- }
-
- protected void focusHint() {
- if (hintOn) {
- super.setText("");
- if (getHintStyleName() != null) {
- removeStyleName(getHintStyleName());
- }
- hintOn = false;
- }
- }
-
- @Override
- public void setFocus(boolean focus) {
- super.setFocus(focus);
-
- if (focus != isFocused) {
- if (focus) {
- focusHint();
- } else {
- blurHint();
- }
- }
-
- isFocused = focus;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java
deleted file mode 100644
index c35d097319..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java
+++ /dev/null
@@ -1,68 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
-
-/** Standard GWT hyperlink with late updating of the token. */
-public class Hyperlink extends com.google.gwt.user.client.ui.Hyperlink {
- public static final HyperlinkImpl impl = GWT.create(HyperlinkImpl.class);
-
- /** Initialize a default hyperlink with no target and no text. */
- public Hyperlink() {}
-
- /**
- * Creates a hyperlink with its text and target history token specified.
- *
- * @param text the hyperlink's text
- * @param token the history token to which it will link, which may not be null (use {@link Anchor}
- * instead if you don't need history processing)
- */
- public Hyperlink(String text, String token) {
- super(text, token);
- }
-
- /**
- * Creates a hyperlink with its text and target history token specified.
- *
- * @param text the hyperlink's text
- * @param asHTML {@code true} to treat the specified text as html
- * @param token the history token to which it will link
- * @see #setTargetHistoryToken
- */
- public Hyperlink(String text, boolean asHTML, String token) {
- super(text, asHTML, token);
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- if (DOM.eventGetType(event) == Event.ONCLICK && impl.handleAsClick(event)) {
- event.preventDefault();
- go();
- } else {
- super.onBrowserEvent(event);
- }
- }
-
- /** Create the screen and start rendering, updating the browser history. */
- public void go() {
- Gerrit.display(getTargetHistoryToken());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/InlineHyperlink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/InlineHyperlink.java
deleted file mode 100644
index a4edb5b528..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/InlineHyperlink.java
+++ /dev/null
@@ -1,52 +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.client.ui;
-
-import static com.google.gerrit.client.ui.Hyperlink.impl;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-
-/** Standard GWT hyperlink with late updating of the token. */
-public class InlineHyperlink extends com.google.gwt.user.client.ui.InlineHyperlink {
- /**
- * Creates a link with its text and target history token specified.
- *
- * @param text the hyperlink's text
- * @param token the history token to which it will link
- */
- public InlineHyperlink(String text, String token) {
- super(text, token);
- }
-
- /** Creates an empty link. */
- public InlineHyperlink() {}
-
- @Override
- public void onBrowserEvent(Event event) {
- if (DOM.eventGetType(event) == Event.ONCLICK && impl.handleAsClick(event)) {
- event.preventDefault();
- go();
- } else {
- super.onBrowserEvent(event);
- }
- }
-
- /** Create the screen and start rendering, updating the browser history. */
- public void go() {
- Gerrit.display(getTargetHistoryToken());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
deleted file mode 100644
index d3db09897a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
+++ /dev/null
@@ -1,91 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-public class LinkMenuBar extends Composite implements ScreenLoadHandler {
- private final FlowPanel body;
-
- public LinkMenuBar() {
- body = new FlowPanel();
- initWidget(body);
- setStyleName(Gerrit.RESOURCES.css().linkMenuBar());
- Roles.getMenubarRole().set(getElement());
- Gerrit.EVENT_BUS.addHandler(ScreenLoadEvent.TYPE, this);
- }
-
- public void addItem(String text, Command imp) {
- add(new CommandMenuItem(text, imp));
- }
-
- public void addItem(CommandMenuItem i) {
- add(i);
- }
-
- public void addItem(LinkMenuItem i) {
- i.setMenuBar(this);
- add(i);
- }
-
- public void insertItem(LinkMenuItem i, int beforeIndex) {
- i.setMenuBar(this);
- insert(i, beforeIndex);
- }
-
- public void clear() {
- body.clear();
- }
-
- public LinkMenuItem find(String targetToken) {
- for (Widget w : body) {
- if (w instanceof LinkMenuItem) {
- LinkMenuItem m = (LinkMenuItem) w;
- if (targetToken.equals(m.getTargetHistoryToken())) {
- return m;
- }
- }
- }
- return null;
- }
-
- public void add(Widget i) {
- if (body.getWidgetCount() > 0) {
- final Widget p = body.getWidget(body.getWidgetCount() - 1);
- p.addStyleName(Gerrit.RESOURCES.css().linkMenuItemNotLast());
- }
- body.add(i);
- }
-
- public void insert(Widget i, int beforeIndex) {
- if (body.getWidgetCount() == 0 || body.getWidgetCount() <= beforeIndex) {
- add(i);
- return;
- }
- body.insert(i, beforeIndex);
- }
-
- public int getWidgetIndex(Widget i) {
- return body.getWidgetIndex(i);
- }
-
- @Override
- public void onScreenLoad(ScreenLoadEvent event) {}
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
deleted file mode 100644
index 8a8ab25637..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
+++ /dev/null
@@ -1,54 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.aria.client.Roles;
-import com.google.gwt.dom.client.AnchorElement;
-
-public class LinkMenuItem extends InlineHyperlink implements ScreenLoadHandler {
- private LinkMenuBar bar;
-
- public LinkMenuItem(String text, String targetHistoryToken) {
- super(text, targetHistoryToken);
- setStyleName(Gerrit.RESOURCES.css().menuItem());
- Roles.getMenuitemRole().set(getElement());
- Gerrit.EVENT_BUS.addHandler(ScreenLoadEvent.TYPE, this);
- }
-
- @Override
- public void go() {
- super.go();
- AnchorElement.as(getElement()).blur();
- }
-
- public void setMenuBar(LinkMenuBar bar) {
- this.bar = bar;
- }
-
- @Override
- public void onScreenLoad(ScreenLoadEvent event) {
- if (match(event.getScreen().getToken())) {
- Gerrit.selectMenu(bar);
- addStyleName(Gerrit.RESOURCES.css().activeRow());
- } else {
- removeStyleName(Gerrit.RESOURCES.css().activeRow());
- }
- }
-
- protected boolean match(String token) {
- return token.equals(getTargetHistoryToken());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MenuScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MenuScreen.java
deleted file mode 100644
index 0f28ddc742..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MenuScreen.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-public abstract class MenuScreen extends Screen {
- private final LinkMenuBar menu;
- private final FlowPanel body;
-
- public MenuScreen() {
- menu = new LinkMenuBar();
- menu.setStyleName(Gerrit.RESOURCES.css().menuScreenMenuBar());
- body = new FlowPanel();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- HorizontalPanel hp = new HorizontalPanel();
- hp.add(menu);
- hp.add(body);
- super.add(hp);
- }
-
- @Override
- public void setToken(String token) {
- LinkMenuItem self = menu.find(token);
- if (self != null) {
- self.addStyleName(Gerrit.RESOURCES.css().activeRow());
- }
- super.setToken(token);
- }
-
- @Override
- protected FlowPanel getBody() {
- return body;
- }
-
- @Override
- protected void add(Widget w) {
- body.add(w);
- }
-
- protected void link(String text, String target) {
- link(text, target, true);
- }
-
- protected void link(String text, String target, boolean visible) {
- final LinkMenuItem item = new LinkMenuItem(text, target);
- item.setStyleName(Gerrit.RESOURCES.css().menuItem());
- item.setVisible(visible);
- menu.add(item);
- }
-
- protected void setLinkVisible(String token, boolean visible) {
- final LinkMenuItem item = menu.find(token);
- item.setVisible(visible);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MorphingTabPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MorphingTabPanel.java
deleted file mode 100644
index 7fd6432b42..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MorphingTabPanel.java
+++ /dev/null
@@ -1,101 +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.client.ui;
-
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.user.client.ui.TabPanel;
-import com.google.gwt.user.client.ui.Widget;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A TabPanel which allows entries to be hidden. This class is not yet designed to handle removes or
- * any other add methods than the one overridden here. It is also not designed to handle anything
- * other than text for the tab.
- */
-public class MorphingTabPanel extends TabPanel {
- // Keep track of the order the widgets/texts should be in when not hidden.
- private List<Widget> widgets = new ArrayList<>();
- private List<String> texts = new ArrayList<>();
-
- // currently visible widgets
- private List<Widget> visibles = new ArrayList<>();
-
- private int selection;
-
- public MorphingTabPanel() {
- addSelectionHandler(
- new SelectionHandler<Integer>() {
- @Override
- public void onSelection(SelectionEvent<Integer> ev) {
- selection = ev.getSelectedItem();
- }
- });
- }
-
- public int getSelectedIndex() {
- return selection;
- }
-
- public Widget getSelectedWidget() {
- return getWidget(getSelectedIndex());
- }
-
- @Override
- public void clear() {
- super.clear();
- widgets.clear();
- texts.clear();
- visibles.clear();
- }
-
- @Override
- public void add(Widget w, String tabText) {
- addInvisible(w, tabText);
- visibles.add(w);
- super.add(w, tabText);
- }
-
- public void addInvisible(Widget w, String tabText) {
- widgets.add(w);
- texts.add(tabText);
- }
-
- public void setVisible(Widget w, boolean visible) {
- if (visible) {
- if (!visibles.contains(w)) {
- int origPos = widgets.indexOf(w);
-
- /* Re-insert the widget right after the first visible widget found
- when scanning backwards from the current widget */
- for (int pos = origPos - 1; pos >= 0; pos--) {
- int visiblePos = visibles.indexOf(widgets.get(pos));
- if (visiblePos != -1) {
- visibles.add(visiblePos + 1, w);
- insert(w, texts.get(origPos), visiblePos + 1);
- break;
- }
- }
- }
- } else {
- int i = visibles.indexOf(w);
- if (i != -1) {
- visibles.remove(i);
- remove(i);
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java
deleted file mode 100644
index 3821e930eb..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/MoveDialog.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.projects.BranchInfo;
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class MoveDialog extends TextAreaActionDialog {
- private SuggestBox newBranch;
- private List<BranchInfo> branches;
-
- public MoveDialog(Project.NameKey project) {
- super(Util.C.moveTitle(), Util.C.moveChangeMessage());
- ProjectApi.getBranches(
- project,
- new GerritCallback<JsArray<BranchInfo>>() {
- @Override
- public void onSuccess(JsArray<BranchInfo> result) {
- branches = Natives.asList(result);
- }
- });
-
- newBranch =
- new SuggestBox(
- new HighlightSuggestOracle() {
- @Override
- protected void onRequestSuggestions(Request request, Callback done) {
- List<BranchSuggestion> suggestions = new ArrayList<>();
- for (BranchInfo b : branches) {
- if (b.ref().contains(request.getQuery())) {
- suggestions.add(new BranchSuggestion(b));
- }
- }
- done.onSuggestionsReady(request, new Response(suggestions));
- }
- });
-
- newBranch.setWidth("100%");
- newBranch.getElement().getStyle().setProperty("boxSizing", "border-box");
- message.setCharacterWidth(70);
-
- FlowPanel mwrap = new FlowPanel();
- mwrap.setStyleName(Gerrit.RESOURCES.css().commentedActionMessage());
- mwrap.add(newBranch);
-
- panel.insert(mwrap, 0);
- panel.insert(new SmallHeading(Util.C.headingMoveBranch()), 0);
- }
-
- @Override
- public void center() {
- super.center();
- GlobalKey.dialog(this);
- newBranch.setFocus(true);
- }
-
- public String getDestinationBranch() {
- return newBranch.getText();
- }
-
- static class BranchSuggestion implements Suggestion {
- private BranchInfo branch;
-
- BranchSuggestion(BranchInfo branch) {
- this.branch = branch;
- }
-
- @Override
- public String getDisplayString() {
- String refsHeads = "refs/heads/";
- if (branch.ref().startsWith(refsHeads)) {
- return branch.ref().substring(refsHeads.length());
- }
- return branch.ref();
- }
-
- @Override
- public String getReplacementString() {
- return branch.getShortName();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java
deleted file mode 100644
index b2306a6b96..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java
+++ /dev/null
@@ -1,408 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import java.util.LinkedHashMap;
-import java.util.Map.Entry;
-
-public abstract class NavigationTable<RowItem> extends FancyFlexTable<RowItem> {
- protected class MyFlexTable extends FancyFlexTable.MyFlexTable {
- public MyFlexTable() {
- sinkEvents(Event.ONDBLCLICK | Event.ONCLICK);
- }
-
- @Override
- public void onBrowserEvent(Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.ONCLICK:
- {
- // Find out which cell was actually clicked.
- final Element td = getEventTargetCell(event);
- if (td == null) {
- break;
- }
- final int row = rowOf(td);
- if (getRowItem(row) != null) {
- onCellSingleClick(event, rowOf(td), columnOf(td));
- return;
- }
- break;
- }
- case Event.ONDBLCLICK:
- {
- // Find out which cell was actually clicked.
- Element td = getEventTargetCell(event);
- if (td == null) {
- return;
- }
- onCellDoubleClick(rowOf(td), columnOf(td));
- return;
- }
- }
- super.onBrowserEvent(event);
- }
- }
-
- private static final LinkedHashMap<String, Object> savedPositions =
- new LinkedHashMap<String, Object>(10, 0.75f, true) {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected boolean removeEldestEntry(Entry<String, Object> eldest) {
- return size() >= 20;
- }
- };
-
- private final Image pointer;
- protected final KeyCommandSet keysNavigation;
- protected final KeyCommandSet keysAction;
- private HandlerRegistration regNavigation;
- private HandlerRegistration regAction;
- private int currentRow = -1;
- private String saveId;
-
- private boolean computedScrollType;
- private ScrollPanel parentScrollPanel;
-
- protected NavigationTable(String itemHelpName) {
- this();
- keysNavigation.add(
- new PrevKeyCommand(0, 'k', Util.M.helpListPrev(itemHelpName)),
- new NextKeyCommand(0, 'j', Util.M.helpListNext(itemHelpName)));
- keysNavigation.add(new OpenKeyCommand(0, 'o', Util.M.helpListOpen(itemHelpName)));
- keysNavigation.add(
- new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.M.helpListOpen(itemHelpName)));
- }
-
- protected NavigationTable() {
- pointer = new Image(Gerrit.RESOURCES.arrowRight());
- keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
- keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
- }
-
- protected abstract void onOpenRow(int row);
-
- protected abstract Object getRowItemKey(RowItem item);
-
- private void onUp() {
- for (int row = currentRow - 1; row >= 0; row--) {
- if (getRowItem(row) != null) {
- movePointerTo(row);
- break;
- }
- }
- }
-
- private void onDown() {
- final int max = table.getRowCount();
- for (int row = currentRow + 1; row < max; row++) {
- if (getRowItem(row) != null) {
- movePointerTo(row);
- break;
- }
- }
- }
-
- private void onOpen() {
- if (0 <= currentRow && currentRow < table.getRowCount()) {
- if (getRowItem(currentRow) != null) {
- onOpenRow(currentRow);
- }
- }
- }
-
- /**
- * Invoked when the user double clicks on a table cell.
- *
- * @param row row number.
- * @param column column number.
- */
- protected void onCellDoubleClick(int row, int column) {
- onOpenRow(row);
- }
-
- /**
- * Invoked when the user clicks on a table cell.
- *
- * @param event click event.
- * @param row row number.
- * @param column column number.
- */
- protected void onCellSingleClick(Event event, int row, int column) {
- movePointerTo(row);
- }
-
- protected int getCurrentRow() {
- return currentRow;
- }
-
- protected void ensurePointerVisible() {
- final int max = table.getRowCount();
- int row = currentRow;
- final int init = row;
- if (row < 0) {
- row = 0;
- } else if (max <= row) {
- row = max - 1;
- }
-
- final CellFormatter fmt = table.getCellFormatter();
- final int sTop = Document.get().getScrollTop();
- final int sEnd = sTop + Document.get().getClientHeight();
-
- while (0 <= row && row < max) {
- final Element cur = fmt.getElement(row, C_ARROW).getParentElement();
- final int cTop = cur.getAbsoluteTop();
- final int cEnd = cTop + cur.getOffsetHeight();
-
- if (cEnd < sTop) {
- row++;
- } else if (sEnd < cTop) {
- row--;
- } else {
- break;
- }
- }
-
- if (init != row) {
- movePointerTo(row, false);
- }
- }
-
- protected void movePointerTo(int newRow) {
- movePointerTo(newRow, true);
- }
-
- protected void movePointerTo(int newRow, boolean scroll) {
- final CellFormatter fmt = table.getCellFormatter();
- final boolean clear = 0 <= currentRow && currentRow < table.getRowCount();
- if (clear) {
- final Element tr = fmt.getElement(currentRow, C_ARROW).getParentElement();
- UIObject.setStyleName(tr, Gerrit.RESOURCES.css().activeRow(), false);
- }
- if (0 <= newRow && newRow < table.getRowCount() && getRowItem(newRow) != null) {
- table.setWidget(newRow, C_ARROW, pointer);
- final Element tr = fmt.getElement(newRow, C_ARROW).getParentElement();
- UIObject.setStyleName(tr, Gerrit.RESOURCES.css().activeRow(), true);
- if (scroll && isAttached()) {
- scrollIntoView(tr);
- }
- } else if (clear) {
- table.setWidget(currentRow, C_ARROW, null);
- pointer.removeFromParent();
- }
- currentRow = newRow;
- }
-
- protected void scrollIntoView(Element tr) {
- if (!computedScrollType) {
- parentScrollPanel = null;
- Widget w = getParent();
- while (w != null) {
- if (w instanceof ScrollPanel) {
- parentScrollPanel = (ScrollPanel) w;
- break;
- }
- w = w.getParent();
- }
- computedScrollType = true;
- }
-
- if (parentScrollPanel != null) {
- parentScrollPanel.ensureVisible(
- new UIObject() {
- {
- setElement(tr);
- }
- });
- } else {
- int rt = tr.getAbsoluteTop();
- int rl = tr.getAbsoluteLeft();
- int rb = tr.getAbsoluteBottom();
-
- int wt = Window.getScrollTop();
- int wl = Window.getScrollLeft();
-
- int wh = Window.getClientHeight();
- int ww = Window.getClientWidth();
- int wb = wt + wh;
-
- // If the row is partially or fully obscured, scroll:
- //
- // rl < wl: Row left edge is off screen to left.
- // rt < wt: Row top is above top of window.
- // wb < rt: Row top is below bottom of window.
- // wb < rb: Row bottom is below bottom of window.
- if (rl < wl || rt < wt || wb < rt || wb < rb) {
- if (rl < wl) {
- // Left edge needs to move to make it visible.
- // If the row fully fits in the window, set 0.
- if (tr.getAbsoluteRight() < ww) {
- wl = 0;
- } else {
- wl = Math.max(tr.getAbsoluteLeft() - 5, 0);
- }
- }
-
- // Vertically center the row in the window.
- int h = (wh - (rb - rt)) / 2;
- Window.scrollTo(wl, Math.max(rt - h, 0));
- }
- }
- }
-
- protected void movePointerTo(Object oldId) {
- final int row = findRow(oldId);
- if (0 <= row) {
- movePointerTo(row);
- }
- }
-
- protected int findRow(Object oldId) {
- if (oldId != null) {
- final int max = table.getRowCount();
- for (int row = 0; row < max; row++) {
- final RowItem c = getRowItem(row);
- if (c != null && oldId.equals(getRowItemKey(c))) {
- return row;
- }
- }
- }
- return -1;
- }
-
- @Override
- public void resetHtml(SafeHtml body) {
- currentRow = -1;
- super.resetHtml(body);
- }
-
- public void finishDisplay() {
- if (currentRow >= table.getRowCount()) {
- currentRow = -1;
- }
- if (saveId != null) {
- movePointerTo(savedPositions.get(saveId));
- }
- if (currentRow < 0) {
- onDown();
- }
- }
-
- public void setSavePointerId(String id) {
- saveId = id;
- }
-
- public void setRegisterKeys(boolean on) {
- if (on && isAttached()) {
- if (regNavigation == null) {
- regNavigation = GlobalKey.add(this, keysNavigation);
- }
- if (regAction == null) {
- regAction = GlobalKey.add(this, keysAction);
- }
- } else {
- if (regNavigation != null) {
- regNavigation.removeHandler();
- regNavigation = null;
- }
- if (regAction != null) {
- regAction.removeHandler();
- regAction = null;
- }
- }
- }
-
- @Override
- protected void onLoad() {
- computedScrollType = false;
- parentScrollPanel = null;
- }
-
- @Override
- protected void onUnload() {
- setRegisterKeys(false);
-
- if (saveId != null && currentRow >= 0) {
- final RowItem c = getRowItem(currentRow);
- if (c != null) {
- savedPositions.put(saveId, getRowItemKey(c));
- }
- }
-
- computedScrollType = false;
- parentScrollPanel = null;
- super.onUnload();
- }
-
- @Override
- protected MyFlexTable createFlexTable() {
- return new MyFlexTable();
- }
-
- public class PrevKeyCommand extends KeyCommand {
- public PrevKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- ensurePointerVisible();
- onUp();
- }
- }
-
- public class NextKeyCommand extends KeyCommand {
- public NextKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- ensurePointerVisible();
- onDown();
- }
- }
-
- public class OpenKeyCommand extends KeyCommand {
- public OpenKeyCommand(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- ensurePointerVisible();
- onOpen();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java
deleted file mode 100644
index 44207621b0..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java
+++ /dev/null
@@ -1,27 +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.client.ui;
-
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-
-public abstract class NeedsSignInKeyCommand extends KeyCommand {
- public NeedsSignInKeyCommand(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- public NeedsSignInKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java
deleted file mode 100644
index 8be6647e93..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyEvent;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-/** Text box that accepts only integer values. */
-public class NpIntTextBox extends NpTextBox {
- private int intValue;
-
- public NpIntTextBox() {
- init();
- }
-
- private void init() {
- addKeyDownHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent event) {
- int code = event.getNativeKeyCode();
- onKey(event, code, code);
- }
- });
- addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- int charCode = event.getCharCode();
- int keyCode = event.getNativeEvent().getKeyCode();
- onKey(event, charCode, keyCode);
- }
- });
- }
-
- private void onKey(KeyEvent<?> event, int charCode, int keyCode) {
- if ('0' <= charCode && charCode <= '9') {
- if (event.isAnyModifierKeyDown()) {
- event.preventDefault();
- }
- } else {
- switch (keyCode) {
- case KeyCodes.KEY_BACKSPACE:
- case KeyCodes.KEY_LEFT:
- case KeyCodes.KEY_RIGHT:
- case KeyCodes.KEY_HOME:
- case KeyCodes.KEY_END:
- case KeyCodes.KEY_TAB:
- case KeyCodes.KEY_DELETE:
- break;
-
- default:
- // Allow copy and paste using ctl-c/ctrl-v,
- // or whatever the platform's convention is.
- if (!(event.isControlKeyDown() || event.isMetaKeyDown() || event.isAltKeyDown())) {
- event.preventDefault();
- }
- break;
- }
- }
- }
-
- public int getIntValue() {
- String txt = getText().trim();
- if (!txt.isEmpty()) {
- try {
- intValue = Integer.parseInt(getText());
- } catch (NumberFormatException e) {
- // Ignored
- }
- }
- return intValue;
- }
-
- public void setIntValue(int v) {
- intValue = v;
- setText(Integer.toString(v));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java
deleted file mode 100644
index 2c7fcd4346..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/OnEditEnabler.java
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.MouseUpEvent;
-import com.google.gwt.event.dom.client.MouseUpHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.GwtEvent;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FocusWidget;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.TextBoxBase;
-import com.google.gwt.user.client.ui.ValueBoxBase;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Enables a FocusWidget (e.g. a Button) if an edit is detected from any registered input widget.
- */
-public class OnEditEnabler
- implements KeyPressHandler,
- KeyDownHandler,
- MouseUpHandler,
- ChangeHandler,
- ValueChangeHandler<Object> {
-
- private final FocusWidget widget;
- private Map<TextBoxBase, String> strings = new HashMap<>();
- private String originalValue;
-
- // The first parameter to the contructors must be the FocusWidget to enable,
- // subsequent parameters are widgets to listenTo.
-
- public OnEditEnabler(FocusWidget w, TextBoxBase tb) {
- this(w);
- originalValue = tb.getValue().trim();
- listenTo(tb);
- }
-
- public OnEditEnabler(FocusWidget w, ListBox lb) {
- this(w);
- listenTo(lb);
- }
-
- public OnEditEnabler(FocusWidget w, CheckBox cb) {
- this(w);
- listenTo(cb);
- }
-
- public OnEditEnabler(FocusWidget w) {
- widget = w;
- }
-
- public void updateOriginalValue(TextBoxBase tb) {
- originalValue = tb.getValue().trim();
- }
-
- // Register input widgets to be listened to
-
- public void listenTo(TextBoxBase tb) {
- strings.put(tb, tb.getText().trim());
- tb.addKeyPressHandler(this);
-
- // Is there another way to capture middle button X11 pastes in browsers
- // which do not yet support ONPASTE events (Firefox)?
- tb.addMouseUpHandler(this);
-
- // Resetting the "original text" on focus ensures that we are
- // up to date with non-user updates of the text (calls to
- // setText()...) and also up to date with user changes which
- // occurred after enabling "widget".
- tb.addFocusHandler(
- new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- strings.put(tb, tb.getText().trim());
- }
- });
-
- // CTRL-V Pastes in Chrome seem only detectable via BrowserEvents or
- // KeyDownEvents, the latter is better.
- tb.addKeyDownHandler(this);
- }
-
- public void listenTo(ListBox lb) {
- lb.addChangeHandler(this);
- }
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- public void listenTo(CheckBox cb) {
- cb.addValueChangeHandler((ValueChangeHandler) this);
- }
-
- // Handlers
-
- @Override
- public void onKeyPress(KeyPressEvent e) {
- on(e);
- }
-
- @Override
- public void onKeyDown(KeyDownEvent e) {
- on(e);
- }
-
- @Override
- public void onMouseUp(MouseUpEvent e) {
- on(e);
- }
-
- @Override
- public void onChange(ChangeEvent e) {
- on(e);
- }
-
- @SuppressWarnings("rawtypes")
- @Override
- public void onValueChange(ValueChangeEvent e) {
- on(e);
- }
-
- private void on(GwtEvent<?> e) {
- if (widget.isEnabled()
- || !(e.getSource() instanceof FocusWidget)
- || !((FocusWidget) e.getSource()).isEnabled()) {
- if (e.getSource() instanceof ValueBoxBase) {
- final TextBoxBase box = ((TextBoxBase) e.getSource());
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- if (box.getValue().trim().equals(originalValue)) {
- widget.setEnabled(false);
- }
- }
- });
- }
- return;
- }
-
- if (e.getSource() instanceof TextBoxBase) {
- onTextBoxBase((TextBoxBase) e.getSource());
- } else {
- // For many widgets, we can assume that a change is an edit. If
- // a widget does not work that way, it should be special cased
- // above.
- widget.setEnabled(true);
- }
- }
-
- private void onTextBoxBase(TextBoxBase tb) {
- // The text appears to not get updated until the handlers complete.
- Scheduler.get()
- .scheduleDeferred(
- new ScheduledCommand() {
- @Override
- public void execute() {
- String orig = strings.get(tb);
- if (orig == null) {
- orig = "";
- }
- if (!orig.equals(tb.getText().trim())) {
- widget.setEnabled(true);
- }
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PagingHyperlink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PagingHyperlink.java
deleted file mode 100644
index 7f0ef68c1f..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PagingHyperlink.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2015 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.admin.AdminConstants;
-
-public class PagingHyperlink extends Hyperlink {
-
- public static PagingHyperlink createPrev() {
- return new PagingHyperlink(AdminConstants.I.pagedListPrev());
- }
-
- public static PagingHyperlink createNext() {
- return new PagingHyperlink(AdminConstants.I.pagedListNext());
- }
-
- private PagingHyperlink(String text) {
- super(text, true, "");
- setStyleName(Gerrit.RESOURCES.css().pagingLink());
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java
deleted file mode 100644
index ddb8487e76..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ParentProjectBox.java
+++ /dev/null
@@ -1,100 +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.client.ui;
-
-import com.google.gerrit.client.projects.ProjectApi;
-import com.google.gerrit.client.projects.ProjectInfo;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Composite;
-import java.util.HashSet;
-import java.util.Set;
-
-public class ParentProjectBox extends Composite {
- private final RemoteSuggestBox suggestBox;
- private final ParentProjectNameSuggestOracle suggestOracle;
-
- public ParentProjectBox() {
- suggestOracle = new ParentProjectNameSuggestOracle();
- suggestBox = new RemoteSuggestBox(suggestOracle);
- initWidget(suggestBox);
- }
-
- public void setVisibleLength(int len) {
- suggestBox.setVisibleLength(len);
- }
-
- public void setProject(Project.NameKey project) {
- suggestOracle.setProject(project);
- }
-
- public void setParentProject(Project.NameKey parent) {
- suggestBox.setText(parent != null ? parent.get() : "");
- }
-
- public Project.NameKey getParentProjectName() {
- final String projectName = suggestBox.getText().trim();
- if (projectName.isEmpty()) {
- return null;
- }
- return new Project.NameKey(projectName);
- }
-
- private static class ParentProjectNameSuggestOracle extends ProjectNameSuggestOracle {
- private Set<String> exclude = new HashSet<>();
-
- public void setProject(Project.NameKey project) {
- exclude.clear();
- exclude.add(project.get());
- ProjectApi.getChildren(
- project,
- true,
- new AsyncCallback<JsArray<ProjectInfo>>() {
- @Override
- public void onSuccess(JsArray<ProjectInfo> result) {
- for (ProjectInfo p : Natives.asList(result)) {
- exclude.add(p.name());
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- });
- }
-
- @Override
- public void _onRequestSuggestions(Request req, Callback callback) {
- super._onRequestSuggestions(
- req,
- new Callback() {
- @Override
- public void onSuggestionsReady(Request request, Response response) {
- if (!exclude.isEmpty()) {
- Set<Suggestion> filteredSuggestions = new HashSet<>(response.getSuggestions());
- for (Suggestion s : response.getSuggestions()) {
- if (exclude.contains(s.getReplacementString())) {
- filteredSuggestions.remove(s);
- }
- }
- response.setSuggestions(filteredSuggestions);
- }
- callback.onSuggestionsReady(request, response);
- }
- });
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java
deleted file mode 100644
index c5b609a5f8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java
+++ /dev/null
@@ -1,29 +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.client.ui;
-
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-
-/** Link to the default dashboard of a project. */
-public class ProjectLink extends InlineHyperlink {
- public ProjectLink(Project.NameKey proj) {
- this(proj.get(), proj);
- }
-
- public ProjectLink(String text, Project.NameKey proj) {
- super(text, PageLinks.toProjectDefaultDashboard(proj));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java
deleted file mode 100644
index 114f794c08..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLinkMenuItem.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2014 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.client.ui;
-
-import com.google.gerrit.client.Dispatcher;
-import com.google.gerrit.client.admin.ProjectScreen;
-import com.google.gerrit.reviewdb.client.Project;
-
-public class ProjectLinkMenuItem extends LinkMenuItem {
- protected final String panel;
-
- public ProjectLinkMenuItem(String text, String panel) {
- super(text, "");
- this.panel = panel;
- }
-
- @Override
- public void onScreenLoad(ScreenLoadEvent event) {
- Screen screen = event.getScreen();
- Project.NameKey projectKey;
- if (screen instanceof ProjectScreen) {
- projectKey = ((ProjectScreen) screen).getProjectKey();
- } else {
- projectKey = ProjectScreen.getSavedKey();
- }
-
- if (projectKey != null) {
- setVisible(true);
- onScreenLoad(projectKey);
- } else {
- setVisible(false);
- }
- super.onScreenLoad(event);
- }
-
- protected void onScreenLoad(Project.NameKey project) {
- setTargetHistoryToken(Dispatcher.toProjectAdmin(project, panel));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java
deleted file mode 100644
index 89bff711d4..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java
+++ /dev/null
@@ -1,233 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.account.Util;
-import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.DialogBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.HidePopupPanelCommand;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-/** A popup containing all projects. */
-public class ProjectListPopup {
- private HighlightingProjectsTable projectsTab;
- private DialogBox popup;
- private NpTextBox filterTxt;
- private HorizontalPanel filterPanel;
- private String match;
- private Query query;
- private Button closeTop;
- private Button closeBottom;
- private ScrollPanel sp;
- private PopupPanel.PositionCallback popupPosition;
- private int preferredTop;
- private int preferredLeft;
- private boolean poppingUp;
- private boolean firstPopupLoad = true;
-
- public void initPopup(String popupText, String currentPageLink) {
- createWidgets(popupText, currentPageLink);
- final FlowPanel pfp = new FlowPanel();
- pfp.add(filterPanel);
- pfp.add(closeTop);
- sp = new ScrollPanel(projectsTab);
- sp.setSize("100%", "100%");
- pfp.add(sp);
- pfp.add(closeBottom);
- popup.setWidget(pfp);
- popup.setHeight("100%");
- popupPosition = getPositionCallback();
- }
-
- protected PopupPanel.PositionCallback getPositionCallback() {
- return new PopupPanel.PositionCallback() {
- @Override
- public void setPosition(int offsetWidth, int offsetHeight) {
- if (preferredTop + offsetHeight > Window.getClientWidth()) {
- preferredTop = Window.getClientWidth() - offsetHeight;
- }
- if (preferredLeft + offsetWidth > Window.getClientWidth()) {
- preferredLeft = Window.getClientWidth() - offsetWidth;
- }
-
- if (preferredTop < 0) {
- sp.setHeight((sp.getOffsetHeight() + preferredTop) + "px");
- preferredTop = 0;
- }
- if (preferredLeft < 0) {
- sp.setWidth((sp.getOffsetWidth() + preferredLeft) + "px");
- preferredLeft = 0;
- }
-
- popup.setPopupPosition(preferredLeft, preferredTop);
- }
- };
- }
-
- /**
- * Invoked after moving pointer to a project.
- *
- * @param projectName project name.
- */
- protected void onMovePointerTo(String projectName) {}
-
- /**
- * Invoked after opening a project row.
- *
- * @param projectName project name.
- */
- protected void openRow(String projectName) {}
-
- public boolean isPoppingUp() {
- return poppingUp;
- }
-
- private void createWidgets(String popupText, String currentPageLink) {
- filterPanel = new HorizontalPanel();
- filterPanel.setStyleName(Gerrit.RESOURCES.css().projectFilterPanel());
- final Label filterLabel =
- new Label(com.google.gerrit.client.admin.AdminConstants.I.projectFilter());
- filterLabel.setStyleName(Gerrit.RESOURCES.css().projectFilterLabel());
- filterPanel.add(filterLabel);
- filterTxt = new NpTextBox();
- filterTxt.addKeyUpHandler(
- new KeyUpHandler() {
- @Override
- public void onKeyUp(KeyUpEvent event) {
- Query q = new Query(filterTxt.getValue());
- if (!match.equals(q.qMatch)) {
- if (query == null) {
- q.run();
- }
- query = q;
- }
- }
- });
- filterPanel.add(filterTxt);
-
- projectsTab =
- new HighlightingProjectsTable() {
- @Override
- protected void movePointerTo(int row, boolean scroll) {
- super.movePointerTo(row, scroll);
- onMovePointerTo(getRowItem(row).name());
- }
-
- @Override
- protected void onOpenRow(int row) {
- super.onOpenRow(row);
- openRow(getRowItem(row).name());
- }
- };
- projectsTab.setSavePointerId(currentPageLink);
-
- closeTop = createCloseButton();
- closeBottom = createCloseButton();
-
- popup = new DialogBox();
- popup.setModal(false);
- popup.setText(popupText);
- }
-
- private Button createCloseButton() {
- Button close = new Button(Util.C.projectsClose());
- close.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- closePopup();
- }
- });
- return close;
- }
-
- public void displayPopup() {
- poppingUp = true;
- if (firstPopupLoad) { // For sizing/positioning, delay display until loaded
- match = "";
- query = new Query(match).run();
- } else {
- popup.setPopupPositionAndShow(popupPosition);
- GlobalKey.dialog(popup);
- GlobalKey.addApplication(popup, new HidePopupPanelCommand(0, KeyCodes.KEY_ESCAPE, popup));
- projectsTab.setRegisterKeys(true);
- projectsTab.finishDisplay();
- filterTxt.setFocus(true);
- poppingUp = false;
- }
- }
-
- public void closePopup() {
- popup.hide();
- }
-
- public void setPreferredCoordinates(int top, int left) {
- this.preferredTop = top;
- this.preferredLeft = left;
- }
-
- private class Query {
- private final String qMatch;
-
- Query(String match) {
- this.qMatch = match;
- }
-
- Query run() {
- ProjectMap.match(
- qMatch,
- new GerritCallback<ProjectMap>() {
- @Override
- public void onSuccess(ProjectMap result) {
- if (!firstPopupLoad && !popup.isShowing()) {
- query = null;
- } else if (query == Query.this) {
- query = null;
- showMap(result);
- } else {
- query.run();
- }
- }
- });
- return this;
- }
-
- private void showMap(ProjectMap result) {
- ProjectListPopup.this.match = qMatch;
- projectsTab.display(result, qMatch);
-
- if (firstPopupLoad) {
- // Display was delayed until table was loaded
- firstPopupLoad = false;
- displayPopup();
- }
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java
deleted file mode 100644
index f2ebf817f9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java
+++ /dev/null
@@ -1,35 +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.client.ui;
-
-import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-
-/** Suggestion Oracle for Project.NameKey entities. */
-public class ProjectNameSuggestOracle extends SuggestAfterTypingNCharsOracle {
- @Override
- public void _onRequestSuggestions(Request req, Callback callback) {
- ProjectMap.suggest(
- req.getQuery(),
- req.getLimit(),
- new GerritCallback<ProjectMap>() {
- @Override
- public void onSuccess(ProjectMap map) {
- callback.onSuggestionsReady(req, new Response(Natives.asList(map.values())));
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectSearchLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectSearchLink.java
deleted file mode 100644
index f0e06a0f42..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectSearchLink.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.admin.AdminConstants;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Image;
-
-public class ProjectSearchLink extends InlineHyperlink {
-
- public ProjectSearchLink(Project.NameKey projectName) {
- super(" ", PageLinks.toProjectDefaultDashboard(projectName));
- setTitle(AdminConstants.I.projectListQueryLink());
- final Image image = new Image(Gerrit.RESOURCES.queryIcon());
- image.setStyleName(Gerrit.RESOURCES.css().queryIcon());
- DOM.insertBefore(getElement(), image.getElement(), DOM.getFirstChild(getElement()));
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
deleted file mode 100644
index 3576b12598..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.projects.ProjectInfo;
-import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.Image;
-import java.util.List;
-
-public class ProjectsTable extends NavigationTable<ProjectInfo> {
- public static final int C_STATE = 1;
- public static final int C_NAME = 2;
- public static final int C_DESCRIPTION = 3;
- public static final int C_REPO_BROWSER = 4;
-
- public ProjectsTable() {
- super(Util.C.projectItemHelp());
- initColumnHeaders();
- }
-
- protected void initColumnHeaders() {
- table.setText(0, C_STATE, Util.C.projectStateAbbrev());
- table.getCellFormatter().getElement(0, C_STATE).setTitle(Util.C.projectStateHelp());
- table.setText(0, C_NAME, Util.C.projectName());
- table.setText(0, C_DESCRIPTION, Util.C.projectDescription());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, C_STATE, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, C_NAME, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, C_DESCRIPTION, Gerrit.RESOURCES.css().dataHeader());
- }
-
- @Override
- protected Object getRowItemKey(ProjectInfo item) {
- return item.name();
- }
-
- @Override
- protected void onOpenRow(int row) {
- if (row > 0) {
- movePointerTo(row);
- }
- }
-
- public void display(ProjectMap projects) {
- displaySubset(projects, 0, projects.size());
- }
-
- public void displaySubset(ProjectMap projects, int fromIndex, int toIndex) {
- while (1 < table.getRowCount()) {
- table.removeRow(table.getRowCount() - 1);
- }
-
- List<ProjectInfo> list = Natives.asList(projects.values());
- list.sort(comparing(ProjectInfo::name));
- for (ProjectInfo p : list.subList(fromIndex, toIndex)) {
- insert(table.getRowCount(), p);
- }
-
- finishDisplay();
- }
-
- protected void insert(int row, ProjectInfo k) {
- table.insertRow(row);
-
- applyDataRowStyle(row);
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, C_STATE, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, C_NAME, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, C_NAME, Gerrit.RESOURCES.css().projectNameColumn());
- fmt.addStyleName(row, C_DESCRIPTION, Gerrit.RESOURCES.css().dataCell());
-
- populate(row, k);
- }
-
- protected void populate(int row, ProjectInfo k) {
- populateState(row, k);
- table.setText(row, C_NAME, k.name());
- table.setText(row, C_DESCRIPTION, k.description());
-
- setRowItem(row, k);
- }
-
- protected void populateState(int row, ProjectInfo k) {
- Image state = new Image();
- switch (k.state()) {
- case HIDDEN:
- state.setResource(Gerrit.RESOURCES.redNot());
- state.setTitle(com.google.gerrit.client.admin.Util.toLongString(k.state()));
- table.setWidget(row, ProjectsTable.C_STATE, state);
- break;
- case READ_ONLY:
- state.setResource(Gerrit.RESOURCES.readOnly());
- state.setTitle(com.google.gerrit.client.admin.Util.toLongString(k.state()));
- table.setWidget(row, ProjectsTable.C_STATE, state);
- break;
- case ACTIVE:
- default:
- // Intentionally left blank, do not show an icon when active.
- break;
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java
deleted file mode 100644
index e03ac4673b..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RebaseDialog.java
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (C) 2015 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeList;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.extensions.client.ListChangesOption;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public abstract class RebaseDialog extends CommentedActionDialog {
- private final SuggestBox base;
- private final CheckBox changeParent;
- private List<ChangeInfo> candidateChanges;
- private final boolean sendEnabled;
-
- public RebaseDialog(
- final Project.NameKey project,
- final String branch,
- final Change.Id changeId,
- final boolean sendEnabled) {
- super(Util.C.rebaseTitle(), null);
- this.sendEnabled = sendEnabled;
- sendButton.setText(Util.C.buttonRebaseChangeSend());
-
- // Create the suggestion box to filter over a list of recent changes
- // open on the same branch. The list of candidates is primed by the
- // changeParent CheckBox (below) getting enabled by the user.
- base =
- new SuggestBox(
- new HighlightSuggestOracle() {
- @Override
- protected void onRequestSuggestions(Request request, Callback done) {
- String query = request.getQuery().toLowerCase();
- List<ChangeSuggestion> suggestions = new ArrayList<>();
- for (ChangeInfo ci : candidateChanges) {
- if (changeId.equals(ci.legacyId())) {
- continue; // do not suggest current change
- }
- String id = String.valueOf(ci.legacyId().get());
- if (id.contains(query) || ci.subject().toLowerCase().contains(query)) {
- suggestions.add(new ChangeSuggestion(ci));
- if (suggestions.size() >= 50) { // limit to 50 suggestions
- break;
- }
- }
- }
- done.onSuggestionsReady(request, new Response(suggestions));
- }
- });
- base.getElement().setAttribute("placeholder", Util.C.rebasePlaceholderMessage());
- base.setStyleName(Gerrit.RESOURCES.css().rebaseSuggestBox());
-
- // The changeParent checkbox must be clicked to load into browser memory
- // a list of open changes from the same project and same branch that this
- // change may rebase onto.
- changeParent = new CheckBox(Util.C.rebaseConfirmMessage());
- changeParent.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- if (changeParent.getValue()) {
- ChangeList.query(
- PageLinks.projectQuery(project)
- + " "
- + PageLinks.op("branch", branch)
- + " is:open -age:90d",
- Collections.<ListChangesOption>emptySet(),
- new GerritCallback<ChangeList>() {
- @Override
- public void onSuccess(ChangeList result) {
- candidateChanges = Natives.asList(result);
- updateControls(true);
- }
-
- @Override
- public void onFailure(Throwable err) {
- updateControls(false);
- changeParent.setValue(false);
- super.onFailure(err);
- }
- });
- } else {
- updateControls(false);
- }
- }
- });
-
- // add the checkbox and suggestbox widgets to the content panel
- contentPanel.add(changeParent);
- contentPanel.add(base);
- contentPanel.setStyleName(Gerrit.RESOURCES.css().rebaseContentPanel());
- }
-
- @Override
- public void center() {
- super.center();
- GlobalKey.dialog(this);
- updateControls(false);
- }
-
- private void updateControls(boolean changeParentEnabled) {
- if (changeParentEnabled) {
- sendButton.setTitle(null);
- sendButton.setEnabled(true);
- base.setEnabled(true);
- base.setFocus(true);
- } else {
- base.setEnabled(false);
- sendButton.setEnabled(sendEnabled);
- if (sendEnabled) {
- sendButton.setTitle(null);
- sendButton.setFocus(true);
- } else {
- sendButton.setTitle(Util.C.rebaseNotPossibleMessage());
- cancelButton.setFocus(true);
- }
- }
- }
-
- public String getBase() {
- return changeParent.getValue() ? base.getText() : null;
- }
-
- private static class ChangeSuggestion implements Suggestion {
- private ChangeInfo change;
-
- ChangeSuggestion(ChangeInfo change) {
- this.change = change;
- }
-
- @Override
- public String getDisplayString() {
- return String.valueOf(change.legacyId().get()) + ": " + change.subject();
- }
-
- @Override
- public String getReplacementString() {
- return String.valueOf(change.legacyId().get());
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RemoteSuggestBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RemoteSuggestBox.java
deleted file mode 100644
index 5d741cfa22..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/RemoteSuggestBox.java
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (C) 2014 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.client.ui;
-
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.HasCloseHandlers;
-import com.google.gwt.event.logical.shared.HasSelectionHandlers;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Focusable;
-import com.google.gwt.user.client.ui.HasText;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
-import com.google.gwt.user.client.ui.TextBoxBase;
-
-public class RemoteSuggestBox extends Composite
- implements Focusable,
- HasText,
- HasSelectionHandlers<String>,
- HasCloseHandlers<RemoteSuggestBox> {
- private final RemoteSuggestOracle remoteSuggestOracle;
- private final DefaultSuggestionDisplay display;
- private final HintTextBox textBox;
- private final SuggestBox suggestBox;
- private boolean submitOnSelection;
-
- public RemoteSuggestBox(SuggestOracle oracle) {
- remoteSuggestOracle = new RemoteSuggestOracle(oracle);
- remoteSuggestOracle.setServeSuggestions(true);
- display = new DefaultSuggestionDisplay();
-
- textBox = new HintTextBox();
- textBox.addKeyDownHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent e) {
- submitOnSelection = false;
- if (e.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
- CloseEvent.fire(RemoteSuggestBox.this, RemoteSuggestBox.this);
- } else if (e.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
- if (display.isSuggestionListShowing()) {
- if (textBox.getValue().equals(remoteSuggestOracle.getLast())) {
- submitOnSelection = true;
- } else {
- display.hideSuggestions();
- }
- } else {
- SelectionEvent.fire(RemoteSuggestBox.this, getText());
- }
- }
- }
- });
-
- suggestBox = new SuggestBox(remoteSuggestOracle, textBox, display);
- suggestBox.addSelectionHandler(
- new SelectionHandler<Suggestion>() {
- @Override
- public void onSelection(SelectionEvent<Suggestion> event) {
- if (submitOnSelection) {
- SelectionEvent.fire(RemoteSuggestBox.this, getText());
- }
- remoteSuggestOracle.cancelOutstandingRequest();
- display.hideSuggestions();
- }
- });
- initWidget(suggestBox);
- }
-
- public void setHintText(String hint) {
- textBox.setHintText(hint);
- }
-
- public void setVisibleLength(int len) {
- textBox.setVisibleLength(len);
- }
-
- public void setEnabled(boolean enabled) {
- suggestBox.setEnabled(enabled);
- }
-
- public TextBoxBase getTextBox() {
- return textBox;
- }
-
- @Override
- public String getText() {
- return suggestBox.getText();
- }
-
- @Override
- public void setText(String value) {
- suggestBox.setText(value);
- }
-
- @Override
- public void setFocus(boolean focus) {
- suggestBox.setFocus(focus);
- }
-
- @Override
- public int getTabIndex() {
- return suggestBox.getTabIndex();
- }
-
- @Override
- public void setAccessKey(char key) {
- suggestBox.setAccessKey(key);
- }
-
- @Override
- public void setTabIndex(int index) {
- suggestBox.setTabIndex(index);
- }
-
- @Override
- public HandlerRegistration addSelectionHandler(SelectionHandler<String> h) {
- return addHandler(h, SelectionEvent.getType());
- }
-
- @Override
- public HandlerRegistration addCloseHandler(CloseHandler<RemoteSuggestBox> h) {
- return addHandler(h, CloseEvent.getType());
- }
-
- public void selectAll() {
- suggestBox.getValueBox().selectAll();
- }
-
- public void enableDefaultSuggestions() {
- textBox.addFocusHandler(
- new FocusHandler() {
- @Override
- public void onFocus(FocusEvent focusEvent) {
- if (textBox.getText().equals("")) {
- suggestBox.showSuggestionList();
- }
- }
- });
- }
-
- public void setServeSuggestionsOnOracle(boolean serveSuggestions) {
- remoteSuggestOracle.setServeSuggestions(serveSuggestions);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
deleted file mode 100644
index 03ed899e3d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
+++ /dev/null
@@ -1,199 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.projects.ThemeInfo;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.user.client.View;
-
-/**
- * A Screen layout with a header and a body.
- *
- * <p>The header is mainly a text title, but it can be decorated in the West, the East, and the
- * FarEast by any Widget. The West and East decorations will surround the text on the left and right
- * respectively, and the FarEast will be right justified to the right edge of the screen. The East
- * decoration will expand to take up any extra space.
- */
-public abstract class Screen extends View {
- private Grid header;
- private InlineLabel headerText;
- private FlowPanel body;
- private String token;
- private boolean requiresSignIn;
- private String windowTitle;
- private Widget titleWidget;
-
- private ThemeInfo theme;
- private boolean setTheme;
-
- protected Screen() {
- initWidget(new FlowPanel());
- setStyleName(Gerrit.RESOURCES.css().screen());
- body = new FlowPanel();
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- if (header == null) {
- onInitUI();
- }
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- if (setTheme) {
- Gerrit.THEMER.clear();
- }
- }
-
- public void registerKeys() {}
-
- private enum Cols {
- West,
- Title,
- East,
- FarEast
- }
-
- protected void onInitUI() {
- final FlowPanel me = (FlowPanel) getWidget();
- me.add(header = new Grid(1, Cols.values().length));
- me.add(body);
-
- headerText = new InlineLabel();
- if (titleWidget == null) {
- titleWidget = headerText;
- }
- FlowPanel title = new FlowPanel();
- title.add(titleWidget);
- title.setStyleName(Gerrit.RESOURCES.css().screenHeader());
- header.setWidget(0, Cols.Title.ordinal(), title);
-
- header.setStyleName(Gerrit.RESOURCES.css().screenHeader());
- header
- .getCellFormatter()
- .setHorizontalAlignment(0, Cols.FarEast.ordinal(), HasHorizontalAlignment.ALIGN_RIGHT);
- // force FarEast all the way to the right
- header.getCellFormatter().setWidth(0, Cols.FarEast.ordinal(), "100%");
- }
-
- protected void setWindowTitle(String text) {
- windowTitle = text;
- Gerrit.setWindowTitle(this, text);
- }
-
- protected void setPageTitle(String text) {
- final String old = headerText.getText();
- if (text.isEmpty()) {
- header.setVisible(false);
- } else {
- headerText.setText(text);
- header.setVisible(true);
- }
- if (windowTitle == null || windowTitle.equals(old)) {
- setWindowTitle(text);
- }
- }
-
- protected void setHeaderVisible(boolean value) {
- header.setVisible(value);
- }
-
- public void setTitle(Widget w) {
- titleWidget = w;
- }
-
- protected void setTitleEast(Widget w) {
- header.setWidget(0, Cols.East.ordinal(), w);
- }
-
- protected void setTitleFarEast(Widget w) {
- header.setWidget(0, Cols.FarEast.ordinal(), w);
- }
-
- protected void setTitleWest(Widget w) {
- header.setWidget(0, Cols.West.ordinal(), w);
- }
-
- protected void add(Widget w) {
- body.add(w);
- }
-
- protected FlowPanel getBody() {
- return body;
- }
-
- protected void setTheme(ThemeInfo t) {
- theme = t;
- }
-
- /** Get the history token for this screen. */
- public String getToken() {
- return token;
- }
-
- /** Set the history token for this screen. */
- public void setToken(String t) {
- assert t != null && !t.isEmpty();
- token = t;
-
- if (isCurrentView()) {
- Gerrit.updateImpl(token);
- }
- }
-
- /**
- * If this view can display the given token, update it.
- *
- * @param newToken token the UI wants to show.
- * @return true if this view can show the token immediately, false if not.
- */
- public boolean displayToken(String newToken) {
- return false;
- }
-
- /** Set whether or not {@link Gerrit#isSignedIn()} must be true. */
- public final void setRequiresSignIn(boolean b) {
- requiresSignIn = b;
- }
-
- /** Does {@link Gerrit#isSignedIn()} have to be true to be on this screen? */
- public final boolean isRequiresSignIn() {
- return requiresSignIn;
- }
-
- public void onShowView() {
- if (windowTitle != null) {
- Gerrit.setWindowTitle(this, windowTitle);
- }
- Gerrit.EVENT_BUS.fireEvent(new ScreenLoadEvent(this));
- Gerrit.setQueryString(null);
- registerKeys();
-
- if (theme != null) {
- Gerrit.THEMER.set(theme);
- setTheme = true;
- } else {
- Gerrit.THEMER.clear();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadEvent.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadEvent.java
deleted file mode 100644
index debd9a61b9..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gwt.event.shared.GwtEvent;
-
-public class ScreenLoadEvent extends GwtEvent<ScreenLoadHandler> {
- private final Screen screen;
-
- public ScreenLoadEvent(Screen screen) {
- super();
- this.screen = screen;
- }
-
- public static final Type<ScreenLoadHandler> TYPE = new Type<>();
-
- @Override
- protected void dispatch(ScreenLoadHandler handler) {
- handler.onScreenLoad(this);
- }
-
- @Override
- public GwtEvent.Type<ScreenLoadHandler> getAssociatedType() {
- return TYPE;
- }
-
- public Screen getScreen() {
- return screen;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadHandler.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadHandler.java
deleted file mode 100644
index 9a5eb0310c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ScreenLoadHandler.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gwt.event.shared.EventHandler;
-
-public interface ScreenLoadHandler extends EventHandler {
- void onScreenLoad(ScreenLoadEvent event);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java
deleted file mode 100644
index ea18d62042..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java
+++ /dev/null
@@ -1,29 +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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwt.user.client.ui.Label;
-
-public class SmallHeading extends Label {
- public SmallHeading() {
- setStyleName(Gerrit.RESOURCES.css().smallHeading());
- }
-
- public SmallHeading(String text) {
- this();
- setText(text);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestAfterTypingNCharsOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestAfterTypingNCharsOracle.java
deleted file mode 100644
index e24f347c0a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestAfterTypingNCharsOracle.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Suggest oracle that only provides suggestions if the user has typed at least as many characters
- * as configured by 'suggest.from'. If 'suggest.from' is set to 0, suggestions will always be
- * provided.
- */
-public abstract class SuggestAfterTypingNCharsOracle extends HighlightSuggestOracle {
-
- @Override
- protected void onRequestSuggestions(Request req, Callback cb) {
- if (req.getQuery() != null && req.getQuery().length() >= Gerrit.info().suggest().from()) {
- _onRequestSuggestions(req, cb);
- } else {
- List<Suggestion> none = Collections.emptyList();
- cb.onSuggestionsReady(req, new Response(none));
- }
- }
-
- protected abstract void _onRequestSuggestions(Request request, Callback done);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextAreaActionDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextAreaActionDialog.java
deleted file mode 100644
index d7d5d6a42a..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextAreaActionDialog.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2015 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.client.ui;
-
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-
-public abstract class TextAreaActionDialog extends CommentedActionDialog
- implements CloseHandler<PopupPanel> {
- protected final NpTextArea message;
-
- public TextAreaActionDialog(String title, String heading) {
- super(title, heading);
-
- message = new NpTextArea();
- message.setCharacterWidth(60);
- message.setVisibleLines(10);
- message.getElement().setPropertyBoolean("spellcheck", true);
- setFocusOn(message);
-
- contentPanel.add(message);
- }
-
- public String getMessageText() {
- return message.getText().trim();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java
deleted file mode 100644
index 32bb7964f8..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gwt.i18n.client.Constants;
-
-public interface UIConstants extends Constants {
- String commentedActionButtonSend();
-
- String commentedActionButtonCancel();
-
- String projectName();
-
- String projectDescription();
-
- String projectItemHelp();
-
- String projectStateAbbrev();
-
- String projectStateHelp();
-
- String dialogCreateChangeTitle();
-
- String dialogCreateChangeHeading();
-
- String newChangeBranchSuggestion();
-
- String newChangeTopicSuggestion();
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties
deleted file mode 100644
index 736e210362..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIConstants.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-commentedActionButtonSend = Submit
-commentedActionButtonCancel = Cancel
-
-projectName = Project Name
-projectDescription = Project Description
-projectItemHelp = project
-projectStateAbbrev = S
-projectStateHelp = State
-
-dialogCreateChangeTitle = Create Change
-dialogCreateChangeHeading = Description
-newChangeBranchSuggestion = Select branch for new change
-newChangeTopicSuggestion = Enter topic for new change
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.java
deleted file mode 100644
index af1739050c..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2012 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.client.ui;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface UIMessages extends Messages {
- String helpListOpen(String item);
-
- String helpListPrev(String item);
-
- String helpListNext(String item);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.properties
deleted file mode 100644
index 1439245259..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UIMessages.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-helpListOpen = Select {0}
-helpListPrev = Previous {0}
-helpListNext = Next {0}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UserActivityMonitor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UserActivityMonitor.java
deleted file mode 100644
index 2b76b9b44d..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/UserActivityMonitor.java
+++ /dev/null
@@ -1,113 +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.client.ui;
-
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.RepeatingCommand;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.MouseMoveEvent;
-import com.google.gwt.event.dom.client.MouseMoveHandler;
-import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.event.shared.GwtEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.event.shared.SimpleEventBus;
-import com.google.gwt.user.client.History;
-import com.google.gwtexpui.globalkey.client.DocWidget;
-
-/** Checks for user keyboard and mouse activity. */
-public class UserActivityMonitor {
- private static final long TIMEOUT = 10 * 60 * 1000;
- private static final MonitorImpl impl;
-
- /**
- * @return true if there has been keyboard and/or mouse activity in recent enough history to
- * believe a user is still controlling this session.
- */
- public static boolean isActive() {
- return impl.active || impl.recent;
- }
-
- public static HandlerRegistration addValueChangeHandler(ValueChangeHandler<Boolean> handler) {
- return impl.addValueChangeHandler(handler);
- }
-
- static {
- impl = new MonitorImpl();
- DocWidget.get().addKeyPressHandler(impl);
- DocWidget.get().addMouseMoveHandler(impl);
- History.addValueChangeHandler(impl);
- Scheduler.get().scheduleFixedDelay(impl, 60 * 1000);
- }
-
- private UserActivityMonitor() {}
-
- private static class MonitorImpl
- implements RepeatingCommand,
- KeyPressHandler,
- MouseMoveHandler,
- ValueChangeHandler<String>,
- HasValueChangeHandlers<Boolean> {
- private final EventBus bus = new SimpleEventBus();
- private boolean recent = true;
- private boolean active = true;
- private long last = System.currentTimeMillis();
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- recent = true;
- }
-
- @Override
- public void onMouseMove(MouseMoveEvent event) {
- recent = true;
- }
-
- @Override
- public void onValueChange(ValueChangeEvent<String> event) {
- recent = true;
- }
-
- @Override
- public boolean execute() {
- long now = System.currentTimeMillis();
- if (recent) {
- if (!active) {
- ValueChangeEvent.fire(this, active);
- }
- recent = false;
- active = true;
- last = now;
- } else if (active && (now - last) > TIMEOUT) {
- active = false;
- ValueChangeEvent.fire(this, false);
- }
- return true;
- }
-
- @Override
- public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Boolean> handler) {
- return bus.addHandler(ValueChangeEvent.getType(), handler);
- }
-
- @Override
- public void fireEvent(GwtEvent<?> event) {
- bus.fireEvent(event);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Util.java
deleted file mode 100644
index 41e3573000..0000000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Util.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2010 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.client.ui;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-public class Util {
- public static final UIConstants C = GWT.create(UIConstants.class);
- public static final UIMessages M = GWT.create(UIMessages.class);
-
- public static String highlight(String text, String toHighlight) {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- if (toHighlight == null || "".equals(toHighlight)) {
- b.append(text);
- return b.toSafeHtml().asString();
- }
-
- int pos = 0;
- int endPos = 0;
- while ((pos = text.toLowerCase().indexOf(toHighlight.toLowerCase(), pos)) > -1) {
- if (pos > endPos) {
- b.append(text.substring(endPos, pos));
- }
- endPos = pos + toHighlight.length();
- b.openElement("b");
- b.append(text.substring(pos, endPos));
- b.closeElement("b");
- pos = endPos;
- }
- if (endPos < text.length()) {
- b.append(text.substring(endPos));
- }
- return b.toSafeHtml().asString();
- }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml b/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml
deleted file mode 100644
index add033fce0..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<module>
- <inherits name='com.google.gwt.logging.Logging'/>
- <inherits name='com.google.gwt.resources.Resources'/>
- <source path='addon'/>
- <source path='lib'/>
- <source path='keymap'/>
- <source path='mode'/>
- <source path='theme'/>
-</module>
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java b/gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java
deleted file mode 100644
index cb1891e75f..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/addon/AddonInjector.java
+++ /dev/null
@@ -1,92 +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 net.codemirror.addon;
-
-import com.google.gwt.safehtml.shared.SafeUri;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import net.codemirror.lib.Loader;
-
-public class AddonInjector {
- private static final Map<String, SafeUri> addonUris = new HashMap<>();
-
- static {
- addonUris.put(Addons.I.merge_bundled().getName(), Addons.I.merge_bundled().getSafeUri());
- }
-
- public static SafeUri getAddonScriptUri(String addon) {
- return addonUris.get(addon);
- }
-
- private static boolean canLoad(String addon) {
- return getAddonScriptUri(addon) != null;
- }
-
- private final Set<String> loading = new HashSet<>();
- private int pending;
- private AsyncCallback<Void> appCallback;
-
- public AddonInjector add(String name) {
- if (name == null) {
- return this;
- }
-
- if (!canLoad(name)) {
- Logger.getLogger("net.codemirror")
- .log(Level.WARNING, "CodeMirror addon " + name + " not configured.");
- return this;
- }
-
- loading.add(name);
- return this;
- }
-
- public void inject(AsyncCallback<Void> appCallback) {
- this.appCallback = appCallback;
- for (String addon : loading) {
- beginLoading(addon);
- }
- if (pending == 0) {
- appCallback.onSuccess(null);
- }
- }
-
- private void beginLoading(String addon) {
- pending++;
- Loader.injectScript(
- getAddonScriptUri(addon),
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- pending--;
- if (pending == 0) {
- appCallback.onSuccess(null);
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (--pending == 0) {
- appCallback.onFailure(caught);
- }
- }
- });
- }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
deleted file mode 100644
index 19a681cb79..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
+++ /dev/null
@@ -1,28 +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 net.codemirror.addon;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.DataResource;
-import com.google.gwt.resources.client.DataResource.DoNotEmbed;
-
-public interface Addons extends ClientBundle {
- Addons I = GWT.create(Addons.class);
-
- @Source("merge_bundled.js")
- @DoNotEmbed
- DataResource merge_bundled();
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.java
deleted file mode 100644
index 1e81f83a20..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.java
+++ /dev/null
@@ -1,23 +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 net.codemirror.lib;
-
-import com.google.gwt.i18n.client.Messages;
-
-public interface BlameConfig extends Messages {
- String shortBlameMsg(String commitId, String date, String author);
-
- String detailedBlameMsg(String commitId, String author, String time, String msg);
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.properties b/gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.properties
deleted file mode 100644
index 658b50f60f..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/BlameConfig.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-shortBlameMsg={0} {1} {2}
-detailedBlameMsg=commit {0}\nAuthor: {1}\nDate: {2}\n\n{3}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
deleted file mode 100644
index a84c46414f..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ /dev/null
@@ -1,448 +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 net.codemirror.lib;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.diff.DisplaySide;
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import net.codemirror.lib.TextMarker.FromTo;
-
-/**
- * Glue to connect CodeMirror to be callable from GWT.
- *
- * @see <a href="http://codemirror.net/doc/manual.html#api">CodeMirror API</a>
- */
-public class CodeMirror extends JavaScriptObject {
- public static void preload() {
- initLibrary(CallbackGroup.<Void>emptyCallback());
- }
-
- public static void initLibrary(AsyncCallback<Void> cb) {
- Loader.initLibrary(cb);
- }
-
- interface Style extends CssResource {
- String activeLine();
-
- String showTabs();
-
- String margin();
- }
-
- static Style style() {
- return Lib.I.style();
- }
-
- public static CodeMirror create(Element p, Configuration cfg) {
- CodeMirror cm = newCM(p, cfg);
- Extras.attach(cm);
- return cm;
- }
-
- private static native CodeMirror newCM(Element p, Configuration cfg) /*-{
- return $wnd.CodeMirror(p, cfg);
- }-*/;
-
- public final native void setOption(String option, boolean value) /*-{
- this.setOption(option, value)
- }-*/;
-
- public final native void setOption(String option, double value) /*-{
- this.setOption(option, value)
- }-*/;
-
- public final native void setOption(String option, String value) /*-{
- this.setOption(option, value)
- }-*/;
-
- public final native void setOption(String option, JavaScriptObject val) /*-{
- this.setOption(option, val)
- }-*/;
-
- public final native String getStringOption(String o) /*-{ return this.getOption(o) }-*/;
-
- public final native String getValue() /*-{ return this.getValue() }-*/;
-
- public final native void setValue(String v) /*-{ this.setValue(v) }-*/;
-
- public final native int changeGeneration(boolean closeEvent)
- /*-{ return this.changeGeneration(closeEvent) }-*/ ;
-
- public final native boolean isClean(int generation) /*-{ return this.isClean(generation) }-*/;
-
- public final native void setWidth(double w) /*-{ this.setSize(w, null) }-*/;
-
- public final native void setHeight(double h) /*-{ this.setSize(null, h) }-*/;
-
- public final int getHeight() {
- return getWrapperElement().getClientHeight();
- }
-
- public final void adjustHeight(int localHeader) {
- int rest = Gerrit.getHeaderFooterHeight() + localHeader + 5; // Estimate
- setHeight(Window.getClientHeight() - rest);
- }
-
- public final native String getLine(int n) /*-{ return this.getLine(n) }-*/;
-
- public final native double barHeight() /*-{ return this.display.barHeight }-*/;
-
- public final native double barWidth() /*-{ return this.display.barWidth }-*/;
-
- public final native int lastLine() /*-{ return this.lastLine() }-*/;
-
- public final native void refresh() /*-{ this.refresh() }-*/;
-
- public final native TextMarker markText(Pos from, Pos to, Configuration options) /*-{
- return this.markText(from, to, options)
- }-*/;
-
- public enum LineClassWhere {
- TEXT {
- @Override
- String value() {
- return "text";
- }
- },
- BACKGROUND {
- @Override
- String value() {
- return "background";
- }
- },
- WRAP {
- @Override
- String value() {
- return "wrap";
- }
- };
-
- abstract String value();
- }
-
- public final void addLineClass(int line, LineClassWhere where, String className) {
- addLineClassNative(line, where.value(), className);
- }
-
- private native void addLineClassNative(int line, String where, String lineClass) /*-{
- this.addLineClass(line, where, lineClass)
- }-*/;
-
- public final void addLineClass(LineHandle line, LineClassWhere where, String className) {
- addLineClassNative(line, where.value(), className);
- }
-
- private native void addLineClassNative(LineHandle line, String where, String lineClass) /*-{
- this.addLineClass(line, where, lineClass)
- }-*/;
-
- public final void removeLineClass(int line, LineClassWhere where, String className) {
- removeLineClassNative(line, where.value(), className);
- }
-
- private native void removeLineClassNative(int line, String where, String lineClass) /*-{
- this.removeLineClass(line, where, lineClass)
- }-*/;
-
- public final void removeLineClass(LineHandle line, LineClassWhere where, String className) {
- removeLineClassNative(line, where.value(), className);
- }
-
- private native void removeLineClassNative(LineHandle line, String where, String lineClass) /*-{
- this.removeLineClass(line, where, lineClass)
- }-*/;
-
- public final native void addWidget(Pos pos, Element node) /*-{
- this.addWidget(pos, node, false)
- }-*/;
-
- public final native LineWidget addLineWidget(int line, Element node, Configuration options) /*-{
- return this.addLineWidget(line, node, options)
- }-*/;
-
- public final native int lineAtHeight(double height) /*-{
- return this.lineAtHeight(height)
- }-*/;
-
- public final native int lineAtHeight(double height, String mode) /*-{
- return this.lineAtHeight(height, mode)
- }-*/;
-
- public final native double heightAtLine(int line) /*-{
- return this.heightAtLine(line)
- }-*/;
-
- public final native double heightAtLine(int line, String mode) /*-{
- return this.heightAtLine(line, mode)
- }-*/;
-
- public final native Rect charCoords(Pos pos, String mode) /*-{
- return this.charCoords(pos, mode)
- }-*/;
-
- public final native CodeMirrorDoc getDoc() /*-{
- return this.getDoc()
- }-*/;
-
- public final native void scrollTo(double x, double y) /*-{
- this.scrollTo(x, y)
- }-*/;
-
- public final native void scrollToY(double y) /*-{
- this.scrollTo(null, y)
- }-*/;
-
- public final void scrollToLine(int line) {
- int height = getHeight();
- if (lineAtHeight(height - 20) < line) {
- scrollToY(heightAtLine(line, "local") - 0.5 * height);
- }
- setCursor(Pos.create(line, 0));
- }
-
- public final native ScrollInfo getScrollInfo() /*-{
- return this.getScrollInfo()
- }-*/;
-
- public final native Viewport getViewport() /*-{
- return this.getViewport()
- }-*/;
-
- public final native void operation(Runnable thunk) /*-{
- this.operation(function() {
- thunk.@java.lang.Runnable::run()();
- })
- }-*/;
-
- public final native void off(String event, RegisteredHandler h) /*-{
- this.off(event, h)
- }-*/;
-
- public final native RegisteredHandler on(String event, Runnable thunk) /*-{
- var h = $entry(function() { thunk.@java.lang.Runnable::run()() });
- this.on(event, h);
- return h;
- }-*/;
-
- public final native void on(String event, EventHandler handler) /*-{
- this.on(event, $entry(function(cm, e) {
- handler.@net.codemirror.lib.CodeMirror.EventHandler::handle(
- Lnet/codemirror/lib/CodeMirror;
- Lcom/google/gwt/dom/client/NativeEvent;)(cm, e);
- }))
- }-*/;
-
- public final native void on(String event, RenderLineHandler handler) /*-{
- this.on(event, $entry(function(cm, h, e) {
- handler.@net.codemirror.lib.CodeMirror.RenderLineHandler::handle(
- Lnet/codemirror/lib/CodeMirror;
- Lnet/codemirror/lib/CodeMirror$LineHandle;
- Lcom/google/gwt/dom/client/Element;)(cm, h, e);
- }))
- }-*/;
-
- public final native void on(String event, GutterClickHandler handler) /*-{
- this.on(event, $entry(function(cm, l, g, e) {
- handler.@net.codemirror.lib.CodeMirror.GutterClickHandler::handle(
- Lnet/codemirror/lib/CodeMirror;
- I
- Ljava/lang/String;
- Lcom/google/gwt/dom/client/NativeEvent;)(cm, l, g, e);
- }))
- }-*/;
-
- public final native void on(String event, BeforeSelectionChangeHandler handler) /*-{
- this.on(event, $entry(function(cm, o) {
- var e = o.ranges[o.ranges.length-1];
- handler.@net.codemirror.lib.CodeMirror.BeforeSelectionChangeHandler::handle(
- Lnet/codemirror/lib/CodeMirror;
- Lnet/codemirror/lib/Pos;
- Lnet/codemirror/lib/Pos;)(cm, e.anchor, e.head);
- }))
- }-*/;
-
- public final native void on(ChangesHandler handler) /*-{
- this.on('changes', $entry(function(cm, o) {
- handler.@net.codemirror.lib.CodeMirror.ChangesHandler::handle(
- Lnet/codemirror/lib/CodeMirror;)(cm);
- }))
- }-*/;
-
- public final native void setCursor(Pos p) /*-{ this.setCursor(p) }-*/;
-
- public final native Pos getCursor() /*-{ return this.getCursor() }-*/;
-
- public final native Pos getCursor(String start) /*-{
- return this.getCursor(start)
- }-*/;
-
- public final FromTo getSelectedRange() {
- return FromTo.create(getCursor("start"), getCursor("end"));
- }
-
- public final native void setSelection(Pos p) /*-{ this.setSelection(p) }-*/;
-
- public final native void setSelection(Pos anchor, Pos head) /*-{
- this.setSelection(anchor, head)
- }-*/;
-
- public final native boolean somethingSelected() /*-{
- return this.somethingSelected()
- }-*/;
-
- public final native void addKeyMap(KeyMap map) /*-{ this.addKeyMap(map) }-*/;
-
- public final native void removeKeyMap(KeyMap map) /*-{ this.removeKeyMap(map) }-*/;
-
- public final native LineHandle getLineHandle(int line) /*-{
- return this.getLineHandle(line)
- }-*/;
-
- public final native LineHandle getLineHandleVisualStart(int line) /*-{
- return this.getLineHandleVisualStart(line)
- }-*/;
-
- public final native int getLineNumber(LineHandle handle) /*-{
- return this.getLineNumber(handle)
- }-*/;
-
- public final native void focus() /*-{
- this.focus()
- }-*/;
-
- public final native Element getWrapperElement() /*-{
- return this.getWrapperElement()
- }-*/;
-
- public final native Element getGutterElement() /*-{
- return this.getGutterElement()
- }-*/;
-
- public final native Element sizer() /*-{
- return this.display.sizer
- }-*/;
-
- public final native Element mover() /*-{
- return this.display.mover
- }-*/;
-
- public final native Element measure() /*-{
- return this.display.measure
- }-*/;
-
- public final native Element scrollbarV() /*-{
- return this.display.scrollbars.vert.node;
- }-*/;
-
- public final native void execCommand(String cmd) /*-{
- this.execCommand(cmd)
- }-*/;
-
- public static final native KeyMap getKeyMap(String name) /*-{
- return $wnd.CodeMirror.keyMap[name];
- }-*/;
-
- public static final native void addKeyMap(String name, KeyMap km) /*-{
- $wnd.CodeMirror.keyMap[name] = km
- }-*/;
-
- public static final native void normalizeKeyMap(KeyMap km) /*-{
- $wnd.CodeMirror.normalizeKeyMap(km);
- }-*/;
-
- public static final native void addCommand(String name, CommandRunner runner) /*-{
- $wnd.CodeMirror.commands[name] = function(cm) {
- runner.@net.codemirror.lib.CodeMirror.CommandRunner::run(
- Lnet/codemirror/lib/CodeMirror;)(cm);
- };
- }-*/;
-
- public final native Vim vim() /*-{
- return this;
- }-*/;
-
- public final DisplaySide side() {
- return extras().side();
- }
-
- public final Extras extras() {
- return Extras.get(this);
- }
-
- public final native LineHandle setGutterMarker(int line, String gutterId, Element value) /*-{
- return this.setGutterMarker(line, gutterId, value);
- }-*/;
-
- public final native LineHandle setGutterMarker(
- LineHandle line, String gutterId, Element value) /*-{
- return this.setGutterMarker(line, gutterId, value);
- }-*/;
-
- public final native boolean hasSearchHighlight() /*-{
- return this.state.search && !!this.state.search.query;
- }-*/;
-
- protected CodeMirror() {}
-
- public static class Viewport extends JavaScriptObject {
- public final native int from() /*-{ return this.from }-*/;
-
- public final native int to() /*-{ return this.to }-*/;
-
- public final boolean contains(int line) {
- return from() <= line && line < to();
- }
-
- protected Viewport() {}
- }
-
- public static class LineHandle extends JavaScriptObject {
- protected LineHandle() {}
- }
-
- public static class RegisteredHandler extends JavaScriptObject {
- protected RegisteredHandler() {}
- }
-
- public interface EventHandler {
- void handle(CodeMirror instance, NativeEvent event);
- }
-
- public interface RenderLineHandler {
- void handle(CodeMirror instance, LineHandle handle, Element element);
- }
-
- public interface GutterClickHandler {
- void handle(CodeMirror instance, int line, String gutter, NativeEvent clickEvent);
- }
-
- public interface BeforeSelectionChangeHandler {
- void handle(CodeMirror instance, Pos anchor, Pos head);
- }
-
- public interface ChangesHandler {
- void handle(CodeMirror instance);
- }
-
- public interface CommandRunner {
- void run(CodeMirror instance);
- }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java
deleted file mode 100644
index be1af05503..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirrorDoc.java
+++ /dev/null
@@ -1,31 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** The Doc object representing the content in a CodeMirror */
-public class CodeMirrorDoc extends JavaScriptObject {
-
- public final native void replaceRange(String replacement, Pos from, Pos to) /*-{
- this.replaceRange(replacement, from, to);
- }-*/;
-
- public final native void insertText(String insertion, Pos at) /*-{
- this.replaceRange(insertion, at);
- }-*/;
-
- protected CodeMirrorDoc() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
deleted file mode 100644
index d37b70b1d8..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
+++ /dev/null
@@ -1,46 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/**
- * Simple map-like structure to pass configuration to CodeMirror.
- *
- * @see <a href="http://codemirror.net/doc/manual.html#config">CodeMirror config</a>
- * @see CodeMirror#create(com.google.gwt.dom.client.Element, Configuration)
- */
-public class Configuration extends JavaScriptObject {
- public static Configuration create() {
- return createObject().cast();
- }
-
- public final native Configuration set(String name, String val)
- /*-{ this[name] = val; return this; }-*/ ;
-
- public final native Configuration set(String name, int val)
- /*-{ this[name] = val; return this; }-*/ ;
-
- public final native Configuration set(String name, double val)
- /*-{ this[name] = val; return this; }-*/ ;
-
- public final native Configuration set(String name, boolean val)
- /*-{ this[name] = val; return this; }-*/ ;
-
- public final native Configuration set(String name, JavaScriptObject val)
- /*-{ this[name] = val; return this; }-*/ ;
-
- protected Configuration() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Extras.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Extras.java
deleted file mode 100644
index a5af703775..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Extras.java
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright (C) 2015 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 net.codemirror.lib;
-
-import static com.google.gwt.dom.client.Style.Display.INLINE_BLOCK;
-import static com.google.gwt.dom.client.Style.Unit.PX;
-import static net.codemirror.lib.CodeMirror.LineClassWhere.WRAP;
-import static net.codemirror.lib.CodeMirror.style;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.RangeInfo;
-import com.google.gerrit.client.blame.BlameInfo;
-import com.google.gerrit.client.diff.DisplaySide;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.user.client.DOM;
-import java.util.Date;
-import java.util.Objects;
-import net.codemirror.lib.CodeMirror.LineHandle;
-
-/** Additional features added to CodeMirror by Gerrit Code Review. */
-public class Extras {
- private static final String ANNOTATION_GUTTER_ID = "CodeMirror-lint-markers";
- private static final BlameConfig C = GWT.create(BlameConfig.class);
-
- static final native Extras get(CodeMirror c) /*-{ return c.gerritExtras }-*/;
-
- private static native void set(CodeMirror c, Extras e) /*-{ c.gerritExtras = e }-*/;
-
- static void attach(CodeMirror c) {
- set(c, new Extras(c));
- }
-
- private final CodeMirror cm;
- private Element margin;
- private DisplaySide side;
- private double charWidthPx;
- private double lineHeightPx;
- private LineHandle activeLine;
- private boolean annotated;
-
- private Extras(CodeMirror cm) {
- this.cm = cm;
- }
-
- public DisplaySide side() {
- return side;
- }
-
- public void side(DisplaySide s) {
- side = s;
- }
-
- public double charWidthPx() {
- if (charWidthPx <= 1) {
- int len = 100;
- StringBuilder s = new StringBuilder();
- for (int i = 0; i < len; i++) {
- s.append('m');
- }
-
- Element e = DOM.createSpan();
- e.getStyle().setDisplay(INLINE_BLOCK);
- e.setInnerText(s.toString());
-
- cm.measure().appendChild(e);
- charWidthPx = ((double) e.getOffsetWidth()) / len;
- e.removeFromParent();
- }
- return charWidthPx;
- }
-
- public double lineHeightPx() {
- if (lineHeightPx <= 1) {
- Element p = DOM.createDiv();
- int lines = 1;
- for (int i = 0; i < lines; i++) {
- Element e = DOM.createDiv();
- p.appendChild(e);
-
- Element pre = DOM.createElement("pre");
- pre.setInnerText("gqyŚŻŹŃ");
- e.appendChild(pre);
- }
-
- cm.measure().appendChild(p);
- lineHeightPx = ((double) p.getOffsetHeight()) / lines;
- p.removeFromParent();
- }
- return lineHeightPx;
- }
-
- public void lineLength(int columns) {
- if (margin == null) {
- margin = DOM.createDiv();
- margin.setClassName(style().margin());
- cm.mover().appendChild(margin);
- }
- margin.getStyle().setMarginLeft(columns * charWidthPx(), PX);
- }
-
- public void showTabs(boolean show) {
- Element e = cm.getWrapperElement();
- if (show) {
- e.addClassName(style().showTabs());
- } else {
- e.removeClassName(style().showTabs());
- }
- }
-
- public final boolean hasActiveLine() {
- return activeLine != null;
- }
-
- public final LineHandle activeLine() {
- return activeLine;
- }
-
- public final boolean activeLine(LineHandle line) {
- if (Objects.equals(activeLine, line)) {
- return false;
- }
-
- if (activeLine != null) {
- cm.removeLineClass(activeLine, WRAP, style().activeLine());
- }
- activeLine = line;
- cm.addLineClass(activeLine, WRAP, style().activeLine());
- return true;
- }
-
- public final void clearActiveLine() {
- if (activeLine != null) {
- cm.removeLineClass(activeLine, WRAP, style().activeLine());
- activeLine = null;
- }
- }
-
- public boolean isAnnotated() {
- return annotated;
- }
-
- public final void clearAnnotations() {
- JsArrayString gutters = ((JsArrayString) JsArrayString.createArray());
- cm.setOption("gutters", gutters);
- annotated = false;
- }
-
- public final void setAnnotations(JsArray<BlameInfo> blameInfos) {
- if (blameInfos.length() > 0) {
- setBlameInfo(blameInfos);
- JsArrayString gutters = ((JsArrayString) JsArrayString.createArray());
- gutters.push(ANNOTATION_GUTTER_ID);
- cm.setOption("gutters", gutters);
- annotated = true;
- DateTimeFormat format = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_SHORT);
- JsArray<LintLine> annotations = JsArray.createArray().cast();
- for (BlameInfo blameInfo : Natives.asList(blameInfos)) {
- for (RangeInfo range : Natives.asList(blameInfo.ranges())) {
- Date commitTime = new Date(blameInfo.time() * 1000L);
- String shortId = blameInfo.id().substring(0, 8);
- String shortBlame =
- C.shortBlameMsg(shortId, format.format(commitTime), blameInfo.author());
- String detailedBlame =
- C.detailedBlameMsg(
- blameInfo.id(),
- blameInfo.author(),
- FormatUtil.mediumFormat(commitTime),
- blameInfo.commitMsg());
-
- annotations.push(
- LintLine.create(shortBlame, detailedBlame, shortId, Pos.create(range.start() - 1)));
- }
- }
- cm.setOption("lint", getAnnotation(annotations));
- }
- }
-
- private native JavaScriptObject getAnnotation(JsArray<LintLine> annotations) /*-{
- return {
- getAnnotations: function(text, options, cm) { return annotations; }
- };
- }-*/;
-
- public final native JsArray<BlameInfo> getBlameInfo() /*-{
- return this.blameInfos;
- }-*/;
-
- public final native void setBlameInfo(JsArray<BlameInfo> blameInfos) /*-{
- this['blameInfos'] = blameInfos;
- }-*/;
-
- public final void toggleAnnotation() {
- toggleAnnotation(getBlameInfo());
- }
-
- public final void toggleAnnotation(JsArray<BlameInfo> blameInfos) {
- if (isAnnotated()) {
- clearAnnotations();
- } else {
- setAnnotations(blameInfos);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/KeyMap.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/KeyMap.java
deleted file mode 100644
index be1852fc46..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/KeyMap.java
+++ /dev/null
@@ -1,43 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** Object that associates a key or key combination with a handler. */
-public class KeyMap extends JavaScriptObject {
- public static KeyMap create() {
- return createObject().cast();
- }
-
- public final native KeyMap on(String key, Runnable thunk) /*-{
- this[key] = function() { $entry(thunk.@java.lang.Runnable::run()()); };
- return this;
- }-*/;
-
- /** Do not handle inside of CodeMirror; instead push up the DOM tree. */
- public final native KeyMap propagate(String key) /*-{
- this[key] = false;
- return this;
- }-*/;
-
- /** Delegate undefined keys to another KeyMap implementation. */
- public final native KeyMap fallthrough(KeyMap m) /*-{
- this.fallthrough = m;
- return this;
- }-*/;
-
- protected KeyMap() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java
deleted file mode 100644
index f205ef9854..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Lib.java
+++ /dev/null
@@ -1,35 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.DataResource;
-import com.google.gwt.resources.client.DataResource.DoNotEmbed;
-import com.google.gwt.resources.client.ExternalTextResource;
-
-interface Lib extends ClientBundle {
- Lib I = GWT.create(Lib.class);
-
- @Source("cm.css")
- ExternalTextResource css();
-
- @Source("cm.js")
- @DoNotEmbed
- DataResource js();
-
- @Source("style.css")
- CodeMirror.Style style();
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
deleted file mode 100644
index 4a15fd38f4..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
+++ /dev/null
@@ -1,41 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** LineWidget objects used within CodeMirror. */
-public class LineWidget extends JavaScriptObject {
- public final native void clear() /*-{ this.clear() }-*/;
-
- public final native void changed() /*-{ this.changed() }-*/;
-
- public final native void onRedraw(Runnable thunk) /*-{
- this.on("redraw", $entry(function() {
- thunk.@java.lang.Runnable::run()();
- }))
- }-*/;
-
- public final native void onFirstRedraw(Runnable thunk) /*-{
- var w = this;
- var h = $entry(function() {
- thunk.@java.lang.Runnable::run()();
- w.off("redraw", h);
- });
- w.on("redraw", h);
- }-*/;
-
- protected LineWidget() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/LintLine.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/LintLine.java
deleted file mode 100644
index b0b2ae0bbb..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/LintLine.java
+++ /dev/null
@@ -1,63 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.StyleInjector;
-
-public class LintLine extends JavaScriptObject {
- public static LintLine create(String shortMsg, String msg, String sev, Pos line) {
- StyleInjector.inject(
- ".CodeMirror-lint-marker-"
- + sev
- + " {\n"
- + " visibility: hidden;\n"
- + " text-overflow: ellipsis;\n"
- + " white-space: nowrap;\n"
- + " overflow: hidden;\n"
- + " position: relative;\n"
- + "}\n"
- + ".CodeMirror-lint-marker-"
- + sev
- + ":after {\n"
- + " content:'"
- + shortMsg
- + "';\n"
- + " visibility: visible;\n"
- + "}");
- return create(msg, sev, line, null);
- }
-
- public static native LintLine create(String msg, String sev, Pos f, Pos t) /*-{
- return {
- message : msg,
- severity : sev,
- from : f,
- to : t
- };
- }-*/;
-
- public final native String message() /*-{ return this.message; }-*/;
-
- public final native String detailedMessage() /*-{ return this.message; }-*/;
-
- public final native String severity() /*-{ return this.severity; }-*/;
-
- public final native Pos from() /*-{ return this.from; }-*/;
-
- public final native Pos to() /*-{ return this.to; }-*/;
-
- protected LintLine() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
deleted file mode 100644
index 01bc7e2053..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
+++ /dev/null
@@ -1,100 +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 net.codemirror.lib;
-
-import com.google.gerrit.client.rpc.CallbackGroup;
-import com.google.gwt.core.client.Callback;
-import com.google.gwt.core.client.ScriptInjector;
-import com.google.gwt.dom.client.ScriptElement;
-import com.google.gwt.dom.client.StyleInjector;
-import com.google.gwt.resources.client.ExternalTextResource;
-import com.google.gwt.resources.client.ResourceCallback;
-import com.google.gwt.resources.client.ResourceException;
-import com.google.gwt.resources.client.TextResource;
-import com.google.gwt.safehtml.shared.SafeUri;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-public class Loader {
- private static native boolean isLibLoaded() /*-{ return $wnd.hasOwnProperty('CodeMirror'); }-*/;
-
- static void initLibrary(AsyncCallback<Void> cb) {
- if (isLibLoaded()) {
- cb.onSuccess(null);
- return;
- }
-
- CallbackGroup group = new CallbackGroup();
- injectCss(Lib.I.css(), group.<Void>addEmpty());
- injectScript(
- Lib.I.js().getSafeUri(),
- group.add(
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- Vim.initKeyMap();
- }
-
- @Override
- public void onFailure(Throwable caught) {}
- }));
- group.addListener(cb);
- group.done();
- }
-
- private static void injectCss(ExternalTextResource css, AsyncCallback<Void> cb) {
- try {
- css.getText(
- new ResourceCallback<TextResource>() {
- @Override
- public void onSuccess(TextResource resource) {
- StyleInjector.inject(resource.getText());
- Lib.I.style().ensureInjected();
- cb.onSuccess(null);
- }
-
- @Override
- public void onError(ResourceException e) {
- cb.onFailure(e);
- }
- });
- } catch (ResourceException e) {
- cb.onFailure(e);
- }
- }
-
- public static void injectScript(SafeUri js, AsyncCallback<Void> callback) {
- final ScriptElement[] script = new ScriptElement[1];
- script[0] =
- ScriptInjector.fromUrl(js.asString())
- .setWindow(ScriptInjector.TOP_WINDOW)
- .setCallback(
- new Callback<Void, Exception>() {
- @Override
- public void onSuccess(Void result) {
- script[0].removeFromParent();
- callback.onSuccess(result);
- }
-
- @Override
- public void onFailure(Exception reason) {
- callback.onFailure(reason);
- }
- })
- .inject()
- .cast();
- }
-
- private Loader() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java
deleted file mode 100644
index 38c3906f7b..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/MergeView.java
+++ /dev/null
@@ -1,50 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.Element;
-
-/** Object that represents a text marker within CodeMirror */
-public class MergeView extends JavaScriptObject {
- public static MergeView create(Element p, Configuration cfg) {
- MergeView mv = newMergeView(p, cfg);
- Extras.attach(mv.leftOriginal());
- Extras.attach(mv.editor());
- return mv;
- }
-
- private static native MergeView newMergeView(Element p, Configuration cfg) /*-{
- return $wnd.CodeMirror.MergeView(p, cfg);
- }-*/;
-
- public final native CodeMirror leftOriginal() /*-{
- return this.leftOriginal();
- }-*/;
-
- public final native CodeMirror editor() /*-{
- return this.editor();
- }-*/;
-
- public final native void setShowDifferences(boolean b) /*-{
- this.setShowDifferences(b);
- }-*/;
-
- public final native Element getGapElement() /*-{
- return $doc.getElementsByClassName("CodeMirror-merge-gap")[0];
- }-*/;
-
- protected MergeView() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java
deleted file mode 100644
index 7f83b75d22..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Pos.java
+++ /dev/null
@@ -1,42 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** Pos (or {line, ch}) objects used within CodeMirror. */
-public class Pos extends JavaScriptObject {
- public static final native Pos create(int line) /*-{
- return $wnd.CodeMirror.Pos(line)
- }-*/;
-
- public static final native Pos create(int line, int ch) /*-{
- return $wnd.CodeMirror.Pos(line, ch)
- }-*/;
-
- public final native void line(int l) /*-{ this.line = l }-*/;
-
- public final native void ch(int c) /*-{ this.ch = c }-*/;
-
- public final native int line() /*-{ return this.line }-*/;
-
- public final native int ch() /*-{ return this.ch || 0 }-*/;
-
- public final boolean equals(Pos o) {
- return this == o || (line() == o.line() && ch() == o.ch());
- }
-
- protected Pos() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Rect.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Rect.java
deleted file mode 100644
index 1114403173..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Rect.java
+++ /dev/null
@@ -1,30 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** {left, right, top, bottom} objects used within CodeMirror. */
-public class Rect extends JavaScriptObject {
- public final native double left() /*-{ return this.left }-*/;
-
- public final native double right() /*-{ return this.right }-*/;
-
- public final native double top() /*-{ return this.top }-*/;
-
- public final native double bottom() /*-{ return this.bottom }-*/;
-
- protected Rect() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java
deleted file mode 100644
index 432a60f089..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/ScrollInfo.java
+++ /dev/null
@@ -1,40 +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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** Returned by {@link CodeMirror#getScrollInfo()}. */
-public class ScrollInfo extends JavaScriptObject {
- public final native double left() /*-{ return this.left }-*/;
-
- public final native double top() /*-{ return this.top }-*/;
-
- /**
- * Pixel height of the full content being scrolled. This may only be an estimate given by
- * CodeMirror. Line widgets further down in the document may not be measured, so line heights can
- * be incorrect until drawn.
- */
- public final native double height() /*-{ return this.height }-*/;
-
- public final native double width() /*-{ return this.width }-*/;
-
- /** Visible height of the viewport, excluding scrollbars. */
- public final native double clientHeight() /*-{ return this.clientHeight }-*/;
-
- public final native double clientWidth() /*-{ return this.clientWidth }-*/;
-
- protected ScrollInfo() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
deleted file mode 100644
index c3d248a2b8..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/TextMarker.java
+++ /dev/null
@@ -1,54 +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 net.codemirror.lib;
-
-import com.google.gerrit.client.diff.CommentRange;
-import com.google.gwt.core.client.JavaScriptObject;
-
-/** Object that represents a text marker within CodeMirror */
-public class TextMarker extends JavaScriptObject {
- public final native void clear() /*-{ this.clear(); }-*/;
-
- public final native void changed() /*-{ this.changed(); }-*/;
-
- public final native FromTo find() /*-{ return this.find(); }-*/;
-
- public final native void on(String event, Runnable thunk)
- /*-{ this.on(event, function(){$entry(thunk.@java.lang.Runnable::run()())}) }-*/ ;
-
- protected TextMarker() {}
-
- public static class FromTo extends JavaScriptObject {
- public static final native FromTo create(Pos f, Pos t) /*-{
- return {from: f, to: t}
- }-*/;
-
- public static FromTo create(CommentRange range) {
- return create(
- Pos.create(range.startLine() - 1, range.startCharacter()),
- Pos.create(range.endLine() - 1, range.endCharacter()));
- }
-
- public final native Pos from() /*-{ return this.from }-*/;
-
- public final native Pos to() /*-{ return this.to }-*/;
-
- public final native void from(Pos f) /*-{ this.from = f }-*/;
-
- public final native void to(Pos t) /*-{ this.to = t }-*/;
-
- protected FromTo() {}
- }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java
deleted file mode 100644
index 06016ef008..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Vim.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2014 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 net.codemirror.lib;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-/**
- * Glue around the Vim emulation for {@link CodeMirror}.
- *
- * <p>As an instance {@code this} is actually the {@link CodeMirror} object. Class Vim is providing
- * a new namespace for Vim related methods that are associated with an editor.
- */
-public class Vim extends JavaScriptObject {
- static void initKeyMap() {
- KeyMap km = KeyMap.create();
- for (String key : new String[] {"A", "C", "I", "O", "R", "U"}) {
- km.propagate(key);
- km.propagate("'" + key.toLowerCase() + "'");
- }
- for (String key :
- new String[] {
- "Ctrl-C", "Ctrl-O", "Ctrl-P", "Ctrl-S", "Ctrl-F", "Ctrl-B", "Ctrl-R",
- }) {
- km.propagate(key);
- }
- for (int i = 0; i <= 9; i++) {
- km.propagate("Ctrl-" + i);
- }
- km.fallthrough(CodeMirror.getKeyMap("vim"));
- CodeMirror.addKeyMap("vim_ro", km);
-
- mapKey("j", "gj");
- mapKey("k", "gk");
- mapKey("Down", "gj");
- mapKey("Up", "gk");
- mapKey("<PageUp>", "<C-u>");
- mapKey("<PageDown>", "<C-d>");
- }
-
- public static final native void mapKey(String alias, String actual) /*-{
- $wnd.CodeMirror.Vim.map(alias, actual)
- }-*/;
-
- public final native void handleKey(String key) /*-{
- $wnd.CodeMirror.Vim.handleKey(this, key)
- }-*/;
-
- public final native void handleEx(String exCommand) /*-{
- $wnd.CodeMirror.Vim.handleEx(this, exCommand);
- }-*/;
-
- public final native boolean hasSearchHighlight() /*-{
- var v = this.state.vim;
- return v && v.searchState_ && !!v.searchState_.getOverlay();
- }-*/;
-
- protected Vim() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/style.css b/gerrit-gwtui/src/main/java/net/codemirror/lib/style.css
deleted file mode 100644
index 022a800762..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/style.css
+++ /dev/null
@@ -1,124 +0,0 @@
-/* Copyright (C) 2015 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.
- */
-
-@external .CodeMirror;
-@external .CodeMirror-lines;
-@external .CodeMirror-linenumber;
-@external .CodeMirror-lint-markers;
-@external .CodeMirror-lint-tooltip;
-@external .CodeMirror-overlayscroll-horizontal;
-@external .CodeMirror-overlayscroll-vertical;
-@external .CodeMirror-scrollbar-filler;
-@external .cm-tab;
-@external .cm-searching;
-@external .cm-trailingspace;
-@external .unifiedLineNumber;
-
-/* Reduce margins around CodeMirror to save space. */
-.CodeMirror-lines {
- padding: 0;
-}
-.CodeMirror pre {
- padding: 0;
- line-height: normal;
-}
-
-/* Minimum scrollbar bubble size even on large files. */
-.CodeMirror-overlayscroll-horizontal div {
- min-width: 25px;
-}
-.CodeMirror-overlayscroll-vertical div {
- min-height: 25px;
-}
-/* Ensure the scrollbars are not too narrow */
-.CodeMirror-overlayscroll-horizontal {
- min-height: 12px;
-}
-.CodeMirror-overlayscroll-vertical {
- min-width: 12px;
-}
-.CodeMirror-scrollbar-filler {
- min-height: 12px;
- min-width: 12px;
-}
-/* Stack the scrollbar so annotations can receive clicks. */
-.CodeMirror-overlayscroll-vertical {
- z-index: inherit;
-}
-.CodeMirror-overlayscroll-horizontal div,
-.CodeMirror-overlayscroll-vertical div {
- background-color: rgba(128, 128, 128, 0.50);
- z-index: 8;
-}
-
-/* Highlight current line number in the line gutter. */
-.activeLine .CodeMirror-linenumber,
-.activeLine .unifiedLineNumber {
- background-color: #bcf !important;
- color: #000;
-}
-
-.showTabs .cm-tab:before {
- position: absolute;
- content: "\00bb";
- color: #f00;
-}
-
-.cm-searching {
- background-color: #ffa !important;
-}
-
-.cm-trailingspace {
- background-color: red !important;
-}
-
-/* Line length margin displayed at NN columns to provide
- * a visual guide for length of any single line of code.
- */
-.margin {
- position: absolute;
- top: 0;
- bottom: 0;
- width: 0;
- border-right: 1px dashed #ffa500;
- z-index: 2;
- cursor: text;
-}
-
-.CodeMirror-lint-markers {
- width: 250px;
-}
-
-.CodeMirror-lint-tooltip {
- background-color: infobackground;
- border: 1px solid black;
- border-radius: 4px 4px 4px 4px;
- color: infotext;
- font-family: monospace;
- font-size: 10pt;
- overflow: hidden;
- padding: 2px 5px;
- position: fixed;
- white-space: pre;
- white-space: pre-wrap;
- z-index: 100;
- max-width: 600px;
- opacity: 0;
- transition: opacity .4s;
- -moz-transition: opacity .4s;
- -webkit-transition: opacity .4s;
- -o-transition: opacity .4s;
- -ms-transition: opacity .4s;
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
deleted file mode 100644
index 9df066dc0f..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright (C) 2014 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 net.codemirror.mode;
-
-import static java.util.Comparator.comparing;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.rpc.Natives;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArray;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.resources.client.DataResource;
-import com.google.gwt.safehtml.shared.SafeUri;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Description of a CodeMirror language mode. */
-public class ModeInfo extends JavaScriptObject {
- private static NativeMap<ModeInfo> byMime;
- private static NativeMap<ModeInfo> byExt;
-
- /** Map of names such as "clike" to URI for code download. */
- private static final Map<String, SafeUri> modeUris = new HashMap<>();
-
- static {
- indexModes(
- new DataResource[] {
- Modes.I.apl(),
- Modes.I.asciiarmor(),
- Modes.I.asn_1(),
- Modes.I.asterisk(),
- Modes.I.brainfuck(),
- Modes.I.clike(),
- Modes.I.clojure(),
- Modes.I.cmake(),
- Modes.I.cobol(),
- Modes.I.coffeescript(),
- Modes.I.commonlisp(),
- Modes.I.crystal(),
- Modes.I.css(),
- Modes.I.cypher(),
- Modes.I.d(),
- Modes.I.dart(),
- Modes.I.diff(),
- Modes.I.django(),
- Modes.I.dockerfile(),
- Modes.I.dtd(),
- Modes.I.dylan(),
- Modes.I.ebnf(),
- Modes.I.ecl(),
- Modes.I.eiffel(),
- Modes.I.elm(),
- Modes.I.erlang(),
- Modes.I.factor(),
- Modes.I.fcl(),
- Modes.I.forth(),
- Modes.I.fortran(),
- Modes.I.gas(),
- Modes.I.gerrit_commit(),
- Modes.I.gfm(),
- Modes.I.gherkin(),
- Modes.I.go(),
- Modes.I.groovy(),
- Modes.I.haml(),
- Modes.I.handlebars(),
- Modes.I.haskell_literate(),
- Modes.I.haskell(),
- Modes.I.haxe(),
- Modes.I.htmlembedded(),
- Modes.I.htmlmixed(),
- Modes.I.http(),
- Modes.I.idl(),
- Modes.I.javascript(),
- Modes.I.jinja2(),
- Modes.I.jsx(),
- Modes.I.julia(),
- Modes.I.livescript(),
- Modes.I.lua(),
- Modes.I.markdown(),
- Modes.I.mathematica(),
- Modes.I.mbox(),
- Modes.I.mirc(),
- Modes.I.mllike(),
- Modes.I.modelica(),
- Modes.I.mscgen(),
- Modes.I.mumps(),
- Modes.I.nginx(),
- Modes.I.nsis(),
- Modes.I.ntriples(),
- Modes.I.octave(),
- Modes.I.oz(),
- Modes.I.pascal(),
- Modes.I.pegjs(),
- Modes.I.perl(),
- Modes.I.php(),
- Modes.I.pig(),
- Modes.I.powershell(),
- Modes.I.properties(),
- Modes.I.protobuf(),
- Modes.I.pug(),
- Modes.I.puppet(),
- Modes.I.python(),
- Modes.I.q(),
- Modes.I.r(),
- Modes.I.rpm(),
- Modes.I.rst(),
- Modes.I.ruby(),
- Modes.I.rust(),
- Modes.I.sas(),
- Modes.I.sass(),
- Modes.I.scheme(),
- Modes.I.shell(),
- Modes.I.smalltalk(),
- Modes.I.smarty(),
- Modes.I.solr(),
- Modes.I.soy(),
- Modes.I.sparql(),
- Modes.I.spreadsheet(),
- Modes.I.sql(),
- Modes.I.stex(),
- Modes.I.stylus(),
- Modes.I.swift(),
- Modes.I.tcl(),
- Modes.I.textile(),
- Modes.I.tiddlywiki(),
- Modes.I.tiki(),
- Modes.I.toml(),
- Modes.I.tornado(),
- Modes.I.troff(),
- Modes.I.ttcn_cfg(),
- Modes.I.ttcn(),
- Modes.I.turtle(),
- Modes.I.twig(),
- Modes.I.vb(),
- Modes.I.vbscript(),
- Modes.I.velocity(),
- Modes.I.verilog(),
- Modes.I.vhdl(),
- Modes.I.vue(),
- Modes.I.webidl(),
- Modes.I.xml(),
- Modes.I.xquery(),
- Modes.I.yacas(),
- Modes.I.yaml_frontmatter(),
- Modes.I.yaml(),
- Modes.I.z80(),
- });
-
- alias("application/x-httpd-php-open", "application/x-httpd-php");
- alias("application/x-javascript", "application/javascript");
- alias("application/x-shellscript", "text/x-sh");
- alias("application/x-tcl", "text/x-tcl");
- alias("text/typescript", "application/typescript");
- alias("text/x-c", "text/x-csrc");
- alias("text/x-c++hdr", "text/x-c++src");
- alias("text/x-chdr", "text/x-csrc");
- alias("text/x-h", "text/x-csrc");
- alias("text/x-ini", "text/x-properties");
- alias("text/x-java-source", "text/x-java");
- alias("text/x-php", "application/x-httpd-php");
- alias("text/x-scripttcl", "text/x-tcl");
- }
-
- /** All supported modes. */
- public static native JsArray<ModeInfo> all() /*-{
- return $wnd.CodeMirror.modeInfo
- }-*/;
-
- private static native void setAll(JsArray<ModeInfo> m) /*-{
- $wnd.CodeMirror.modeInfo = m
- }-*/;
-
- /** Look up mode by primary or alternate MIME types. */
- public static ModeInfo findModeByMIME(String mime) {
- return byMime.get(mime);
- }
-
- public static SafeUri getModeScriptUri(String mode) {
- return modeUris.get(mode);
- }
-
- /** Look up mode by MIME type or file extension from a path. */
- public static ModeInfo findMode(String mime, String path) {
- ModeInfo m = byMime.get(mime);
- if (m != null) {
- return m;
- }
-
- int s = path.lastIndexOf('/');
- int d = path.lastIndexOf('.');
- if (d == -1 || s > d) {
- return null; // punt on "foo.src/bar" type paths.
- }
-
- if (byExt == null) {
- byExt = NativeMap.create();
- for (ModeInfo mode : Natives.asList(all())) {
- for (String ext : Natives.asList(mode.ext())) {
- byExt.put(ext, mode);
- }
- }
- }
- return byExt.get(path.substring(d + 1));
- }
-
- private static void alias(String serverMime, String toMime) {
- ModeInfo mode = byMime.get(toMime);
- if (mode != null) {
- byMime.put(serverMime, mode);
- }
- }
-
- private static void indexModes(DataResource[] all) {
- for (DataResource r : all) {
- modeUris.put(r.getName(), r.getSafeUri());
- }
-
- JsArray<ModeInfo> modeList = all();
- modeList.push(gerrit_commit());
-
- byMime = NativeMap.create();
- JsArray<ModeInfo> filtered = JsArray.createArray().cast();
- for (ModeInfo m : Natives.asList(modeList)) {
- if (modeUris.containsKey(m.mode())) {
- filtered.push(m);
-
- for (String mimeType : Natives.asList(m.mimes())) {
- byMime.put(mimeType, m);
- }
- byMime.put(m.mode(), m);
- }
- }
- Natives.asList(filtered).sort(comparing(m -> m.name().toLowerCase()));
- setAll(filtered);
- }
-
- /** Human readable name of the mode, such as "C++". */
- public final native String name() /*-{ return this.name }-*/;
-
- /** Internal CodeMirror name for {@code mode.js} file to load. */
- public final native String mode() /*-{ return this.mode }-*/;
-
- /** Primary MIME type to activate this mode. */
- public final native String mime() /*-{ return this.mime }-*/;
-
- /** Primary and additional MIME types that activate this mode. */
- public final native JsArrayString mimes() /*-{ return this.mimes || [this.mime] }-*/;
-
- private native JsArrayString ext() /*-{ return this.ext || [] }-*/;
-
- protected ModeInfo() {}
-
- private static native ModeInfo gerrit_commit() /*-{
- return {name: "Git Commit Message",
- mime: "text/x-gerrit-commit-message",
- mode: "gerrit_commit"}
- }-*/;
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInjector.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInjector.java
deleted file mode 100644
index 5fda6082f7..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInjector.java
+++ /dev/null
@@ -1,114 +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 net.codemirror.mode;
-
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import net.codemirror.lib.Loader;
-
-public class ModeInjector {
- private static boolean canLoad(String mode) {
- return ModeInfo.getModeScriptUri(mode) != null;
- }
-
- private static native boolean isModeLoaded(String n)
- /*-{ return $wnd.CodeMirror.modes.hasOwnProperty(n); }-*/ ;
-
- private static native boolean isMimeLoaded(String n)
- /*-{ return $wnd.CodeMirror.mimeModes.hasOwnProperty(n); }-*/ ;
-
- private static native JsArrayString getDependencies(String n)
- /*-{ return $wnd.CodeMirror.modes[n].dependencies || []; }-*/ ;
-
- private final Set<String> loading = new HashSet<>(4);
- private int pending;
- private AsyncCallback<Void> appCallback;
-
- public ModeInjector add(String name) {
- if (name == null || isModeLoaded(name) || isMimeLoaded(name)) {
- return this;
- }
-
- ModeInfo m = ModeInfo.findModeByMIME(name);
- if (m != null) {
- name = m.mode();
- }
-
- if (!canLoad(name)) {
- Logger.getLogger("net.codemirror")
- .log(Level.WARNING, "CodeMirror mode " + name + " not configured.");
- return this;
- }
-
- loading.add(name);
- return this;
- }
-
- public void inject(AsyncCallback<Void> appCallback) {
- this.appCallback = appCallback;
- for (String mode : loading) {
- beginLoading(mode);
- }
- if (pending == 0) {
- appCallback.onSuccess(null);
- }
- }
-
- private void beginLoading(String mode) {
- pending++;
- Loader.injectScript(
- ModeInfo.getModeScriptUri(mode),
- new AsyncCallback<Void>() {
- @Override
- public void onSuccess(Void result) {
- pending--;
- ensureDependenciesAreLoaded(mode);
- if (pending == 0) {
- appCallback.onSuccess(null);
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- if (--pending == 0) {
- appCallback.onFailure(caught);
- }
- }
- });
- }
-
- private void ensureDependenciesAreLoaded(String mode) {
- JsArrayString deps = getDependencies(mode);
- for (int i = 0; i < deps.length(); i++) {
- String d = deps.get(i);
- if (loading.contains(d) || isModeLoaded(d)) {
- continue;
- }
-
- if (!canLoad(d)) {
- Logger.getLogger("net.codemirror")
- .log(Level.SEVERE, "CodeMirror mode " + d + " needs " + d);
- continue;
- }
-
- loading.add(d);
- beginLoading(d);
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
deleted file mode 100644
index 2b87a347f6..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java
+++ /dev/null
@@ -1,510 +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 net.codemirror.mode;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.DataResource;
-import com.google.gwt.resources.client.DataResource.DoNotEmbed;
-
-public interface Modes extends ClientBundle {
- Modes I = GWT.create(Modes.class);
-
- @Source("apl.js")
- @DoNotEmbed
- DataResource apl();
-
- @Source("asciiarmor.js")
- @DoNotEmbed
- DataResource asciiarmor();
-
- @Source("asn.1.js")
- @DoNotEmbed
- DataResource asn_1();
-
- @Source("asterisk.js")
- @DoNotEmbed
- DataResource asterisk();
-
- @Source("brainfuck.js")
- @DoNotEmbed
- DataResource brainfuck();
-
- @Source("clike.js")
- @DoNotEmbed
- DataResource clike();
-
- @Source("clojure.js")
- @DoNotEmbed
- DataResource clojure();
-
- @Source("cmake.js")
- @DoNotEmbed
- DataResource cmake();
-
- @Source("cobol.js")
- @DoNotEmbed
- DataResource cobol();
-
- @Source("coffeescript.js")
- @DoNotEmbed
- DataResource coffeescript();
-
- @Source("commonlisp.js")
- @DoNotEmbed
- DataResource commonlisp();
-
- @Source("crystal.js")
- @DoNotEmbed
- DataResource crystal();
-
- @Source("css.js")
- @DoNotEmbed
- DataResource css();
-
- @Source("cypher.js")
- @DoNotEmbed
- DataResource cypher();
-
- @Source("d.js")
- @DoNotEmbed
- DataResource d();
-
- @Source("dart.js")
- @DoNotEmbed
- DataResource dart();
-
- @Source("diff.js")
- @DoNotEmbed
- DataResource diff();
-
- @Source("django.js")
- @DoNotEmbed
- DataResource django();
-
- @Source("dockerfile.js")
- @DoNotEmbed
- DataResource dockerfile();
-
- @Source("dtd.js")
- @DoNotEmbed
- DataResource dtd();
-
- @Source("dylan.js")
- @DoNotEmbed
- DataResource dylan();
-
- @Source("ebnf.js")
- @DoNotEmbed
- DataResource ebnf();
-
- @Source("ecl.js")
- @DoNotEmbed
- DataResource ecl();
-
- @Source("eiffel.js")
- @DoNotEmbed
- DataResource eiffel();
-
- @Source("elm.js")
- @DoNotEmbed
- DataResource elm();
-
- @Source("erlang.js")
- @DoNotEmbed
- DataResource erlang();
-
- @Source("factor.js")
- @DoNotEmbed
- DataResource factor();
-
- @Source("fcl.js")
- @DoNotEmbed
- DataResource fcl();
-
- @Source("forth.js")
- @DoNotEmbed
- DataResource forth();
-
- @Source("fortran.js")
- @DoNotEmbed
- DataResource fortran();
-
- @Source("gas.js")
- @DoNotEmbed
- DataResource gas();
-
- @Source("gerrit/commit.js")
- @DoNotEmbed
- DataResource gerrit_commit();
-
- @Source("gfm.js")
- @DoNotEmbed
- DataResource gfm();
-
- @Source("gherkin.js")
- @DoNotEmbed
- DataResource gherkin();
-
- @Source("go.js")
- @DoNotEmbed
- DataResource go();
-
- @Source("groovy.js")
- @DoNotEmbed
- DataResource groovy();
-
- @Source("haml.js")
- @DoNotEmbed
- DataResource haml();
-
- @Source("handlebars.js")
- @DoNotEmbed
- DataResource handlebars();
-
- @Source("haskell-literate.js")
- @DoNotEmbed
- DataResource haskell_literate();
-
- @Source("haskell.js")
- @DoNotEmbed
- DataResource haskell();
-
- @Source("haxe.js")
- @DoNotEmbed
- DataResource haxe();
-
- @Source("htmlembedded.js")
- @DoNotEmbed
- DataResource htmlembedded();
-
- @Source("htmlmixed.js")
- @DoNotEmbed
- DataResource htmlmixed();
-
- @Source("http.js")
- @DoNotEmbed
- DataResource http();
-
- @Source("idl.js")
- @DoNotEmbed
- DataResource idl();
-
- @Source("javascript.js")
- @DoNotEmbed
- DataResource javascript();
-
- @Source("jinja2.js")
- @DoNotEmbed
- DataResource jinja2();
-
- @Source("jsx.js")
- @DoNotEmbed
- DataResource jsx();
-
- @Source("julia.js")
- @DoNotEmbed
- DataResource julia();
-
- @Source("livescript.js")
- @DoNotEmbed
- DataResource livescript();
-
- @Source("lua.js")
- @DoNotEmbed
- DataResource lua();
-
- @Source("markdown.js")
- @DoNotEmbed
- DataResource markdown();
-
- @Source("mathematica.js")
- @DoNotEmbed
- DataResource mathematica();
-
- @Source("mbox.js")
- @DoNotEmbed
- DataResource mbox();
-
- @Source("mirc.js")
- @DoNotEmbed
- DataResource mirc();
-
- @Source("mllike.js")
- @DoNotEmbed
- DataResource mllike();
-
- @Source("modelica.js")
- @DoNotEmbed
- DataResource modelica();
-
- @Source("mscgen.js")
- @DoNotEmbed
- DataResource mscgen();
-
- @Source("mumps.js")
- @DoNotEmbed
- DataResource mumps();
-
- @Source("nginx.js")
- @DoNotEmbed
- DataResource nginx();
-
- @Source("nsis.js")
- @DoNotEmbed
- DataResource nsis();
-
- @Source("ntriples.js")
- @DoNotEmbed
- DataResource ntriples();
-
- @Source("octave.js")
- @DoNotEmbed
- DataResource octave();
-
- @Source("oz.js")
- @DoNotEmbed
- DataResource oz();
-
- @Source("pascal.js")
- @DoNotEmbed
- DataResource pascal();
-
- @Source("pegjs.js")
- @DoNotEmbed
- DataResource pegjs();
-
- @Source("perl.js")
- @DoNotEmbed
- DataResource perl();
-
- @Source("php.js")
- @DoNotEmbed
- DataResource php();
-
- @Source("pig.js")
- @DoNotEmbed
- DataResource pig();
-
- @Source("powershell.js")
- @DoNotEmbed
- DataResource powershell();
-
- @Source("properties.js")
- @DoNotEmbed
- DataResource properties();
-
- @Source("protobuf.js")
- @DoNotEmbed
- DataResource protobuf();
-
- @Source("pug.js")
- @DoNotEmbed
- DataResource pug();
-
- @Source("puppet.js")
- @DoNotEmbed
- DataResource puppet();
-
- @Source("python.js")
- @DoNotEmbed
- DataResource python();
-
- @Source("q.js")
- @DoNotEmbed
- DataResource q();
-
- @Source("r.js")
- @DoNotEmbed
- DataResource r();
-
- @Source("rpm.js")
- @DoNotEmbed
- DataResource rpm();
-
- @Source("rst.js")
- @DoNotEmbed
- DataResource rst();
-
- @Source("ruby.js")
- @DoNotEmbed
- DataResource ruby();
-
- @Source("rust.js")
- @DoNotEmbed
- DataResource rust();
-
- @Source("sas.js")
- @DoNotEmbed
- DataResource sas();
-
- @Source("sass.js")
- @DoNotEmbed
- DataResource sass();
-
- @Source("scheme.js")
- @DoNotEmbed
- DataResource scheme();
-
- @Source("shell.js")
- @DoNotEmbed
- DataResource shell();
-
- @Source("sieve.js")
- @DoNotEmbed
- DataResource sieve();
-
- @Source("slim.js")
- @DoNotEmbed
- DataResource slim();
-
- @Source("smalltalk.js")
- @DoNotEmbed
- DataResource smalltalk();
-
- @Source("smarty.js")
- @DoNotEmbed
- DataResource smarty();
-
- @Source("solr.js")
- @DoNotEmbed
- DataResource solr();
-
- @Source("soy.js")
- @DoNotEmbed
- DataResource soy();
-
- @Source("sparql.js")
- @DoNotEmbed
- DataResource sparql();
-
- @Source("spreadsheet.js")
- @DoNotEmbed
- DataResource spreadsheet();
-
- @Source("sql.js")
- @DoNotEmbed
- DataResource sql();
-
- @Source("stex.js")
- @DoNotEmbed
- DataResource stex();
-
- @Source("stylus.js")
- @DoNotEmbed
- DataResource stylus();
-
- @Source("swift.js")
- @DoNotEmbed
- DataResource swift();
-
- @Source("tcl.js")
- @DoNotEmbed
- DataResource tcl();
-
- @Source("textile.js")
- @DoNotEmbed
- DataResource textile();
-
- @Source("tiddlywiki.js")
- @DoNotEmbed
- DataResource tiddlywiki();
-
- @Source("tiki.js")
- @DoNotEmbed
- DataResource tiki();
-
- @Source("toml.js")
- @DoNotEmbed
- DataResource toml();
-
- @Source("tornado.js")
- @DoNotEmbed
- DataResource tornado();
-
- @Source("troff.js")
- @DoNotEmbed
- DataResource troff();
-
- @Source("ttcn-cfg.js")
- @DoNotEmbed
- DataResource ttcn_cfg();
-
- @Source("ttcn.js")
- @DoNotEmbed
- DataResource ttcn();
-
- @Source("turtle.js")
- @DoNotEmbed
- DataResource turtle();
-
- @Source("twig.js")
- @DoNotEmbed
- DataResource twig();
-
- @Source("vb.js")
- @DoNotEmbed
- DataResource vb();
-
- @Source("vbscript.js")
- @DoNotEmbed
- DataResource vbscript();
-
- @Source("velocity.js")
- @DoNotEmbed
- DataResource velocity();
-
- @Source("verilog.js")
- @DoNotEmbed
- DataResource verilog();
-
- @Source("vhdl.js")
- @DoNotEmbed
- DataResource vhdl();
-
- @Source("vue.js")
- @DoNotEmbed
- DataResource vue();
-
- @Source("webidl.js")
- @DoNotEmbed
- DataResource webidl();
-
- @Source("xml.js")
- @DoNotEmbed
- DataResource xml();
-
- @Source("xquery.js")
- @DoNotEmbed
- DataResource xquery();
-
- @Source("yacas.js")
- @DoNotEmbed
- DataResource yacas();
-
- @Source("yaml-frontmatter.js")
- @DoNotEmbed
- DataResource yaml_frontmatter();
-
- @Source("yaml.js")
- @DoNotEmbed
- DataResource yaml();
-
- @Source("z80.js")
- @DoNotEmbed
- DataResource z80();
-
- // When adding a resource, update static initializer in ModeInfo.
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js b/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js
deleted file mode 100644
index e1fe89833b..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/gerrit/commit.js
+++ /dev/null
@@ -1,26 +0,0 @@
-CodeMirror.defineMode('gerrit_commit', function() {
- var header = /^(Parent|Author|AuthorDate|Commit|CommitDate):/;
- var id = /^Change-Id: I[0-9a-f]{40}/;
- var footer = /^[A-Z][A-Za-z0-9-]+:/;
- var sha1 = /\b[0-9a-f]{6,40}/;
-
- return {
- token: function(stream) {
- if (stream.sol()) {
- if (stream.match(header))
- return 'keyword';
- if (stream.match(id) || stream.match(footer))
- return 'builtin';
- }
-
- stream.eatSpace();
- if (stream.match(sha1))
- return 'variable-2';
- if (stream.match(/".*"/))
- return 'string';
- stream.next();
- return null;
- }
- };
-});
-CodeMirror.defineMIME('text/x-gerrit-commit-message', 'gerrit_commit');
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/theme/ThemeLoader.java b/gerrit-gwtui/src/main/java/net/codemirror/theme/ThemeLoader.java
deleted file mode 100644
index 23039d44cb..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/theme/ThemeLoader.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2014 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 net.codemirror.theme;
-
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gwt.dom.client.StyleInjector;
-import com.google.gwt.resources.client.ExternalTextResource;
-import com.google.gwt.resources.client.ResourceCallback;
-import com.google.gwt.resources.client.ResourceException;
-import com.google.gwt.resources.client.TextResource;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.EnumSet;
-
-/** Dynamically loads a known CodeMirror theme's CSS */
-public class ThemeLoader {
- private static final ExternalTextResource[] THEMES = {
- Themes.I.day_3024(),
- Themes.I.night_3024(),
- Themes.I.abcdef(),
- Themes.I.ambiance(),
- Themes.I.base16_dark(),
- Themes.I.base16_light(),
- Themes.I.bespin(),
- Themes.I.blackboard(),
- Themes.I.cobalt(),
- Themes.I.colorforth(),
- Themes.I.dracula(),
- Themes.I.eclipse(),
- Themes.I.elegant(),
- Themes.I.erlang_dark(),
- Themes.I.hopscotch(),
- Themes.I.icecoder(),
- Themes.I.isotope(),
- Themes.I.lesser_dark(),
- Themes.I.liquibyte(),
- Themes.I.material(),
- Themes.I.mbo(),
- Themes.I.mdn_like(),
- Themes.I.midnight(),
- Themes.I.monokai(),
- Themes.I.neat(),
- Themes.I.neo(),
- Themes.I.night(),
- Themes.I.paraiso_dark(),
- Themes.I.paraiso_light(),
- Themes.I.pastel_on_dark(),
- Themes.I.railscasts(),
- Themes.I.rubyblue(),
- Themes.I.seti(),
- Themes.I.solarized(),
- Themes.I.the_matrix(),
- Themes.I.tomorrow_night_bright(),
- Themes.I.tomorrow_night_eighties(),
- Themes.I.ttcn(),
- Themes.I.twilight(),
- Themes.I.vibrant_ink(),
- Themes.I.xq_dark(),
- Themes.I.xq_light(),
- Themes.I.yeti(),
- Themes.I.zenburn(),
- };
-
- private static final EnumSet<Theme> loaded = EnumSet.of(Theme.DEFAULT);
-
- public static final void loadTheme(Theme theme, AsyncCallback<Void> cb) {
- if (loaded.contains(theme)) {
- cb.onSuccess(null);
- return;
- }
-
- ExternalTextResource resource = findTheme(theme);
- if (resource == null) {
- cb.onFailure(new Exception("unknown theme " + theme));
- return;
- }
-
- try {
- resource.getText(
- new ResourceCallback<TextResource>() {
- @Override
- public void onSuccess(TextResource resource) {
- StyleInjector.inject(resource.getText());
- loaded.add(theme);
- cb.onSuccess(null);
- }
-
- @Override
- public void onError(ResourceException e) {
- cb.onFailure(e);
- }
- });
- } catch (ResourceException e) {
- cb.onFailure(e);
- }
- }
-
- private static ExternalTextResource findTheme(Theme theme) {
- for (ExternalTextResource r : THEMES) {
- if (theme.name().toLowerCase().equals(r.getName())) {
- return r;
- }
- }
- return null;
- }
-
- private ThemeLoader() {}
-}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/theme/Themes.java b/gerrit-gwtui/src/main/java/net/codemirror/theme/Themes.java
deleted file mode 100644
index cfe853f6e8..0000000000
--- a/gerrit-gwtui/src/main/java/net/codemirror/theme/Themes.java
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (C) 2014 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 net.codemirror.theme;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.ExternalTextResource;
-
-public interface Themes extends ClientBundle {
- Themes I = GWT.create(Themes.class);
-
- @Source("3024-day.css")
- ExternalTextResource day_3024();
-
- @Source("3024-night.css")
- ExternalTextResource night_3024();
-
- @Source("abcdef.css")
- ExternalTextResource abcdef();
-
- @Source("ambiance.css")
- ExternalTextResource ambiance();
-
- @Source("base16-dark.css")
- ExternalTextResource base16_dark();
-
- @Source("base16-light.css")
- ExternalTextResource base16_light();
-
- @Source("bespin.css")
- ExternalTextResource bespin();
-
- @Source("blackboard.css")
- ExternalTextResource blackboard();
-
- @Source("cobalt.css")
- ExternalTextResource cobalt();
-
- @Source("colorforth.css")
- ExternalTextResource colorforth();
-
- @Source("dracula.css")
- ExternalTextResource dracula();
-
- @Source("duotone-dark.css")
- ExternalTextResource duotone_dark();
-
- @Source("duotone-light.css")
- ExternalTextResource duotone_light();
-
- @Source("eclipse.css")
- ExternalTextResource eclipse();
-
- @Source("elegant.css")
- ExternalTextResource elegant();
-
- @Source("erlang-dark.css")
- ExternalTextResource erlang_dark();
-
- @Source("hopscotch.css")
- ExternalTextResource hopscotch();
-
- @Source("icecoder.css")
- ExternalTextResource icecoder();
-
- @Source("isotope.css")
- ExternalTextResource isotope();
-
- @Source("lesser-dark.css")
- ExternalTextResource lesser_dark();
-
- @Source("liquibyte.css")
- ExternalTextResource liquibyte();
-
- @Source("material.css")
- ExternalTextResource material();
-
- @Source("mbo.css")
- ExternalTextResource mbo();
-
- @Source("mdn-like.css")
- ExternalTextResource mdn_like();
-
- @Source("midnight.css")
- ExternalTextResource midnight();
-
- @Source("monokai.css")
- ExternalTextResource monokai();
-
- @Source("neat.css")
- ExternalTextResource neat();
-
- @Source("neo.css")
- ExternalTextResource neo();
-
- @Source("night.css")
- ExternalTextResource night();
-
- @Source("paraiso-dark.css")
- ExternalTextResource paraiso_dark();
-
- @Source("paraiso-light.css")
- ExternalTextResource paraiso_light();
-
- @Source("pastel-on-dark.css")
- ExternalTextResource pastel_on_dark();
-
- @Source("railscasts.css")
- ExternalTextResource railscasts();
-
- @Source("rubyblue.css")
- ExternalTextResource rubyblue();
-
- @Source("seti.css")
- ExternalTextResource seti();
-
- @Source("solarized.css")
- ExternalTextResource solarized();
-
- @Source("the-matrix.css")
- ExternalTextResource the_matrix();
-
- @Source("tomorrow-night-bright.css")
- ExternalTextResource tomorrow_night_bright();
-
- @Source("tomorrow-night-eighties.css")
- ExternalTextResource tomorrow_night_eighties();
-
- @Source("ttcn.css")
- ExternalTextResource ttcn();
-
- @Source("twilight.css")
- ExternalTextResource twilight();
-
- @Source("vibrant-ink.css")
- ExternalTextResource vibrant_ink();
-
- @Source("xq-dark.css")
- ExternalTextResource xq_dark();
-
- @Source("xq-light.css")
- ExternalTextResource xq_light();
-
- @Source("yeti.css")
- ExternalTextResource yeti();
-
- @Source("zenburn.css")
- ExternalTextResource zenburn();
-
- // When adding a resource, update:
- // - static initializer in ThemeLoader
- // - enum value in com.google.gerrit.extensions.common.Theme
-}
diff --git a/gerrit-gwtui/src/test/java/com/google/gerrit/client/change/ProjectChangeIdTest.java b/gerrit-gwtui/src/test/java/com/google/gerrit/client/change/ProjectChangeIdTest.java
deleted file mode 100644
index 1d47a82f85..0000000000
--- a/gerrit-gwtui/src/test/java/com/google/gerrit/client/change/ProjectChangeIdTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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.client.change;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class ProjectChangeIdTest {
-
- @Rule public ExpectedException exception = ExpectedException.none();
-
- @Test
- public void emptyStringThrowsException() {
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage(" is not a valid change identifier");
- ProjectChangeId.create("");
- }
-
- @Test
- public void noChangeIdThrowsException() {
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("some/path is not a valid change identifier");
- ProjectChangeId.create("some/path");
- }
-
- @Test
- public void noChangeButProjectIdThrowsException() {
- exception.expect(IllegalArgumentException.class);
- exception.expectMessage("some/+/path is not a valid change identifier");
- ProjectChangeId.create("some/+/path");
- }
-
- @Test
- public void project() {
- assertThat(ProjectChangeId.create("test/+/123/some/path")).isEqualTo(result("test", 123));
- assertThat(ProjectChangeId.create("test/+/123/some/path/")).isEqualTo(result("test", 123));
- assertThat(ProjectChangeId.create("test/+/123/")).isEqualTo(result("test", 123));
- assertThat(ProjectChangeId.create("test/+/123")).isEqualTo(result("test", 123));
- // Numeric Project.NameKey
- assertThat(ProjectChangeId.create("123/+/123")).isEqualTo(result("123", 123));
- // Numeric Project.NameKey with ,edit as part of the name
- assertThat(ProjectChangeId.create("123,edit/+/123")).isEqualTo(result("123,edit", 123));
- }
-
- @Test
- public void noProject() {
- assertThat(ProjectChangeId.create("123/some/path")).isEqualTo(result(null, 123));
- assertThat(ProjectChangeId.create("123/some/path/")).isEqualTo(result(null, 123));
- assertThat(ProjectChangeId.create("123/")).isEqualTo(result(null, 123));
- assertThat(ProjectChangeId.create("123")).isEqualTo(result(null, 123));
- }
-
- @Test
- public void editSuffix() {
- assertThat(ProjectChangeId.create("123,edit/some/path")).isEqualTo(result(null, 123));
- assertThat(ProjectChangeId.create("123,edit/")).isEqualTo(result(null, 123));
- assertThat(ProjectChangeId.create("123,edit")).isEqualTo(result(null, 123));
-
- assertThat(ProjectChangeId.create("test/+/123,edit/some/path")).isEqualTo(result("test", 123));
- assertThat(ProjectChangeId.create("test/+/123,edit/")).isEqualTo(result("test", 123));
- assertThat(ProjectChangeId.create("test/+/123,edit")).isEqualTo(result("test", 123));
- }
-
- private static ProjectChangeId result(@Nullable String project, int id) {
- return new ProjectChangeId(
- project == null ? null : new Project.NameKey(project), new Change.Id(id));
- }
-}
diff --git a/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/LineMapperTest.java b/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/LineMapperTest.java
deleted file mode 100644
index fcc214e712..0000000000
--- a/gerrit-gwtui/src/test/java/com/google/gerrit/client/diff/LineMapperTest.java
+++ /dev/null
@@ -1,117 +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.client.diff;
-
-import static org.junit.Assert.assertEquals;
-
-import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
-import org.junit.Test;
-
-/** Unit tests for LineMapper */
-public class LineMapperTest {
-
- @Test
- public void appendCommon() {
- LineMapper mapper = new LineMapper();
- mapper.appendCommon(10);
- assertEquals(10, mapper.getLineA());
- assertEquals(10, mapper.getLineB());
- }
-
- @Test
- public void appendInsert() {
- LineMapper mapper = new LineMapper();
- mapper.appendInsert(10);
- assertEquals(0, mapper.getLineA());
- assertEquals(10, mapper.getLineB());
- }
-
- @Test
- public void appendDelete() {
- LineMapper mapper = new LineMapper();
- mapper.appendDelete(10);
- assertEquals(10, mapper.getLineA());
- assertEquals(0, mapper.getLineB());
- }
-
- @Test
- public void findInCommon() {
- LineMapper mapper = new LineMapper();
- mapper.appendCommon(10);
- assertEquals(new LineOnOtherInfo(9, true), mapper.lineOnOther(DisplaySide.A, 9));
- assertEquals(new LineOnOtherInfo(9, true), mapper.lineOnOther(DisplaySide.B, 9));
- }
-
- @Test
- public void findAfterCommon() {
- LineMapper mapper = new LineMapper();
- mapper.appendCommon(10);
- assertEquals(new LineOnOtherInfo(10, true), mapper.lineOnOther(DisplaySide.A, 10));
- assertEquals(new LineOnOtherInfo(10, true), mapper.lineOnOther(DisplaySide.B, 10));
- }
-
- @Test
- public void findInInsertGap() {
- LineMapper mapper = new LineMapper();
- mapper.appendInsert(10);
- assertEquals(new LineOnOtherInfo(-1, false), mapper.lineOnOther(DisplaySide.B, 9));
- }
-
- @Test
- public void findAfterInsertGap() {
- LineMapper mapper = new LineMapper();
- mapper.appendInsert(10);
- assertEquals(new LineOnOtherInfo(0, true), mapper.lineOnOther(DisplaySide.B, 10));
- assertEquals(new LineOnOtherInfo(10, true), mapper.lineOnOther(DisplaySide.A, 0));
- }
-
- @Test
- public void findInDeleteGap() {
- LineMapper mapper = new LineMapper();
- mapper.appendDelete(10);
- assertEquals(new LineOnOtherInfo(-1, false), mapper.lineOnOther(DisplaySide.A, 9));
- }
-
- @Test
- public void findAfterDeleteGap() {
- LineMapper mapper = new LineMapper();
- mapper.appendDelete(10);
- assertEquals(new LineOnOtherInfo(0, true), mapper.lineOnOther(DisplaySide.A, 10));
- assertEquals(new LineOnOtherInfo(10, true), mapper.lineOnOther(DisplaySide.B, 0));
- }
-
- @Test
- public void replaceWithInsertInB() {
- // 0 c c
- // 1 a b
- // 2 a b
- // 3 - b
- // 4 - b
- // 5 c c
- LineMapper mapper = new LineMapper();
- mapper.appendCommon(1);
- mapper.appendReplace(2, 4);
- mapper.appendCommon(1);
-
- assertEquals(4, mapper.getLineA());
- assertEquals(6, mapper.getLineB());
-
- assertEquals(new LineOnOtherInfo(1, true), mapper.lineOnOther(DisplaySide.B, 1));
- assertEquals(new LineOnOtherInfo(3, true), mapper.lineOnOther(DisplaySide.B, 5));
-
- assertEquals(new LineOnOtherInfo(2, true), mapper.lineOnOther(DisplaySide.B, 2));
- assertEquals(new LineOnOtherInfo(2, false), mapper.lineOnOther(DisplaySide.B, 3));
- }
-}
diff --git a/gerrit-plugin-gwtui/BUILD b/gerrit-plugin-gwtui/BUILD
deleted file mode 100644
index 366cf82bce..0000000000
--- a/gerrit-plugin-gwtui/BUILD
+++ /dev/null
@@ -1,83 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_binary")
-load("//tools/bzl:java.bzl", "java_library2")
-load("//tools/bzl:javadoc.bzl", "java_doc")
-
-package(default_visibility = ["//visibility:public"])
-
-SRCS = glob(["src/main/java/com/google/gerrit/**/*.java"])
-
-DEPS = ["//lib/gwt:user-neverlink"]
-
-java_binary(
- name = "gwtui-api",
- main_class = "Dummy",
- runtime_deps = [
- ":gwtui-api-lib",
- "//gerrit-gwtui-common:client-lib",
- ],
-)
-
-java_library2(
- name = "gwtui-api-lib",
- srcs = SRCS,
- exported_deps = [
- "//gerrit-gwtui:client-lib",
- "//gerrit-gwtui-common:client-lib",
- ],
- resources = glob(["src/main/**/*"]),
- deps = DEPS + [
- "//java/org/eclipse/jgit:libclient-src.jar",
- "//java/org/eclipse/jgit:libEdit-src.jar",
- "//java/com/google/gerrit/common:libclient-src.jar",
- "//java/com/google/gerrit/extensions:libapi-src.jar",
- "//java/com/google/gwtexpui/clippy:libclippy-src.jar",
- "//java/com/google/gwtexpui/globalkey:libglobalkey-src.jar",
- "//java/com/google/gwtexpui/progress:libprogress-src.jar",
- "//java/com/google/gwtexpui/safehtml:libsafehtml-src.jar",
- "//java/com/google/gwtexpui/user:libagent-src.jar",
- "//gerrit-gwtui-common:libclient-src.jar",
- "//java/com/google/gerrit/prettify:libclient-src.jar",
- "//java/com/google/gerrit/reviewdb:libclient-src.jar",
- "//lib/gwt:dev-neverlink",
- ],
-)
-
-java_library2(
- name = "gwtui-api-lib-neverlink",
- srcs = SRCS,
- exported_deps = ["//gerrit-gwtui-common:client-lib"],
- neverlink = 1, # we want this to be exported deps
- resources = glob(["src/main/**/*"]),
- deps = DEPS + ["//lib/gwt:dev"],
-)
-
-java_binary(
- name = "gwtui-api-source",
- main_class = "Dummy",
- runtime_deps = [
- ":libgwtui-api-lib-src.jar",
- "//gerrit-gwtui-common:libclient-lib-src.jar",
- "//java/com/google/gwtexpui/clippy:libclippy-src.jar",
- "//java/com/google/gwtexpui/globalkey:libglobalkey-src.jar",
- "//java/com/google/gwtexpui/progress:libprogress-src.jar",
- "//java/com/google/gwtexpui/safehtml:libsafehtml-src.jar",
- "//java/com/google/gwtexpui/user:libagent-src.jar",
- ],
-)
-
-java_doc(
- name = "gwtui-api-javadoc",
- libs = DEPS + [
- ":gwtui-api-lib",
- "//lib:gwtjsonrpc",
- "//lib:gwtorm-client",
- "//lib/gwt:dev",
- "//gerrit-gwtui-common:client-lib",
- "//java/com/google/gerrit/common:client",
- "//java/com/google/gerrit/reviewdb:client",
- ],
- pkgs = [
- "com.google.gerrit.plugin",
- ],
- title = "Gerrit Review GWT Extension API Documentation",
-)
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
deleted file mode 100644
index e0b0833b76..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Copyright (C) 2012 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.
--->
-<module>
- <inherits name="com.google.gwt.json.JSON"/>
- <inherits name='com.google.gerrit.GerritGwtUICommon'/>
-
- <define-linker name="gerrit_plugin" class="com.google.gerrit.plugin.linker.GerritPluginLinker"/>
- <add-linker name="gerrit_plugin"/>
- <generate-with class="com.google.gerrit.plugin.rebind.PluginGenerator">
- <when-type-assignable class="com.google.gerrit.plugin.client.Plugin"/>
- </generate-with>
- <source path="plugin/client"/>
-</module>
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java
deleted file mode 100644
index 85bc9c3706..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (C) 2015 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.plugin.client;
-
-import com.google.gerrit.client.AccountFormatter;
-import com.google.gerrit.client.DateFormatter;
-import com.google.gerrit.client.RelativeDateFormatter;
-import com.google.gerrit.client.info.AccountInfo;
-import java.util.Date;
-
-public class FormatUtil {
- private static final AccountFormatter accountFormatter =
- new AccountFormatter(Plugin.get().getServerInfo().user().anonymousCowardName());
-
- /** Format a date using a really short format. */
- public static String shortFormat(Date dt) {
- return createDateFormatter().shortFormat(dt);
- }
-
- /** Format a date using a really short format. */
- public static String shortFormatDayTime(Date dt) {
- return createDateFormatter().shortFormatDayTime(dt);
- }
-
- /** Format a date using the locale's medium length format. */
- public static String mediumFormat(Date dt) {
- return createDateFormatter().mediumFormat(dt);
- }
-
- private static DateFormatter createDateFormatter() {
- return new DateFormatter(Plugin.get().getUserPreferences());
- }
-
- /** Format a date using git log's relative date format. */
- public static String relativeFormat(Date dt) {
- return RelativeDateFormatter.format(dt);
- }
-
- /**
- * Formats an account as a name and an email address.
- *
- * <p>Example output:
- *
- * <ul>
- * <li>{@code A U. Thor &lt;author@example.com&gt;}: full populated
- * <li>{@code A U. Thor (12)}: missing email address
- * <li>{@code Anonymous Coward &lt;author@example.com&gt;}: missing name
- * <li>{@code Anonymous Coward (12)}: missing name and email address
- * </ul>
- */
- public static String nameEmail(AccountInfo info) {
- return accountFormatter.nameEmail(info);
- }
-
- /**
- * Formats an account name.
- *
- * <p>If the account has a full name, it returns only the full name. Otherwise it returns a longer
- * form that includes the email address.
- */
- public static String name(AccountInfo info) {
- return accountFormatter.name(info);
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java
deleted file mode 100644
index bfcb3d6563..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/Plugin.java
+++ /dev/null
@@ -1,155 +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.plugin.client;
-
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.info.GeneralPreferences;
-import com.google.gerrit.client.info.ServerInfo;
-import com.google.gerrit.plugin.client.extension.Panel;
-import com.google.gerrit.plugin.client.screen.Screen;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-
-/**
- * Wrapper around the plugin instance exposed by Gerrit.
- *
- * <p>Listeners for events generated by the main UI must be registered through this instance.
- */
-public final class Plugin extends JavaScriptObject {
- private static final Plugin self =
- install(GWT.getModuleBaseURL() + GWT.getModuleName() + ".nocache.js");
-
- /** Obtain the plugin instance wrapper. */
- public static Plugin get() {
- return self;
- }
-
- /** Installed name of the plugin. */
- public String getName() {
- return getPluginName();
- }
-
- /** Installed name of the plugin. */
- public native String getPluginName() /*-{ return this.getPluginName() }-*/;
-
- /** Navigate the UI to the screen identified by the token. */
- public native void go(String token) /*-{ return this.go(token) }-*/;
-
- /** Refresh the current UI. */
- public native void refresh() /*-{ return this.refresh() }-*/;
-
- /** Refresh Gerrit's menu bar. */
- public native void refreshMenuBar() /*-{ return this.refreshMenuBar() }-*/;
-
- /**
- * @return the preferences of the currently signed in user, the default preferences if not signed
- * in
- */
- public native GeneralPreferences getUserPreferences() /*-{ return this.getUserPreferences() }-*/;
-
- /** Refresh the user preferences of the current user. */
- public native void refreshUserPreferences() /*-{ return this.refreshUserPreferences() }-*/;
-
- /** @return the server info */
- public native ServerInfo getServerInfo() /*-{ return this.getServerInfo() }-*/;
-
- /** @return the current user */
- public native AccountInfo getCurrentUser() /*-{ return this.getCurrentUser() }-*/;
-
- /** Check if user is signed in. */
- public native boolean isSignedIn() /*-{ return this.isSignedIn() }-*/;
-
- /** Show message in Gerrit's ErrorDialog. */
- public native void showError(String message) /*-{ return this.showError(message) }-*/;
-
- /**
- * Register a screen displayed at {@code /#/x/plugin/token}.
- *
- * @param token literal anchor token appearing after the plugin name. For regular expression
- * matching use {@code screenRegex()} .
- * @param entry callback function invoked to create the screen widgets.
- */
- public void screen(String token, Screen.EntryPoint entry) {
- screen(token, wrap(entry));
- }
-
- private native void screen(String t, JavaScriptObject e) /*-{ this.screen(t, e) }-*/;
-
- /**
- * Register a screen displayed at {@code /#/x/plugin/regex}.
- *
- * @param regex JavaScript {@code RegExp} expression to match the anchor token after the plugin
- * name. Matching groups are exposed through the {@code Screen} object passed into the {@code
- * Screen.EntryPoint}.
- * @param entry callback function invoked to create the screen widgets.
- */
- public void screenRegex(String regex, Screen.EntryPoint entry) {
- screenRegex(regex, wrap(entry));
- }
-
- private native void screenRegex(String p, JavaScriptObject e)
- /*-{ this.screen(new $wnd.RegExp(p), e) }-*/ ;
-
- /**
- * Register a settings screen displayed at {@code /#/settings/x/plugin/token}.
- *
- * @param token literal anchor token appearing after the plugin name.
- * @param entry callback function invoked to create the settings screen widgets.
- */
- public void settingsScreen(String token, String menu, Screen.EntryPoint entry) {
- settingsScreen(token, menu, wrap(entry));
- }
-
- private native void settingsScreen(String t, String m, JavaScriptObject e)
- /*-{ this.settingsScreen(t, m, e) }-*/ ;
-
- /**
- * Register a panel for a UI extension point.
- *
- * @param extensionPoint the UI extension point for which the panel should be registered.
- * @param entry callback function invoked to create the panel widgets.
- * @param name the name of the panel which can be used to specify panel ordering via project
- * config
- */
- public final void panel(
- GerritUiExtensionPoint extensionPoint, Panel.EntryPoint entry, String name) {
- panel(extensionPoint.name(), wrap(entry), name);
- }
-
- private native void panel(String i, JavaScriptObject e, String n) /*-{ this.panel(i, e, n) }-*/;
-
- protected Plugin() {}
-
- native void _initialized() /*-{ this._success = true }-*/;
-
- native void _loaded() /*-{ this._loadedGwt() }-*/;
-
- private static native Plugin install(String u) /*-{ return $wnd.Gerrit.installGwt(u) }-*/;
-
- private static native JavaScriptObject wrap(Screen.EntryPoint b) /*-{
- return $entry(function(c){
- b.@com.google.gerrit.plugin.client.screen.Screen.EntryPoint::onLoad(Lcom/google/gerrit/plugin/client/screen/Screen;)(
- @com.google.gerrit.plugin.client.screen.Screen::new(Lcom/google/gerrit/plugin/client/screen/Screen$Context;)(c));
- });
- }-*/;
-
- private static native JavaScriptObject wrap(Panel.EntryPoint b) /*-{
- return $entry(function(c){
- b.@com.google.gerrit.plugin.client.extension.Panel.EntryPoint::onLoad(Lcom/google/gerrit/plugin/client/extension/Panel;)(
- @com.google.gerrit.plugin.client.extension.Panel::new(Lcom/google/gerrit/plugin/client/extension/Panel$Context;)(c));
- });
- }-*/;
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/PluginEntryPoint.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/PluginEntryPoint.java
deleted file mode 100644
index 808cda356d..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/PluginEntryPoint.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2012 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.plugin.client;
-
-import com.google.gwt.core.client.EntryPoint;
-
-/**
- * Base class for writing Gerrit Web UI plugins
- *
- * <p>Writing a plugin:
- *
- * <ol>
- * <li>Declare subtype of Plugin
- * <li>Bind WebUiPlugin to GwtPlugin implementation in Gerrit-Module
- * </ol>
- */
-public abstract class PluginEntryPoint implements EntryPoint {
- /**
- * The plugin entry point method, called automatically by loading a module that declares an
- * implementing class as an entry point.
- */
- public abstract void onPluginLoad();
-
- @Override
- public final void onModuleLoad() {
- Plugin self = Plugin.get();
- try {
- onPluginLoad();
- self._initialized();
- } finally {
- self._loaded();
- }
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/extension/Panel.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/extension/Panel.java
deleted file mode 100644
index 8ee6d0eaf3..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/extension/Panel.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2015 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.plugin.client.extension;
-
-import com.google.gerrit.client.GerritUiExtensionPoint;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.ui.SimplePanel;
-
-/**
- * Panel that extends a Gerrit core screen contributed by this plugin.
- *
- * <p>Panel should be registered early at module load:
- *
- * <pre>
- * &#064;Override
- * public void onModuleLoad() {
- * Plugin.get().panel(GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
- * new Panel.EntryPoint() {
- * &#064;Override
- * public void onLoad(Panel panel) {
- * panel.setWidget(new Label(&quot;World&quot;));
- * }
- * });
- * }
- * </pre>
- */
-public class Panel extends SimplePanel {
- /** Initializes a panel for display. */
- public interface EntryPoint {
- /**
- * Invoked when the panel has been created.
- *
- * <p>The implementation should create a single widget to define the content of this panel and
- * add it to the passed panel instance.
- *
- * <p>To use multiple widgets, compose them in panels such as {@code FlowPanel} and add only the
- * top level widget to the panel.
- *
- * <p>The panel is already attached to the browser DOM. Any widgets added to the screen will
- * immediately receive {@code onLoad()}. GWT will fire {@code onUnload()} when the panel is
- * removed from the UI, generally caused by the user navigating to another screen.
- *
- * @param panel panel that will contain the panel widget.
- */
- void onLoad(Panel panel);
- }
-
- static final class Context extends JavaScriptObject {
- native Element body() /*-{ return this.body }-*/;
-
- native String get(String k) /*-{ return this.p[k]; }-*/;
-
- native int getInt(String k, int d) /*-{
- return this.p.hasOwnProperty(k) ? this.p[k] : d
- }-*/;
-
- native int getBoolean(String k, boolean d) /*-{
- return this.p.hasOwnProperty(k) ? this.p[k] : d
- }-*/;
-
- native JavaScriptObject getObject(String k) /*-{ return this.p[k]; }-*/;
-
- native void detach(Panel p) /*-{
- this.onUnload($entry(function(){
- p.@com.google.gwt.user.client.ui.Widget::onDetach()();
- }));
- }-*/;
-
- protected Context() {}
- }
-
- private final Context ctx;
-
- Panel(Context ctx) {
- super(ctx.body());
- this.ctx = ctx;
- onAttach();
- ctx.detach(this);
- }
-
- public String get(GerritUiExtensionPoint.Key key) {
- return ctx.get(key.name());
- }
-
- public int getInt(GerritUiExtensionPoint.Key key, int defaultValue) {
- return ctx.getInt(key.name(), defaultValue);
- }
-
- public int getBoolean(GerritUiExtensionPoint.Key key, boolean defaultValue) {
- return ctx.getBoolean(key.name(), defaultValue);
- }
-
- public JavaScriptObject getObject(GerritUiExtensionPoint.Key key) {
- return ctx.getObject(key.name());
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java
deleted file mode 100644
index 9ff4fed581..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/NoContent.java
+++ /dev/null
@@ -1,21 +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.plugin.client.rpc;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-public class NoContent extends JavaScriptObject {
- protected NoContent() {}
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java
deleted file mode 100644
index 86791f8410..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/rpc/RestApi.java
+++ /dev/null
@@ -1,160 +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.plugin.client.rpc;
-
-import com.google.gerrit.client.rpc.NativeString;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.http.client.URL;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-public class RestApi {
- private final StringBuilder path;
- private boolean hasQueryParams;
-
- public RestApi(String name) {
- path = new StringBuilder();
- path.append(name);
- }
-
- public RestApi view(String name) {
- return idRaw(name);
- }
-
- public RestApi view(String pluginName, String name) {
- return idRaw(pluginName + "~" + name);
- }
-
- public RestApi id(String id) {
- return idRaw(URL.encodePathSegment(id));
- }
-
- public RestApi id(int id) {
- return idRaw(Integer.toString(id));
- }
-
- public RestApi idRaw(String name) {
- if (hasQueryParams) {
- throw new IllegalStateException();
- }
- if (path.charAt(path.length() - 1) != '/') {
- path.append('/');
- }
- path.append(name);
- return this;
- }
-
- public RestApi addParameter(String name, String value) {
- return addParameterRaw(name, URL.encodeQueryString(value));
- }
-
- public RestApi addParameter(String name, String... value) {
- for (String val : value) {
- addParameter(name, val);
- }
- return this;
- }
-
- public RestApi addParameterTrue(String name) {
- return addParameterRaw(name, null);
- }
-
- public RestApi addParameter(String name, boolean value) {
- return addParameterRaw(name, value ? "t" : "f");
- }
-
- public RestApi addParameter(String name, int value) {
- return addParameterRaw(name, String.valueOf(value));
- }
-
- public RestApi addParameter(String name, Enum<?> value) {
- return addParameterRaw(name, value.name());
- }
-
- public RestApi addParameterRaw(String name, String value) {
- if (hasQueryParams) {
- path.append("&");
- } else {
- path.append("?");
- hasQueryParams = true;
- }
- path.append(name);
- if (value != null) {
- path.append("=").append(value);
- }
- return this;
- }
-
- public String path() {
- return path.toString();
- }
-
- public <T extends JavaScriptObject> void get(AsyncCallback<T> cb) {
- get(path(), wrap(cb));
- }
-
- public void getString(AsyncCallback<String> cb) {
- get(NativeString.unwrap(cb));
- }
-
- private static native void get(String p, JavaScriptObject r) /*-{ $wnd.Gerrit.get_raw(p, r) }-*/;
-
- public <T extends JavaScriptObject> void put(AsyncCallback<T> cb) {
- put(path(), wrap(cb));
- }
-
- private static native void put(String p, JavaScriptObject r) /*-{ $wnd.Gerrit.put_raw(p, r) }-*/;
-
- public <T extends JavaScriptObject> void put(String content, AsyncCallback<T> cb) {
- put(path(), content, wrap(cb));
- }
-
- private static native void put(String p, String c, JavaScriptObject r)
- /*-{ $wnd.Gerrit.put_raw(p, c, r) }-*/ ;
-
- public <T extends JavaScriptObject> void put(JavaScriptObject content, AsyncCallback<T> cb) {
- put(path(), content, wrap(cb));
- }
-
- private static native void put(String p, JavaScriptObject c, JavaScriptObject r)
- /*-{ $wnd.Gerrit.put_raw(p, c, r) }-*/ ;
-
- public <T extends JavaScriptObject> void post(String content, AsyncCallback<T> cb) {
- post(path(), content, wrap(cb));
- }
-
- private static native void post(String p, String c, JavaScriptObject r)
- /*-{ $wnd.Gerrit.post_raw(p, c, r) }-*/ ;
-
- public <T extends JavaScriptObject> void post(JavaScriptObject content, AsyncCallback<T> cb) {
- post(path(), content, wrap(cb));
- }
-
- private static native void post(String p, JavaScriptObject c, JavaScriptObject r)
- /*-{ $wnd.Gerrit.post_raw(p, c, r) }-*/ ;
-
- public void delete(AsyncCallback<NoContent> cb) {
- delete(path(), wrap(cb));
- }
-
- private static native void delete(String p, JavaScriptObject r)
- /*-{ $wnd.Gerrit.del_raw(p, r) }-*/ ;
-
- private static native <T extends JavaScriptObject> JavaScriptObject wrap(
- AsyncCallback<T> b) /*-{
- return function(r) {
- b.@com.google.gwt.user.client.rpc.AsyncCallback::onSuccess(Ljava/lang/Object;)(r)
- }
- }-*/;
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/screen/Screen.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/screen/Screen.java
deleted file mode 100644
index 226ac48426..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/screen/Screen.java
+++ /dev/null
@@ -1,144 +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.plugin.client.screen;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.Widget;
-
-/**
- * Screen contributed by this plugin.
- *
- * <p>Screens should be registered early at module load:
- *
- * <pre>
- * &#064;Override
- * public void onModuleLoad() {
- * Plugin.get().screen(&quot;hi&quot;, new Screen.EntryPoint() {
- * &#064;Override
- * public void onLoad(Screen screen) {
- * screen.setPageTitle(&quot;Hi&quot;);
- * screen.show(new Label(&quot;World&quot;));
- * }
- * });
- * }
- * </pre>
- */
-public final class Screen extends SimplePanel {
- /** Initializes a screen for display. */
- public interface EntryPoint {
- /**
- * Invoked when the screen has been created, but not yet displayed.
- *
- * <p>The implementation should create a single widget to define the content of this screen and
- * added it to the passed screen instance. When the screen is ready to be displayed, call {@link
- * Screen#show()}.
- *
- * <p>To use multiple widgets, compose them in panels such as {@code FlowPanel} and add only the
- * top level widget to the screen.
- *
- * <p>The screen is already attached to the browser DOM in an invisible area. Any widgets added
- * to the screen will immediately receive {@code onLoad()}. GWT will fire {@code onUnload()}
- * when the screen is removed from the UI, generally caused by the user navigating to another
- * screen.
- *
- * @param screen panel that will contain the screen widget.
- */
- void onLoad(Screen screen);
- }
-
- static final class Context extends JavaScriptObject {
- native Element body() /*-{ return this.body }-*/;
-
- native JsArrayString token_match() /*-{ return this.token_match }-*/;
-
- native void show() /*-{ this.show() }-*/;
-
- native void setTitle(String t) /*-{ this.setTitle(t) }-*/;
-
- native void setWindowTitle(String t) /*-{ this.setWindowTitle(t) }-*/;
-
- native void detach(Screen s) /*-{
- this.onUnload($entry(function(){
- s.@com.google.gwt.user.client.ui.Widget::onDetach()();
- }));
- }-*/;
-
- protected Context() {}
- }
-
- private final Context ctx;
-
- Screen(Context ctx) {
- super(ctx.body());
- this.ctx = ctx;
- onAttach();
- ctx.detach(this);
- }
-
- /** @return the token suffix after {@code "/#/x/plugin-name/"}. */
- public String getToken() {
- return getToken(0);
- }
-
- /**
- * @param group groups range from 1 to {@code getTokenGroups() - 1}. Token group 0 is the entire
- * token, see {@link #getToken()}.
- * @return the token from the regex match group.
- */
- public String getToken(int group) {
- return ctx.token_match().get(group);
- }
-
- /** @return total number of token groups. */
- public int getTokenGroups() {
- return ctx.token_match().length();
- }
-
- /**
- * Set the page title text; appears above the widget.
- *
- * @param titleText text to display above the widget.
- */
- public void setPageTitle(String titleText) {
- ctx.setTitle(titleText);
- }
-
- /**
- * Set the window title text; appears in the browser window title bar.
- *
- * @param titleText text to display in the window title bar.
- */
- public void setWindowTitle(String titleText) {
- ctx.setWindowTitle(titleText);
- }
-
- /**
- * Add the widget and immediately show the screen.
- *
- * @param w child containing the content.
- */
- public void show(Widget w) {
- setWidget(w);
- ctx.show();
- }
-
- /** Show this screen in the web interface. */
- public void show() {
- ctx.show();
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/ui/GroupSuggestOracle.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/ui/GroupSuggestOracle.java
deleted file mode 100644
index df5be2cc53..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/ui/GroupSuggestOracle.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2015 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.plugin.client.ui;
-
-import com.google.gerrit.client.rpc.NativeMap;
-import com.google.gerrit.client.ui.HighlightSuggestion;
-import com.google.gerrit.plugin.client.rpc.RestApi;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** A {@code SuggestOracle} for groups. */
-public class GroupSuggestOracle extends SuggestOracle {
-
- private final int chars;
-
- /** @param chars minimum chars to start suggesting. */
- public GroupSuggestOracle(int chars) {
- this.chars = chars;
- }
-
- @Override
- public boolean isDisplayStringHTML() {
- return true;
- }
-
- @Override
- public void requestSuggestions(Request req, Callback done) {
- if (req.getQuery().length() < chars) {
- responseEmptySuggestion(req, done);
- return;
- }
- RestApi rest = new RestApi("/groups/").addParameter("suggest", req.getQuery());
- if (req.getLimit() > 0) {
- rest.addParameter("n", req.getLimit());
- }
- rest.get(
- new AsyncCallback<NativeMap<JavaScriptObject>>() {
- @Override
- public void onSuccess(NativeMap<JavaScriptObject> result) {
- List<String> keys = result.sortedKeys();
- List<Suggestion> suggestions = new ArrayList<>(keys.size());
- for (String g : keys) {
- suggestions.add(new HighlightSuggestion(req.getQuery(), g));
- }
- done.onSuggestionsReady(req, new Response(suggestions));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- responseEmptySuggestion(req, done);
- }
- });
- }
-
- private static void responseEmptySuggestion(Request req, Callback done) {
- List<Suggestion> empty = Collections.emptyList();
- done.onSuggestionsReady(req, new Response(empty));
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java
deleted file mode 100644
index 18f6e540bf..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/linker/GerritPluginLinker.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2012 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.plugin.linker;
-
-import com.google.gwt.core.ext.LinkerContext;
-import com.google.gwt.core.linker.CrossSiteIframeLinker;
-
-/** Finalizes the module manifest file with the selection script. */
-public final class GerritPluginLinker extends CrossSiteIframeLinker {
- @Override
- public String getDescription() {
- return "Gerrit GWT UI plugin";
- }
-
- @Override
- protected String getJsComputeUrlForResource(LinkerContext context) {
- return "com/google/gerrit/linker/computeUrlForPluginResource.js";
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java
deleted file mode 100644
index ba145563e5..0000000000
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/rebind/PluginGenerator.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-// 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.gerrit.plugin.rebind;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.ext.Generator;
-import com.google.gwt.core.ext.GeneratorContext;
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
-import com.google.gwt.user.rebind.SourceWriter;
-import java.io.PrintWriter;
-
-/**
- * Write the top layer in the Gadget bootstrap sandwich and generate a stub manifest that will be
- * completed by the linker.
- *
- * <p>Based on gwt-gadgets GadgetGenerator class
- */
-public class PluginGenerator extends Generator {
- @Override
- public String generate(TreeLogger logger, GeneratorContext context, String typeName)
- throws UnableToCompleteException {
-
- // The TypeOracle knows about all types in the type system
- TypeOracle typeOracle = context.getTypeOracle();
-
- // Get a reference to the type that the generator should implement
- JClassType sourceType = typeOracle.findType(typeName);
-
- // Ensure that the requested type exists
- if (sourceType == null) {
- logger.log(TreeLogger.ERROR, "Could not find requested typeName", null);
- throw new UnableToCompleteException();
- }
-
- // Make sure the Gadget type is correctly defined
- validateType(logger, sourceType);
-
- // Pick a name for the generated class to not conflict.
- String generatedSimpleSourceName = sourceType.getSimpleSourceName() + "PluginImpl";
-
- // Begin writing the generated source.
- ClassSourceFileComposerFactory f =
- new ClassSourceFileComposerFactory(
- sourceType.getPackage().getName(), generatedSimpleSourceName);
- f.addImport(GWT.class.getName());
- f.setSuperclass(typeName);
-
- // All source gets written through this Writer
- PrintWriter out =
- context.tryCreate(logger, sourceType.getPackage().getName(), generatedSimpleSourceName);
-
- // If an implementation already exists, we don't need to do any work
- if (out != null) {
-
- // We really use a SourceWriter since it's convenient
- SourceWriter sw = f.createSourceWriter(context, out);
- sw.commit(logger);
- }
-
- return f.getCreatedClassName();
- }
-
- protected void validateType(TreeLogger logger, JClassType type) throws UnableToCompleteException {
- if (!type.isDefaultInstantiable()) {
- logger.log(TreeLogger.ERROR, "Plugin types must be default instantiable", null);
- throw new UnableToCompleteException();
- }
- }
-}
diff --git a/gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js b/gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js
deleted file mode 100644
index 3e20e94c6e..0000000000
--- a/gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js
+++ /dev/null
@@ -1,3 +0,0 @@
-function computeUrlForResource(resource) {
- return __MODULE_FUNC__.__moduleBase + resource;
-}
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 2fbc3e988e..f05c598b07 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -14,6 +14,7 @@
package com.google.gerrit.acceptance;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.Truth8.assertThat;
@@ -21,10 +22,12 @@ import static com.google.common.truth.TruthJUnit.assume;
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 java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.HEAD;
@@ -38,23 +41,23 @@ import com.google.common.jimfs.Jimfs;
import com.google.common.primitives.Chars;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
+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.ContributorAgreement;
import com.google.gerrit.common.data.GroupDescription;
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.common.data.PermissionRule.Action;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
-import com.google.gerrit.extensions.api.groups.GroupApi;
-import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
@@ -72,6 +75,7 @@ import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.index.project.ProjectIndex;
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;
@@ -82,12 +86,10 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.PluginUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
@@ -103,6 +105,7 @@ import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.InternalGroup;
@@ -113,9 +116,11 @@ import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeIndexer;
+import com.google.gerrit.server.notedb.AbstractChangeNotes;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
+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;
@@ -126,13 +131,8 @@ import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeEmailSender;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import com.google.gerrit.testing.FakeGroupAuditService;
-import com.google.gerrit.testing.NoteDbMode;
import com.google.gerrit.testing.SshMode;
-import com.google.gerrit.testing.TempFileUtil;
import com.google.gson.Gson;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
@@ -141,6 +141,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Modifier;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
@@ -163,7 +164,6 @@ 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;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
@@ -181,8 +181,10 @@ import org.eclipse.jgit.transport.URIish;
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;
import org.junit.runner.RunWith;
@@ -193,6 +195,8 @@ public abstract class AbstractDaemonTest {
private static GerritServer commonServer;
private static Description firstTest;
+ @ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder();
+
@ConfigSuite.Parameter public Config baseConfig;
@ConfigSuite.Name private String configName;
@@ -210,10 +214,7 @@ public abstract class AbstractDaemonTest {
firstTest = description;
}
beforeTest(description);
- ProjectResetter.Config input = resetProjects();
- if (input == null) {
- input = defaultResetProjects();
- }
+ ProjectResetter.Config input = requireNonNull(resetProjects());
try (ProjectResetter resetter = projectResetter.builder().build(input)) {
AbstractDaemonTest.this.resetter = resetter;
@@ -243,7 +244,6 @@ public abstract class AbstractDaemonTest {
@Inject protected ChangeNoteUtil changeNoteUtil;
@Inject protected ChangeResource.Factory changeResourceFactory;
@Inject protected FakeEmailSender sender;
- @Inject protected FakeGroupAuditService auditService;
@Inject protected GerritApi gApi;
@Inject protected GitRepositoryManager repoManager;
@Inject protected GroupBackend groupBackend;
@@ -252,13 +252,13 @@ public abstract class AbstractDaemonTest {
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected PatchSetUtil psUtil;
@Inject protected ProjectCache projectCache;
+ @Inject protected ProjectConfig.Factory projectConfigFactory;
@Inject protected ProjectResetter.Builder.Factory projectResetter;
@Inject protected Provider<InternalChangeQuery> queryProvider;
@Inject protected PushOneCommit.Factory pushFactory;
@Inject protected PluginConfigFactory pluginConfig;
@Inject protected Revisions revisions;
@Inject protected SystemGroupBackend systemGroupBackend;
- @Inject protected MutableNotesMigration notesMigration;
@Inject protected ChangeNotes.Factory notesFactory;
@Inject protected BatchAbandon batchAbandon;
@Inject protected TestSshKeys sshKeys;
@@ -268,7 +268,7 @@ public abstract class AbstractDaemonTest {
protected Project.NameKey project;
protected RestSession adminRestSession;
protected RestSession userRestSession;
- protected ReviewDb db;
+ protected RestSession anonymousRestSession;
protected SshSession adminSshSession;
protected SshSession userSshSession;
protected TestAccount admin;
@@ -279,14 +279,18 @@ public abstract class AbstractDaemonTest {
protected boolean testRequiresSsh;
protected BlockStrategy noSleepBlockStrategy = t -> {}; // Don't sleep in tests.
- @Inject private ChangeIndexCollection changeIndexes;
+ @Inject private AbstractChangeNotes.Args changeNotesArgs;
@Inject private AccountIndexCollection accountIndexes;
- @Inject private ProjectIndexCollection projectIndexes;
+ @Inject private AccountIndexer accountIndexer;
+ @Inject private ChangeIndexCollection changeIndexes;
@Inject private EventRecorder.Factory eventRecorderFactory;
@Inject private InProcessProtocol inProcessProtocol;
- @Inject private Provider<AnonymousUser> anonymousUser;
- @Inject private SchemaFactory<ReviewDb> reviewDbProvider;
- @Inject private AccountIndexer accountIndexer;
+ @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;
private ProjectResetter resetter;
private List<Repository> toClose;
@@ -330,15 +334,10 @@ public abstract class AbstractDaemonTest {
commonServer = null;
}
}
- TempFileUtil.cleanup();
}
/** Controls which project and branches should be reset after each test case. */
protected ProjectResetter.Config resetProjects() {
- return null;
- }
-
- private ProjectResetter.Config defaultResetProjects() {
return new ProjectResetter.Config()
// Don't reset all refs so that refs/sequences/changes is not touched and change IDs are
// not reused.
@@ -365,7 +364,16 @@ public abstract class AbstractDaemonTest {
initSsh();
}
- protected void evictAndReindexAccount(Account.Id accountId) throws IOException {
+ protected void restart() throws Exception {
+ server = GerritServer.restart(server, createModule(), createSshModule());
+ server.getTestInjector().injectMembers(this);
+ if (resetter != null) {
+ server.getTestInjector().injectMembers(resetter);
+ }
+ initSsh();
+ }
+
+ protected void evictAndReindexAccount(Account.Id accountId) {
accountCache.evict(accountId);
accountIndexer.index(accountId);
}
@@ -400,30 +408,32 @@ public abstract class AbstractDaemonTest {
baseConfig.setInt("receive", null, "changeUpdateThreads", 4);
Module module = createModule();
+ Module sshModule = createSshModule();
if (classDesc.equals(methodDesc) && !classDesc.sandboxed() && !methodDesc.sandboxed()) {
if (commonServer == null) {
- commonServer = GerritServer.initAndStart(classDesc, baseConfig, module);
+ commonServer =
+ GerritServer.initAndStart(temporaryFolder, classDesc, baseConfig, module, sshModule);
}
server = commonServer;
} else {
- server = GerritServer.initAndStart(methodDesc, baseConfig, module);
+ server =
+ GerritServer.initAndStart(temporaryFolder, methodDesc, baseConfig, module, sshModule);
}
server.getTestInjector().injectMembers(this);
Transport.register(inProcessProtocol);
- toClose = Collections.synchronizedList(new ArrayList<Repository>());
-
- db = reviewDbProvider.open();
+ toClose = Collections.synchronizedList(new ArrayList<>());
admin = accountCreator.admin();
user = accountCreator.user();
// Evict and reindex accounts in case tests modify them.
- evictAndReindexAccount(admin.getId());
- evictAndReindexAccount(user.getId());
+ evictAndReindexAccount(admin.id());
+ evictAndReindexAccount(user.id());
adminRestSession = new RestSession(server, admin);
userRestSession = new RestSession(server, user);
+ anonymousRestSession = new RestSession(server, null);
initSsh();
@@ -437,7 +447,9 @@ public abstract class AbstractDaemonTest {
ProjectInput in = projectInput(description);
gApi.projects().create(in);
project = new Project.NameKey(in.name);
- testRepo = cloneProject(project, getCloneAsAccount(description));
+ if (!classDesc.skipProjectClone()) {
+ testRepo = cloneProject(project, getCloneAsAccount(description));
+ }
}
/** Override to bind an additional Guice module */
@@ -445,6 +457,11 @@ public abstract class AbstractDaemonTest {
return null;
}
+ /** Override to bind an additional Guice module for SSH injector */
+ public Module createSshModule() {
+ return null;
+ }
+
protected void initSsh() throws Exception {
if (testRequiresSsh
&& SshMode.useSsh()
@@ -524,24 +541,7 @@ public abstract class AbstractDaemonTest {
return resourcePrefix + name;
}
- protected Project.NameKey createProject(String nameSuffix) throws RestApiException {
- return createProject(nameSuffix, null);
- }
-
- protected Project.NameKey createProject(String nameSuffix, Project.NameKey parent)
- throws RestApiException {
- // Default for createEmptyCommit should match TestProjectConfig.
- return createProject(nameSuffix, parent, true, null);
- }
-
- protected Project.NameKey createProject(
- String nameSuffix, Project.NameKey parent, boolean createEmptyCommit)
- throws RestApiException {
- // Default for createEmptyCommit should match TestProjectConfig.
- return createProject(nameSuffix, parent, createEmptyCommit, null);
- }
-
- protected Project.NameKey createProject(
+ protected Project.NameKey createProjectOverAPI(
String nameSuffix, Project.NameKey parent, boolean createEmptyCommit, SubmitType submitType)
throws RestApiException {
ProjectInput in = new ProjectInput();
@@ -570,8 +570,7 @@ public abstract class AbstractDaemonTest {
protected String registerRepoConnection(Project.NameKey p, TestAccount testAccount)
throws Exception {
InProcessProtocol.Context ctx =
- new InProcessProtocol.Context(
- reviewDbProvider, identifiedUserFactory, testAccount.getId(), p);
+ new InProcessProtocol.Context(identifiedUserFactory, testAccount.id(), p);
Repository repo = repoManager.openRepository(p);
toClose.add(repo);
return inProcessProtocol.register(ctx, repo).toString();
@@ -582,13 +581,11 @@ public abstract class AbstractDaemonTest {
for (Repository repo : toClose) {
repo.close();
}
- db.close();
closeSsh();
if (server != commonServer) {
server.close();
server = null;
}
- NoteDbMode.resetFromEnv(notesMigration);
}
protected void closeSsh() {
@@ -625,7 +622,7 @@ public abstract class AbstractDaemonTest {
}
protected PushOneCommit.Result createChange(String ref) throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result result = push.to(ref);
result.assertOkStatus();
return result;
@@ -641,8 +638,7 @@ public abstract class AbstractDaemonTest {
PushOneCommit.Result p1 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"parent 1",
ImmutableMap.of(file, "foo-1", "bar", "bar-1"))
@@ -654,8 +650,7 @@ public abstract class AbstractDaemonTest {
PushOneCommit.Result p2 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"parent 2",
ImmutableMap.of(file, "foo-2", "bar", "bar-2"))
@@ -663,11 +658,7 @@ public abstract class AbstractDaemonTest {
PushOneCommit m =
pushFactory.create(
- db,
- admin.getIdent(),
- testRepo,
- "merge",
- ImmutableMap.of(file, "foo-1", "bar", "bar-2"));
+ admin.newIdent(), testRepo, "merge", ImmutableMap.of(file, "foo-1", "bar", "bar-2"));
m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
PushOneCommit.Result result = m.to(ref);
result.assertOkStatus();
@@ -682,7 +673,7 @@ public abstract class AbstractDaemonTest {
String content)
throws Exception {
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), repo, commitMsg, fileName, content).to(ref);
+ pushFactory.create(admin.newIdent(), repo, commitMsg, fileName, content).to(ref);
result.assertOkStatus();
return result;
}
@@ -701,8 +692,7 @@ public abstract class AbstractDaemonTest {
protected PushOneCommit.Result createChange(String subject, String fileName, String content)
throws Exception {
- PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
return push.to("refs/for/master");
}
@@ -714,7 +704,7 @@ public abstract class AbstractDaemonTest {
String content,
String topic)
throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo, subject, fileName, content);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), repo, subject, fileName, content);
return push.to("refs/for/" + branch + "%topic=" + name(topic));
}
@@ -768,7 +758,7 @@ public abstract class AbstractDaemonTest {
String content)
throws Exception {
PushOneCommit push =
- pushFactory.create(db, testAccount.getIdent(), repo, subject, fileName, content, changeId);
+ pushFactory.create(testAccount.newIdent(), repo, subject, fileName, content, changeId);
return push.to(ref);
}
@@ -794,28 +784,8 @@ public abstract class AbstractDaemonTest {
}
private Context newRequestContext(TestAccount account) {
- return atrScope.newContext(
- reviewDbProvider,
- new SshSession(sshKeys, server, account),
- identifiedUserFactory.create(account.getId()));
- }
-
- /**
- * Enforce a new request context for the current API user.
- *
- * <p>This recreates the IdentifiedUser, hence everything which is cached in the IdentifiedUser is
- * reloaded (e.g. the email addresses of the user).
- */
- protected Context resetCurrentApiUser() {
- return atrScope.set(newRequestContext(atrScope.get().getSession().getAccount()));
- }
-
- protected Context setApiUser(TestAccount account) {
- return atrScope.set(newRequestContext(account));
- }
-
- protected Context setApiUserAnonymous() {
- return atrScope.set(atrScope.newContext(reviewDbProvider, null, anonymousUser.get()));
+ requestScopeOperations.setApiUser(account.id());
+ return atrScope.get();
}
protected Account getAccount(Account.Id accountId) {
@@ -828,14 +798,13 @@ public abstract class AbstractDaemonTest {
return accountState.get();
}
- protected Context disableDb() {
- notesMigration.setFailOnLoadForTest(true);
- return atrScope.disableDb();
- }
-
- protected void enableDb(Context preDisableContext) {
- notesMigration.setFailOnLoadForTest(false);
- atrScope.set(preDisableContext);
+ protected AutoCloseable disableNoteDb() {
+ changeNotesArgs.failOnLoadForTest.set(true);
+ Context oldContext = atrScope.disableNoteDb();
+ return () -> {
+ changeNotesArgs.failOnLoadForTest.set(false);
+ atrScope.set(oldContext);
+ };
}
protected void disableChangeIndexWrites() {
@@ -856,55 +825,49 @@ public abstract class AbstractDaemonTest {
protected AutoCloseable disableChangeIndex() {
disableChangeIndexWrites();
- ChangeIndex searchIndex = changeIndexes.getSearchIndex();
- if (!(searchIndex instanceof DisabledChangeIndex)) {
- changeIndexes.setSearchIndex(new DisabledChangeIndex(searchIndex), false);
+ ChangeIndex maybeDisabledSearchIndex = changeIndexes.getSearchIndex();
+ if (!(maybeDisabledSearchIndex instanceof DisabledChangeIndex)) {
+ changeIndexes.setSearchIndex(new DisabledChangeIndex(maybeDisabledSearchIndex), false);
}
- return new AutoCloseable() {
- @Override
- public void close() throws Exception {
- enableChangeIndexWrites();
- ChangeIndex searchIndex = changeIndexes.getSearchIndex();
- if (searchIndex instanceof DisabledChangeIndex) {
- changeIndexes.setSearchIndex(((DisabledChangeIndex) searchIndex).unwrap(), false);
- }
+ return () -> {
+ enableChangeIndexWrites();
+ ChangeIndex maybeEnabledSearchIndex = changeIndexes.getSearchIndex();
+ if (maybeEnabledSearchIndex instanceof DisabledChangeIndex) {
+ changeIndexes.setSearchIndex(
+ ((DisabledChangeIndex) maybeEnabledSearchIndex).unwrap(), false);
}
};
}
protected AutoCloseable disableAccountIndex() {
- AccountIndex searchIndex = accountIndexes.getSearchIndex();
- if (!(searchIndex instanceof DisabledAccountIndex)) {
- accountIndexes.setSearchIndex(new DisabledAccountIndex(searchIndex), false);
+ AccountIndex maybeDisabledSearchIndex = accountIndexes.getSearchIndex();
+ if (!(maybeDisabledSearchIndex instanceof DisabledAccountIndex)) {
+ accountIndexes.setSearchIndex(new DisabledAccountIndex(maybeDisabledSearchIndex), false);
}
- return new AutoCloseable() {
- @Override
- public void close() {
- AccountIndex searchIndex = accountIndexes.getSearchIndex();
- if (searchIndex instanceof DisabledAccountIndex) {
- accountIndexes.setSearchIndex(((DisabledAccountIndex) searchIndex).unwrap(), false);
- }
+ return () -> {
+ AccountIndex maybeEnabledSearchIndex = accountIndexes.getSearchIndex();
+ if (maybeEnabledSearchIndex instanceof DisabledAccountIndex) {
+ accountIndexes.setSearchIndex(
+ ((DisabledAccountIndex) maybeEnabledSearchIndex).unwrap(), false);
}
};
}
protected AutoCloseable disableProjectIndex() {
disableProjectIndexWrites();
- ProjectIndex searchIndex = projectIndexes.getSearchIndex();
- if (!(searchIndex instanceof DisabledProjectIndex)) {
- projectIndexes.setSearchIndex(new DisabledProjectIndex(searchIndex), false);
+ ProjectIndex maybeDisabledSearchIndex = projectIndexes.getSearchIndex();
+ if (!(maybeDisabledSearchIndex instanceof DisabledProjectIndex)) {
+ projectIndexes.setSearchIndex(new DisabledProjectIndex(maybeDisabledSearchIndex), false);
}
- return new AutoCloseable() {
- @Override
- public void close() {
- enableProjectIndexWrites();
- ProjectIndex searchIndex = projectIndexes.getSearchIndex();
- if (searchIndex instanceof DisabledProjectIndex) {
- projectIndexes.setSearchIndex(((DisabledProjectIndex) searchIndex).unwrap(), false);
- }
+ return () -> {
+ enableProjectIndexWrites();
+ ProjectIndex maybeEnabledSearchIndex = projectIndexes.getSearchIndex();
+ if (maybeEnabledSearchIndex instanceof DisabledProjectIndex) {
+ projectIndexes.setSearchIndex(
+ ((DisabledProjectIndex) maybeEnabledSearchIndex).unwrap(), false);
}
};
}
@@ -945,6 +908,17 @@ public abstract class AbstractDaemonTest {
}
}
+ 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));
@@ -975,18 +949,9 @@ public abstract class AbstractDaemonTest {
}
}
- protected void setUseContributorAgreements(InheritableBoolean value) throws Exception {
- try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
- ProjectConfig config = ProjectConfig.read(md);
- config.getProject().setBooleanConfig(BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS, value);
- config.commit(md);
- projectCache.evict(config.getProject());
- }
- }
-
protected void setUseSignedOffBy(InheritableBoolean value) throws Exception {
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
config.getProject().setBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY, value);
config.commit(md);
projectCache.evict(config.getProject());
@@ -995,7 +960,7 @@ public abstract class AbstractDaemonTest {
protected void setRequireChangeId(InheritableBoolean value) throws Exception {
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
config.getProject().setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, value);
config.commit(md);
projectCache.evict(config.getProject());
@@ -1057,7 +1022,7 @@ public abstract class AbstractDaemonTest {
throws RepositoryNotFoundException, IOException, ConfigInvalidException {
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
md.setMessage(String.format("Grant %s on %s", permission, ref));
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
AccessSection s = config.getAccessSection(ref, true);
Permission p = s.getPermission(permission, true);
PermissionRule rule = Util.newRule(config, groupUUID);
@@ -1081,7 +1046,7 @@ public abstract class AbstractDaemonTest {
String permission = Permission.LABEL + label;
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
md.setMessage(String.format("Grant %s on %s", permission, ref));
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
AccessSection s = config.getAccessSection(ref, true);
Permission p = s.getPermission(permission, true);
p.setExclusiveGroup(exclusive);
@@ -1099,7 +1064,7 @@ public abstract class AbstractDaemonTest {
throws IOException, ConfigInvalidException {
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
md.setMessage(String.format("Remove %s on %s", permission, ref));
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
AccessSection s = config.getAccessSection(ref, true);
Permission p = s.getPermission(permission, true);
p.clearRules();
@@ -1112,8 +1077,19 @@ public abstract class AbstractDaemonTest {
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();
+ }
+ }
+
protected PushOneCommit.Result pushTo(String ref) throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
return push.to(ref);
}
@@ -1139,12 +1115,12 @@ public abstract class AbstractDaemonTest {
.inOrder();
}
- protected PatchSet getPatchSet(PatchSet.Id psId) throws OrmException {
- return changeDataFactory.create(db, project, psId.getParentKey()).patchSet(psId);
+ protected PatchSet getPatchSet(PatchSet.Id psId) {
+ return changeDataFactory.create(project, psId.getParentKey()).patchSet(psId);
}
protected IdentifiedUser user(TestAccount testAccount) {
- return identifiedUserFactory.create(testAccount.getId());
+ return identifiedUserFactory.create(testAccount.id());
}
protected RevisionResource parseCurrentRevisionResource(String changeId) throws Exception {
@@ -1169,46 +1145,16 @@ public abstract class AbstractDaemonTest {
return changeResourceFactory.create(notes.get(0), atrScope.get().getUser());
}
- protected String createGroup(String name) throws Exception {
- return createGroup(name, "Administrators");
- }
-
- protected String createGroupWithRealName(String name) throws Exception {
- GroupInput in = new GroupInput();
- in.name = name;
- in.ownerId = "Administrators";
- gApi.groups().create(in);
- return name;
- }
-
- protected String createGroup(String name, String owner) throws Exception {
- name = name(name);
- GroupInput in = new GroupInput();
- in.name = name;
- in.ownerId = owner;
- gApi.groups().create(in);
- return name;
- }
-
protected RevCommit getHead(Repository repo, String name) throws Exception {
try (RevWalk rw = new RevWalk(repo)) {
Ref r = repo.exactRef(name);
- return r != null ? rw.parseCommit(r.getObjectId()) : null;
+ return rw.parseCommit(r.getObjectId());
}
}
- protected RevCommit getHead(Repository repo) throws Exception {
- return getHead(repo, "HEAD");
- }
-
+ // TODO(hanwen): push this down.
protected RevCommit getRemoteHead(Project.NameKey project, String branch) throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- return getHead(repo, branch.startsWith(Constants.R_REFS) ? branch : "refs/heads/" + branch);
- }
- }
-
- protected RevCommit getRemoteHead(String project, String branch) throws Exception {
- return getRemoteHead(new Project.NameKey(project), branch);
+ return projectOperations.project(project).getHead(branch);
}
protected RevCommit getRemoteHead() throws Exception {
@@ -1221,33 +1167,6 @@ public abstract class AbstractDaemonTest {
assertThat(replyTo.getString()).contains(email);
}
- protected ContributorAgreement configureContributorAgreement(boolean autoVerify)
- throws Exception {
- ContributorAgreement ca;
- if (autoVerify) {
- String g = createGroup("cla-test-group");
- GroupApi groupApi = gApi.groups().id(g);
- groupApi.description("CLA test group");
- InternalGroup caGroup = group(new AccountGroup.UUID(groupApi.detail().id));
- GroupReference groupRef = new GroupReference(caGroup.getGroupUUID(), caGroup.getName());
- PermissionRule rule = new PermissionRule(groupRef);
- rule.setAction(PermissionRule.Action.ALLOW);
- ca = new ContributorAgreement("cla-test");
- ca.setAutoVerify(groupRef);
- ca.setAccepted(ImmutableList.of(rule));
- } else {
- ca = new ContributorAgreement("cla-test-no-auto-verify");
- }
- ca.setDescription("description");
- ca.setAgreementUrl("agreement-url");
-
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- u.getConfig().replace(ca);
- u.save();
- return ca;
- }
- }
-
protected Map<Branch.NameKey, ObjectId> fetchFromSubmitPreview(String changeId) throws Exception {
try (BinaryResult result = gApi.changes().id(changeId).current().submitPreview()) {
return fetchFromBundles(result);
@@ -1465,7 +1384,7 @@ public abstract class AbstractDaemonTest {
}
protected void assertNotifyTo(TestAccount expected) {
- assertNotifyTo(expected.email, expected.fullName);
+ assertNotifyTo(expected.email(), expected.fullName());
}
protected void assertNotifyTo(String expectedEmail, String expectedFullname) {
@@ -1479,7 +1398,7 @@ public abstract class AbstractDaemonTest {
}
protected void assertNotifyCc(TestAccount expected) {
- assertNotifyCc(expected.emailAddress);
+ assertNotifyCc(expected.getEmailAddress());
}
protected void assertNotifyCc(String expectedEmail, String expectedFullname) {
@@ -1499,7 +1418,7 @@ public abstract class AbstractDaemonTest {
protected void assertNotifyBcc(TestAccount expected) {
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(expected.emailAddress);
+ assertThat(m.rcpt()).containsExactly(expected.getEmailAddress());
assertThat(m.headers().get("To").isEmpty()).isTrue();
assertThat(m.headers().get("Cc").isEmpty()).isTrue();
}
@@ -1525,7 +1444,7 @@ public abstract class AbstractDaemonTest {
}
protected void watch(PushOneCommit.Result r, ProjectWatchInfoConfiguration config)
- throws OrmException, RestApiException {
+ throws RestApiException {
watch(r.getChange().project().get(), config);
}
@@ -1646,7 +1565,7 @@ public abstract class AbstractDaemonTest {
private ProjectConfigUpdate(Project.NameKey projectName) throws Exception {
metaDataUpdate = metaDataUpdateFactory.create(projectName);
- projectConfig = ProjectConfig.read(metaDataUpdate);
+ projectConfig = projectConfigFactory.read(metaDataUpdate);
}
public ProjectConfig getConfig() {
@@ -1654,7 +1573,7 @@ public abstract class AbstractDaemonTest {
}
public void save() throws Exception {
- metaDataUpdate.setAuthor(identifiedUserFactory.create(admin.getId()));
+ metaDataUpdate.setAuthor(identifiedUserFactory.create(admin.id()));
projectConfig.commit(metaDataUpdate);
metaDataUpdate.close();
metaDataUpdate = null;
@@ -1693,4 +1612,45 @@ public abstract class AbstractDaemonTest {
comments.sort(Comparator.comparing(c -> c.id));
return comments;
}
+
+ protected AutoCloseable installPlugin(String pluginName, Class<? extends Module> sysModuleClass)
+ throws Exception {
+ return installPlugin(pluginName, sysModuleClass, null, null);
+ }
+
+ protected AutoCloseable installPlugin(
+ String pluginName,
+ @Nullable Class<? extends Module> sysModuleClass,
+ @Nullable Class<? extends Module> httpModuleClass,
+ @Nullable Class<? extends Module> sshModuleClass)
+ throws Exception {
+ checkStatic(sysModuleClass);
+ checkStatic(httpModuleClass);
+ checkStatic(sshModuleClass);
+ TestServerPlugin plugin =
+ new TestServerPlugin(
+ pluginName,
+ "http://example.com/" + pluginName,
+ pluginUserFactory.create(pluginName),
+ getClass().getClassLoader(),
+ sysModuleClass != null ? sysModuleClass.getName() : null,
+ httpModuleClass != null ? httpModuleClass.getName() : null,
+ sshModuleClass != null ? sshModuleClass.getName() : null,
+ sitePaths.data_dir.resolve(pluginName));
+ plugin.start(pluginGuiceEnvironment);
+ pluginGuiceEnvironment.onStartPlugin(plugin);
+ return () -> {
+ plugin.stop(pluginGuiceEnvironment);
+ pluginGuiceEnvironment.onStopPlugin(plugin);
+ };
+ }
+
+ private static void checkStatic(@Nullable Class<? extends Module> moduleClass) {
+ if (moduleClass != null) {
+ checkArgument(
+ (moduleClass.getModifiers() & Modifier.STATIC) != 0,
+ "module must be static: %s",
+ moduleClass.getName());
+ }
+ }
}
diff --git a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
index 671f0fd5fe..fa501e6200 100644
--- a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
import com.google.common.truth.Truth;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -41,7 +42,7 @@ import com.google.gerrit.mail.EmailHeader.AddressList;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.testing.FakeEmailSender;
import com.google.gerrit.testing.FakeEmailSender.Message;
-import java.io.IOException;
+import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -54,9 +55,11 @@ import org.junit.After;
import org.junit.Before;
public abstract class AbstractNotificationTest extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Before
public void enableReviewerByEmail() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(conf);
@@ -82,7 +85,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
if (record) {
accountsModifyingEmailStrategy.add(account);
}
- setApiUser(account);
+ requestScopeOperations.setApiUser(account.id());
GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
prefs.emailStrategy = strategy;
gApi.accounts().self().setPreferences(prefs);
@@ -99,9 +102,10 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
super(failureMetadata, target);
}
- public FakeEmailSenderSubject notSent() {
- if (actual().peekMessage() != null) {
- failWithoutActual(fact("expected message", "sent"));
+ public FakeEmailSenderSubject didNotSend() {
+ Message message = actual().peekMessage();
+ if (message != null) {
+ failWithoutActual(fact("expected no message", message));
}
return this;
}
@@ -127,7 +131,13 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
}
EmailHeader header = message.headers().get("X-Gerrit-MessageType");
if (!header.equals(new EmailHeader.String(messageType))) {
- failWithoutActual(fact("expected message of type", messageType));
+ failWithoutActual(
+ fact("expected message of type", messageType),
+ fact(
+ "actual",
+ header instanceof EmailHeader.String
+ ? ((EmailHeader.String) header).getString()
+ : header));
}
// Return a named subject that displays a human-readable table of
@@ -203,7 +213,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
public FakeEmailSenderSubject noOneElse() {
for (Map.Entry<NotifyType, TestAccount> watchEntry : users.watchers.entrySet()) {
- if (!accountedFor.contains(watchEntry.getValue().email)) {
+ if (!accountedFor.contains(watchEntry.getValue().email())) {
notTo(watchEntry.getKey());
}
}
@@ -256,7 +266,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
}
private void rcpt(@Nullable RecipientType type, TestAccount account) {
- rcpt(type, account.email);
+ rcpt(type, account.email());
}
public FakeEmailSenderSubject to(NotifyType... watches) {
@@ -325,8 +335,8 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
return description.getClassName();
}
- private TestAccount evictAndCopy(TestAccount account) throws IOException {
- evictAndReindexAccount(account.id);
+ private TestAccount evictAndCopy(TestAccount account) {
+ evictAndReindexAccount(account.id());
return account;
}
@@ -355,7 +365,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
assignee = testAccount("assignee");
watchingProjectOwner = testAccount("watchingProjectOwner", "Administrators");
- setApiUser(watchingProjectOwner);
+ requestScopeOperations.setApiUser(watchingProjectOwner.id());
watch(allProjects.get(), pwi -> pwi.notifyNewChanges = true);
for (NotifyType watch : NotifyType.values()) {
@@ -363,7 +373,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
continue;
}
TestAccount watcher = testAccount(watch.toString());
- setApiUser(watcher);
+ requestScopeOperations.setApiUser(watcher.id());
watch(
allProjects.get(),
pwi -> {
@@ -394,20 +404,20 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
public TestAccount testAccount(String name) throws Exception {
String username = name(name);
TestAccount account = accountCreator.create(username, email(username), name);
- accountsByEmail.put(account.email, account);
+ accountsByEmail.put(account.email(), account);
return account;
}
public TestAccount testAccount(String name, String groupName) throws Exception {
String username = name(name);
TestAccount account = accountCreator.create(username, email(username), name, groupName);
- accountsByEmail.put(account.email, account);
+ accountsByEmail.put(account.email(), account);
return account;
}
String emailToName(String email) {
if (accountsByEmail.containsKey(email)) {
- return accountsByEmail.get(email).fullName;
+ return accountsByEmail.get(email).fullName();
}
return email;
}
@@ -415,9 +425,9 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
protected void addReviewers(PushOneCommit.Result r) throws Exception {
ReviewInput in =
ReviewInput.noScore()
- .reviewer(reviewer.email)
+ .reviewer(reviewer.email())
.reviewer(REVIEWER_BY_EMAIL)
- .reviewer(ccer.email, ReviewerState.CC, false)
+ .reviewer(ccer.email(), ReviewerState.CC, false)
.reviewer(CC_BY_EMAIL, ReviewerState.CC, false);
ReviewResult result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
supportReviewersByEmail = true;
@@ -425,8 +435,8 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
supportReviewersByEmail = false;
in =
ReviewInput.noScore()
- .reviewer(reviewer.email)
- .reviewer(ccer.email, ReviewerState.CC, false);
+ .reviewer(reviewer.email())
+ .reviewer(ccer.email(), ReviewerState.CC, false);
result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
}
Truth.assertThat(result.reviewers.values().stream().allMatch(v -> v.error == null)).isTrue();
@@ -456,9 +466,9 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
if (pushOptions != null) {
ref = ref + '%' + Joiner.on(',').join(pushOptions);
}
- setApiUser(owner);
+ requestScopeOperations.setApiUser(owner.id());
repo = cloneProject(project, owner);
- PushOneCommit push = pushFactory.create(db, owner.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(owner.newIdent(), repo);
result = push.to(ref);
result.assertOkStatus();
changeId = result.getChangeId();
@@ -478,33 +488,38 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
StagedChange(String ref) throws Exception {
super(ref);
- setApiUser(starrer);
+ requestScopeOperations.setApiUser(starrer.id());
gApi.accounts().self().starChange(result.getChangeId());
- setApiUser(owner);
+ requestScopeOperations.setApiUser(owner.id());
addReviewers(result);
sender.clear();
}
}
protected StagedChange stageReviewableChange() throws Exception {
- return new StagedChange("refs/for/master");
+ StagedChange sc = new StagedChange("refs/for/master");
+ sender.clear();
+ return sc;
}
protected StagedChange stageWipChange() throws Exception {
- return new StagedChange("refs/for/master%wip");
+ StagedChange sc = new StagedChange("refs/for/master%wip");
+ sender.clear();
+ return sc;
}
protected StagedChange stageReviewableWipChange() throws Exception {
StagedChange sc = stageReviewableChange();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
gApi.changes().id(sc.changeId).setWorkInProgress();
+ sender.clear();
return sc;
}
protected StagedChange stageAbandonedReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
gApi.changes().id(sc.changeId).abandon();
sender.clear();
return sc;
@@ -512,7 +527,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
protected StagedChange stageAbandonedReviewableWipChange() throws Exception {
StagedChange sc = stageReviewableWipChange();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
gApi.changes().id(sc.changeId).abandon();
sender.clear();
return sc;
@@ -520,7 +535,7 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
protected StagedChange stageAbandonedWipChange() throws Exception {
StagedChange sc = stageWipChange();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
gApi.changes().id(sc.changeId).abandon();
sender.clear();
return sc;
diff --git a/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
new file mode 100644
index 0000000000..ccd30ab5a4
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/AbstractPluginFieldsTest.java
@@ -0,0 +1,201 @@
+// 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 com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+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.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;
+import com.google.gerrit.server.restapi.change.QueryChanges;
+import com.google.gerrit.sshd.commands.Query;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.AbstractModule;
+import com.google.inject.Module;
+import java.util.List;
+import java.util.Objects;
+import org.kohsuke.args4j.Option;
+
+public class AbstractPluginFieldsTest extends AbstractDaemonTest {
+ protected static class MyInfo extends PluginDefinedInfo {
+ @Nullable String theAttribute;
+
+ public MyInfo(@Nullable String theAttribute) {
+ this.theAttribute = theAttribute;
+ }
+
+ MyInfo(String name, @Nullable String theAttribute) {
+ this.name = requireNonNull(name);
+ this.theAttribute = theAttribute;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof MyInfo)) {
+ return false;
+ }
+ MyInfo i = (MyInfo) o;
+ return Objects.equals(name, i.name) && Objects.equals(theAttribute, i.theAttribute);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, theAttribute);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("name", name)
+ .add("theAttribute", theAttribute)
+ .toString();
+ }
+ }
+
+ protected static class NullAttributeModule extends AbstractModule {
+ @Override
+ public void configure() {
+ DynamicSet.bind(binder(), ChangeAttributeFactory.class).toInstance((cd, bp, p) -> null);
+ }
+ }
+
+ protected static class SimpleAttributeModule extends AbstractModule {
+ @Override
+ public void configure() {
+ DynamicSet.bind(binder(), ChangeAttributeFactory.class)
+ .toInstance((cd, bp, p) -> new MyInfo("change " + cd.getId()));
+ }
+ }
+
+ private static class MyOptions implements DynamicBean {
+ @Option(name = "--opt")
+ private String opt;
+ }
+
+ protected static class OptionAttributeModule extends AbstractModule {
+ @Override
+ public void configure() {
+ DynamicSet.bind(binder(), ChangeAttributeFactory.class)
+ .toInstance(
+ (cd, bp, p) -> {
+ MyOptions opts = (MyOptions) bp.getDynamicBean(p);
+ return opts != null ? new MyInfo("opt " + opts.opt) : null;
+ });
+ bind(DynamicBean.class).annotatedWith(Exports.named(Query.class)).to(MyOptions.class);
+ bind(DynamicBean.class).annotatedWith(Exports.named(QueryChanges.class)).to(MyOptions.class);
+ bind(DynamicBean.class).annotatedWith(Exports.named(GetChange.class)).to(MyOptions.class);
+ }
+ }
+
+ protected void getChangeWithNullAttribute(PluginInfoGetter getter) throws Exception {
+ Change.Id id = createChange().getChange().getId();
+ assertThat(getter.call(id)).isNull();
+
+ try (AutoCloseable ignored = installPlugin("my-plugin", NullAttributeModule.class)) {
+ assertThat(getter.call(id)).isNull();
+ }
+
+ assertThat(getter.call(id)).isNull();
+ }
+
+ protected void getChangeWithSimpleAttribute(PluginInfoGetter getter) throws Exception {
+ getChangeWithSimpleAttribute(getter, SimpleAttributeModule.class);
+ }
+
+ protected void getChangeWithSimpleAttribute(
+ PluginInfoGetter getter, Class<? extends Module> moduleClass) throws Exception {
+ Change.Id id = createChange().getChange().getId();
+ assertThat(getter.call(id)).isNull();
+
+ try (AutoCloseable ignored = installPlugin("my-plugin", moduleClass)) {
+ assertThat(getter.call(id)).containsExactly(new MyInfo("my-plugin", "change " + id));
+ }
+
+ assertThat(getter.call(id)).isNull();
+ }
+
+ protected void getChangeWithOption(
+ PluginInfoGetter getterWithoutOptions, PluginInfoGetterWithOptions getterWithOptions)
+ throws Exception {
+ Change.Id id = createChange().getChange().getId();
+ assertThat(getterWithoutOptions.call(id)).isNull();
+
+ try (AutoCloseable ignored = installPlugin("my-plugin", OptionAttributeModule.class)) {
+ assertThat(getterWithoutOptions.call(id))
+ .containsExactly(new MyInfo("my-plugin", "opt null"));
+ assertThat(getterWithOptions.call(id, ImmutableListMultimap.of("my-plugin--opt", "foo")))
+ .containsExactly(new MyInfo("my-plugin", "opt foo"));
+ }
+
+ assertThat(getterWithoutOptions.call(id)).isNull();
+ }
+
+ protected static List<MyInfo> pluginInfoFromSingletonList(List<ChangeInfo> changeInfos) {
+ assertThat(changeInfos).hasSize(1);
+ return pluginInfoFromChangeInfo(changeInfos.get(0));
+ }
+
+ protected static List<MyInfo> pluginInfoFromChangeInfo(ChangeInfo changeInfo) {
+ List<PluginDefinedInfo> pluginInfo = changeInfo.plugins;
+ if (pluginInfo == null) {
+ return null;
+ }
+ return pluginInfo.stream().map(MyInfo.class::cast).collect(toImmutableList());
+ }
+
+ /**
+ * Decode {@code MyInfo}s from a raw list of maps returned from Gson.
+ *
+ * <p>This method is used instead of decoding {@code ChangeInfo} or {@code ChangAttribute}, since
+ * Gson would decode the {@code plugins} field as a {@code List<PluginDefinedInfo>}, which would
+ * return the base type and silently ignore any fields that are defined only in the subclass.
+ * Instead, decode the enclosing {@code ChangeInfo} or {@code ChangeAttribute} as a raw {@code
+ * Map<String, Object>}, and pass the {@code "plugins"} value to this method.
+ *
+ * @param gson Gson converter.
+ * @param plugins list of {@code MyInfo} objects, each as a raw map returned from Gson.
+ * @return decoded list of {@code MyInfo}s.
+ */
+ protected static List<MyInfo> decodeRawPluginsList(Gson gson, @Nullable Object plugins) {
+ if (plugins == null) {
+ return null;
+ }
+ checkArgument(plugins instanceof List, "not a list: %s", plugins);
+ return gson.fromJson(gson.toJson(plugins), new TypeToken<List<MyInfo>>() {}.getType());
+ }
+
+ @FunctionalInterface
+ protected interface PluginInfoGetter {
+ List<MyInfo> call(Change.Id id) throws Exception;
+ }
+
+ @FunctionalInterface
+ protected interface PluginInfoGetterWithOptions {
+ List<MyInfo> call(Change.Id id, ImmutableListMultimap<String, String> pluginOptions)
+ throws Exception;
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java b/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
index 9a3e811157..50536d83cf 100644
--- a/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
+++ b/java/com/google/gerrit/acceptance/AcceptanceTestRequestScope.java
@@ -14,22 +14,17 @@
package com.google.gerrit.acceptance;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.RequestCleanup;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.DisabledReviewDb;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
import com.google.inject.Scope;
-import com.google.inject.util.Providers;
import java.util.HashMap;
import java.util.Map;
@@ -37,13 +32,9 @@ import java.util.Map;
public class AcceptanceTestRequestScope {
private static final Key<RequestCleanup> RC_KEY = Key.get(RequestCleanup.class);
- private static final Key<RequestScopedReviewDbProvider> DB_KEY =
- Key.get(RequestScopedReviewDbProvider.class);
-
public static class Context implements RequestContext {
private final RequestCleanup cleanup = new RequestCleanup();
private final Map<Key<?>, Object> map = new HashMap<>();
- private final SchemaFactory<ReviewDb> schemaFactory;
private final SshSession session;
private final CurrentUser user;
@@ -51,22 +42,20 @@ public class AcceptanceTestRequestScope {
volatile long started;
volatile long finished;
- private Context(SchemaFactory<ReviewDb> sf, SshSession s, CurrentUser u, long at) {
- schemaFactory = sf;
+ private Context(SshSession s, CurrentUser u, long at) {
session = s;
user = u;
created = started = finished = at;
map.put(RC_KEY, cleanup);
- map.put(DB_KEY, new RequestScopedReviewDbProvider(schemaFactory, Providers.of(cleanup)));
}
private Context(Context p, SshSession s, CurrentUser c) {
- this(p.schemaFactory, s, c, p.created);
+ this(s, c, p.created);
started = p.started;
finished = p.finished;
}
- SshSession getSession() {
+ public SshSession getSession() {
return session;
}
@@ -78,11 +67,6 @@ public class AcceptanceTestRequestScope {
return user;
}
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return (RequestScopedReviewDbProvider) map.get(DB_KEY);
- }
-
synchronized <T> T get(Key<T> key, Provider<T> creator) {
@SuppressWarnings("unchecked")
T t = (T) map.get(key);
@@ -112,11 +96,8 @@ public class AcceptanceTestRequestScope {
private final AcceptanceTestRequestScope atrScope;
@Inject
- Propagator(
- AcceptanceTestRequestScope atrScope,
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
- super(REQUEST, current, local, dbProviderProvider);
+ Propagator(AcceptanceTestRequestScope atrScope, ThreadLocalRequestContext local) {
+ super(REQUEST, current, local);
this.atrScope = atrScope;
}
@@ -145,8 +126,8 @@ public class AcceptanceTestRequestScope {
this.local = local;
}
- public Context newContext(SchemaFactory<ReviewDb> sf, SshSession s, CurrentUser user) {
- return new Context(sf, s, user, TimeUtil.nowMs());
+ public Context newContext(SshSession s, CurrentUser user) {
+ return new Context(s, user, TimeUtil.nowMs());
}
private Context newContinuingContext(Context ctx) {
@@ -164,23 +145,18 @@ public class AcceptanceTestRequestScope {
return current.get();
}
- public Context disableDb() {
+ /**
+ * Disables read and write access to NoteDb and returns the context prior to that modification.
+ */
+ public Context disableNoteDb() {
Context old = current.get();
- SchemaFactory<ReviewDb> sf = DisabledReviewDb::new;
- Context ctx = new Context(sf, old.session, old.user, old.created);
+ Context ctx = new Context(old.session, old.user, old.created);
current.set(ctx);
local.setContext(ctx);
return old;
}
- public Context reopenDb() {
- // Setting a new context with the same fields is enough to get the ReviewDb
- // provider to reopen the database.
- Context old = current.get();
- return set(new Context(old.schemaFactory, old.session, old.user, old.created));
- }
-
/** Returns exactly one instance per command executed. */
static final Scope REQUEST =
new Scope() {
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index b8a8776d26..aeae2c2cfd 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -20,10 +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.common.errors.NoSuchGroupException;
+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.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.GroupCache;
@@ -31,7 +30,7 @@ import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -108,7 +107,7 @@ public class AccountCreator {
}
}
- account = new TestAccount(id, username, email, fullName, httpPass);
+ account = TestAccount.create(id, username, email, fullName, httpPass);
if (username != null) {
accounts.put(username, account);
}
@@ -149,7 +148,7 @@ public class AccountCreator {
}
public void evict(Collection<Account.Id> ids) {
- accounts.values().removeIf(a -> ids.contains(a.id));
+ accounts.values().removeIf(a -> ids.contains(a.id()));
}
public ImmutableList<TestAccount> getAll() {
@@ -157,7 +156,7 @@ public class AccountCreator {
}
private void addGroupMember(AccountGroup.UUID groupUuid, Account.Id accountId)
- throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+ throws IOException, NoSuchGroupException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.union(memberIds, ImmutableSet.of(accountId)))
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index 55d38d88f2..25d8df1ae7 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -19,6 +19,7 @@ java_library(
"//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",
@@ -28,6 +29,7 @@ java_library(
"//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",
@@ -36,12 +38,10 @@ java_library(
"//lib:args4j",
"//lib:gson",
"//lib:guava-retrying",
- "//lib:gwtjsonrpc",
- "//lib:gwtorm",
"//lib:h2",
"//lib:jimfs",
"//lib:jsch",
- "//lib:servlet-api-3_1-without-neverlink",
+ "//lib:servlet-api-without-neverlink",
"//lib/bouncycastle:bcpg",
"//lib/bouncycastle:bcprov",
"//lib/commons:compress",
@@ -68,6 +68,7 @@ java_library2(
testonly = True,
srcs = glob(["**/*.java"]),
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",
@@ -83,11 +84,13 @@ java_library2(
"//lib:jimfs",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
+ "//lib/flogger: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",
@@ -100,20 +103,24 @@ java_library2(
"//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:gwtorm",
"//lib:jsch",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
+ "//lib/commons:lang",
"//lib/greenmail",
"//lib/guice",
"//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java b/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
index 0aa56cf117..0a1d76535f 100644
--- a/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
+++ b/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
@@ -14,10 +14,10 @@
package com.google.gerrit.acceptance;
+import com.google.auto.value.AutoAnnotation;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
-import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -45,32 +45,13 @@ class ConfigAnnotationParser {
return cfg;
}
- static class GlobalPluginConfigToGerritConfig implements GerritConfig {
- private final GlobalPluginConfig delegate;
-
- GlobalPluginConfigToGerritConfig(GlobalPluginConfig delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return delegate.annotationType();
- }
-
- @Override
- public String name() {
- return delegate.name();
- }
-
- @Override
- public String value() {
- return delegate.value();
- }
+ private static GerritConfig toGerritConfig(GlobalPluginConfig annotation) {
+ return newGerritConfig(annotation.name(), annotation.value(), annotation.values());
+ }
- @Override
- public String[] values() {
- return delegate.values();
- }
+ @AutoAnnotation
+ private static GerritConfig newGerritConfig(String name, String value, String[] values) {
+ return new AutoAnnotation_ConfigAnnotationParser_newGerritConfig(name, value, values);
}
static Map<String, Config> parse(GlobalPluginConfig annotation) {
@@ -79,7 +60,7 @@ class ConfigAnnotationParser {
}
Map<String, Config> result = new HashMap<>();
Config cfg = new Config();
- parseAnnotation(cfg, new GlobalPluginConfigToGerritConfig(annotation));
+ parseAnnotation(cfg, toGerritConfig(annotation));
result.put(annotation.pluginName(), cfg);
return result;
}
@@ -100,7 +81,7 @@ class ConfigAnnotationParser {
config = new Config();
result.put(pluginName, config);
}
- parseAnnotation(config, new GlobalPluginConfigToGerritConfig(c));
+ parseAnnotation(config, toGerritConfig(c));
}
return result;
diff --git a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
index 0d473afcc9..a32c6d13d4 100644
--- a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
@@ -20,10 +20,8 @@ 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.reviewdb.client.Change.Id;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.query.change.ChangeData;
-import java.io.IOException;
import java.util.Optional;
/**
@@ -54,17 +52,17 @@ public class DisabledChangeIndex implements ChangeIndex {
}
@Override
- public void replace(ChangeData obj) throws IOException {
+ public void replace(ChangeData obj) {
throw new UnsupportedOperationException("ChangeIndex is disabled");
}
@Override
- public void delete(Id key) throws IOException {
+ public void delete(Change.Id key) {
throw new UnsupportedOperationException("ChangeIndex is disabled");
}
@Override
- public void deleteAll() throws IOException {
+ public void deleteAll() {
throw new UnsupportedOperationException("ChangeIndex is disabled");
}
@@ -75,12 +73,12 @@ public class DisabledChangeIndex implements ChangeIndex {
}
@Override
- public void markReady(boolean ready) throws IOException {
+ public void markReady(boolean ready) {
throw new UnsupportedOperationException("ChangeIndex is disabled");
}
@Override
- public Optional<ChangeData> get(Change.Id key, QueryOptions opts) throws IOException {
+ public Optional<ChangeData> get(Change.Id key, QueryOptions opts) {
throw new UnsupportedOperationException("ChangeIndex is disabled");
}
}
diff --git a/java/com/google/gerrit/acceptance/EventRecorder.java b/java/com/google/gerrit/acceptance/EventRecorder.java
index fda77d267c..cab6b58b37 100644
--- a/java/com/google/gerrit/acceptance/EventRecorder.java
+++ b/java/com/google/gerrit/acceptance/EventRecorder.java
@@ -56,7 +56,7 @@ public class EventRecorder {
}
public EventRecorder create(TestAccount user) {
- return new EventRecorder(eventListeners, userFactory.create(user.id));
+ return new EventRecorder(eventListeners, userFactory.create(user.id()));
}
}
diff --git a/java/com/google/gerrit/acceptance/FakeGroupAuditService.java b/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
new file mode 100644
index 0000000000..48dc4083dd
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/FakeGroupAuditService.java
@@ -0,0 +1,95 @@
+// 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;
+
+import static java.util.Comparator.comparing;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Ints;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.httpd.GitOverHttpServlet;
+import com.google.gerrit.server.AuditEvent;
+import com.google.gerrit.server.audit.AuditListener;
+import com.google.gerrit.server.audit.AuditService;
+import com.google.gerrit.server.audit.HttpAuditEvent;
+import com.google.gerrit.server.audit.group.GroupAuditListener;
+import com.google.gerrit.server.group.GroupAuditService;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Singleton
+public class FakeGroupAuditService extends AuditService {
+ public static class Module extends AbstractModule {
+ @Override
+ public void configure() {
+ DynamicSet.setOf(binder(), GroupAuditListener.class);
+ DynamicSet.setOf(binder(), AuditListener.class);
+
+ // Use this fake service at the Guice level rather than depending on tests binding their own
+ // audit listeners. If we used per-test listeners, then there would be a race between
+ // dispatching the audit events from HTTP requests performed during test setup in
+ // AbstractDaemonTest, and the later test setup binding the audit listener. Using a separate
+ // audit service implementation ensures all events get recorded.
+ bind(GroupAuditService.class).to(FakeGroupAuditService.class);
+ }
+ }
+
+ private final GitOverHttpServlet.Metrics httpMetrics;
+ private final BlockingQueue<HttpAuditEvent> httpEvents;
+ private final AtomicLong drainedSoFar;
+
+ @Inject
+ FakeGroupAuditService(
+ PluginSetContext<AuditListener> auditListeners,
+ PluginSetContext<GroupAuditListener> groupAuditListeners,
+ GitOverHttpServlet.Metrics httpMetrics) {
+ super(auditListeners, groupAuditListeners);
+ this.httpMetrics = httpMetrics;
+ this.httpEvents = new LinkedBlockingQueue<>();
+ this.drainedSoFar = new AtomicLong();
+ }
+
+ @Override
+ public void dispatch(AuditEvent action) {
+ super.dispatch(action);
+ if (action instanceof HttpAuditEvent) {
+ httpEvents.add((HttpAuditEvent) action);
+ }
+ }
+
+ public ImmutableList<HttpAuditEvent> drainHttpAuditEvents() throws Exception {
+ // Assumes that all HttpAuditEvents are produced by GitOverHttpServlet.
+ int expectedSize = Ints.checkedCast(httpMetrics.getRequestsStarted() - drainedSoFar.get());
+ List<HttpAuditEvent> result = new ArrayList<>();
+ for (int i = 0; i < expectedSize; i++) {
+ HttpAuditEvent e = httpEvents.poll(30, SECONDS);
+ if (e == null) {
+ throw new AssertionError(
+ String.format("Timeout after receiving %d/%d audit events", i, expectedSize));
+ }
+ drainedSoFar.incrementAndGet();
+ result.add(e);
+ }
+ return ImmutableList.sortedCopyOf(comparing(e -> e.when), result);
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/GcAssert.java b/java/com/google/gerrit/acceptance/GcAssert.java
index 7f90c3a90c..b9ef629df1 100644
--- a/java/com/google/gerrit/acceptance/GcAssert.java
+++ b/java/com/google/gerrit/acceptance/GcAssert.java
@@ -20,7 +20,6 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import java.io.File;
-import java.io.FilenameFilter;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
@@ -53,13 +52,7 @@ public class GcAssert {
private String[] getPackFiles(Project.NameKey p) throws RepositoryNotFoundException, IOException {
try (Repository repo = repoManager.openRepository(p)) {
File packDir = new File(repo.getDirectory(), "objects/pack");
- return packDir.list(
- new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(".pack");
- }
- });
+ return packDir.list((dir, name) -> name.endsWith(".pack"));
}
}
}
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index ceada64bfc..bb828afece 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -16,6 +16,7 @@ package com.google.gerrit.acceptance;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
@@ -26,6 +27,10 @@ import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.AccountOperationsImpl;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.group.GroupOperationsImpl;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperationsImpl;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperationsImpl;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.lucene.LuceneIndexModule;
@@ -35,25 +40,23 @@ import com.google.gerrit.server.config.GerritRuntime;
import com.google.gerrit.server.config.GerritServerConfig;
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.ManualRequestContext;
-import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testing.FakeEmailSender;
-import com.google.gerrit.testing.FakeGroupAuditService;
-import com.google.gerrit.testing.InMemoryDatabase;
import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.NoteDbChecker;
-import com.google.gerrit.testing.NoteDbMode;
import com.google.gerrit.testing.SshMode;
-import com.google.gerrit.testing.TempFileUtil;
import com.google.gerrit.testing.TestLoggingActivator;
import com.google.inject.AbstractModule;
+import com.google.inject.BindingAnnotation;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -73,6 +76,7 @@ import java.util.stream.Stream;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.util.FS;
+import org.junit.rules.TemporaryFolder;
public class GerritServer implements AutoCloseable {
public static class StartupException extends Exception {
@@ -83,6 +87,11 @@ public class GerritServer implements AutoCloseable {
}
}
+ /** Marker on {@link InetSocketAddress} for test SSH server. */
+ @Retention(RUNTIME)
+ @BindingAnnotation
+ public @interface TestSshServerAddress {}
+
@AutoValue
public abstract static class Description {
public static Description forTestClass(
@@ -93,6 +102,7 @@ public class GerritServer implements AutoCloseable {
!has(UseLocalDisk.class, testDesc.getTestClass()) && !forceLocalDisk(),
!has(NoHttpd.class, testDesc.getTestClass()),
has(Sandboxed.class, testDesc.getTestClass()),
+ has(SkipProjectClone.class, testDesc.getTestClass()),
has(UseSsh.class, testDesc.getTestClass()),
null, // @GerritConfig is only valid on methods.
null, // @GerritConfigs is only valid on methods.
@@ -112,6 +122,8 @@ public class GerritServer implements AutoCloseable {
&& !has(NoHttpd.class, testDesc.getTestClass()),
testDesc.getAnnotation(Sandboxed.class) != null
|| has(Sandboxed.class, testDesc.getTestClass()),
+ testDesc.getAnnotation(SkipProjectClone.class) != null
+ || has(SkipProjectClone.class, testDesc.getTestClass()),
testDesc.getAnnotation(UseSsh.class) != null
|| has(UseSsh.class, testDesc.getTestClass()),
testDesc.getAnnotation(GerritConfig.class),
@@ -140,6 +152,8 @@ public class GerritServer implements AutoCloseable {
abstract boolean sandboxed();
+ abstract boolean skipProjectClone();
+
abstract boolean useSshAnnotation();
boolean useSsh() {
@@ -251,27 +265,32 @@ public class GerritServer implements AutoCloseable {
/**
* Initializes new Gerrit site and returns started server.
*
- * <p>A new temporary directory for the site will be created with {@link TempFileUtil}, even in
+ * <p>A new temporary directory for the site will be created with {@code temporaryFolder}, even in
* the server is otherwise configured in-memory. Closing the server stops the daemon but does not
- * delete the temporary directory. Callers may either get the directory with {@link
- * #getSitePath()} and delete it manually, or call {@link TempFileUtil#cleanup()}.
+ * delete the temporary directory..
*
+ * @param temporaryFolder helper rule for creating site directories.
* @param desc server description.
* @param baseConfig default config values; merged with config from {@code desc}.
* @param testSysModule additional Guice module to use.
+ * @param testSshModule additional Guice module to use.
* @return started server.
* @throws Exception
*/
public static GerritServer initAndStart(
- Description desc, Config baseConfig, @Nullable Module testSysModule) throws Exception {
- Path site = TempFileUtil.createTempDirectory().toPath();
+ TemporaryFolder temporaryFolder,
+ Description desc,
+ Config baseConfig,
+ @Nullable Module testSysModule,
+ @Nullable Module testSshModule)
+ throws Exception {
+ Path site = temporaryFolder.newFolder().toPath();
try {
if (!desc.memory()) {
init(desc, baseConfig, site);
}
- return start(desc, baseConfig, site, testSysModule, null, null);
+ return start(desc, baseConfig, site, testSysModule, testSshModule, null);
} catch (Exception e) {
- TempFileUtil.recursivelyDelete(site.toFile());
throw e;
}
}
@@ -286,10 +305,9 @@ public class GerritServer implements AutoCloseable {
* initialize this directory. Can be retrieved from the returned instance via {@link
* #getSitePath()}.
* @param testSysModule optional additional module to add to the system injector.
+ * @param testSshModule optional additional module to add to the ssh injector.
* @param inMemoryRepoManager {@link InMemoryRepositoryManager} that should be used if the site is
* started in memory
- * @param inMemoryDatabaseInstance {@link com.google.gerrit.testing.InMemoryDatabase.Instance}
- * that should be used if the site is started in memory
* @param additionalArgs additional command-line arguments for the daemon program; only allowed if
* the test is not in-memory.
* @return started server.
@@ -300,8 +318,8 @@ public class GerritServer implements AutoCloseable {
Config baseConfig,
Path site,
@Nullable Module testSysModule,
+ @Nullable Module testSshModule,
@Nullable InMemoryRepositoryManager inMemoryRepoManager,
- @Nullable InMemoryDatabase.Instance inMemoryDatabaseInstance,
String... additionalArgs)
throws Exception {
checkArgument(site != null, "site is required (even for in-memory server");
@@ -323,12 +341,14 @@ public class GerritServer implements AutoCloseable {
if (testSysModule != null) {
daemon.addAdditionalSysModuleForTesting(testSysModule);
}
+ if (testSshModule != null) {
+ daemon.addAdditionalSshModuleForTesting(testSshModule);
+ }
daemon.setEnableSshd(desc.useSsh());
if (desc.memory()) {
checkArgument(additionalArgs.length == 0, "cannot pass args to in-memory server");
- return startInMemory(
- desc, site, baseConfig, daemon, inMemoryRepoManager, inMemoryDatabaseInstance);
+ return startInMemory(desc, site, baseConfig, daemon, inMemoryRepoManager);
}
return startOnDisk(desc, site, daemon, serverStarted, additionalArgs);
}
@@ -338,8 +358,7 @@ public class GerritServer implements AutoCloseable {
Path site,
Config baseConfig,
Daemon daemon,
- @Nullable InMemoryRepositoryManager inMemoryRepoManager,
- @Nullable InMemoryDatabase.Instance inMemoryDatabaseInstance)
+ @Nullable InMemoryRepositoryManager inMemoryRepoManager)
throws Exception {
Config cfg = desc.buildConfig(baseConfig);
daemon.setSlave(isSlave(baseConfig) || cfg.getBoolean("container", "slave", false));
@@ -352,12 +371,13 @@ public class GerritServer implements AutoCloseable {
cfg.setBoolean("index", "lucene", "testInmemory", true);
cfg.setBoolean("index", null, "onlineUpgrade", false);
cfg.setString("gitweb", null, "cgi", "");
+ cfg.setString(
+ "accountPatchReviewDb", null, "url", JdbcAccountPatchReviewStore.TEST_IN_MEMORY_URL);
daemon.setEnableHttpd(desc.httpd());
daemon.setLuceneModule(LuceneIndexModule.singleVersionAllLatest(0, isSlave(baseConfig)));
daemon.setDatabaseForTesting(
- ImmutableList.<Module>of(
- new InMemoryTestingDatabaseModule(
- cfg, site, inMemoryRepoManager, inMemoryDatabaseInstance),
+ ImmutableList.of(
+ new InMemoryTestingDatabaseModule(cfg, site, inMemoryRepoManager),
new AbstractModule() {
@Override
protected void configure() {
@@ -433,8 +453,6 @@ public class GerritServer implements AutoCloseable {
cfg.setInt("receive", null, "threadPoolSize", 1);
cfg.setInt("index", null, "threads", 1);
cfg.setBoolean("index", null, "reindexAfterRefUpdate", false);
-
- NoteDbMode.newNotesMigrationFromEnv().setConfigValues(cfg);
}
private static Injector createTestInjector(Daemon daemon) throws Exception {
@@ -447,12 +465,26 @@ public class GerritServer implements AutoCloseable {
bind(AccountCreator.class);
bind(AccountOperations.class).to(AccountOperationsImpl.class);
bind(GroupOperations.class).to(GroupOperationsImpl.class);
+ bind(ProjectOperations.class).to(ProjectOperationsImpl.class);
+ bind(RequestScopeOperations.class).to(RequestScopeOperationsImpl.class);
factory(PushOneCommit.Factory.class);
install(InProcessProtocol.module());
install(new NoSshModule());
install(new AsyncReceiveCommits.Module());
factory(ProjectResetter.Builder.Factory.class);
}
+
+ @Provides
+ @Singleton
+ @Nullable
+ @TestSshServerAddress
+ InetSocketAddress getSshAddress(@GerritServerConfig Config cfg) {
+ String addr = cfg.getString("sshd", null, "listenAddress");
+ // We do not use InitSshd.isOff to avoid coupling GerritServer to the SSH code.
+ return !"off".equalsIgnoreCase(addr)
+ ? SocketUtil.resolve(cfg.getString("sshd", null, "listenAddress"), 0)
+ : null;
+ }
};
return sysInjector.createChildInjector(module);
}
@@ -477,7 +509,6 @@ public class GerritServer implements AutoCloseable {
private ExecutorService daemonService;
private Injector testInjector;
private String url;
- private InetSocketAddress sshdAddress;
private InetSocketAddress httpAddress;
private GerritServer(
@@ -495,12 +526,6 @@ public class GerritServer implements AutoCloseable {
Config cfg = testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
url = cfg.getString("gerrit", null, "canonicalWebUrl");
URI uri = URI.create(url);
-
- String addr = cfg.getString("sshd", null, "listenAddress");
- // We do not use InitSshd.isOff to avoid coupling GerritServer to the SSH code.
- if (!"off".equalsIgnoreCase(addr)) {
- sshdAddress = SocketUtil.resolve(cfg.getString("sshd", null, "listenAddress"), 0);
- }
httpAddress = new InetSocketAddress(uri.getHost(), uri.getPort());
}
@@ -508,10 +533,6 @@ public class GerritServer implements AutoCloseable {
return url;
}
- public InetSocketAddress getSshdAddress() {
- return sshdAddress;
- }
-
InetSocketAddress getHttpAddress() {
return httpAddress;
}
@@ -537,21 +558,26 @@ public class GerritServer implements AutoCloseable {
inMemoryRepoManager = server.testInjector.getInstance(InMemoryRepositoryManager.class);
}
- InMemoryDatabase.Instance dbInstance = null;
- if (hasBinding(server.testInjector, InMemoryDatabase.class)) {
- InMemoryDatabase inMemoryDatabase = server.testInjector.getInstance(InMemoryDatabase.class);
- dbInstance = inMemoryDatabase.getDbInstance();
- dbInstance.setKeepOpen(true);
- }
- try {
- server.close();
- server.daemon.stop();
- return start(server.desc, cfg, site, null, inMemoryRepoManager, dbInstance);
- } finally {
- if (dbInstance != null) {
- dbInstance.setKeepOpen(false);
- }
+ server.close();
+ server.daemon.stop();
+ return start(server.desc, cfg, site, null, null, inMemoryRepoManager);
+ }
+
+ public static GerritServer restart(
+ GerritServer server, @Nullable Module testSysModule, @Nullable Module testSshModule)
+ throws Exception {
+ checkState(server.desc.sandboxed(), "restarting as slave requires @Sandboxed");
+ Config cfg = server.testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
+ Path site = server.testInjector.getInstance(Key.get(Path.class, SitePath.class));
+
+ InMemoryRepositoryManager inMemoryRepoManager = null;
+ if (hasBinding(server.testInjector, InMemoryRepositoryManager.class)) {
+ inMemoryRepoManager = server.testInjector.getInstance(InMemoryRepositoryManager.class);
}
+
+ server.close();
+ server.daemon.stop();
+ return start(server.desc, cfg, site, testSysModule, testSshModule, inMemoryRepoManager);
}
private static boolean hasBinding(Injector injector, Class<?> clazz) {
@@ -560,40 +586,19 @@ public class GerritServer implements AutoCloseable {
@Override
public void close() throws Exception {
- try {
- checkNoteDbState();
- } finally {
- daemon.getLifecycleManager().stop();
- if (daemonService != null) {
- System.out.println("Gerrit Server Shutdown");
- daemonService.shutdownNow();
- daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
- }
- RepositoryCache.clear();
+ daemon.getLifecycleManager().stop();
+ if (daemonService != null) {
+ System.out.println("Gerrit Server Shutdown");
+ daemonService.shutdownNow();
+ daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
+ RepositoryCache.clear();
}
public Path getSitePath() {
return sitePath;
}
- private void checkNoteDbState() throws Exception {
- NoteDbMode mode = NoteDbMode.get();
- if (mode != NoteDbMode.CHECK && mode != NoteDbMode.PRIMARY) {
- return;
- }
- NoteDbChecker checker = testInjector.getInstance(NoteDbChecker.class);
- OneOffRequestContext oneOffRequestContext =
- testInjector.getInstance(OneOffRequestContext.class);
- try (ManualRequestContext ctx = oneOffRequestContext.open()) {
- if (mode == NoteDbMode.CHECK) {
- checker.rebuildAndCheckAllChanges();
- } else if (mode == NoteDbMode.PRIMARY) {
- checker.assertNoReviewDbChanges(desc.testDescription());
- }
- }
- }
-
@Override
public String toString() {
return MoreObjects.toStringHelper(this).addValue(desc).toString();
diff --git a/java/com/google/gerrit/acceptance/HttpSession.java b/java/com/google/gerrit/acceptance/HttpSession.java
index fe446f4a40..833c53b4ff 100644
--- a/java/com/google/gerrit/acceptance/HttpSession.java
+++ b/java/com/google/gerrit/acceptance/HttpSession.java
@@ -19,8 +19,10 @@ import com.google.gerrit.common.Nullable;
import java.io.IOException;
import java.net.URI;
import org.apache.http.HttpHost;
+import org.apache.http.client.HttpClient;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
+import org.apache.http.impl.client.HttpClientBuilder;
public class HttpSession {
protected TestAccount account;
@@ -30,11 +32,12 @@ public class HttpSession {
public HttpSession(GerritServer server, @Nullable TestAccount account) {
this.url = CharMatcher.is('/').trimTrailingFrom(server.getUrl());
URI uri = URI.create(url);
- this.executor = Executor.newInstance();
+ HttpClient noRedirectClient = HttpClientBuilder.create().disableRedirectHandling().build();
+ this.executor = Executor.newInstance(noRedirectClient);
this.account = account;
if (account != null) {
executor.auth(
- new HttpHost(uri.getHost(), uri.getPort()), account.username, account.httpPassword);
+ new HttpHost(uri.getHost(), uri.getPort()), account.username(), account.httpPassword());
}
}
diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 6ee5c096e8..a704d2fc8b 100644
--- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -17,56 +17,38 @@ package com.google.gerrit.acceptance;
import static com.google.inject.Scopes.SINGLETON;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.config.TrackingFootersProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.gerrit.server.schema.NotesMigrationSchemaFactory;
-import com.google.gerrit.server.schema.ReviewDbFactory;
+import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.schema.SchemaModule;
-import com.google.gerrit.server.schema.SchemaVersion;
-import com.google.gerrit.testing.InMemoryDatabase;
-import com.google.gerrit.testing.InMemoryH2Type;
import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
-import com.google.inject.Key;
import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import com.google.inject.util.Providers;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
class InMemoryTestingDatabaseModule extends LifecycleModule {
private final Config cfg;
private final Path sitePath;
@Nullable private final InMemoryRepositoryManager repoManager;
- @Nullable private final InMemoryDatabase.Instance inMemoryDatabaseInstance;
InMemoryTestingDatabaseModule(
- Config cfg,
- Path sitePath,
- @Nullable InMemoryRepositoryManager repoManager,
- @Nullable InMemoryDatabase.Instance inMemoryDatabaseInstance) {
+ Config cfg, Path sitePath, @Nullable InMemoryRepositoryManager repoManager) {
this.cfg = cfg;
this.sitePath = sitePath;
this.repoManager = repoManager;
- this.inMemoryDatabaseInstance = inMemoryDatabaseInstance;
makeSiteDirs(sitePath);
}
@@ -85,49 +67,36 @@ class InMemoryTestingDatabaseModule extends LifecycleModule {
}
bind(MetricMaker.class).to(DisabledMetricMaker.class);
- bind(DataSourceType.class).to(InMemoryH2Type.class);
- install(new NotesMigration.Module());
- TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
- new TypeLiteral<SchemaFactory<ReviewDb>>() {};
- bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
- bind(InMemoryDatabase.Instance.class).toProvider(Providers.of(inMemoryDatabaseInstance));
- bind(Key.get(schemaFactory, ReviewDbFactory.class)).to(InMemoryDatabase.class);
- bind(InMemoryDatabase.class).in(SINGLETON);
- bind(ChangeBundleReader.class).to(GwtormChangeBundleReader.class);
-
- listener().to(CreateDatabase.class);
+ listener().to(CreateSchema.class);
bind(SitePaths.class);
bind(TrackingFooters.class).toProvider(TrackingFootersProvider.class).in(SINGLETON);
install(new SchemaModule());
- bind(SchemaVersion.class).to(SchemaVersion.C);
install(new SshdModule());
}
- static class CreateDatabase implements LifecycleListener {
- private final InMemoryDatabase mem;
+ static class CreateSchema implements LifecycleListener {
+ private final SchemaCreator schemaCreator;
@Inject
- CreateDatabase(InMemoryDatabase mem) {
- this.mem = mem;
+ CreateSchema(SchemaCreator schemaCreator) {
+ this.schemaCreator = schemaCreator;
}
@Override
public void start() {
try {
- mem.create();
- } catch (OrmException e) {
- throw new OrmRuntimeException(e);
+ schemaCreator.ensureCreated();
+ } catch (IOException | ConfigInvalidException e) {
+ throw new StorageException(e);
}
}
@Override
- public void stop() {
- mem.getDbInstance().drop();
- }
+ public void stop() {}
}
private static void makeSiteDirs(Path p) {
diff --git a/java/com/google/gerrit/acceptance/InProcessProtocol.java b/java/com/google/gerrit/acceptance/InProcessProtocol.java
index 94c4635d40..69715a9cf8 100644
--- a/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -14,6 +14,10 @@
package com.google.gerrit.acceptance;
+import static com.google.gerrit.server.git.receive.LazyPostReceiveHookChain.affectsSize;
+import static com.google.gerrit.server.quota.QuotaGroupDefinitions.REPOSITORY_SIZE_GROUP;
+
+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;
@@ -21,14 +25,12 @@ 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.reviewdb.server.ReviewDb;
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.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.TransferConfig;
@@ -39,13 +41,16 @@ 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.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.quota.QuotaBackend;
+import com.google.gerrit.server.quota.QuotaException;
+import com.google.gerrit.server.quota.QuotaResponse;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Key;
@@ -55,7 +60,6 @@ import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.Scope;
import com.google.inject.servlet.RequestScoped;
-import com.google.inject.util.Providers;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.HashMap;
@@ -122,10 +126,8 @@ class InProcessProtocol extends TestProtocol<Context> {
private static class Propagator extends ThreadLocalRequestScopePropagator<Context> {
@Inject
- Propagator(
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
- super(REQUEST, current, local, dbProviderProvider);
+ Propagator(ThreadLocalRequestContext local) {
+ super(REQUEST, current, local);
}
@Override
@@ -150,12 +152,9 @@ class InProcessProtocol extends TestProtocol<Context> {
* request.
*/
static class Context implements RequestContext {
- private static final Key<RequestScopedReviewDbProvider> DB_KEY =
- Key.get(RequestScopedReviewDbProvider.class);
private static final Key<RequestCleanup> RC_KEY = Key.get(RequestCleanup.class);
private static final Key<CurrentUser> USER_KEY = Key.get(CurrentUser.class);
- private final SchemaFactory<ReviewDb> schemaFactory;
private final IdentifiedUser.GenericFactory userFactory;
private final Account.Id accountId;
private final Project.NameKey project;
@@ -163,17 +162,12 @@ class InProcessProtocol extends TestProtocol<Context> {
private final Map<Key<?>, Object> map;
Context(
- SchemaFactory<ReviewDb> schemaFactory,
- IdentifiedUser.GenericFactory userFactory,
- Account.Id accountId,
- Project.NameKey project) {
- this.schemaFactory = schemaFactory;
+ IdentifiedUser.GenericFactory userFactory, Account.Id accountId, Project.NameKey project) {
this.userFactory = userFactory;
this.accountId = accountId;
this.project = project;
map = new HashMap<>();
cleanup = new RequestCleanup();
- map.put(DB_KEY, new RequestScopedReviewDbProvider(schemaFactory, Providers.of(cleanup)));
map.put(RC_KEY, cleanup);
IdentifiedUser user = userFactory.create(accountId);
@@ -182,7 +176,7 @@ class InProcessProtocol extends TestProtocol<Context> {
}
private Context newContinuingContext() {
- return new Context(schemaFactory, userFactory, accountId, project);
+ return new Context(userFactory, accountId, project);
}
@Override
@@ -190,11 +184,6 @@ class InProcessProtocol extends TestProtocol<Context> {
return get(USER_KEY, null);
}
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return get(DB_KEY, null);
- }
-
private synchronized <T> T get(Key<T> key, Provider<T> creator) {
@SuppressWarnings("unchecked")
T t = (T) map.get(key);
@@ -208,7 +197,7 @@ class InProcessProtocol extends TestProtocol<Context> {
private static class Upload implements UploadPackFactory<Context> {
private final TransferConfig transferConfig;
- private final DynamicSet<UploadPackInitializer> uploadPackInitializers;
+ private final PluginSetContext<UploadPackInitializer> uploadPackInitializers;
private final DynamicSet<PreUploadHook> preUploadHooks;
private final UploadValidators.Factory uploadValidatorsFactory;
private final ThreadLocalRequestContext threadContext;
@@ -218,7 +207,7 @@ class InProcessProtocol extends TestProtocol<Context> {
@Inject
Upload(
TransferConfig transferConfig,
- DynamicSet<UploadPackInitializer> uploadPackInitializers,
+ PluginSetContext<UploadPackInitializer> uploadPackInitializers,
DynamicSet<PreUploadHook> preUploadHooks,
UploadValidators.Factory uploadValidatorsFactory,
ThreadLocalRequestContext threadContext,
@@ -267,9 +256,7 @@ class InProcessProtocol extends TestProtocol<Context> {
List<PreUploadHook> hooks = Lists.newArrayList(preUploadHooks);
hooks.add(uploadValidatorsFactory.create(projectState.getProject(), repo, "localhost-test"));
up.setPreUploadHook(PreUploadHookChain.newChain(hooks));
- for (UploadPackInitializer initializer : uploadPackInitializers) {
- initializer.init(req.project, up);
- }
+ uploadPackInitializers.runEach(initializer -> initializer.init(req.project, up));
return up;
}
}
@@ -279,10 +266,11 @@ class InProcessProtocol extends TestProtocol<Context> {
private final ProjectCache projectCache;
private final AsyncReceiveCommits.Factory factory;
private final TransferConfig config;
- private final DynamicSet<ReceivePackInitializer> receivePackInitializers;
+ private final PluginSetContext<ReceivePackInitializer> receivePackInitializers;
private final DynamicSet<PostReceiveHook> postReceiveHooks;
private final ThreadLocalRequestContext threadContext;
private final PermissionBackend permissionBackend;
+ private final QuotaBackend quotaBackend;
@Inject
Receive(
@@ -290,10 +278,11 @@ class InProcessProtocol extends TestProtocol<Context> {
ProjectCache projectCache,
AsyncReceiveCommits.Factory factory,
TransferConfig config,
- DynamicSet<ReceivePackInitializer> receivePackInitializers,
+ PluginSetContext<ReceivePackInitializer> receivePackInitializers,
DynamicSet<PostReceiveHook> postReceiveHooks,
ThreadLocalRequestContext threadContext,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ QuotaBackend quotaBackend) {
this.userProvider = userProvider;
this.projectCache = projectCache;
this.factory = factory;
@@ -302,6 +291,7 @@ class InProcessProtocol extends TestProtocol<Context> {
this.postReceiveHooks = postReceiveHooks;
this.threadContext = threadContext;
this.permissionBackend = permissionBackend;
+ this.quotaBackend = quotaBackend;
}
@Override
@@ -339,13 +329,37 @@ class InProcessProtocol extends TestProtocol<Context> {
rp.setTimeout(config.getTimeout());
rp.setMaxObjectSizeLimit(config.getMaxObjectSizeLimit());
- for (ReceivePackInitializer initializer : receivePackInitializers) {
- initializer.init(projectState.getNameKey(), rp);
- }
+ receivePackInitializers.runEach(
+ initializer -> initializer.init(projectState.getNameKey(), rp));
+ QuotaResponse.Aggregated availableTokens =
+ quotaBackend
+ .user(identifiedUser)
+ .project(req.project)
+ .availableTokens(REPOSITORY_SIZE_GROUP);
+ availableTokens.throwOnError();
+ availableTokens.availableTokens().ifPresent(v -> rp.setMaxObjectSizeLimit(v));
- rp.setPostReceiveHook(PostReceiveHookChain.newChain(Lists.newArrayList(postReceiveHooks)));
+ ImmutableList<PostReceiveHook> hooks =
+ ImmutableList.<PostReceiveHook>builder()
+ .add(
+ (pack, commands) -> {
+ if (affectsSize(pack, commands)) {
+ try {
+ quotaBackend
+ .user(identifiedUser)
+ .project(req.project)
+ .requestTokens(REPOSITORY_SIZE_GROUP, pack.getPackSize())
+ .throwOnError();
+ } catch (QuotaException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ })
+ .addAll(postReceiveHooks)
+ .build();
+ rp.setPostReceiveHook(PostReceiveHookChain.newChain(hooks));
return rp;
- } catch (IOException | PermissionBackendException e) {
+ } catch (IOException | PermissionBackendException | QuotaException e) {
throw new RuntimeException(e);
}
}
diff --git a/java/com/google/gerrit/acceptance/ProjectResetter.java b/java/com/google/gerrit/acceptance/ProjectResetter.java
index cc263c68b6..ea958f661b 100644
--- a/java/com/google/gerrit/acceptance/ProjectResetter.java
+++ b/java/com/google/gerrit/acceptance/ProjectResetter.java
@@ -298,7 +298,7 @@ public class ProjectResetter implements AutoCloseable {
}
/** Evict projects for which the config was changed. */
- private void evictAndReindexProjects() throws IOException {
+ private void evictAndReindexProjects() {
if (projectCache == null) {
return;
}
@@ -353,7 +353,7 @@ public class ProjectResetter implements AutoCloseable {
}
/** Evict groups that were modified. */
- private void evictAndReindexGroups() throws IOException {
+ private void evictAndReindexGroups() {
if (groupCache != null || groupIndexer != null) {
Set<AccountGroup.UUID> modifiedGroups =
new HashSet<>(groupUUIDs(restoredRefsByProject.get(allUsersName)));
@@ -367,7 +367,7 @@ public class ProjectResetter implements AutoCloseable {
}
}
- private void evictAndReindexAccount(Account.Id accountId) throws IOException {
+ private void evictAndReindexAccount(Account.Id accountId) {
if (accountCache != null) {
accountCache.evict(accountId);
}
@@ -379,7 +379,7 @@ public class ProjectResetter implements AutoCloseable {
}
}
- private void evictAndReindexGroup(AccountGroup.UUID uuid) throws IOException {
+ private void evictAndReindexGroup(AccountGroup.UUID uuid) {
if (groupCache != null) {
groupCache.evict(uuid);
}
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index 5e45df24a8..e15dd40d57 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -16,7 +16,6 @@ package com.google.gerrit.acceptance;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
-import static java.util.stream.Collectors.toList;
import static org.junit.Assert.assertEquals;
import com.google.common.base.Strings;
@@ -28,14 +27,11 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -43,7 +39,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Stream;
import org.eclipse.jgit.api.TagCommand;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.PersonIdent;
@@ -77,16 +72,12 @@ public class PushOneCommit {
+ PATCH_FILE_ONLY;
public interface Factory {
- PushOneCommit create(ReviewDb db, PersonIdent i, TestRepository<?> testRepo);
+ PushOneCommit create(PersonIdent i, TestRepository<?> testRepo);
PushOneCommit create(
- ReviewDb db,
- PersonIdent i,
- TestRepository<?> testRepo,
- @Assisted("changeId") String changeId);
+ PersonIdent i, TestRepository<?> testRepo, @Assisted("changeId") String changeId);
PushOneCommit create(
- ReviewDb db,
PersonIdent i,
TestRepository<?> testRepo,
@Assisted("subject") String subject,
@@ -94,14 +85,12 @@ public class PushOneCommit {
@Assisted("content") String content);
PushOneCommit create(
- ReviewDb db,
PersonIdent i,
TestRepository<?> testRepo,
@Assisted String subject,
@Assisted Map<String, String> files);
PushOneCommit create(
- ReviewDb db,
PersonIdent i,
TestRepository<?> testRepo,
@Assisted("subject") String subject,
@@ -144,8 +133,6 @@ public class PushOneCommit {
private final ChangeNotes.Factory notesFactory;
private final ApprovalsUtil approvalsUtil;
private final Provider<InternalChangeQuery> queryProvider;
- private final NotesMigration notesMigration;
- private final ReviewDb db;
private final TestRepository<?> testRepo;
private final String subject;
@@ -162,22 +149,10 @@ public class PushOneCommit {
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
Provider<InternalChangeQuery> queryProvider,
- NotesMigration notesMigration,
- @Assisted ReviewDb db,
@Assisted PersonIdent i,
@Assisted TestRepository<?> testRepo)
throws Exception {
- this(
- notesFactory,
- approvalsUtil,
- queryProvider,
- notesMigration,
- db,
- i,
- testRepo,
- SUBJECT,
- FILE_NAME,
- FILE_CONTENT);
+ this(notesFactory, approvalsUtil, queryProvider, i, testRepo, SUBJECT, FILE_NAME, FILE_CONTENT);
}
@AssistedInject
@@ -185,8 +160,6 @@ public class PushOneCommit {
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
Provider<InternalChangeQuery> queryProvider,
- NotesMigration notesMigration,
- @Assisted ReviewDb db,
@Assisted PersonIdent i,
@Assisted TestRepository<?> testRepo,
@Assisted("changeId") String changeId)
@@ -195,8 +168,6 @@ public class PushOneCommit {
notesFactory,
approvalsUtil,
queryProvider,
- notesMigration,
- db,
i,
testRepo,
SUBJECT,
@@ -210,26 +181,13 @@ public class PushOneCommit {
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
Provider<InternalChangeQuery> queryProvider,
- NotesMigration notesMigration,
- @Assisted ReviewDb db,
@Assisted PersonIdent i,
@Assisted TestRepository<?> testRepo,
@Assisted("subject") String subject,
@Assisted("fileName") String fileName,
@Assisted("content") String content)
throws Exception {
- this(
- notesFactory,
- approvalsUtil,
- queryProvider,
- notesMigration,
- db,
- i,
- testRepo,
- subject,
- fileName,
- content,
- null);
+ this(notesFactory, approvalsUtil, queryProvider, i, testRepo, subject, fileName, content, null);
}
@AssistedInject
@@ -237,24 +195,12 @@ public class PushOneCommit {
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
Provider<InternalChangeQuery> queryProvider,
- NotesMigration notesMigration,
- @Assisted ReviewDb db,
@Assisted PersonIdent i,
@Assisted TestRepository<?> testRepo,
@Assisted String subject,
@Assisted Map<String, String> files)
throws Exception {
- this(
- notesFactory,
- approvalsUtil,
- queryProvider,
- notesMigration,
- db,
- i,
- testRepo,
- subject,
- files,
- null);
+ this(notesFactory, approvalsUtil, queryProvider, i, testRepo, subject, files, null);
}
@AssistedInject
@@ -262,8 +208,6 @@ public class PushOneCommit {
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
Provider<InternalChangeQuery> queryProvider,
- NotesMigration notesMigration,
- @Assisted ReviewDb db,
@Assisted PersonIdent i,
@Assisted TestRepository<?> testRepo,
@Assisted("subject") String subject,
@@ -275,8 +219,6 @@ public class PushOneCommit {
notesFactory,
approvalsUtil,
queryProvider,
- notesMigration,
- db,
i,
testRepo,
subject,
@@ -288,20 +230,16 @@ public class PushOneCommit {
ChangeNotes.Factory notesFactory,
ApprovalsUtil approvalsUtil,
Provider<InternalChangeQuery> queryProvider,
- NotesMigration notesMigration,
- ReviewDb db,
PersonIdent i,
TestRepository<?> testRepo,
String subject,
Map<String, String> files,
String changeId)
throws Exception {
- this.db = db;
this.testRepo = testRepo;
this.notesFactory = notesFactory;
this.approvalsUtil = approvalsUtil;
this.queryProvider = queryProvider;
- this.notesMigration = notesMigration;
this.subject = subject;
this.files = files;
this.changeId = changeId;
@@ -395,15 +333,15 @@ public class PushOneCommit {
this.resSubj = subject;
}
- public ChangeData getChange() throws OrmException {
+ public ChangeData getChange() {
return Iterables.getOnlyElement(queryProvider.get().byKeyPrefix(changeId));
}
- public PatchSet getPatchSet() throws OrmException {
+ public PatchSet getPatchSet() {
return getChange().currentPatchSet();
}
- public PatchSet.Id getPatchSetId() throws OrmException {
+ public PatchSet.Id getPatchSetId() {
return getChange().change().currentPatchSetId();
}
@@ -420,8 +358,7 @@ public class PushOneCommit {
}
public void assertChange(
- Change.Status expectedStatus, String expectedTopic, TestAccount... expectedReviewers)
- throws OrmException {
+ Change.Status expectedStatus, String expectedTopic, TestAccount... expectedReviewers) {
assertChange(
expectedStatus, expectedTopic, Arrays.asList(expectedReviewers), ImmutableList.of());
}
@@ -430,28 +367,19 @@ public class PushOneCommit {
Change.Status expectedStatus,
String expectedTopic,
List<TestAccount> expectedReviewers,
- List<TestAccount> expectedCcs)
- throws OrmException {
+ List<TestAccount> expectedCcs) {
Change c = getChange().change();
assertThat(c.getSubject()).isEqualTo(resSubj);
assertThat(c.getStatus()).isEqualTo(expectedStatus);
assertThat(Strings.emptyToNull(c.getTopic())).isEqualTo(expectedTopic);
- if (notesMigration.readChanges()) {
- assertReviewers(c, ReviewerStateInternal.REVIEWER, expectedReviewers);
- assertReviewers(c, ReviewerStateInternal.CC, expectedCcs);
- } else {
- assertReviewers(
- c,
- ReviewerStateInternal.REVIEWER,
- Stream.concat(expectedReviewers.stream(), expectedCcs.stream()).collect(toList()));
- }
+ assertReviewers(c, ReviewerStateInternal.REVIEWER, expectedReviewers);
+ assertReviewers(c, ReviewerStateInternal.CC, expectedCcs);
}
private void assertReviewers(
- Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers)
- throws OrmException {
+ Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers) {
Iterable<Account.Id> actualIds =
- approvalsUtil.getReviewers(db, notesFactory.createChecked(db, c)).byState(state);
+ approvalsUtil.getReviewers(notesFactory.createChecked(c)).byState(state);
assertThat(actualIds)
.containsExactlyElementsIn(Sets.newHashSet(TestAccount.ids(expectedReviewers)));
}
diff --git a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
index 5209f9065c..19910db042 100644
--- a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
@@ -19,10 +19,9 @@ 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.Id;
+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.io.IOException;
class ReadOnlyChangeIndex implements ChangeIndex {
private final ChangeIndex index;
@@ -46,17 +45,17 @@ class ReadOnlyChangeIndex implements ChangeIndex {
}
@Override
- public void replace(ChangeData obj) throws IOException {
+ public void replace(ChangeData obj) {
// do nothing
}
@Override
- public void delete(Id key) throws IOException {
+ public void delete(Change.Id key) {
// do nothing
}
@Override
- public void deleteAll() throws IOException {
+ public void deleteAll() {
// do nothing
}
@@ -67,7 +66,7 @@ class ReadOnlyChangeIndex implements ChangeIndex {
}
@Override
- public void markReady(boolean ready) throws IOException {
+ public void markReady(boolean ready) {
// do nothing
}
}
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
index 84e798c714..bd8a926bf8 100644
--- a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -63,15 +63,7 @@ public class ReindexGroupsAtStartup implements LifecycleListener {
throw new IllegalStateException("Unable to reindex groups, tests may fail", e);
}
- allGroupReferences.forEach(
- group -> {
- try {
- groupIndexer.index(group.getUUID());
- } catch (IOException e) {
- throw new IllegalStateException(
- String.format("Unable to index %s, tests may fail", group), e);
- }
- });
+ allGroupReferences.forEach(group -> groupIndexer.index(group.getUUID()));
}
@Override
diff --git a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
index f585f81810..2f0ffcb699 100644
--- a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
@@ -20,7 +20,6 @@ import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.Scopes;
-import java.io.IOException;
/** Reindex all projects at Gerrit daemon startup. */
public class ReindexProjectsAtStartup implements LifecycleListener {
@@ -42,16 +41,7 @@ public class ReindexProjectsAtStartup implements LifecycleListener {
@Override
public void start() {
- repoMgr.list().stream()
- .forEach(
- projectName -> {
- try {
- projectIndexer.index(projectName);
- } catch (IOException e) {
- throw new IllegalStateException(
- String.format("Unable to index %s, tests may fail", projectName), e);
- }
- });
+ repoMgr.list().stream().forEach(projectIndexer::index);
}
@Override
diff --git a/java/com/google/gerrit/acceptance/RestResponse.java b/java/com/google/gerrit/acceptance/RestResponse.java
index da082150e6..e8de5c6220 100644
--- a/java/com/google/gerrit/acceptance/RestResponse.java
+++ b/java/com/google/gerrit/acceptance/RestResponse.java
@@ -14,6 +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 com.google.gerrit.httpd.restapi.RestApiServlet.JSON_MAGIC;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -21,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
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 {
@@ -83,4 +85,9 @@ public class RestResponse extends HttpResponse {
public void assertPreconditionFailed() throws Exception {
assertStatus(HttpStatus.SC_PRECONDITION_FAILED);
}
+
+ public void assertTemporaryRedirect(String path) throws Exception {
+ assertStatus(HttpStatus.SC_MOVED_TEMPORARILY);
+ assertThat(URI.create(getHeader("Location")).getPath()).isEqualTo(path);
+ }
}
diff --git a/java/com/google/gerrit/acceptance/RestSession.java b/java/com/google/gerrit/acceptance/RestSession.java
index e77e527ee0..0e7ad4bc24 100644
--- a/java/com/google/gerrit/acceptance/RestSession.java
+++ b/java/com/google/gerrit/acceptance/RestSession.java
@@ -20,7 +20,7 @@ import static java.util.Objects.requireNonNull;
import com.google.common.net.HttpHeaders;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.RawInput;
-import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.json.OutputFormat;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.client.fluent.Request;
diff --git a/java/com/google/gerrit/acceptance/SkipProjectClone.java b/java/com/google/gerrit/acceptance/SkipProjectClone.java
new file mode 100644
index 0000000000..9a326d80dd
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/SkipProjectClone.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.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;
+
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+public @interface SkipProjectClone {}
diff --git a/java/com/google/gerrit/acceptance/SshSession.java b/java/com/google/gerrit/acceptance/SshSession.java
index 52d7f28f7d..fd60d16ed3 100644
--- a/java/com/google/gerrit/acceptance/SshSession.java
+++ b/java/com/google/gerrit/acceptance/SshSession.java
@@ -19,6 +19,7 @@ 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 com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
@@ -35,9 +36,9 @@ public class SshSession {
private Session session;
private String error;
- public SshSession(TestSshKeys sshKeys, GerritServer server, TestAccount account) {
+ public SshSession(TestSshKeys sshKeys, InetSocketAddress addr, TestAccount account) {
this.sshKeys = sshKeys;
- this.addr = server.getSshdAddress();
+ this.addr = addr;
this.account = account;
}
@@ -65,6 +66,22 @@ public class SshSession {
}
}
+ @SuppressWarnings("resource")
+ public int execAndReturnStatus(String command) throws Exception {
+ ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
+ try {
+ channel.setCommand(command);
+ InputStream err = channel.getErrStream();
+ channel.connect();
+
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
+ error = s.hasNext() ? s.next() : null;
+ return channel.getExitStatus();
+ } finally {
+ channel.disconnect();
+ }
+ }
+
public InputStream exec2(String command, InputStream opt) throws Exception {
ChannelExec channel = (ChannelExec) getSession().openChannel("exec");
channel.setCommand(command);
@@ -112,8 +129,14 @@ public class SshSession {
JSch jsch = new JSch();
jsch.addIdentity(
"KeyPair", TestSshKeys.privateKey(keyPair), keyPair.getPublicKeyBlob(), null);
- session =
- jsch.getSession(account.username, addr.getAddress().getHostAddress(), addr.getPort());
+ String username =
+ account
+ .username()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ "account " + account.accountId() + " must have a username to use SSH"));
+ session = jsch.getSession(username, addr.getAddress().getHostAddress(), addr.getPort());
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
}
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index 482a073af5..d20124ae7b 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -30,7 +30,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.util.ManualRequestContext;
@@ -39,7 +38,6 @@ import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Injector;
import com.google.inject.Module;
-import com.google.inject.Provider;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -70,7 +68,7 @@ public abstract class StandaloneSiteTest {
this.server = server;
Injector i = server.getTestInjector();
if (adminId == null) {
- adminId = i.getInstance(AccountCreator.class).admin().getId();
+ adminId = i.getInstance(AccountCreator.class).admin().id();
}
ctx = i.getInstance(OneOffRequestContext.class).openAs(adminId);
GerritApi gApi = i.getInstance(GerritApi.class);
@@ -91,19 +89,10 @@ public abstract class StandaloneSiteTest {
return ctx.getUser();
}
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return ctx.getReviewDbProvider();
- }
-
public Injector getInjector() {
return server.getTestInjector();
}
- public GerritServer getServer() {
- return server;
- }
-
@Override
public void close() throws Exception {
try {
@@ -120,10 +109,8 @@ public abstract class StandaloneSiteTest {
private final TemporaryFolder tempSiteDir = new TemporaryFolder();
private final TestRule testRunner =
- new TestRule() {
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
+ (base, description) ->
+ new Statement() {
@Override
public void evaluate() throws Throwable {
try {
@@ -134,8 +121,6 @@ public abstract class StandaloneSiteTest {
}
}
};
- }
- };
@Rule public RuleChain ruleChain = RuleChain.outerRule(tempSiteDir).around(testRunner);
diff --git a/java/com/google/gerrit/acceptance/TestAccount.java b/java/com/google/gerrit/acceptance/TestAccount.java
index ac197bdb8a..c937aed558 100644
--- a/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/java/com/google/gerrit/acceptance/TestAccount.java
@@ -14,67 +14,80 @@
package com.google.gerrit.acceptance;
-import static java.util.stream.Collectors.toList;
+import static com.google.common.collect.ImmutableList.toImmutableList;
-import com.google.common.base.MoreObjects;
+import com.google.auto.value.AutoValue;
+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.mail.Address;
import com.google.gerrit.reviewdb.client.Account;
import java.net.InetSocketAddress;
import java.util.Arrays;
-import java.util.List;
import org.apache.http.client.utils.URIBuilder;
import org.eclipse.jgit.lib.PersonIdent;
-public class TestAccount {
- public static List<Account.Id> ids(List<TestAccount> accounts) {
- return accounts.stream().map(a -> a.id).collect(toList());
+@AutoValue
+public abstract class TestAccount {
+ public static ImmutableList<Account.Id> ids(Iterable<TestAccount> accounts) {
+ return Streams.stream(accounts).map(TestAccount::id).collect(toImmutableList());
}
- public static List<String> names(List<TestAccount> accounts) {
- return accounts.stream().map(a -> a.fullName).collect(toList());
+ public static ImmutableList<String> names(Iterable<TestAccount> accounts) {
+ return Streams.stream(accounts).map(TestAccount::fullName).collect(toImmutableList());
}
- public static List<String> names(TestAccount... accounts) {
+ public static ImmutableList<String> names(TestAccount... accounts) {
return names(Arrays.asList(accounts));
}
- public final Account.Id id;
- public final String username;
- public final String email;
- public final Address emailAddress;
- public final String fullName;
- public final String httpPassword;
-
- TestAccount(Account.Id id, String username, String email, String fullName, String httpPassword) {
- this.id = id;
- this.username = username;
- this.email = email;
- this.emailAddress = new Address(fullName, email);
- this.fullName = fullName;
- this.httpPassword = httpPassword;
+ static TestAccount create(
+ Account.Id id,
+ @Nullable String username,
+ @Nullable String email,
+ @Nullable String fullName,
+ @Nullable String httpPassword) {
+ return new AutoValue_TestAccount(id, username, email, fullName, httpPassword);
}
- public PersonIdent getIdent() {
- return new PersonIdent(fullName, email);
+ public abstract Account.Id id();
+
+ @Nullable
+ public abstract String username();
+
+ @Nullable
+ public abstract String email();
+
+ @Nullable
+ public abstract String fullName();
+
+ @Nullable
+ public abstract String httpPassword();
+
+ public PersonIdent newIdent() {
+ return new PersonIdent(fullName(), email());
}
public String getHttpUrl(GerritServer server) {
InetSocketAddress addr = server.getHttpAddress();
return new URIBuilder()
.setScheme("http")
- .setUserInfo(username, httpPassword)
+ .setUserInfo(username(), httpPassword())
.setHost(InetAddresses.toUriString(addr.getAddress()))
.setPort(addr.getPort())
.toString();
}
- public Account.Id getId() {
- return id;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this).add("id", id).add("username", username).toString();
+ public Address getEmailAddress() {
+ // Address is weird enough that it's safer and clearer to create a new instance in a
+ // non-abstract method rather than, say, having an abstract emailAddress() as part of this
+ // AutoValue class. Specifically:
+ // * Email is not specified as @Nullable in Address, but it is nullable in this class. If this
+ // is a problem, at least it's a problem only for users of TestAccount that actually call
+ // emailAddress().
+ // * Address#equals only considers email, not name, whereas TestAccount#equals should include
+ // name.
+ return new Address(fullName(), email());
}
}
diff --git a/java/com/google/gerrit/acceptance/ssh/GracefulCommand.java b/java/com/google/gerrit/acceptance/ssh/GracefulCommand.java
new file mode 100644
index 0000000000..ddaf341ee7
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/GracefulCommand.java
@@ -0,0 +1,31 @@
+// 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.ssh;
+
+import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+
+import com.google.gerrit.sshd.CommandMetaData;
+
+@CommandMetaData(
+ name = "graceful",
+ description = "Test command for graceful shutdown",
+ runsAt = MASTER_OR_SLAVE)
+public class GracefulCommand extends TestCommand {
+
+ @Override
+ boolean isGraceful() {
+ return true;
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java b/java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java
new file mode 100644
index 0000000000..ed635c8fdb
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/NonGracefulCommand.java
@@ -0,0 +1,31 @@
+// 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.ssh;
+
+import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+
+import com.google.gerrit.sshd.CommandMetaData;
+
+@CommandMetaData(
+ name = "non-graceful",
+ description = "Test command for immediate shutdown",
+ runsAt = MASTER_OR_SLAVE)
+public class NonGracefulCommand extends TestCommand {
+
+ @Override
+ boolean isGraceful() {
+ return false;
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/ssh/TestCommand.java b/java/com/google/gerrit/acceptance/ssh/TestCommand.java
new file mode 100644
index 0000000000..783957805a
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/TestCommand.java
@@ -0,0 +1,49 @@
+// 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.ssh;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.sshd.SshCommand;
+import java.util.concurrent.CyclicBarrier;
+import org.kohsuke.args4j.Option;
+
+public abstract class TestCommand extends SshCommand {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ public static final CyclicBarrier syncPoint = new CyclicBarrier(2);
+
+ @Option(
+ name = "--duration",
+ aliases = {"-d"},
+ required = true,
+ usage = "Duration of the command execution in seconds")
+ private int duration;
+
+ @Override
+ protected void run() throws UnloggedFailure, Failure, Exception {
+ logger.atFine().log("Starting command.");
+ if (isGraceful()) {
+ enableGracefulStop();
+ }
+ try {
+ syncPoint.await();
+ Thread.sleep(duration * 1000);
+ logger.atFine().log("Stopping command.");
+ } catch (Exception e) {
+ throw die("Command ended prematurely.", e);
+ }
+ }
+
+ abstract boolean isGraceful();
+}
diff --git a/java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java b/java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.java
new file mode 100644
index 0000000000..626092bdbd
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ssh/TestSshCommandModule.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.acceptance.ssh;
+
+import com.google.gerrit.sshd.CommandModule;
+
+public class TestSshCommandModule extends CommandModule {
+ @Override
+ protected void configure() {
+ command("graceful").to(GracefulCommand.class);
+ command("non-graceful").to(NonGracefulCommand.class);
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/ThrowingConsumer.java b/java/com/google/gerrit/acceptance/testsuite/ThrowingConsumer.java
index 5efdc81c74..8efb6aea50 100644
--- a/java/com/google/gerrit/acceptance/testsuite/ThrowingConsumer.java
+++ b/java/com/google/gerrit/acceptance/testsuite/ThrowingConsumer.java
@@ -17,4 +17,12 @@ package com.google.gerrit.acceptance.testsuite;
@FunctionalInterface
public interface ThrowingConsumer<T> {
void accept(T t) throws Exception;
+
+ default void acceptAndThrowSilently(T t) {
+ try {
+ accept(t);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/ThrowingFunction.java b/java/com/google/gerrit/acceptance/testsuite/ThrowingFunction.java
index d41672ad3f..2337331210 100644
--- a/java/com/google/gerrit/acceptance/testsuite/ThrowingFunction.java
+++ b/java/com/google/gerrit/acceptance/testsuite/ThrowingFunction.java
@@ -18,4 +18,12 @@ package com.google.gerrit.acceptance.testsuite;
public interface ThrowingFunction<T, R> {
R apply(T value) throws Exception;
+
+ default R applyAndThrowSilently(T t) {
+ try {
+ return apply(t);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
index 61b7599258..61b828ee36 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
@@ -28,11 +28,11 @@ public interface AccountOperations {
/**
* Starts the fluent chain for a querying or modifying an account. Please see the methods of
- * {@link MoreAccountOperations} for details on possible operations.
+ * {@link PerAccountOperations} for details on possible operations.
*
* @return an aggregation of operations on a specific account
*/
- MoreAccountOperations account(Account.Id accountId);
+ PerAccountOperations account(Account.Id accountId);
/**
* Starts the fluent chain to create an account. The returned builder can be used to specify the
@@ -58,14 +58,14 @@ public interface AccountOperations {
TestAccountCreation.Builder newAccount();
/** An aggregation of methods on a specific account. */
- interface MoreAccountOperations {
+ interface PerAccountOperations {
/**
* Checks whether the account exists.
*
* @return {@code true} if the account exists
*/
- boolean exists() throws Exception;
+ boolean exists();
/**
* Retrieves the account.
@@ -76,7 +76,7 @@ public interface AccountOperations {
*
* @return the corresponding {@code TestAccount}
*/
- TestAccount get() throws Exception;
+ TestAccount get();
/**
* Starts the fluent chain to update an account. The returned builder can be used to specify how
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index ebbcfe47f3..7641e4784f 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -17,14 +17,13 @@ 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.server.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.InternalAccountUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.Optional;
@@ -50,8 +49,8 @@ public class AccountOperationsImpl implements AccountOperations {
}
@Override
- public MoreAccountOperations account(Account.Id accountId) {
- return new MoreAccountOperationsImpl(accountId);
+ public PerAccountOperations account(Account.Id accountId) {
+ return new PerAccountOperationsImpl(accountId);
}
@Override
@@ -68,7 +67,7 @@ public class AccountOperationsImpl implements AccountOperations {
}
private AccountState createAccount(AccountsUpdate.AccountUpdater accountUpdater)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
Account.Id accountId = new Account.Id(seq.nextAccountId());
return accountsUpdate.insert("Create Test Account", accountId, accountUpdater);
}
@@ -100,28 +99,35 @@ public class AccountOperationsImpl implements AccountOperations {
return builder.addExternalId(ExternalId.createUsername(username, accountId, httpPassword));
}
- private class MoreAccountOperationsImpl implements MoreAccountOperations {
+ private class PerAccountOperationsImpl implements PerAccountOperations {
private final Account.Id accountId;
- MoreAccountOperationsImpl(Account.Id accountId) {
+ PerAccountOperationsImpl(Account.Id accountId) {
this.accountId = accountId;
}
@Override
- public boolean exists() throws Exception {
- return accounts.get(accountId).isPresent();
+ public boolean exists() {
+ return getAccountState(accountId).isPresent();
}
@Override
- public TestAccount get() throws Exception {
+ public TestAccount get() {
AccountState account =
- accounts
- .get(accountId)
+ getAccountState(accountId)
.orElseThrow(
() -> new IllegalStateException("Tried to get non-existing test account"));
return toTestAccount(account);
}
+ private Optional<AccountState> getAccountState(Account.Id accountId) {
+ try {
+ return accounts.get(accountId);
+ } catch (IOException | ConfigInvalidException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
private TestAccount toTestAccount(AccountState accountState) {
Account account = accountState.getAccount();
return TestAccount.builder()
@@ -139,7 +145,7 @@ public class AccountOperationsImpl implements AccountOperations {
}
private void updateAccount(TestAccountUpdate accountUpdate)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
AccountsUpdate.AccountUpdater accountUpdater =
(account, updateBuilder) -> fillBuilder(updateBuilder, accountUpdate, accountId);
Optional<AccountState> updatedAccount = updateAccount(accountUpdater);
@@ -147,7 +153,7 @@ public class AccountOperationsImpl implements AccountOperations {
}
private Optional<AccountState> updateAccount(AccountsUpdate.AccountUpdater accountUpdater)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
return accountsUpdate.update("Update Test Account", accountId, accountUpdater);
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
index ab32409866..f2414e029e 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
@@ -88,9 +88,9 @@ public abstract class TestAccountCreation {
abstract TestAccountCreation autoBuild();
- public Account.Id create() throws Exception {
+ public Account.Id create() {
TestAccountCreation accountUpdate = autoBuild();
- return accountUpdate.accountCreator().apply(accountUpdate);
+ return accountUpdate.accountCreator().applyAndThrowSilently(accountUpdate);
}
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountUpdate.java b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountUpdate.java
index 251f452536..da599e75c6 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountUpdate.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountUpdate.java
@@ -86,9 +86,9 @@ public abstract class TestAccountUpdate {
abstract TestAccountUpdate autoBuild();
- public void update() throws Exception {
+ public void update() {
TestAccountUpdate accountUpdate = autoBuild();
- accountUpdate.accountUpdater().accept(accountUpdate);
+ accountUpdate.accountUpdater().acceptAndThrowSilently(accountUpdate);
}
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
index 0cb5cf3816..4847fdb5bc 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
@@ -55,14 +55,14 @@ public class TestSshKeys {
public KeyPair getKeyPair(com.google.gerrit.acceptance.TestAccount account) throws Exception {
checkState(sshEnabled, "Requested SSH key pair, but SSH is disabled");
checkState(
- account.username != null,
+ account.username() != null,
"Requested SSH key pair for account %s, but username is not set",
- account.id);
+ account.id());
- String username = account.username;
+ String username = account.username();
KeyPair keyPair = sshKeyPairs.get(username);
if (keyPair == null) {
- keyPair = createKeyPair(account.id, username, account.email);
+ keyPair = createKeyPair(account.id(), username, account.email());
sshKeyPairs.put(username, keyPair);
}
return keyPair;
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
index f75ca2ebc3..533d06b0fc 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
@@ -27,11 +27,11 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
public interface GroupOperations {
/**
* Starts the fluent chain for querying or modifying a group. Please see the methods of {@link
- * MoreGroupOperations} for details on possible operations.
+ * PerGroupOperations} for details on possible operations.
*
* @return an aggregation of operations on a specific group
*/
- MoreGroupOperations group(AccountGroup.UUID groupUuid);
+ PerGroupOperations group(AccountGroup.UUID groupUuid);
/**
* Starts the fluent chain to create a group. The returned builder can be used to specify the
@@ -56,14 +56,14 @@ public interface GroupOperations {
TestGroupCreation.Builder newGroup();
/** An aggregation of methods on a specific group. */
- interface MoreGroupOperations {
+ interface PerGroupOperations {
/**
* Checks whether the group exists.
*
* @return {@code true} if the group exists
*/
- boolean exists() throws Exception;
+ boolean exists();
/**
* Retrieves the group.
@@ -74,7 +74,7 @@ public interface GroupOperations {
*
* @return the corresponding {@code TestGroup}
*/
- TestGroup get() throws Exception;
+ TestGroup get();
/**
* Starts the fluent chain to update a group. The returned builder can be used to specify how
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
index f9769c5fd6..e0ddee56fd 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
@@ -16,10 +16,10 @@ package com.google.gerrit.acceptance.testsuite.group;
import static com.google.common.base.Preconditions.checkState;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+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.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.group.InternalGroup;
@@ -27,8 +27,7 @@ import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.Optional;
@@ -60,8 +59,8 @@ public class GroupOperationsImpl implements GroupOperations {
}
@Override
- public MoreGroupOperations group(AccountGroup.UUID groupUuid) {
- return new MoreGroupOperationsImpl(groupUuid);
+ public PerGroupOperations group(AccountGroup.UUID groupUuid) {
+ return new PerGroupOperationsImpl(groupUuid);
}
@Override
@@ -70,7 +69,7 @@ public class GroupOperationsImpl implements GroupOperations {
}
private AccountGroup.UUID createNewGroup(TestGroupCreation groupCreation)
- throws ConfigInvalidException, IOException, OrmException {
+ throws ConfigInvalidException, IOException {
InternalGroupCreation internalGroupCreation = toInternalGroupCreation(groupCreation);
InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupCreation);
InternalGroup internalGroup =
@@ -78,8 +77,7 @@ public class GroupOperationsImpl implements GroupOperations {
return internalGroup.getGroupUUID();
}
- private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation)
- throws OrmException {
+ private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation) {
AccountGroup.Id groupId = new AccountGroup.Id(seq.nextGroupId());
String groupName = groupCreation.name().orElse("group-with-id-" + groupId.get());
AccountGroup.UUID groupUuid = GroupUUID.make(groupName, serverIdent);
@@ -101,25 +99,33 @@ public class GroupOperationsImpl implements GroupOperations {
return builder.build();
}
- private class MoreGroupOperationsImpl implements MoreGroupOperations {
+ private class PerGroupOperationsImpl implements PerGroupOperations {
private final AccountGroup.UUID groupUuid;
- MoreGroupOperationsImpl(AccountGroup.UUID groupUuid) {
+ PerGroupOperationsImpl(AccountGroup.UUID groupUuid) {
this.groupUuid = groupUuid;
}
@Override
- public boolean exists() throws Exception {
- return groups.getGroup(groupUuid).isPresent();
+ public boolean exists() {
+ return getGroup(groupUuid).isPresent();
}
@Override
- public TestGroup get() throws Exception {
- Optional<InternalGroup> group = groups.getGroup(groupUuid);
+ public TestGroup get() {
+ Optional<InternalGroup> group = getGroup(groupUuid);
checkState(group.isPresent(), "Tried to get non-existing test group");
return toTestGroup(group.get());
}
+ private Optional<InternalGroup> getGroup(AccountGroup.UUID groupUuid) {
+ try {
+ return groups.getGroup(groupUuid);
+ } catch (IOException | ConfigInvalidException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
private TestGroup toTestGroup(InternalGroup internalGroup) {
return TestGroup.builder()
.groupUuid(internalGroup.getGroupUUID())
@@ -140,7 +146,7 @@ public class GroupOperationsImpl implements GroupOperations {
}
private void updateGroup(TestGroupUpdate groupUpdate)
- throws OrmDuplicateKeyException, NoSuchGroupException, ConfigInvalidException, IOException {
+ throws DuplicateKeyException, NoSuchGroupException, ConfigInvalidException, IOException {
InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupUpdate);
groupsUpdate.updateGroup(groupUuid, internalGroupUpdate);
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
index efed72055b..612ce2a47f 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
@@ -104,9 +104,9 @@ public abstract class TestGroupCreation {
*
* @return the UUID of the created group
*/
- public AccountGroup.UUID create() throws Exception {
+ public AccountGroup.UUID create() {
TestGroupCreation groupCreation = autoBuild();
- return groupCreation.groupCreator().apply(groupCreation);
+ return groupCreation.groupCreator().applyAndThrowSilently(groupCreation);
}
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
index 095a2700a6..bc9d569aa7 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
@@ -126,9 +126,9 @@ public abstract class TestGroupUpdate {
abstract TestGroupUpdate autoBuild();
/** Executes the group update as specified. */
- public void update() throws Exception {
+ public void update() {
TestGroupUpdate groupUpdater = autoBuild();
- groupUpdater.groupUpdater().accept(groupUpdater);
+ groupUpdater.groupUpdater().acceptAndThrowSilently(groupUpdater);
}
}
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
new file mode 100644
index 0000000000..029d161f11
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
@@ -0,0 +1,44 @@
+// 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.testsuite.project;
+
+import com.google.gerrit.reviewdb.client.Project;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+/**
+ * Operations for constructing projects in tests. This does not necessarily use the project REST
+ * API, so don't use it for testing that.
+ */
+public interface ProjectOperations {
+
+ /** Starts a fluent chain for creating a new project. */
+ TestProjectCreation.Builder newProject();
+
+ PerProjectOperations project(Project.NameKey key);
+
+ interface PerProjectOperations {
+ /**
+ * Returns the commit for this project. branchName can either be shortened ("HEAD", "master") or
+ * a fully qualified refname ("refs/heads/master"). The branch must exist.
+ */
+ RevCommit getHead(String branchName);
+
+ /**
+ * Returns true if a branch exists. branchName can either be shortened ("HEAD", "master") or a
+ * fully qualified refname ("refs/heads/master").
+ */
+ boolean hasHead(String branchName);
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
new file mode 100644
index 0000000000..28be3f3a7e
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -0,0 +1,101 @@
+// 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.testsuite.project;
+
+import com.google.common.base.Preconditions;
+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.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.CreateProjectArgs;
+import com.google.gerrit.server.project.ProjectCreator;
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collections;
+import org.apache.commons.lang.RandomStringUtils;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+public class ProjectOperationsImpl implements ProjectOperations {
+ private final ProjectCreator projectCreator;
+ private final GitRepositoryManager repoManager;
+
+ @Inject
+ ProjectOperationsImpl(GitRepositoryManager repoManager, ProjectCreator projectCreator) {
+ this.repoManager = repoManager;
+ this.projectCreator = projectCreator;
+ }
+
+ @Override
+ public Builder newProject() {
+ return TestProjectCreation.builder(this::createNewProject);
+ }
+
+ private Project.NameKey createNewProject(TestProjectCreation projectCreation) throws Exception {
+ String name = projectCreation.name().orElse(RandomStringUtils.randomAlphabetic(8));
+
+ CreateProjectArgs args = new CreateProjectArgs();
+ args.setProjectName(name);
+ args.branch = Collections.singletonList(Constants.R_HEADS + Constants.MASTER);
+ args.createEmptyCommit = projectCreation.createEmptyCommit().orElse(true);
+ projectCreation.parent().ifPresent(p -> args.newParent = p);
+ // ProjectCreator wants non-null owner IDs.
+ args.ownerIds = new ArrayList<>();
+ projectCreation.submitType().ifPresent(st -> args.submitType = st);
+ projectCreator.createProject(args);
+ return new Project.NameKey(name);
+ }
+
+ @Override
+ public ProjectOperations.PerProjectOperations project(Project.NameKey key) {
+ return new PerProjectOperations(key);
+ }
+
+ private class PerProjectOperations implements ProjectOperations.PerProjectOperations {
+
+ Project.NameKey nameKey;
+
+ PerProjectOperations(Project.NameKey nameKey) {
+ this.nameKey = nameKey;
+ }
+
+ @Override
+ public RevCommit getHead(String branch) {
+ return Preconditions.checkNotNull(headOrNull(branch));
+ }
+
+ @Override
+ public boolean hasHead(String branch) {
+ return headOrNull(branch) != null;
+ }
+
+ private RevCommit headOrNull(String branch) {
+ if (!branch.startsWith(Constants.R_REFS)) {
+ branch = RefNames.REFS_HEADS + branch;
+ }
+
+ try (Repository repo = repoManager.openRepository(nameKey);
+ RevWalk rw = new RevWalk(repo)) {
+ Ref r = repo.exactRef(branch);
+ return r == null ? null : rw.parseCommit(r.getObjectId());
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
new file mode 100644
index 0000000000..31af1d2251
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
@@ -0,0 +1,71 @@
+// 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.testsuite.project;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.reviewdb.client.Project;
+import java.util.Optional;
+
+@AutoValue
+public abstract class TestProjectCreation {
+
+ public abstract Optional<String> name();
+
+ public abstract Optional<Project.NameKey> parent();
+
+ public abstract Optional<Boolean> createEmptyCommit();
+
+ public abstract Optional<SubmitType> submitType();
+
+ abstract ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator();
+
+ public static Builder builder(
+ ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator) {
+ return new AutoValue_TestProjectCreation.Builder().projectCreator(projectCreator);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract TestProjectCreation.Builder name(String name);
+
+ public abstract TestProjectCreation.Builder parent(Project.NameKey parent);
+
+ public abstract TestProjectCreation.Builder submitType(SubmitType submitType);
+
+ public abstract TestProjectCreation.Builder createEmptyCommit(boolean value);
+
+ /** Skips the empty commit on creation. This means that project's branches will not exist. */
+ public TestProjectCreation.Builder noEmptyCommit() {
+ return createEmptyCommit(false);
+ }
+
+ abstract TestProjectCreation.Builder projectCreator(
+ ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator);
+
+ abstract TestProjectCreation autoBuild();
+
+ /**
+ * Executes the project creation as specified.
+ *
+ * @return the name of the created project
+ */
+ public Project.NameKey create() {
+ TestProjectCreation creation = autoBuild();
+ return creation.projectCreator().applyAndThrowSilently(creation);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.java
new file mode 100644
index 0000000000..17d9294583
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperations.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.acceptance.testsuite.request;
+
+import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
+import com.google.gerrit.acceptance.testsuite.account.TestAccount;
+import com.google.gerrit.reviewdb.client.Account;
+
+/**
+ * An aggregation of operations on Guice request scopes for test purposes.
+ *
+ * <p>To execute the operations, no Gerrit permissions are necessary.
+ */
+public interface RequestScopeOperations {
+ /**
+ * Sets the Guice request scope to the given account.
+ *
+ * <p>The resulting context has an SSH session attached. In order to use the SSH session returned
+ * by {@link com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context#getSession()}, SSH
+ * must be enabled in the test and the account must have a username set. However, these are not
+ * requirements simply to call this method.
+ *
+ * @param accountId account ID. Must exist; throws an unchecked exception otherwise.
+ * @return the previous request scope.
+ */
+ AcceptanceTestRequestScope.Context setApiUser(Account.Id accountId);
+
+ /**
+ * Sets the Guice request scope to the given account.
+ *
+ * <p>The resulting context has an SSH session attached. In order to use the SSH session returned
+ * by {@link com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context#getSession()}, SSH
+ * must be enabled in the test and the account must have a username set. However, these are not
+ * requirements simply to call this method.
+ *
+ * @param testAccount test account from {@code AccountOperations}.
+ * @return the previous request scope.
+ */
+ AcceptanceTestRequestScope.Context setApiUser(TestAccount testAccount);
+
+ /**
+ * Enforces a new request context for the current API user.
+ *
+ * <p>This recreates the {@code IdentifiedUser}, hence everything which is cached in the {@code
+ * IdentifiedUser} is reloaded (e.g. the email addresses of the user).
+ *
+ * <p>The current user must be an identified user.
+ *
+ * @return the previous request scope.
+ */
+ AcceptanceTestRequestScope.Context resetCurrentApiUser();
+
+ /**
+ * Sets the Guice request scope to the anonymous user.
+ *
+ * @return the previous request scope.
+ */
+ AcceptanceTestRequestScope.Context setApiUserAnonymous();
+
+ /**
+ * Sets the Guice request scope to the internal server user.
+ *
+ * @return the previous request scope.
+ */
+ AcceptanceTestRequestScope.Context setApiUserInternal();
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
new file mode 100644
index 0000000000..554642266a
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImpl.java
@@ -0,0 +1,114 @@
+// 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.request;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
+import com.google.gerrit.acceptance.GerritServer.TestSshServerAddress;
+import com.google.gerrit.acceptance.SshSession;
+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.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.IdentifiedUser.GenericFactory;
+import com.google.gerrit.server.InternalUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.net.InetSocketAddress;
+
+/**
+ * The implementation of {@code RequestScopeOperations}.
+ *
+ * <p>There is only one implementation of {@code RequestScopeOperations}. Nevertheless, we keep the
+ * separation between interface and implementation to enhance clarity.
+ */
+@Singleton
+public class RequestScopeOperationsImpl implements RequestScopeOperations {
+ private final AcceptanceTestRequestScope atrScope;
+ private final AccountCache accountCache;
+ private final AccountOperations accountOperations;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final Provider<AnonymousUser> anonymousUserProvider;
+ private final InternalUser.Factory internalUserFactory;
+ private final InetSocketAddress sshAddress;
+ private final TestSshKeys testSshKeys;
+
+ @Inject
+ RequestScopeOperationsImpl(
+ AcceptanceTestRequestScope atrScope,
+ AccountCache accountCache,
+ AccountOperations accountOperations,
+ GenericFactory userFactory,
+ Provider<AnonymousUser> anonymousUserProvider,
+ InternalUser.Factory internalUserFactory,
+ @Nullable @TestSshServerAddress InetSocketAddress sshAddress,
+ TestSshKeys testSshKeys) {
+ this.atrScope = atrScope;
+ this.accountCache = accountCache;
+ this.accountOperations = accountOperations;
+ this.userFactory = userFactory;
+ this.anonymousUserProvider = anonymousUserProvider;
+ this.internalUserFactory = internalUserFactory;
+ this.sshAddress = sshAddress;
+ this.testSshKeys = testSshKeys;
+ }
+
+ @Override
+ public AcceptanceTestRequestScope.Context setApiUser(Account.Id accountId) {
+ return setApiUser(accountOperations.account(accountId).get());
+ }
+
+ @Override
+ public AcceptanceTestRequestScope.Context setApiUser(TestAccount testAccount) {
+ return atrScope.set(
+ atrScope.newContext(
+ new SshSession(testSshKeys, sshAddress, testAccount),
+ createIdentifiedUser(testAccount.accountId())));
+ }
+
+ @Override
+ public AcceptanceTestRequestScope.Context resetCurrentApiUser() {
+ CurrentUser user = atrScope.get().getUser();
+ // More special cases for anonymous users etc. can be added as needed.
+ checkState(user.isIdentifiedUser(), "can only reset IdentifiedUser, not %s", user);
+ return setApiUser(user.getAccountId());
+ }
+
+ @Override
+ public AcceptanceTestRequestScope.Context setApiUserAnonymous() {
+ return atrScope.set(atrScope.newContext(null, anonymousUserProvider.get()));
+ }
+
+ @Override
+ public AcceptanceTestRequestScope.Context setApiUserInternal() {
+ return atrScope.set(atrScope.newContext(null, internalUserFactory.create()));
+ }
+
+ private IdentifiedUser createIdentifiedUser(Account.Id accountId) {
+ return userFactory.create(
+ accountCache
+ .get(requireNonNull(accountId))
+ .orElseThrow(
+ () -> new IllegalArgumentException("account does not exist: " + accountId)));
+ }
+}
diff --git a/java/com/google/gerrit/asciidoctor/AsciiDoctor.java b/java/com/google/gerrit/asciidoctor/AsciiDoctor.java
index 577907092d..9d0a28e14c 100644
--- a/java/com/google/gerrit/asciidoctor/AsciiDoctor.java
+++ b/java/com/google/gerrit/asciidoctor/AsciiDoctor.java
@@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.io.ByteStreams;
import java.io.BufferedReader;
import java.io.File;
-import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -127,7 +126,7 @@ public class AsciiDoctor {
int equalsIndex = attribute.indexOf('=');
if (equalsIndex > -1) {
String name = attribute.substring(0, equalsIndex);
- String value = attribute.substring(equalsIndex + 1, attribute.length());
+ String value = attribute.substring(equalsIndex + 1);
attributeValues.put(name, value);
} else {
@@ -168,14 +167,7 @@ public class AsciiDoctor {
try (ZipOutputStream zip = new ZipOutputStream(Files.newOutputStream(Paths.get(zipFile)))) {
renderFiles(inputFiles, zip);
- File[] cssFiles =
- tmpdir.listFiles(
- new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(".css");
- }
- });
+ File[] cssFiles = tmpdir.listFiles((dir, name) -> name.endsWith(".css"));
for (File css : cssFiles) {
zipFile(css, css.getName(), zip);
}
diff --git a/java/com/google/gerrit/common/BUILD b/java/com/google/gerrit/common/BUILD
index 72c5b4ac1e..3b3d9c65a2 100644
--- a/java/com/google/gerrit/common/BUILD
+++ b/java/com/google/gerrit/common/BUILD
@@ -1,34 +1,15 @@
load("@rules_java//java:defs.bzl", "java_library")
-load("//tools/bzl:gwt.bzl", "gwt_module")
ANNOTATIONS = [
"Nullable.java",
- "audit/Audit.java",
- "auth/SignInRequired.java",
+ "UsedAt.java",
]
java_library(
name = "annotations",
srcs = ANNOTATIONS,
visibility = ["//visibility:public"],
-)
-
-gwt_module(
- name = "client",
- srcs = glob(["**/*.java"]),
- exported_deps = [
- "//java/com/google/gerrit/extensions:api",
- "//java/com/google/gerrit/prettify:client",
- "//lib:guava",
- "//lib:gwtorm-client",
- "//lib:servlet-api-3_1",
- "//lib/auto:auto-value",
- "//lib/auto:auto-value-annotations",
- "//lib/flogger:api",
- "//lib/jgit/org.eclipse.jgit:jgit",
- ],
- gwt_xml = "Common.gwt.xml",
- visibility = ["//visibility:public"],
+ deps = ["//lib:guava"],
)
java_library(
@@ -43,10 +24,9 @@ java_library(
"//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:gwtjsonrpc",
- "//lib:gwtorm",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/common/Common.gwt.xml b/java/com/google/gerrit/common/Common.gwt.xml
deleted file mode 100644
index 56bbb84d23..0000000000
--- a/java/com/google/gerrit/common/Common.gwt.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<module>
- <inherits name='com.google.gerrit.reviewdb.ReviewDB' />
- <inherits name='com.google.gwtjsonrpc.GWTJSONRPC'/>
- <inherits name="com.google.gwt.logging.Logging"/>
- <source path="">
- <exclude name='**/testing/**/*.java'/>
- <include name='**/*.java'/>
- </source>
-</module>
diff --git a/java/com/google/gerrit/common/FileUtil.java b/java/com/google/gerrit/common/FileUtil.java
index 24e3808653..04288bc71b 100644
--- a/java/com/google/gerrit/common/FileUtil.java
+++ b/java/com/google/gerrit/common/FileUtil.java
@@ -14,7 +14,6 @@
package com.google.gerrit.common;
-import com.google.common.annotations.GwtIncompatible;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -25,7 +24,6 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.IO;
-@GwtIncompatible("Unemulated classes in java.io, java.nio and JGit")
public class FileUtil {
public static boolean modified(FileBasedConfig cfg) throws IOException {
byte[] curVers;
diff --git a/java/com/google/gerrit/common/FooterConstants.java b/java/com/google/gerrit/common/FooterConstants.java
index d76c92b4fd..3ec809c226 100644
--- a/java/com/google/gerrit/common/FooterConstants.java
+++ b/java/com/google/gerrit/common/FooterConstants.java
@@ -14,10 +14,8 @@
package com.google.gerrit.common;
-import com.google.common.annotations.GwtIncompatible;
import org.eclipse.jgit.revwalk.FooterKey;
-@GwtIncompatible("Unemulated com.google.gerrit.common.FooterConstants")
public class FooterConstants {
/** The change ID as used to track patch sets. */
public static final FooterKey CHANGE_ID = new FooterKey("Change-Id");
diff --git a/java/com/google/gerrit/common/FormatUtil.java b/java/com/google/gerrit/common/FormatUtil.java
deleted file mode 100644
index 0f6b37ae21..0000000000
--- a/java/com/google/gerrit/common/FormatUtil.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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.common;
-
-public class FormatUtil {
- public static String elide(String s, int max) {
- if (s == null || s.length() <= max) {
- return s;
- }
- int len = (max - 3) / 2;
- return s.substring(0, len) + "..." + s.substring(s.length() - len);
- }
-}
diff --git a/java/com/google/gerrit/common/IoUtil.java b/java/com/google/gerrit/common/IoUtil.java
index 526e88b3fd..37f6c2cfea 100644
--- a/java/com/google/gerrit/common/IoUtil.java
+++ b/java/com/google/gerrit/common/IoUtil.java
@@ -14,7 +14,6 @@
package com.google.gerrit.common;
-import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.InputStream;
@@ -30,7 +29,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Set;
-@GwtIncompatible("Unemulated methods in Class and OutputStream")
public final class IoUtil {
public static void copyWithThread(InputStream src, OutputStream dst) {
new Thread("IoUtil-Copy") {
diff --git a/java/com/google/gerrit/common/PluginData.java b/java/com/google/gerrit/common/PluginData.java
index b14543d343..c440de109a 100644
--- a/java/com/google/gerrit/common/PluginData.java
+++ b/java/com/google/gerrit/common/PluginData.java
@@ -14,11 +14,9 @@
package com.google.gerrit.common;
-import com.google.common.annotations.GwtIncompatible;
import java.nio.file.Path;
import java.util.Objects;
-@GwtIncompatible("Unemulated java.nio.file.Path")
public class PluginData {
public final String name;
public final String version;
diff --git a/java/com/google/gerrit/common/ProjectAccessUtil.java b/java/com/google/gerrit/common/ProjectAccessUtil.java
deleted file mode 100644
index 0369bfe5e4..0000000000
--- a/java/com/google/gerrit/common/ProjectAccessUtil.java
+++ /dev/null
@@ -1,66 +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.common;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.Permission;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class ProjectAccessUtil {
- public static List<AccessSection> mergeSections(List<AccessSection> src) {
- Map<String, AccessSection> map = new LinkedHashMap<>();
- for (AccessSection section : src) {
- if (section.getPermissions().isEmpty()) {
- continue;
- }
-
- final AccessSection prior = map.get(section.getName());
- if (prior != null) {
- prior.mergeFrom(section);
- } else {
- map.put(section.getName(), section);
- }
- }
- return new ArrayList<>(map.values());
- }
-
- public static List<AccessSection> removeEmptyPermissionsAndSections(
- final List<AccessSection> src) {
- final Set<AccessSection> sectionsToRemove = new HashSet<>();
- for (AccessSection section : src) {
- final Set<Permission> permissionsToRemove = new HashSet<>();
- for (Permission permission : section.getPermissions()) {
- if (permission.getRules().isEmpty()) {
- permissionsToRemove.add(permission);
- }
- }
- for (Permission permissionToRemove : permissionsToRemove) {
- section.remove(permissionToRemove);
- }
- if (section.getPermissions().isEmpty()) {
- sectionsToRemove.add(section);
- }
- }
- for (AccessSection sectionToRemove : sectionsToRemove) {
- src.remove(sectionToRemove);
- }
- return src;
- }
-}
diff --git a/java/com/google/gerrit/common/ProjectUtil.java b/java/com/google/gerrit/common/ProjectUtil.java
deleted file mode 100644
index e9d37818b9..0000000000
--- a/java/com/google/gerrit/common/ProjectUtil.java
+++ /dev/null
@@ -1,43 +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.common;
-
-public class ProjectUtil {
- public static String sanitizeProjectName(String name) {
- name = stripGitSuffix(name);
- name = stripTrailingSlash(name);
- return name;
- }
-
- public static String stripGitSuffix(String name) {
- if (name.endsWith(".git")) {
- // Be nice and drop the trailing ".git" suffix, which we never keep
- // in our database, but clients might mistakenly provide anyway.
- //
- name = name.substring(0, name.length() - 4);
- name = stripTrailingSlash(name);
- }
- return name;
- }
-
- private static String stripTrailingSlash(String name) {
- while (name.endsWith("/")) {
- name = name.substring(0, name.length() - 1);
- }
- return name;
- }
-
- private ProjectUtil() {}
-}
diff --git a/java/com/google/gerrit/common/RawInputUtil.java b/java/com/google/gerrit/common/RawInputUtil.java
index e102eab0b4..4a676e6a6e 100644
--- a/java/com/google/gerrit/common/RawInputUtil.java
+++ b/java/com/google/gerrit/common/RawInputUtil.java
@@ -18,14 +18,12 @@ 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.common.annotations.GwtIncompatible;
import com.google.gerrit.extensions.restapi.RawInput;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
-@GwtIncompatible("Unemulated classes in java.io and javax.servlet")
public class RawInputUtil {
public static RawInput create(String content) {
return create(content.getBytes(UTF_8));
diff --git a/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java b/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java
index cf86f74094..fa9b1399c9 100644
--- a/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java
+++ b/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java
@@ -18,7 +18,6 @@ import static com.google.common.flogger.LazyArgs.lazy;
import static com.google.gerrit.common.FileUtil.lastModified;
import static java.util.stream.Collectors.joining;
-import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
@@ -30,7 +29,6 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.List;
-@GwtIncompatible("Unemulated classes in java.nio and Guava")
public final class SiteLibraryLoaderUtil {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -50,12 +48,9 @@ public final class SiteLibraryLoaderUtil {
public static List<Path> listJars(Path dir) throws IOException {
DirectoryStream.Filter<Path> filter =
- new DirectoryStream.Filter<Path>() {
- @Override
- public boolean accept(Path entry) throws IOException {
- String name = entry.getFileName().toString();
- return (name.endsWith(".jar") || name.endsWith(".zip")) && Files.isRegularFile(entry);
- }
+ entry -> {
+ String name = entry.getFileName().toString();
+ return (name.endsWith(".jar") || name.endsWith(".zip")) && Files.isRegularFile(entry);
};
try (DirectoryStream<Path> jars = Files.newDirectoryStream(dir, filter)) {
return new Ordering<Path>() {
diff --git a/java/com/google/gerrit/common/UsedAt.java b/java/com/google/gerrit/common/UsedAt.java
new file mode 100644
index 0000000000..44ed92bc47
--- /dev/null
+++ b/java/com/google/gerrit/common/UsedAt.java
@@ -0,0 +1,43 @@
+// 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.common;
+
+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;
+
+/**
+ * A marker for a method that is public solely because it is called from inside a project or an
+ * organisation using Gerrit.
+ */
+@Target({METHOD, TYPE})
+@Retention(RUNTIME)
+public @interface UsedAt {
+ /** Enumeration of projects that call a method that would otherwise be private. */
+ enum Project {
+ GOOGLE,
+ COLLABNET,
+ PLUGIN_CHECKS,
+ PLUGIN_DELETE_PROJECT,
+ PLUGIN_SERVICEUSER,
+ PLUGINS_ALL, // Use this project if a method/type is generally made available to all plugins.
+ }
+
+ /** Reference to the project that uses the method annotated with this annotation. */
+ Project value();
+}
diff --git a/java/com/google/gerrit/common/Version.java b/java/com/google/gerrit/common/Version.java
index b8d3b67637..6197be5950 100644
--- a/java/com/google/gerrit/common/Version.java
+++ b/java/com/google/gerrit/common/Version.java
@@ -16,7 +16,6 @@ package com.google.gerrit.common;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import java.io.BufferedReader;
@@ -24,7 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-@GwtIncompatible("Unemulated com.google.gerrit.common.Version")
public class Version {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
diff --git a/java/com/google/gerrit/common/audit/Audit.java b/java/com/google/gerrit/common/audit/Audit.java
deleted file mode 100644
index a791e97429..0000000000
--- a/java/com/google/gerrit/common/audit/Audit.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2012 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.common.audit;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Audit annotation for JSON/RPC interfaces.
- *
- * <p>Flag with @Audit all the JSON/RPC methods to be traced in audit-trail and submitted to the
- * GroupAuditService.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD})
-public @interface Audit {
- String action() default "";
-
- /** List of positions of parameters to be obfuscated in audit-trail (i.e. passwords) */
- int[] obfuscate() default {};
-}
diff --git a/java/com/google/gerrit/common/auth/SignInRequired.java b/java/com/google/gerrit/common/auth/SignInRequired.java
deleted file mode 100644
index bcebf5c390..0000000000
--- a/java/com/google/gerrit/common/auth/SignInRequired.java
+++ /dev/null
@@ -1,30 +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.common.auth;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation indicating a service method requires a current user.
- *
- * <p>If there is no current user then {@code com.google.gerrit.common.errors.NotSignedInException}
- * will be given to the callback's onFailure method.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface SignInRequired {}
diff --git a/java/com/google/gerrit/common/data/AccessSection.java b/java/com/google/gerrit/common/data/AccessSection.java
index 49948f8fd9..3670e961a6 100644
--- a/java/com/google/gerrit/common/data/AccessSection.java
+++ b/java/com/google/gerrit/common/data/AccessSection.java
@@ -16,6 +16,7 @@ package com.google.gerrit.common.data;
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 java.util.ArrayList;
@@ -24,24 +25,38 @@ import java.util.List;
import java.util.Set;
/** Portion of a {@link Project} describing access rules. */
-public class AccessSection extends RefConfigSection implements Comparable<AccessSection> {
+public final class AccessSection implements Comparable<AccessSection> {
/** Special name given to the global capabilities; not a valid reference. */
public static final String GLOBAL_CAPABILITIES = "GLOBAL_CAPABILITIES";
+ /** Pattern that matches all references in a project. */
+ public static final String ALL = "refs/*";
- protected List<Permission> permissions;
+ /** Pattern that matches all branches in a project. */
+ public static final String HEADS = "refs/heads/*";
- protected AccessSection() {}
+ /** Prefix that triggers a regular expression pattern. */
+ public static final String REGEX_PREFIX = "^";
- public AccessSection(String refPattern) {
- super(refPattern);
+ /** Name of the access section. It could be a ref pattern or something else. */
+ private String name;
+
+ private List<Permission> permissions;
+
+ public AccessSection(String name) {
+ this.name = name;
}
- // TODO(ekempin): Make this method return an ImmutableList once the GWT UI is gone.
- public List<Permission> getPermissions() {
- if (permissions == null) {
- return new ArrayList<>();
- }
- return new ArrayList<>(permissions);
+ /** @return true if the name is likely to be a valid reference section name. */
+ public static boolean isValidRefSectionName(String name) {
+ return name.startsWith("refs/") || name.startsWith("^refs/");
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ImmutableList<Permission> getPermissions() {
+ return permissions == null ? ImmutableList.of() : ImmutableList.copyOf(permissions);
}
public void setPermissions(List<Permission> list) {
@@ -148,7 +163,12 @@ public class AccessSection extends RefConfigSection implements Comparable<Access
@Override
public boolean equals(Object obj) {
- if (!super.equals(obj) || !(obj instanceof AccessSection)) {
+ if (!(obj instanceof AccessSection)) {
+ return false;
+ }
+
+ AccessSection other = (AccessSection) obj;
+ if (!getName().equals(other.getName())) {
return false;
}
return new HashSet<>(getPermissions())
@@ -163,6 +183,7 @@ public class AccessSection extends RefConfigSection implements Comparable<Access
hashCode += permission.hashCode();
}
}
+ hashCode += getName().hashCode();
return hashCode;
}
}
diff --git a/java/com/google/gerrit/common/data/CommentDetail.java b/java/com/google/gerrit/common/data/CommentDetail.java
index ed7c79bfca..1ae246f94c 100644
--- a/java/com/google/gerrit/common/data/CommentDetail.java
+++ b/java/com/google/gerrit/common/data/CommentDetail.java
@@ -84,7 +84,7 @@ public class CommentDetail {
private static List<Comment> get(Map<Integer, List<Comment>> m, int i) {
List<Comment> r = m.get(i);
- return r != null ? orderComments(r) : Collections.<Comment>emptyList();
+ return r != null ? orderComments(r) : Collections.emptyList();
}
/**
diff --git a/java/com/google/gerrit/common/data/ContributorAgreement.java b/java/com/google/gerrit/common/data/ContributorAgreement.java
index b43dbfaed1..a6e8cdd33f 100644
--- a/java/com/google/gerrit/common/data/ContributorAgreement.java
+++ b/java/com/google/gerrit/common/data/ContributorAgreement.java
@@ -25,6 +25,8 @@ public class ContributorAgreement implements Comparable<ContributorAgreement> {
protected List<PermissionRule> accepted;
protected GroupReference autoVerify;
protected String agreementUrl;
+ protected List<String> excludeProjectsRegexes;
+ protected List<String> matchProjectsRegexes;
protected ContributorAgreement() {}
@@ -75,6 +77,28 @@ public class ContributorAgreement implements Comparable<ContributorAgreement> {
this.agreementUrl = agreementUrl;
}
+ public List<String> getExcludeProjectsRegexes() {
+ if (excludeProjectsRegexes == null) {
+ excludeProjectsRegexes = new ArrayList<>();
+ }
+ return excludeProjectsRegexes;
+ }
+
+ public void setExcludeProjectsRegexes(List<String> excludeProjectsRegexes) {
+ this.excludeProjectsRegexes = excludeProjectsRegexes;
+ }
+
+ public List<String> getMatchProjectsRegexes() {
+ if (matchProjectsRegexes == null) {
+ matchProjectsRegexes = new ArrayList<>();
+ }
+ return matchProjectsRegexes;
+ }
+
+ public void setMatchProjectsRegexes(List<String> matchProjectsRegexes) {
+ this.matchProjectsRegexes = matchProjectsRegexes;
+ }
+
@Override
public int compareTo(ContributorAgreement o) {
return getName().compareTo(o.getName());
diff --git a/java/com/google/gerrit/common/data/GlobalCapability.java b/java/com/google/gerrit/common/data/GlobalCapability.java
index 3e112568da..fbe1deb75f 100644
--- a/java/com/google/gerrit/common/data/GlobalCapability.java
+++ b/java/com/google/gerrit/common/data/GlobalCapability.java
@@ -22,7 +22,7 @@ import java.util.List;
/** Server wide capabilities. Represented as {@link Permission} objects. */
public class GlobalCapability {
- /** Ability to access the database (with gsql). */
+ /** Ability to view code review metadata refs in repositories. */
public static final String ACCESS_DATABASE = "accessDatabase";
/**
diff --git a/java/com/google/gerrit/common/data/GroupDetail.java b/java/com/google/gerrit/common/data/GroupDetail.java
deleted file mode 100644
index 1ac06dbfe8..0000000000
--- a/java/com/google/gerrit/common/data/GroupDetail.java
+++ /dev/null
@@ -1,37 +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.common.data;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import java.util.Set;
-
-public class GroupDetail {
- private Set<Account.Id> members;
- private Set<AccountGroup.UUID> includes;
-
- public GroupDetail(Set<Account.Id> members, Set<AccountGroup.UUID> includes) {
- this.members = members;
- this.includes = includes;
- }
-
- public Set<Account.Id> getMembers() {
- return members;
- }
-
- public Set<AccountGroup.UUID> getIncludes() {
- return includes;
- }
-}
diff --git a/java/com/google/gerrit/common/data/GroupInfo.java b/java/com/google/gerrit/common/data/GroupInfo.java
deleted file mode 100644
index 2b5bf1b27d..0000000000
--- a/java/com/google/gerrit/common/data/GroupInfo.java
+++ /dev/null
@@ -1,71 +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.common.data;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-
-/** Summary information about an {@link AccountGroup}, for simple tabular displays. */
-public class GroupInfo {
- protected AccountGroup.UUID uuid;
- protected String name;
- protected String description;
- protected String url;
-
- protected GroupInfo() {}
-
- /**
- * Create an anonymous group info, when only the id is known.
- *
- * <p>This constructor should only be a last-ditch effort, when the usual group lookup has failed
- * and a stale group id has been discovered in the data store.
- */
- public GroupInfo(AccountGroup.UUID uuid) {
- this.uuid = uuid;
- }
-
- /**
- * Create a group description from a real data store record.
- *
- * @param a the data store record holding the specific group details.
- */
- public GroupInfo(GroupDescription.Basic a) {
- uuid = a.getGroupUUID();
- name = a.getName();
- url = a.getUrl();
-
- if (a instanceof GroupDescription.Internal) {
- description = ((GroupDescription.Internal) a).getDescription();
- }
- }
-
- /** @return the unique local id of the group */
- public AccountGroup.UUID getId() {
- return uuid;
- }
-
- /** @return the name of the group; null if not supplied */
- public String getName() {
- return name;
- }
-
- /** @return the description of the group; null if not supplied */
- public String getDescription() {
- return description;
- }
-
- public String getUrl() {
- return url;
- }
-}
diff --git a/java/com/google/gerrit/common/data/HostPageData.java b/java/com/google/gerrit/common/data/HostPageData.java
deleted file mode 100644
index 517c520e88..0000000000
--- a/java/com/google/gerrit/common/data/HostPageData.java
+++ /dev/null
@@ -1,60 +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.common.data;
-
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import java.util.Date;
-import java.util.List;
-
-/** Data sent as part of the host page, to bootstrap the UI. */
-public class HostPageData {
- /**
- * Name of the cookie in which the XSRF token is sent from the server to the client during host
- * page bootstrapping.
- */
- public static final String XSRF_COOKIE_NAME = "XSRF_TOKEN";
-
- /**
- * Name of the HTTP header in which the client must send the XSRF token to the server on each
- * request.
- */
- public static final String XSRF_HEADER_NAME = "X-Gerrit-Auth";
-
- public String version;
- public DiffPreferencesInfo accountDiffPref;
- public Theme theme;
- public List<String> plugins;
- public List<Message> messages;
- public Integer pluginsLoadTimeout;
- public boolean isNoteDbEnabled;
- public boolean canLoadInIFrame;
-
- public static class Theme {
- public String backgroundColor;
- public String topMenuColor;
- public String textColor;
- public String trimColor;
- public String selectionColor;
- public String changeTableOutdatedColor;
- public String tableOddRowColor;
- public String tableEvenRowColor;
- }
-
- public static class Message {
- public String id;
- public Date redisplay;
- public String html;
- }
-}
diff --git a/java/com/google/gerrit/common/data/LabelType.java b/java/com/google/gerrit/common/data/LabelType.java
index 31d31d7e08..42945c452d 100644
--- a/java/com/google/gerrit/common/data/LabelType.java
+++ b/java/com/google/gerrit/common/data/LabelType.java
@@ -94,8 +94,7 @@ public class LabelType {
protected String name;
- // String rather than LabelFunction for backwards compatibility with GWT JSON interface.
- protected String functionName;
+ protected LabelFunction function;
protected boolean copyMinScore;
protected boolean copyMaxScore;
@@ -123,7 +122,7 @@ public class LabelType {
values = sortValues(valueList);
defaultValue = 0;
- functionName = LabelFunction.MAX_WITH_BLOCK.getFunctionName();
+ function = LabelFunction.MAX_WITH_BLOCK;
maxNegative = Short.MIN_VALUE;
maxPositive = Short.MAX_VALUE;
@@ -160,15 +159,11 @@ public class LabelType {
}
public LabelFunction getFunction() {
- if (functionName == null) {
- return null;
- }
- return LabelFunction.parse(functionName)
- .orElseThrow(() -> new IllegalStateException("Unsupported functionName: " + functionName));
+ return function;
}
public void setFunction(@Nullable LabelFunction function) {
- this.functionName = function != null ? function.getFunctionName() : null;
+ this.function = function;
}
public boolean canOverride() {
diff --git a/java/com/google/gerrit/common/data/ParameterizedString.java b/java/com/google/gerrit/common/data/ParameterizedString.java
index 28e47ee67a..84bb535829 100644
--- a/java/com/google/gerrit/common/data/ParameterizedString.java
+++ b/java/com/google/gerrit/common/data/ParameterizedString.java
@@ -40,7 +40,7 @@ public class ParameterizedString {
private ParameterizedString(Constant c) {
pattern = c.text;
rawPattern = c.text;
- patternOps = Collections.<Format>singletonList(c);
+ patternOps = Collections.singletonList(c);
parameters = Collections.emptyList();
}
@@ -60,7 +60,7 @@ public class ParameterizedString {
break;
}
- raw.append(pattern.substring(i, b));
+ raw.append(pattern, i, b);
ops.add(new Constant(pattern.substring(i, b)));
// "${parameter[.functions...]}" -> "parameter[.functions...]"
diff --git a/java/com/google/gerrit/common/data/Permission.java b/java/com/google/gerrit/common/data/Permission.java
index de6108e0b1..3ba0ba75d1 100644
--- a/java/com/google/gerrit/common/data/Permission.java
+++ b/java/com/google/gerrit/common/data/Permission.java
@@ -14,6 +14,7 @@
package com.google.gerrit.common.data;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -45,6 +46,7 @@ public class Permission implements Comparable<Permission> {
public static final String REMOVE_REVIEWER = "removeReviewer";
public static final String SUBMIT = "submit";
public static final String SUBMIT_AS = "submitAs";
+ public static final String TOGGLE_WORK_IN_PROGRESS_STATE = "toggleWipState";
public static final String VIEW_PRIVATE_CHANGES = "viewPrivateChanges";
private static final List<String> NAMES_LC;
@@ -77,6 +79,7 @@ public class Permission implements Comparable<Permission> {
NAMES_LC.add(REMOVE_REVIEWER.toLowerCase());
NAMES_LC.add(SUBMIT.toLowerCase());
NAMES_LC.add(SUBMIT_AS.toLowerCase());
+ NAMES_LC.add(TOGGLE_WORK_IN_PROGRESS_STATE.toLowerCase());
NAMES_LC.add(VIEW_PRIVATE_CHANGES.toLowerCase());
LABEL_INDEX = NAMES_LC.indexOf(Permission.LABEL);
@@ -157,12 +160,8 @@ public class Permission implements Comparable<Permission> {
exclusiveGroup = newExclusiveGroup;
}
- // TODO(ekempin): Make this method return an ImmutableList once the GWT UI is gone.
- public List<PermissionRule> getRules() {
- if (rules == null) {
- return new ArrayList<>();
- }
- return new ArrayList<>(rules);
+ public ImmutableList<PermissionRule> getRules() {
+ return rules == null ? ImmutableList.of() : ImmutableList.copyOf(rules);
}
public void setRules(List<PermissionRule> list) {
diff --git a/java/com/google/gerrit/common/data/ProjectAccess.java b/java/com/google/gerrit/common/data/ProjectAccess.java
deleted file mode 100644
index ea17525a6d..0000000000
--- a/java/com/google/gerrit/common/data/ProjectAccess.java
+++ /dev/null
@@ -1,142 +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.common.data;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Project;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class ProjectAccess {
- protected Project.NameKey projectName;
- protected String revision;
- protected Project.NameKey inheritsFrom;
- protected List<AccessSection> local;
- protected Set<String> ownerOf;
- protected boolean isConfigVisible;
- protected boolean canUpload;
- protected LabelTypes labelTypes;
- protected Map<String, String> capabilities;
- protected Map<AccountGroup.UUID, GroupInfo> groupInfo;
- protected List<WebLinkInfoCommon> fileHistoryLinks;
-
- public ProjectAccess() {}
-
- public Project.NameKey getProjectName() {
- return projectName;
- }
-
- public void setProjectName(Project.NameKey projectName) {
- this.projectName = projectName;
- }
-
- public String getRevision() {
- return revision;
- }
-
- public void setRevision(String name) {
- revision = name;
- }
-
- public Project.NameKey getInheritsFrom() {
- return inheritsFrom;
- }
-
- public void setInheritsFrom(Project.NameKey name) {
- inheritsFrom = name;
- }
-
- public List<AccessSection> getLocal() {
- return local;
- }
-
- public void setLocal(List<AccessSection> as) {
- local = as;
- }
-
- public AccessSection getLocal(String name) {
- for (AccessSection s : local) {
- if (s.getName().equals(name)) {
- return s;
- }
- }
- return null;
- }
-
- public boolean isOwnerOf(AccessSection section) {
- return isOwnerOf(section.getName());
- }
-
- public boolean isOwnerOf(String name) {
- return ownerOf.contains(name);
- }
-
- public Set<String> getOwnerOf() {
- return ownerOf;
- }
-
- public void setOwnerOf(Set<String> refs) {
- ownerOf = refs;
- }
-
- public boolean isConfigVisible() {
- return isConfigVisible;
- }
-
- public void setConfigVisible(boolean isConfigVisible) {
- this.isConfigVisible = isConfigVisible;
- }
-
- public boolean canUpload() {
- return canUpload;
- }
-
- public void setCanUpload(boolean canUpload) {
- this.canUpload = canUpload;
- }
-
- public LabelTypes getLabelTypes() {
- return labelTypes;
- }
-
- public void setLabelTypes(LabelTypes labelTypes) {
- this.labelTypes = labelTypes;
- }
-
- public Map<String, String> getCapabilities() {
- return capabilities;
- }
-
- public void setCapabilities(Map<String, String> capabilities) {
- this.capabilities = capabilities;
- }
-
- public Map<AccountGroup.UUID, GroupInfo> getGroupInfo() {
- return groupInfo;
- }
-
- public void setGroupInfo(Map<AccountGroup.UUID, GroupInfo> m) {
- groupInfo = m;
- }
-
- public void setFileHistoryLinks(List<WebLinkInfoCommon> links) {
- fileHistoryLinks = links;
- }
-
- public List<WebLinkInfoCommon> getFileHistoryLinks() {
- return fileHistoryLinks;
- }
-}
diff --git a/java/com/google/gerrit/common/data/ProjectAdminService.java b/java/com/google/gerrit/common/data/ProjectAdminService.java
deleted file mode 100644
index e9a7c15ffc..0000000000
--- a/java/com/google/gerrit/common/data/ProjectAdminService.java
+++ /dev/null
@@ -1,49 +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.common.data;
-
-import com.google.gerrit.common.audit.Audit;
-import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.gwtjsonrpc.common.RpcImpl;
-import com.google.gwtjsonrpc.common.RpcImpl.Version;
-import java.util.List;
-
-@RpcImpl(version = Version.V2_0)
-public interface ProjectAdminService extends RemoteJsonService {
- void projectAccess(Project.NameKey projectName, AsyncCallback<ProjectAccess> callback);
-
- @Audit
- @SignInRequired
- void changeProjectAccess(
- Project.NameKey projectName,
- String baseRevision,
- String message,
- List<AccessSection> sections,
- Project.NameKey parentProjectName,
- AsyncCallback<ProjectAccess> callback);
-
- @SignInRequired
- void reviewProjectAccess(
- Project.NameKey projectName,
- String baseRevision,
- String message,
- List<AccessSection> sections,
- Project.NameKey parentProjectName,
- AsyncCallback<Change.Id> callback);
-}
diff --git a/java/com/google/gerrit/common/data/RefConfigSection.java b/java/com/google/gerrit/common/data/RefConfigSection.java
deleted file mode 100644
index 663379ab53..0000000000
--- a/java/com/google/gerrit/common/data/RefConfigSection.java
+++ /dev/null
@@ -1,60 +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.common.data;
-
-public abstract class RefConfigSection {
- /** Pattern that matches all references in a project. */
- public static final String ALL = "refs/*";
-
- /** Pattern that matches all branches in a project. */
- public static final String HEADS = "refs/heads/*";
-
- /** Prefix that triggers a regular expression pattern. */
- public static final String REGEX_PREFIX = "^";
-
- /** @return true if the name is likely to be a valid reference section name. */
- public static boolean isValid(String name) {
- return name.startsWith("refs/") || name.startsWith("^refs/");
- }
-
- protected String name;
-
- public RefConfigSection() {}
-
- public RefConfigSection(String name) {
- setName(name);
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof RefConfigSection)) {
- return false;
- }
- return name.equals(((RefConfigSection) obj).name);
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
- }
-}
diff --git a/java/com/google/gerrit/common/data/SshHostKey.java b/java/com/google/gerrit/common/data/SshHostKey.java
deleted file mode 100644
index 05f16111c7..0000000000
--- a/java/com/google/gerrit/common/data/SshHostKey.java
+++ /dev/null
@@ -1,45 +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.common.data;
-
-/** Description of the SSH daemon host key used by Gerrit. */
-public class SshHostKey {
- protected String hostIdent;
- protected String hostKey;
- protected String fingerprint;
-
- protected SshHostKey() {}
-
- public SshHostKey(String hi, String hk, String fp) {
- hostIdent = hi;
- hostKey = hk;
- fingerprint = fp;
- }
-
- /** @return host name string, to appear in a known_hosts file. */
- public String getHostIdent() {
- return hostIdent;
- }
-
- /** @return base 64 encoded host key string, starting with key type. */
- public String getHostKey() {
- return hostKey;
- }
-
- /** @return the key fingerprint, as displayed by a connecting client. */
- public String getFingerprint() {
- return fingerprint;
- }
-}
diff --git a/java/com/google/gerrit/common/data/SubmitRecord.java b/java/com/google/gerrit/common/data/SubmitRecord.java
index 8638d6d74d..22861b24b1 100644
--- a/java/com/google/gerrit/common/data/SubmitRecord.java
+++ b/java/com/google/gerrit/common/data/SubmitRecord.java
@@ -14,7 +14,6 @@
package com.google.gerrit.common.data;
-import com.google.common.annotations.GwtIncompatible;
import com.google.gerrit.reviewdb.client.Account;
import java.util.Collection;
import java.util.List;
@@ -65,7 +64,7 @@ public class SubmitRecord {
public Status status;
public List<Label> labels;
- @GwtIncompatible public List<SubmitRequirement> requirements;
+ public List<SubmitRequirement> requirements;
public String errorMessage;
public static class Label {
@@ -136,7 +135,6 @@ public class SubmitRecord {
}
}
- @GwtIncompatible
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -164,7 +162,6 @@ public class SubmitRecord {
return sb.toString();
}
- @GwtIncompatible
@Override
public boolean equals(Object o) {
if (o instanceof SubmitRecord) {
@@ -177,7 +174,6 @@ public class SubmitRecord {
return false;
}
- @GwtIncompatible
@Override
public int hashCode() {
return Objects.hash(status, labels, errorMessage, requirements);
diff --git a/java/com/google/gerrit/common/data/SubmitRequirement.java b/java/com/google/gerrit/common/data/SubmitRequirement.java
index 0c978ca539..66e647d6b6 100644
--- a/java/com/google/gerrit/common/data/SubmitRequirement.java
+++ b/java/com/google/gerrit/common/data/SubmitRequirement.java
@@ -17,13 +17,11 @@ package com.google.gerrit.common.data;
import static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
-import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
/** Describes a requirement to submit a change. */
-@GwtIncompatible
@AutoValue
@AutoValue.CopyAnnotations
public abstract class SubmitRequirement {
diff --git a/java/com/google/gerrit/common/data/SubscribeSection.java b/java/com/google/gerrit/common/data/SubscribeSection.java
index a3468d7e06..aaf0798428 100644
--- a/java/com/google/gerrit/common/data/SubscribeSection.java
+++ b/java/com/google/gerrit/common/data/SubscribeSection.java
@@ -14,7 +14,6 @@
package com.google.gerrit.common.data;
-import com.google.common.annotations.GwtIncompatible;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import java.util.ArrayList;
@@ -24,7 +23,6 @@ import java.util.List;
import org.eclipse.jgit.transport.RefSpec;
/** Portion of a {@link Project} describing superproject subscription rules. */
-@GwtIncompatible("Unemulated org.eclipse.jgit.transport.RefSpec")
public class SubscribeSection {
private final List<RefSpec> multiMatchRefSpecs;
diff --git a/java/com/google/gerrit/common/data/SystemInfoService.java b/java/com/google/gerrit/common/data/SystemInfoService.java
deleted file mode 100644
index d88b638682..0000000000
--- a/java/com/google/gerrit/common/data/SystemInfoService.java
+++ /dev/null
@@ -1,31 +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.common.data;
-
-import com.google.gwtjsonrpc.common.AllowCrossSiteRequest;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.gwtjsonrpc.common.RpcImpl;
-import com.google.gwtjsonrpc.common.RpcImpl.Version;
-import com.google.gwtjsonrpc.common.VoidResult;
-import java.util.List;
-
-@RpcImpl(version = Version.V2_0)
-public interface SystemInfoService extends RemoteJsonService {
- @AllowCrossSiteRequest
- void daemonHostKeys(AsyncCallback<List<SshHostKey>> callback);
-
- void clientError(String message, AsyncCallback<VoidResult> callback);
-}
diff --git a/java/com/google/gerrit/common/data/WebLinkInfoCommon.java b/java/com/google/gerrit/common/data/WebLinkInfoCommon.java
deleted file mode 100644
index dd0a70a6d2..0000000000
--- a/java/com/google/gerrit/common/data/WebLinkInfoCommon.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2015 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.common.data;
-
-public class WebLinkInfoCommon {
- public WebLinkInfoCommon() {}
-
- public String name;
- public String imageUrl;
- public String url;
- public String target;
-}
diff --git a/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java b/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
index 1988d66603..265d5901d5 100644
--- a/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
+++ b/java/com/google/gerrit/common/data/testing/GroupReferenceSubject.java
@@ -20,14 +20,17 @@ import com.google.common.truth.ComparableSubject;
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.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
public class GroupReferenceSubject extends Subject<GroupReferenceSubject, GroupReference> {
public static GroupReferenceSubject assertThat(GroupReference group) {
- return assertAbout(GroupReferenceSubject::new).that(group);
+ return assertAbout(groupReferences()).that(group);
+ }
+
+ public static Subject.Factory<GroupReferenceSubject, GroupReference> groupReferences() {
+ return GroupReferenceSubject::new;
}
private GroupReferenceSubject(FailureMetadata metadata, GroupReference group) {
@@ -37,12 +40,12 @@ public class GroupReferenceSubject extends Subject<GroupReferenceSubject, GroupR
public ComparableSubject<?, AccountGroup.UUID> groupUuid() {
isNotNull();
GroupReference group = actual();
- return Truth.assertThat(group.getUUID()).named("groupUuid");
+ return check("groupUuid()").that(group.getUUID());
}
public StringSubject name() {
isNotNull();
GroupReference group = actual();
- return Truth.assertThat(group.getName()).named("name");
+ return check("name()").that(group.getName());
}
}
diff --git a/java/com/google/gerrit/common/errors/EmailException.java b/java/com/google/gerrit/common/errors/EmailException.java
deleted file mode 100644
index 635335d3d8..0000000000
--- a/java/com/google/gerrit/common/errors/EmailException.java
+++ /dev/null
@@ -1,29 +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.common.errors;
-
-public class EmailException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Mail Error: ";
-
- public EmailException(String msg) {
- super(MESSAGE + msg);
- }
-
- public EmailException(String msg, Throwable why) {
- super(MESSAGE + msg, why);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/InvalidNameException.java b/java/com/google/gerrit/common/errors/InvalidNameException.java
deleted file mode 100644
index d975aef37e..0000000000
--- a/java/com/google/gerrit/common/errors/InvalidNameException.java
+++ /dev/null
@@ -1,30 +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.common.errors;
-
-/** Error indicating the entity name is invalid as supplied. */
-public class InvalidNameException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Invalid Name";
-
- public InvalidNameException() {
- super(MESSAGE);
- }
-
- public InvalidNameException(String invalidName) {
- super(MESSAGE + ": " + invalidName);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/InvalidSshKeyException.java b/java/com/google/gerrit/common/errors/InvalidSshKeyException.java
deleted file mode 100644
index 0e5d80d8ee..0000000000
--- a/java/com/google/gerrit/common/errors/InvalidSshKeyException.java
+++ /dev/null
@@ -1,30 +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.common.errors;
-
-/** Error indicating the SSH key string is invalid as supplied. */
-public class InvalidSshKeyException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Invalid SSH Key";
-
- public InvalidSshKeyException() {
- super(MESSAGE);
- }
-
- public InvalidSshKeyException(Throwable cause) {
- super(MESSAGE, cause);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java b/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java
deleted file mode 100644
index ea20e2edb6..0000000000
--- a/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java
+++ /dev/null
@@ -1,26 +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.common.errors;
-
-/** Error indicating entity name is already taken by another entity. */
-public class NameAlreadyUsedException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Name Already Used: ";
-
- public NameAlreadyUsedException(String name) {
- super(MESSAGE + name);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/NoSuchAccountException.java b/java/com/google/gerrit/common/errors/NoSuchAccountException.java
deleted file mode 100644
index 90bf624928..0000000000
--- a/java/com/google/gerrit/common/errors/NoSuchAccountException.java
+++ /dev/null
@@ -1,26 +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.common.errors;
-
-/** Error indicating the account requested doesn't exist. */
-public class NoSuchAccountException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Not Found: ";
-
- public NoSuchAccountException(String who) {
- super(MESSAGE + who);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/NoSuchEntityException.java b/java/com/google/gerrit/common/errors/NoSuchEntityException.java
deleted file mode 100644
index 1829c8b175..0000000000
--- a/java/com/google/gerrit/common/errors/NoSuchEntityException.java
+++ /dev/null
@@ -1,30 +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.common.errors;
-
-/** Error indicating the entity requested doesn't exist. */
-public class NoSuchEntityException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Not Found";
-
- public NoSuchEntityException() {
- super(MESSAGE);
- }
-
- public NoSuchEntityException(String message) {
- super(message);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/NoSuchGroupException.java b/java/com/google/gerrit/common/errors/NoSuchGroupException.java
deleted file mode 100644
index 6e3db9e0c3..0000000000
--- a/java/com/google/gerrit/common/errors/NoSuchGroupException.java
+++ /dev/null
@@ -1,52 +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.common.errors;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-
-/** Indicates the account group does not exist. */
-public class NoSuchGroupException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Group Not Found: ";
-
- public NoSuchGroupException(AccountGroup.Id key) {
- this(key, null);
- }
-
- public NoSuchGroupException(AccountGroup.UUID key) {
- this(key, null);
- }
-
- public NoSuchGroupException(AccountGroup.Id key, Throwable why) {
- super(MESSAGE + key.toString(), why);
- }
-
- public NoSuchGroupException(AccountGroup.UUID key, Throwable why) {
- super(MESSAGE + key.toString(), why);
- }
-
- public NoSuchGroupException(AccountGroup.NameKey k, Throwable why) {
- super(MESSAGE + k.toString(), why);
- }
-
- public NoSuchGroupException(String who) {
- this(who, null);
- }
-
- public NoSuchGroupException(String who, Throwable why) {
- super(MESSAGE + who, why);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/NotSignedInException.java b/java/com/google/gerrit/common/errors/NotSignedInException.java
deleted file mode 100644
index 65caf020dc..0000000000
--- a/java/com/google/gerrit/common/errors/NotSignedInException.java
+++ /dev/null
@@ -1,26 +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.common.errors;
-
-/** Error stating the user must be signed-in in order to perform this action. */
-public class NotSignedInException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Not Signed In";
-
- public NotSignedInException() {
- super(MESSAGE);
- }
-}
diff --git a/java/com/google/gerrit/common/errors/UpdateParentFailedException.java b/java/com/google/gerrit/common/errors/UpdateParentFailedException.java
deleted file mode 100644
index 16d5240e80..0000000000
--- a/java/com/google/gerrit/common/errors/UpdateParentFailedException.java
+++ /dev/null
@@ -1,26 +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.common.errors;
-
-/** Error indicating that updating a parent project failed. */
-public class UpdateParentFailedException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Update Parent Project Failed: ";
-
- public UpdateParentFailedException(String message, Throwable why) {
- super(MESSAGE + ": " + message, why);
- }
-}
diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 2ab9c929b7..8ed9729ec7 100644
--- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -15,14 +15,16 @@
package com.google.gerrit.elasticsearch;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.CharStreams;
import com.google.gerrit.common.Nullable;
@@ -30,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.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.FieldType;
import com.google.gerrit.index.Index;
@@ -37,8 +40,12 @@ 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.FieldBundle;
+import com.google.gerrit.index.query.ListResultSet;
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;
@@ -47,9 +54,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
+import com.google.protobuf.MessageLite;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -59,7 +64,6 @@ import java.net.URLEncoder;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -88,14 +92,22 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
}
protected static <T> List<T> decodeProtos(
- JsonObject doc, String fieldName, ProtobufCodec<T> codec) {
+ JsonObject doc, String fieldName, ProtoConverter<?, T> converter) {
JsonArray field = doc.getAsJsonArray(fieldName);
if (field == null) {
return null;
}
- return FluentIterable.from(field)
- .transform(i -> codec.decode(decodeBase64(i.getAsString())))
- .toList();
+ return Streams.stream(field)
+ .map(JsonElement::getAsString)
+ .map(AbstractElasticIndex::decodeBase64)
+ .map(bytes -> parseProtoFrom(bytes, converter))
+ .collect(toImmutableList());
+ }
+
+ protected static <P extends MessageLite, T> T parseProtoFrom(
+ byte[] bytes, ProtoConverter<P, T> converter) {
+ P message = Protos.parseUnchecked(converter.getParser(), bytes);
+ return converter.fromProto(message);
}
static String getContent(Response response) throws IOException {
@@ -149,23 +161,23 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
}
@Override
- public void markReady(boolean ready) throws IOException {
+ public void markReady(boolean ready) {
IndexUtils.setReady(sitePaths, indexNameRaw, schema.getVersion(), ready);
}
@Override
- public void delete(K id) throws IOException {
+ public void delete(K id) {
String uri = getURI(type, BULK);
Response response = postRequest(uri, getDeleteActions(id), getRefreshParam());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
- throw new IOException(
+ throw new StorageException(
String.format("Failed to delete %s from index %s: %s", id, indexName, statusCode));
}
}
@Override
- public void deleteAll() throws IOException {
+ public void deleteAll() {
// Delete the index, if it exists.
String endpoint = indexName + client.adapter().indicesExistParams();
Response response = performRequest("HEAD", endpoint);
@@ -174,7 +186,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
response = performRequest("DELETE", indexName);
statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
- throw new IOException(
+ throw new StorageException(
String.format("Failed to delete index %s: %s", indexName, statusCode));
}
}
@@ -187,7 +199,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
String error = String.format("Failed to create index %s: %s", indexName, statusCode);
- throw new IOException(error);
+ throw new StorageException(error);
}
}
@@ -293,22 +305,25 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
return sortArray;
}
- protected String getURI(String type, String request) throws UnsupportedEncodingException {
- String encodedIndexName = URLEncoder.encode(indexName, UTF_8.toString());
- if (SEARCH.equals(request) && client.adapter().omitType()) {
- return encodedIndexName + "/" + request;
+ protected String getURI(String type, String request) {
+ try {
+ String encodedIndexName = URLEncoder.encode(indexName, UTF_8.toString());
+ if (SEARCH.equals(request) && client.adapter().omitType()) {
+ return encodedIndexName + "/" + request;
+ }
+ String encodedTypeIfAny =
+ client.adapter().omitType() ? "" : "/" + URLEncoder.encode(type, UTF_8.toString());
+ return encodedIndexName + encodedTypeIfAny + "/" + request;
+ } catch (UnsupportedEncodingException e) {
+ throw new StorageException(e);
}
- String encodedTypeIfAny =
- client.adapter().omitType() ? "" : "/" + URLEncoder.encode(type, UTF_8.toString());
- return encodedIndexName + encodedTypeIfAny + "/" + request;
}
- protected Response postRequest(String uri, Object payload) throws IOException {
+ protected Response postRequest(String uri, Object payload) {
return performRequest("POST", uri, payload);
}
- protected Response postRequest(String uri, Object payload, Map<String, String> params)
- throws IOException {
+ protected Response postRequest(String uri, Object payload, Map<String, String> params) {
return performRequest("POST", uri, payload, params);
}
@@ -316,18 +331,16 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
return target.substring(0, target.length() - 1) + "," + addition.substring(1);
}
- private Response performRequest(String method, String uri) throws IOException {
+ private Response performRequest(String method, String uri) {
return performRequest(method, uri, null);
}
- private Response performRequest(String method, String uri, @Nullable Object payload)
- throws IOException {
+ private Response performRequest(String method, String uri, @Nullable Object payload) {
return performRequest(method, uri, payload, Collections.emptyMap());
}
private Response performRequest(
- String method, String uri, @Nullable Object payload, Map<String, String> params)
- throws IOException {
+ String method, String uri, @Nullable Object payload, Map<String, String> params) {
Request request = new Request(method, uri.startsWith("/") ? uri : "/" + uri);
if (payload != null) {
String payloadStr = payload instanceof String ? (String) payload : payload.toString();
@@ -336,7 +349,11 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
for (Map.Entry<String, String> entry : params.entrySet()) {
request.addParameter(entry.getKey(), entry.getValue());
}
- return client.get().performRequest(request);
+ try {
+ return client.get().performRequest(request);
+ } catch (IOException e) {
+ throw new StorageException(e);
+ }
}
protected class ElasticQuerySource implements DataSource<V> {
@@ -364,18 +381,17 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
}
@Override
- public ResultSet<V> read() throws OrmException {
+ public ResultSet<V> read() {
return readImpl(doc -> AbstractElasticIndex.this.fromDocument(doc, opts.fields()));
}
@Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
+ public ResultSet<FieldBundle> readRaw() {
return readImpl(AbstractElasticIndex.this::toFieldBundle);
}
- private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException {
+ private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) {
try {
- List<T> results = Collections.emptyList();
String uri = getURI(index, SEARCH);
Response response =
performRequest(HttpPost.METHOD_NAME, uri, search, Collections.emptyMap());
@@ -386,36 +402,21 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
new JsonParser().parse(content).getAsJsonObject().getAsJsonObject("hits");
if (obj.get("hits") != null) {
JsonArray json = obj.getAsJsonArray("hits");
- results = Lists.newArrayListWithCapacity(json.size());
+ ImmutableList.Builder<T> results = ImmutableList.builderWithExpectedSize(json.size());
for (int i = 0; i < json.size(); i++) {
T mapperResult = mapper.apply(json.get(i).getAsJsonObject());
if (mapperResult != null) {
results.add(mapperResult);
}
}
+ return new ListResultSet<>(results.build());
}
} else {
logger.atSevere().log(statusLine.getReasonPhrase());
}
- final List<T> r = Collections.unmodifiableList(results);
- return new ResultSet<T>() {
- @Override
- public Iterator<T> iterator() {
- return r.iterator();
- }
-
- @Override
- public List<T> toList() {
- return r;
- }
-
- @Override
- public void close() {
- // Do nothing.
- }
- };
+ return new ListResultSet<>(ImmutableList.of());
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
}
diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD
index c71d536121..a9b145bfbc 100644
--- a/java/com/google/gerrit/elasticsearch/BUILD
+++ b/java/com/google/gerrit/elasticsearch/BUILD
@@ -6,16 +6,17 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
+ "//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",
"//lib:gson",
"//lib:guava",
- "//lib:gwtorm",
"//lib:protobuf",
"//lib/commons:codec",
"//lib/commons:lang",
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index 7f05817723..f646efcb8a 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -20,6 +20,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.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
@@ -38,7 +39,6 @@ import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
import java.util.Set;
import org.apache.http.HttpStatus;
import org.elasticsearch.client.Response;
@@ -73,14 +73,14 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
}
@Override
- public void replace(AccountState as) throws IOException {
+ public void replace(AccountState as) {
BulkRequest bulk = new IndexRequest(getId(as), indexName).add(new UpdateRequest<>(schema, as));
String uri = getURI(type, BULK);
Response response = postRequest(uri, bulk, getRefreshParam());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
- throw new IOException(
+ throw new StorageException(
String.format(
"Failed to replace account %s in index %s: %s",
as.getAccount().getId(), indexName, statusCode));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 6086b2d75d..ca2b1a8203 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -14,9 +14,6 @@
package com.google.gerrit.elasticsearch;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.APPROVAL_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.CHANGE_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.PATCH_SET_CODEC;
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.nio.charset.StandardCharsets.UTF_8;
@@ -33,6 +30,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.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
@@ -41,7 +39,9 @@ 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.server.ReviewDb;
+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;
@@ -56,9 +56,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -87,34 +85,31 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
private static final String CLOSED_CHANGES = "closed_" + CHANGES;
private final ChangeMapping mapping;
- private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final Schema<ChangeData> schema;
@Inject
ElasticChangeIndex(
ElasticConfiguration cfg,
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
SitePaths sitePaths,
ElasticRestClientProvider clientBuilder,
@Assisted Schema<ChangeData> schema) {
super(cfg, sitePaths, schema, clientBuilder, CHANGES);
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.schema = schema;
mapping = new ChangeMapping(schema, client.adapter());
}
@Override
- public void replace(ChangeData cd) throws IOException {
+ public void replace(ChangeData cd) {
BulkRequest bulk = new IndexRequest(getId(cd), indexName).add(new UpdateRequest<>(schema, cd));
String uri = getURI(type, BULK);
Response response = postRequest(uri, bulk, getRefreshParam());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
- throw new IOException(
+ throw new StorageException(
String.format(
"Failed to replace change %s in index %s: %s", cd.getId(), indexName, statusCode));
}
@@ -187,21 +182,24 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt();
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
String projectName = requireNonNull(source.get(ChangeField.PROJECT.getName()).getAsString());
- return changeDataFactory.create(
- db.get(), new Project.NameKey(projectName), new Change.Id(id));
+ return changeDataFactory.create(new Project.NameKey(projectName), new Change.Id(id));
}
ChangeData cd =
- changeDataFactory.create(db.get(), CHANGE_CODEC.decode(decodeBase64(c.getAsString())));
+ changeDataFactory.create(
+ parseProtoFrom(decodeBase64(c.getAsString()), ChangeProtoConverter.INSTANCE));
// Any decoding that is done here must also be done in {@link LuceneChangeIndex}.
// Patch sets.
- cd.setPatchSets(decodeProtos(source, ChangeField.PATCH_SET.getName(), PATCH_SET_CODEC));
+ cd.setPatchSets(
+ decodeProtos(source, ChangeField.PATCH_SET.getName(), PatchSetProtoConverter.INSTANCE));
// Approvals.
if (source.get(ChangeField.APPROVAL.getName()) != null) {
- cd.setCurrentApprovals(decodeProtos(source, ChangeField.APPROVAL.getName(), APPROVAL_CODEC));
+ cd.setCurrentApprovals(
+ decodeProtos(
+ source, ChangeField.APPROVAL.getName(), PatchSetApprovalProtoConverter.INSTANCE));
} else if (fields.contains(ChangeField.APPROVAL.getName())) {
cd.setCurrentApprovals(Collections.emptyList());
}
diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
index 2c10a2c766..eff3d52de9 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.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.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
@@ -36,7 +37,6 @@ import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
import java.util.Set;
import org.apache.http.HttpStatus;
import org.elasticsearch.client.Response;
@@ -71,7 +71,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
}
@Override
- public void replace(InternalGroup group) throws IOException {
+ public void replace(InternalGroup group) {
BulkRequest bulk =
new IndexRequest(getId(group), indexName).add(new UpdateRequest<>(schema, group));
@@ -79,7 +79,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
Response response = postRequest(uri, bulk, getRefreshParam());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
- throw new IOException(
+ throw new StorageException(
String.format(
"Failed to replace group %s in index %s: %s",
group.getGroupUUID().get(), indexName, statusCode));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java
index 8011efa425..b9d86d5967 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java
@@ -17,7 +17,6 @@ package com.google.gerrit.elasticsearch;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.Schema;
@@ -26,6 +25,7 @@ import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.GerritIndexStatus;
import com.google.gerrit.server.index.OnlineUpgradeListener;
import com.google.gerrit.server.index.VersionManager;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -45,7 +45,7 @@ public class ElasticIndexVersionManager extends VersionManager {
ElasticIndexVersionManager(
@GerritServerConfig Config cfg,
SitePaths sitePaths,
- DynamicSet<OnlineUpgradeListener> listeners,
+ PluginSetContext<OnlineUpgradeListener> listeners,
Collection<IndexDefinition<?, ?, ?>> defs,
ElasticIndexVersionDiscovery versionDiscovery) {
super(sitePaths, listeners, defs, VersionManager.getOnlineUpgrade(cfg));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
index d8c53ebbe3..a0ebb07ba3 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.exceptions.StorageException;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.project.ProjectData;
@@ -36,7 +37,6 @@ import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
import java.util.Set;
import org.apache.http.HttpStatus;
import org.elasticsearch.client.Response;
@@ -71,7 +71,7 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P
}
@Override
- public void replace(ProjectData projectState) throws IOException {
+ public void replace(ProjectData projectState) {
BulkRequest bulk =
new IndexRequest(projectState.getProject().getName(), indexName)
.add(new UpdateRequest<>(schema, projectState));
@@ -80,7 +80,7 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P
Response response = postRequest(uri, bulk, getRefreshParam());
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
- throw new IOException(
+ throw new StorageException(
String.format(
"Failed to replace project %s in index %s: %s",
projectState.getProject().getName(), indexName, statusCode));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
index 7233682665..779d433615 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
@@ -14,7 +14,7 @@
package com.google.gerrit.elasticsearch;
-import static com.google.gerrit.elasticsearch.ElasticVersion.V6_7;
+import static com.google.gerrit.elasticsearch.ElasticVersion.V6_8;
public class ElasticQueryAdapter {
static final String V6_TYPE = "_doc";
@@ -41,13 +41,15 @@ public class ElasticQueryAdapter {
this.defaultNumberOfShards = version.isV7OrLater() ? 1 : 5;
this.versionDiscoveryUrl = version.isV6OrLater() ? "/%s*" : "/%s*/_aliases";
this.searchFilteringName = "_source";
- this.indicesExistParams =
- version.isAtLeastMinorVersion(V6_7) ? INDICES + "&" + INCLUDE_TYPE : INDICES;
this.exactFieldType = "keyword";
this.stringFieldType = "text";
this.indexProperty = "true";
this.rawFieldsKey = "_source";
- this.includeTypeNameParam = version.isAtLeastMinorVersion(V6_7) ? "?" + INCLUDE_TYPE : "";
+
+ // Since v6.7 (end-of-life), in fact, for these two parameters:
+ this.indicesExistParams =
+ version.isAtLeastMinorVersion(V6_8) ? INDICES + "&" + INCLUDE_TYPE : INDICES;
+ this.includeTypeNameParam = version.isAtLeastMinorVersion(V6_8) ? "?" + INCLUDE_TYPE : "";
}
public String searchFilteringName() {
diff --git a/java/com/google/gerrit/elasticsearch/ElasticVersion.java b/java/com/google/gerrit/elasticsearch/ElasticVersion.java
index 746a38607d..b3f1471517 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticVersion.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticVersion.java
@@ -18,8 +18,6 @@ import com.google.common.base.Joiner;
import java.util.regex.Pattern;
public enum ElasticVersion {
- V6_6("6.6.*"),
- V6_7("6.7.*"),
V6_8("6.8.*"),
V7_0("7.0.*"),
V7_1("7.1.*"),
@@ -28,7 +26,8 @@ public enum ElasticVersion {
V7_4("7.4.*"),
V7_5("7.5.*"),
V7_6("7.6.*"),
- V7_7("7.7.*");
+ V7_7("7.7.*"),
+ V7_8("7.8.*");
private final String version;
private final Pattern pattern;
diff --git a/java/com/google/gerrit/exceptions/BUILD b/java/com/google/gerrit/exceptions/BUILD
new file mode 100644
index 0000000000..e08c3fd79f
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/BUILD
@@ -0,0 +1,8 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "exceptions",
+ srcs = glob(["*.java"]),
+ visibility = ["//visibility:public"],
+ deps = ["//java/com/google/gerrit/reviewdb:server"],
+)
diff --git a/java/com/google/gerrit/exceptions/DuplicateKeyException.java b/java/com/google/gerrit/exceptions/DuplicateKeyException.java
new file mode 100644
index 0000000000..d052450f74
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/DuplicateKeyException.java
@@ -0,0 +1,28 @@
+// Copyright 2009 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.gerrit.exceptions;
+
+/** Indicates one or more entities were concurrently inserted with the same key. */
+public class DuplicateKeyException extends StorageException {
+ private static final long serialVersionUID = 1L;
+
+ public DuplicateKeyException(String msg) {
+ super(msg);
+ }
+
+ public DuplicateKeyException(String msg, Throwable why) {
+ super(msg, why);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/EmailException.java b/java/com/google/gerrit/exceptions/EmailException.java
new file mode 100644
index 0000000000..a278eedaa8
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/EmailException.java
@@ -0,0 +1,29 @@
+// 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.exceptions;
+
+public class EmailException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Mail Error: ";
+
+ public EmailException(String msg) {
+ super(MESSAGE + msg);
+ }
+
+ public EmailException(String msg, Throwable why) {
+ super(MESSAGE + msg, why);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/InvalidNameException.java b/java/com/google/gerrit/exceptions/InvalidNameException.java
new file mode 100644
index 0000000000..4539500b3b
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/InvalidNameException.java
@@ -0,0 +1,30 @@
+// 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.exceptions;
+
+/** Error indicating the entity name is invalid as supplied. */
+public class InvalidNameException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Invalid Name";
+
+ public InvalidNameException() {
+ super(MESSAGE);
+ }
+
+ public InvalidNameException(String invalidName) {
+ super(MESSAGE + ": " + invalidName);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/InvalidSshKeyException.java b/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
new file mode 100644
index 0000000000..dda80cf3ce
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
@@ -0,0 +1,30 @@
+// 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.exceptions;
+
+/** Error indicating the SSH key string is invalid as supplied. */
+public class InvalidSshKeyException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Invalid SSH Key";
+
+ public InvalidSshKeyException() {
+ super(MESSAGE);
+ }
+
+ public InvalidSshKeyException(Throwable cause) {
+ super(MESSAGE, cause);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/NameAlreadyUsedException.java b/java/com/google/gerrit/exceptions/NameAlreadyUsedException.java
new file mode 100644
index 0000000000..df2631b21a
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/NameAlreadyUsedException.java
@@ -0,0 +1,26 @@
+// 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.exceptions;
+
+/** Error indicating entity name is already taken by another entity. */
+public class NameAlreadyUsedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Name Already Used: ";
+
+ public NameAlreadyUsedException(String name) {
+ super(MESSAGE + name);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/NoSuchAccountException.java b/java/com/google/gerrit/exceptions/NoSuchAccountException.java
new file mode 100644
index 0000000000..d753128329
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/NoSuchAccountException.java
@@ -0,0 +1,26 @@
+// 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.exceptions;
+
+/** Error indicating the account requested doesn't exist. */
+public class NoSuchAccountException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Not Found: ";
+
+ public NoSuchAccountException(String who) {
+ super(MESSAGE + who);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/NoSuchEntityException.java b/java/com/google/gerrit/exceptions/NoSuchEntityException.java
new file mode 100644
index 0000000000..c812a38207
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/NoSuchEntityException.java
@@ -0,0 +1,30 @@
+// 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.exceptions;
+
+/** Error indicating the entity requested doesn't exist. */
+public class NoSuchEntityException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Not Found";
+
+ public NoSuchEntityException() {
+ super(MESSAGE);
+ }
+
+ public NoSuchEntityException(String message) {
+ super(message);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/NoSuchGroupException.java b/java/com/google/gerrit/exceptions/NoSuchGroupException.java
new file mode 100644
index 0000000000..dca28cb91b
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/NoSuchGroupException.java
@@ -0,0 +1,52 @@
+// 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.exceptions;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+/** Indicates the account group does not exist. */
+public class NoSuchGroupException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Group Not Found: ";
+
+ public NoSuchGroupException(AccountGroup.Id key) {
+ this(key, null);
+ }
+
+ public NoSuchGroupException(AccountGroup.UUID key) {
+ this(key, null);
+ }
+
+ public NoSuchGroupException(AccountGroup.Id key, Throwable why) {
+ super(MESSAGE + key.toString(), why);
+ }
+
+ public NoSuchGroupException(AccountGroup.UUID key, Throwable why) {
+ super(MESSAGE + key.toString(), why);
+ }
+
+ public NoSuchGroupException(AccountGroup.NameKey k, Throwable why) {
+ super(MESSAGE + k.toString(), why);
+ }
+
+ public NoSuchGroupException(String who) {
+ this(who, null);
+ }
+
+ public NoSuchGroupException(String who, Throwable why) {
+ super(MESSAGE + who, why);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/NotSignedInException.java b/java/com/google/gerrit/exceptions/NotSignedInException.java
new file mode 100644
index 0000000000..1919dc39ba
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/NotSignedInException.java
@@ -0,0 +1,26 @@
+// 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.exceptions;
+
+/** Error stating the user must be signed-in in order to perform this action. */
+public class NotSignedInException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Not Signed In";
+
+ public NotSignedInException() {
+ super(MESSAGE);
+ }
+}
diff --git a/java/com/google/gerrit/exceptions/StorageException.java b/java/com/google/gerrit/exceptions/StorageException.java
new file mode 100644
index 0000000000..a788fffd6e
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/StorageException.java
@@ -0,0 +1,44 @@
+// 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.gerrit.exceptions;
+
+/**
+ * Any read/write error in a storage layer.
+ *
+ * <p>This includes but is not limited to:
+ *
+ * <ul>
+ * <li>NoteDb exceptions
+ * <li>Secondary index exceptions
+ * <li>{@code AccountPatchReviewStore} exceptions
+ * <li>Wrapped JGit exceptions
+ * <li>Other wrapped {@code IOException}s
+ * </ul>
+ */
+public class StorageException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public StorageException(String message) {
+ super(message);
+ }
+
+ public StorageException(String message, Throwable why) {
+ super(message, why);
+ }
+
+ public StorageException(Throwable why) {
+ super(why);
+ }
+}
diff --git a/java/com/google/gerrit/extensions/BUILD b/java/com/google/gerrit/extensions/BUILD
index 5ba510c8ca..b5e58630d7 100644
--- a/java/com/google/gerrit/extensions/BUILD
+++ b/java/com/google/gerrit/extensions/BUILD
@@ -1,18 +1,8 @@
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:gwt.bzl", "gwt_module")
load("//tools/bzl:javadoc.bzl", "java_doc")
-EXT_API_SRCS = glob(["client/*.java"])
-
-gwt_module(
- name = "client",
- srcs = EXT_API_SRCS,
- gwt_xml = "Extensions.gwt.xml",
- visibility = ["//visibility:public"],
-)
-
java_binary(
name = "extension-api",
main_class = "Dummy",
@@ -26,7 +16,7 @@ java_library(
exports = [
":api",
"//lib:guava",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/guice:guice-servlet",
@@ -41,6 +31,7 @@ java_library(
deps = [
"//java/com/google/gerrit/common:annotations",
"//lib:guava",
+ "//lib/auto:auto-value-annotations",
"//lib/guice",
"//lib/guice:guice-assistedinject",
],
diff --git a/java/com/google/gerrit/extensions/Extensions.gwt.xml b/java/com/google/gerrit/extensions/Extensions.gwt.xml
deleted file mode 100644
index c857b604d9..0000000000
--- a/java/com/google/gerrit/extensions/Extensions.gwt.xml
+++ /dev/null
@@ -1,18 +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.
--->
-<module>
- <source path='client' />
-</module>
diff --git a/java/com/google/gerrit/extensions/annotations/ExportImpl.java b/java/com/google/gerrit/extensions/annotations/ExportImpl.java
deleted file mode 100644
index a3e72bccc3..0000000000
--- a/java/com/google/gerrit/extensions/annotations/ExportImpl.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2012 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.annotations;
-
-import java.io.Serializable;
-import java.lang.annotation.Annotation;
-
-final class ExportImpl implements Export, Serializable {
- private static final long serialVersionUID = 0;
- private final String value;
-
- ExportImpl(String value) {
- this.value = value;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return Export.class;
- }
-
- @Override
- public String value() {
- return value;
- }
-
- @Override
- public int hashCode() {
- return (127 * "value".hashCode()) ^ value.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof Export && value.equals(((Export) o).value());
- }
-
- @Override
- public String toString() {
- return "@" + Export.class.getName() + "(value=" + value + ")";
- }
-}
diff --git a/java/com/google/gerrit/extensions/annotations/Exports.java b/java/com/google/gerrit/extensions/annotations/Exports.java
index 1295ea0efa..9b196b6e01 100644
--- a/java/com/google/gerrit/extensions/annotations/Exports.java
+++ b/java/com/google/gerrit/extensions/annotations/Exports.java
@@ -14,11 +14,14 @@
package com.google.gerrit.extensions.annotations;
+import com.google.auto.value.AutoAnnotation;
+
/** Static constructors for {@link Export} annotations. */
public final class Exports {
/** Create an annotation to export under a specific name. */
- public static Export named(String name) {
- return new ExportImpl(name);
+ @AutoAnnotation
+ public static Export named(String value) {
+ return new AutoAnnotation_Exports_named(value);
}
/** Create an annotation to export based on a cannonical class name. */
diff --git a/java/com/google/gerrit/extensions/api/access/CoreOrPluginProjectPermission.java b/java/com/google/gerrit/extensions/api/access/CoreOrPluginProjectPermission.java
new file mode 100644
index 0000000000..de6898791d
--- /dev/null
+++ b/java/com/google/gerrit/extensions/api/access/CoreOrPluginProjectPermission.java
@@ -0,0 +1,18 @@
+// 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.api.access;
+
+/** A repository permission either defined in Gerrit core or a plugin. */
+public interface CoreOrPluginProjectPermission extends GerritPermission {}
diff --git a/java/com/google/gerrit/extensions/api/access/PluginProjectPermission.java b/java/com/google/gerrit/extensions/api/access/PluginProjectPermission.java
new file mode 100644
index 0000000000..a62fc63672
--- /dev/null
+++ b/java/com/google/gerrit/extensions/api/access/PluginProjectPermission.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.extensions.api.access;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/** Repository permissions defined by plugins. */
+public final class PluginProjectPermission implements CoreOrPluginProjectPermission {
+ public static final String PLUGIN_PERMISSION_NAME_PATTERN_STRING = "[a-zA-Z]+";
+ private static final Pattern PLUGIN_PERMISSION_PATTERN =
+ Pattern.compile("^" + PLUGIN_PERMISSION_NAME_PATTERN_STRING + "$");
+
+ private final String pluginName;
+ private final String permission;
+
+ public PluginProjectPermission(String pluginName, String permission) {
+ requireNonNull(pluginName, "pluginName");
+ requireNonNull(permission, "permission");
+ checkArgument(
+ isValidPluginPermissionName(permission), "invalid plugin permission name: ", permission);
+
+ this.pluginName = pluginName;
+ this.permission = permission;
+ }
+
+ public String pluginName() {
+ return pluginName;
+ }
+
+ public String permission() {
+ return permission;
+ }
+
+ @Override
+ public String describeForException() {
+ return permission + " for plugin " + pluginName;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pluginName, permission);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof PluginProjectPermission) {
+ PluginProjectPermission b = (PluginProjectPermission) other;
+ return pluginName.equals(b.pluginName) && permission.equals(b.permission);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("pluginName", pluginName)
+ .add("permission", permission)
+ .toString();
+ }
+
+ /**
+ * Checks if a given name is valid to be used for plugin permissions.
+ *
+ * @param name a name string.
+ * @return whether the name is valid as a plugin permission.
+ */
+ private static boolean isValidPluginPermissionName(String name) {
+ return PLUGIN_PERMISSION_PATTERN.matcher(name).matches();
+ }
+}
diff --git a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index 61853c9278..3d5fcccde8 100644
--- a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -14,6 +14,7 @@
package com.google.gerrit.extensions.api.changes;
+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;
@@ -48,17 +49,21 @@ public interface ChangeApi {
* @return API for accessing the revision.
* @throws RestApiException if an error occurred.
*/
- RevisionApi current() throws RestApiException;
+ default RevisionApi current() throws RestApiException {
+ return revision("current");
+ }
/**
* Look up a revision of a change by number.
*
* @see #current()
*/
- RevisionApi revision(int id) throws RestApiException;
+ default RevisionApi revision(int id) throws RestApiException {
+ return revision(Integer.toString(id));
+ }
/**
- * Look up a revision of a change by commit SHA-1.
+ * Look up a revision of a change by commit SHA-1 or other supported revision string.
*
* @see #current()
*/
@@ -78,15 +83,23 @@ public interface ChangeApi {
*/
ReviewerApi reviewer(String id) throws RestApiException;
- void abandon() throws RestApiException;
+ default void abandon() throws RestApiException {
+ abandon(new AbandonInput());
+ }
void abandon(AbandonInput in) throws RestApiException;
- void restore() throws RestApiException;
+ default void restore() throws RestApiException {
+ restore(new RestoreInput());
+ }
void restore(RestoreInput in) throws RestApiException;
- void move(String destination) throws RestApiException;
+ default void move(String destination) throws RestApiException {
+ MoveInput in = new MoveInput();
+ in.destinationBranch = destination;
+ move(in);
+ }
void move(MoveInput in) throws RestApiException;
@@ -135,7 +148,9 @@ public interface ChangeApi {
*
* @see Changes#id(int)
*/
- ChangeApi revert() throws RestApiException;
+ default ChangeApi revert() throws RestApiException {
+ return revert(new RevertInput());
+ }
/**
* Create a new change that reverts this change.
@@ -147,17 +162,26 @@ public interface ChangeApi {
/** Create a merge patch set for the change. */
ChangeInfo createMergePatchSet(MergePatchSetInput in) throws RestApiException;
- List<ChangeInfo> submittedTogether() throws RestApiException;
+ default List<ChangeInfo> submittedTogether() throws RestApiException {
+ SubmittedTogetherInfo info =
+ submittedTogether(
+ EnumSet.noneOf(ListChangesOption.class), EnumSet.noneOf(SubmittedTogetherOption.class));
+ return info.changes;
+ }
- SubmittedTogetherInfo submittedTogether(EnumSet<SubmittedTogetherOption> options)
- throws RestApiException;
+ default SubmittedTogetherInfo submittedTogether(EnumSet<SubmittedTogetherOption> options)
+ throws RestApiException {
+ return submittedTogether(EnumSet.noneOf(ListChangesOption.class), options);
+ }
SubmittedTogetherInfo submittedTogether(
EnumSet<ListChangesOption> listOptions, EnumSet<SubmittedTogetherOption> submitOptions)
throws RestApiException;
/** Rebase the current revision of a change using default options. */
- void rebase() throws RestApiException;
+ default void rebase() throws RestApiException {
+ rebase(new RebaseInput());
+ }
/** Rebase the current revision of a change. */
void rebase(RebaseInput in) throws RestApiException;
@@ -171,20 +195,37 @@ public interface ChangeApi {
IncludedInInfo includedIn() throws RestApiException;
- AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException;
+ default AddReviewerResult addReviewer(String reviewer) throws RestApiException {
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = reviewer;
+ return addReviewer(in);
+ }
- AddReviewerResult addReviewer(String in) throws RestApiException;
+ AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException;
SuggestedReviewersRequest suggestReviewers() throws RestApiException;
- SuggestedReviewersRequest suggestReviewers(String query) throws RestApiException;
+ default SuggestedReviewersRequest suggestReviewers(String query) throws RestApiException {
+ return suggestReviewers().withQuery(query);
+ }
/**
* Retrieve reviewers ({@code ReviewerState.REVIEWER} and {@code ReviewerState.CC}) on the change.
*/
List<ReviewerInfo> reviewers() throws RestApiException;
- ChangeInfo get(EnumSet<ListChangesOption> options) throws RestApiException;
+ ChangeInfo get(
+ EnumSet<ListChangesOption> options, ImmutableListMultimap<String, String> pluginOptions)
+ throws RestApiException;
+
+ default ChangeInfo get(ImmutableListMultimap<String, String> pluginOptions)
+ throws RestApiException {
+ return get(EnumSet.noneOf(ListChangesOption.class), pluginOptions);
+ }
+
+ default ChangeInfo get(EnumSet<ListChangesOption> options) throws RestApiException {
+ return get(options, ImmutableListMultimap.of());
+ }
default ChangeInfo get(Iterable<ListChangesOption> options) throws RestApiException {
return get(Sets.newEnumSet(options, ListChangesOption.class));
@@ -202,10 +243,16 @@ public interface ChangeApi {
* <li>{@code SKIP_MERGEABLE} is omitted, so the {@code mergeable} bit <em>is</em> set.
* </ul>
*/
- ChangeInfo get() throws RestApiException;
+ default ChangeInfo get() throws RestApiException {
+ return get(
+ EnumSet.complementOf(
+ EnumSet.of(ListChangesOption.CHECK, ListChangesOption.SKIP_MERGEABLE)));
+ }
/** {@link #get(ListChangesOption...)} with no options included. */
- ChangeInfo info() throws RestApiException;
+ default ChangeInfo info() throws RestApiException {
+ return get(EnumSet.noneOf(ListChangesOption.class));
+ }
/**
* Provides access to an API regarding the change edit of this change.
@@ -216,7 +263,11 @@ public interface ChangeApi {
ChangeEditApi edit() throws RestApiException;
/** Create a new patch set with a new commit message. */
- void setMessage(String message) throws RestApiException;
+ default void setMessage(String message) throws RestApiException {
+ CommitMessageInput in = new CommitMessageInput();
+ in.message = message;
+ setMessage(in);
+ }
/** Create a new patch set with a new commit message. */
void setMessage(CommitMessageInput in) throws RestApiException;
@@ -316,9 +367,8 @@ public interface ChangeApi {
/**
* Look up a change message of a change by its id.
*
- * @param id the id of the change message. Note that in NoteDb, this id is the {@code ObjectId} of
- * a commit on the change meta branch. In ReviewDb, it's a UUID generated randomly. That means
- * a change message id could be different between NoteDb and ReviewDb.
+ * @param id the id of the change message. In NoteDb, this id is the {@code ObjectId} of a commit
+ * on the change meta branch.
* @return API for accessing a change message.
* @throws RestApiException if the id is invalid.
*/
@@ -360,16 +410,6 @@ public interface ChangeApi {
}
@Override
- public RevisionApi current() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
- public RevisionApi revision(int id) throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public ReviewerApi reviewer(String id) throws RestApiException {
throw new NotImplementedException();
}
@@ -380,31 +420,16 @@ public interface ChangeApi {
}
@Override
- public void abandon() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public void abandon(AbandonInput in) throws RestApiException {
throw new NotImplementedException();
}
@Override
- public void restore() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public void restore(RestoreInput in) throws RestApiException {
throw new NotImplementedException();
}
@Override
- public void move(String destination) throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public void move(MoveInput in) throws RestApiException {
throw new NotImplementedException();
}
@@ -425,21 +450,11 @@ public interface ChangeApi {
}
@Override
- public ChangeApi revert() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public ChangeApi revert(RevertInput in) throws RestApiException {
throw new NotImplementedException();
}
@Override
- public void rebase() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public void rebase(RebaseInput in) throws RestApiException {
throw new NotImplementedException();
}
@@ -470,11 +485,6 @@ public interface ChangeApi {
}
@Override
- public AddReviewerResult addReviewer(String in) throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public SuggestedReviewersRequest suggestReviewers() throws RestApiException {
throw new NotImplementedException();
}
@@ -490,22 +500,9 @@ public interface ChangeApi {
}
@Override
- public ChangeInfo get(EnumSet<ListChangesOption> options) throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
- public ChangeInfo get() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
- public ChangeInfo info() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
- public void setMessage(String message) throws RestApiException {
+ public ChangeInfo get(
+ EnumSet<ListChangesOption> options, ImmutableListMultimap<String, String> pluginOptions)
+ throws RestApiException {
throw new NotImplementedException();
}
diff --git a/java/com/google/gerrit/extensions/api/changes/Changes.java b/java/com/google/gerrit/extensions/api/changes/Changes.java
index 52c6565a6c..9b9a8a4ce4 100644
--- a/java/com/google/gerrit/extensions/api/changes/Changes.java
+++ b/java/com/google/gerrit/extensions/api/changes/Changes.java
@@ -14,6 +14,8 @@
package com.google.gerrit.extensions.api.changes;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
@@ -76,7 +78,9 @@ public interface Changes {
private String query;
private int limit;
private int start;
+ private boolean isNoLimit;
private Set<ListChangesOption> options = EnumSet.noneOf(ListChangesOption.class);
+ private ListMultimap<String, String> pluginOptions = ArrayListMultimap.create();
public abstract List<ChangeInfo> get() throws RestApiException;
@@ -90,6 +94,11 @@ public interface Changes {
return this;
}
+ public QueryRequest withNoLimit() {
+ this.isNoLimit = true;
+ return this;
+ }
+
public QueryRequest withStart(int start) {
this.start = start;
return this;
@@ -113,6 +122,18 @@ public interface Changes {
return this;
}
+ /** Set a plugin option on the request, appending to existing options. */
+ public QueryRequest withPluginOption(String name, String value) {
+ this.pluginOptions.put(name, value);
+ return this;
+ }
+
+ /** Set a plugin option on the request, replacing existing options. */
+ public QueryRequest withPluginOptions(ListMultimap<String, String> options) {
+ this.pluginOptions = ArrayListMultimap.create(options);
+ return this;
+ }
+
public String getQuery() {
return query;
}
@@ -121,6 +142,10 @@ public interface Changes {
return limit;
}
+ public boolean getNoLimit() {
+ return isNoLimit;
+ }
+
public int getStart() {
return start;
}
@@ -129,6 +154,10 @@ public interface Changes {
return options;
}
+ public ListMultimap<String, String> getPluginOptions() {
+ return pluginOptions;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('{').append(query);
@@ -141,7 +170,11 @@ public interface Changes {
if (!options.isEmpty()) {
sb.append("options=").append(options);
}
- return sb.append('}').toString();
+ sb.append('}');
+ if (isNoLimit == true) {
+ sb.append(" --no-limit");
+ }
+ return sb.toString();
}
}
diff --git a/java/com/google/gerrit/extensions/api/changes/FileApi.java b/java/com/google/gerrit/extensions/api/changes/FileApi.java
index 39cf2b764d..8d9b2d5bbe 100644
--- a/java/com/google/gerrit/extensions/api/changes/FileApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/FileApi.java
@@ -15,10 +15,12 @@
package com.google.gerrit.extensions.api.changes;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
+import com.google.gerrit.extensions.common.BlameInfo;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import java.util.List;
import java.util.OptionalInt;
public interface FileApi {
@@ -42,6 +44,12 @@ public interface FileApi {
/** Set the file reviewed or not reviewed */
void setReviewed(boolean reviewed) throws RestApiException;
+ /**
+ * Creates a request to retrieve the blame information. On the returned request formatting options
+ * for the blame request can be set.
+ */
+ BlameRequest blameRequest() throws RestApiException;
+
abstract class DiffRequest {
private String base;
private Integer context;
@@ -97,6 +105,21 @@ public interface FileApi {
}
}
+ abstract class BlameRequest {
+ private boolean forBase;
+
+ public abstract List<BlameInfo> get() throws RestApiException;
+
+ public BlameRequest forBase(boolean forBase) {
+ this.forBase = forBase;
+ return this;
+ }
+
+ public boolean isForBase() {
+ return forBase;
+ }
+ }
+
/**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
@@ -131,5 +154,10 @@ public interface FileApi {
public void setReviewed(boolean reviewed) throws RestApiException {
throw new NotImplementedException();
}
+
+ @Override
+ public BlameRequest blameRequest() throws RestApiException {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/java/com/google/gerrit/extensions/api/changes/RevisionApi.java b/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
index 553fcbf6f4..68575ca6cc 100644
--- a/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/RevisionApi.java
@@ -15,6 +15,7 @@
package com.google.gerrit.extensions.api.changes;
import com.google.common.collect.ListMultimap;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -41,11 +42,16 @@ public interface RevisionApi {
ReviewResult review(ReviewInput in) throws RestApiException;
- void submit() throws RestApiException;
+ default void submit() throws RestApiException {
+ SubmitInput in = new SubmitInput();
+ submit(in);
+ }
void submit(SubmitInput in) throws RestApiException;
- BinaryResult submitPreview() throws RestApiException;
+ default BinaryResult submitPreview() throws RestApiException {
+ return submitPreview("zip");
+ }
BinaryResult submitPreview(String format) throws RestApiException;
@@ -53,7 +59,10 @@ public interface RevisionApi {
CherryPickChangeInfo cherryPickAsInfo(CherryPickInput in) throws RestApiException;
- ChangeApi rebase() throws RestApiException;
+ default ChangeApi rebase() throws RestApiException {
+ RebaseInput in = new RebaseInput();
+ return rebase(in);
+ }
ChangeApi rebase(RebaseInput in) throws RestApiException;
@@ -65,9 +74,11 @@ public interface RevisionApi {
Set<String> reviewed() throws RestApiException;
- Map<String, FileInfo> files() throws RestApiException;
+ default Map<String, FileInfo> files() throws RestApiException {
+ return files(null);
+ }
- Map<String, FileInfo> files(String base) throws RestApiException;
+ Map<String, FileInfo> files(@Nullable String base) throws RestApiException;
Map<String, FileInfo> files(int parentNum) throws RestApiException;
@@ -170,11 +181,6 @@ public interface RevisionApi {
}
@Override
- public void submit() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public void submit(SubmitInput in) throws RestApiException {
throw new NotImplementedException();
}
@@ -190,11 +196,6 @@ public interface RevisionApi {
}
@Override
- public ChangeApi rebase() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public ChangeApi rebase(RebaseInput in) throws RestApiException {
throw new NotImplementedException();
}
@@ -240,11 +241,6 @@ public interface RevisionApi {
}
@Override
- public Map<String, FileInfo> files() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public List<String> queryFiles(String query) throws RestApiException {
throw new NotImplementedException();
}
@@ -335,11 +331,6 @@ public interface RevisionApi {
}
@Override
- public BinaryResult submitPreview() throws RestApiException {
- throw new NotImplementedException();
- }
-
- @Override
public BinaryResult submitPreview(String format) throws RestApiException {
throw new NotImplementedException();
}
diff --git a/java/com/google/gerrit/extensions/api/config/Server.java b/java/com/google/gerrit/extensions/api/config/Server.java
index 5ec63af59b..70d1bff96b 100644
--- a/java/com/google/gerrit/extensions/api/config/Server.java
+++ b/java/com/google/gerrit/extensions/api/config/Server.java
@@ -20,6 +20,8 @@ import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.ServerInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.webui.TopMenu;
+import java.util.List;
public interface Server {
/** @return Version of server. */
@@ -41,6 +43,8 @@ public interface Server {
ConsistencyCheckInfo checkConsistency(ConsistencyCheckInput in) throws RestApiException;
+ List<TopMenu.MenuEntry> topMenus() throws RestApiException;
+
/**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
@@ -93,5 +97,10 @@ public interface Server {
public ConsistencyCheckInfo checkConsistency(ConsistencyCheckInput in) throws RestApiException {
throw new NotImplementedException();
}
+
+ @Override
+ public List<TopMenu.MenuEntry> topMenus() throws RestApiException {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/java/com/google/gerrit/extensions/api/groups/GroupApi.java b/java/com/google/gerrit/extensions/api/groups/GroupApi.java
index fe85eaadca..067f120940 100644
--- a/java/com/google/gerrit/extensions/api/groups/GroupApi.java
+++ b/java/com/google/gerrit/extensions/api/groups/GroupApi.java
@@ -20,6 +20,7 @@ import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.GroupOptionsInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import java.util.Arrays;
import java.util.List;
public interface GroupApi {
@@ -97,7 +98,27 @@ public interface GroupApi {
* com.google.gerrit.extensions.api.accounts.Accounts#id(String)}
* @throws RestApiException
*/
- void addMembers(String... members) throws RestApiException;
+ void addMembers(List<String> members) throws RestApiException;
+
+ /**
+ * Add members to a group.
+ *
+ * @param members list of member identifiers, in any format accepted by {@link
+ * com.google.gerrit.extensions.api.accounts.Accounts#id(String)}
+ * @throws RestApiException
+ */
+ default void addMembers(String... members) throws RestApiException {
+ addMembers(Arrays.asList(members));
+ }
+
+ /**
+ * Remove members from a group.
+ *
+ * @param members list of member identifiers, in any format accepted by {@link
+ * com.google.gerrit.extensions.api.accounts.Accounts#id(String)}
+ * @throws RestApiException
+ */
+ void removeMembers(List<String> members) throws RestApiException;
/**
* Remove members from a group.
@@ -106,7 +127,9 @@ public interface GroupApi {
* com.google.gerrit.extensions.api.accounts.Accounts#id(String)}
* @throws RestApiException
*/
- void removeMembers(String... members) throws RestApiException;
+ default void removeMembers(String... members) throws RestApiException {
+ removeMembers(Arrays.asList(members));
+ }
/**
* Lists the subgroups of this group.
@@ -122,7 +145,17 @@ public interface GroupApi {
* @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
* @throws RestApiException
*/
- void addGroups(String... groups) throws RestApiException;
+ void addGroups(List<String> groups) throws RestApiException;
+
+ /**
+ * Adds subgroups to this group.
+ *
+ * @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
+ * @throws RestApiException
+ */
+ default void addGroups(String... groups) throws RestApiException {
+ addGroups(Arrays.asList(groups));
+ }
/**
* Removes subgroups from this group.
@@ -130,7 +163,17 @@ public interface GroupApi {
* @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
* @throws RestApiException
*/
- void removeGroups(String... groups) throws RestApiException;
+ void removeGroups(List<String> groups) throws RestApiException;
+
+ /**
+ * Removes subgroups from this group.
+ *
+ * @param groups list of group identifiers, in any format accepted by {@link Groups#id(String)}
+ * @throws RestApiException
+ */
+ default void removeGroups(String... groups) throws RestApiException {
+ removeGroups(Arrays.asList(groups));
+ }
/**
* Returns the audit log of the group.
@@ -215,12 +258,12 @@ public interface GroupApi {
}
@Override
- public void addMembers(String... members) throws RestApiException {
+ public void addMembers(List<String> members) throws RestApiException {
throw new NotImplementedException();
}
@Override
- public void removeMembers(String... members) throws RestApiException {
+ public void removeMembers(List<String> members) throws RestApiException {
throw new NotImplementedException();
}
@@ -230,12 +273,12 @@ public interface GroupApi {
}
@Override
- public void addGroups(String... groups) throws RestApiException {
+ public void addGroups(List<String> groups) throws RestApiException {
throw new NotImplementedException();
}
@Override
- public void removeGroups(String... groups) throws RestApiException {
+ public void removeGroups(List<String> groups) throws RestApiException {
throw new NotImplementedException();
}
diff --git a/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java b/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
index 08ba486813..fb2a0feb0d 100644
--- a/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
+++ b/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
@@ -48,7 +48,6 @@ public class ConfigInfo {
public Map<String, ActionInfo> actions;
public Map<String, CommentLinkInfo> commentlinks;
- public ThemeInfo theme;
public Map<String, List<String>> extensionPanelNames;
diff --git a/java/com/google/gerrit/extensions/api/projects/Projects.java b/java/com/google/gerrit/extensions/api/projects/Projects.java
index 85ec26fd0c..34ca7d493f 100644
--- a/java/com/google/gerrit/extensions/api/projects/Projects.java
+++ b/java/com/google/gerrit/extensions/api/projects/Projects.java
@@ -80,7 +80,6 @@ public interface Projects {
abstract class ListRequest {
public enum FilterType {
CODE,
- PARENT_CANDIDATES,
PERMISSIONS,
ALL
}
diff --git a/java/com/google/gerrit/extensions/api/projects/ThemeInfo.java b/java/com/google/gerrit/extensions/api/projects/ThemeInfo.java
deleted file mode 100644
index d5d520f023..0000000000
--- a/java/com/google/gerrit/extensions/api/projects/ThemeInfo.java
+++ /dev/null
@@ -1,29 +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.extensions.api.projects;
-
-public class ThemeInfo {
- public static final ThemeInfo INHERIT = new ThemeInfo(null, null, null);
-
- public final String css;
- public final String header;
- public final String footer;
-
- public ThemeInfo(String css, String header, String footer) {
- this.css = css;
- this.header = header;
- this.footer = footer;
- }
-}
diff --git a/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java b/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
index 0d5bdfa01b..652abcc7ab 100644
--- a/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -60,7 +60,6 @@ public class DiffPreferencesInfo {
public Boolean hideEmptyPane;
public Boolean matchBrackets;
public Boolean lineWrapping;
- public Theme theme;
public Whitespace ignoreWhitespace;
public Boolean retainHeader;
public Boolean skipDeleted;
@@ -75,7 +74,6 @@ public class DiffPreferencesInfo {
i.lineLength = DEFAULT_LINE_LENGTH;
i.cursorBlinkRate = 0;
i.ignoreWhitespace = Whitespace.IGNORE_NONE;
- i.theme = Theme.DEFAULT;
i.expandAllComments = false;
i.intralineDifference = true;
i.manualReview = false;
diff --git a/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java b/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
index 7ab22e1c06..6672cb19b0 100644
--- a/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
@@ -30,8 +30,6 @@ public class EditPreferencesInfo {
public Boolean indentWithTabs;
public Boolean autoCloseBrackets;
public Boolean showBase;
- public Theme theme;
- public KeyMapType keyMapType;
public static EditPreferencesInfo defaults() {
EditPreferencesInfo i = new EditPreferencesInfo();
@@ -49,8 +47,6 @@ public class EditPreferencesInfo {
i.indentWithTabs = false;
i.autoCloseBrackets = false;
i.showBase = false;
- i.theme = Theme.DEFAULT;
- i.keyMapType = KeyMapType.DEFAULT;
return i;
}
}
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index 1f16d8d847..8eb54e17b4 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -15,7 +15,6 @@
package com.google.gerrit.extensions.client;
import java.util.List;
-import java.util.Map;
/** Preferences about a single user. */
public class GeneralPreferencesInfo {
@@ -137,8 +136,6 @@ public class GeneralPreferencesInfo {
public Boolean useFlashClipboard;
/** Type of download URL the user prefers to use. */
public String downloadScheme;
- /** Type of download command the user prefers to use. */
- public DownloadCommand downloadCommand;
public DateFormat dateFormat;
public TimeFormat timeFormat;
@@ -153,7 +150,6 @@ public class GeneralPreferencesInfo {
public Boolean signedOffBy;
public List<MenuItem> my;
public List<String> changeTable;
- public Map<String, String> urlAliases;
public EmailStrategy emailStrategy;
public EmailFormat emailFormat;
public DefaultBase defaultBaseForMerges;
@@ -215,7 +211,6 @@ public class GeneralPreferencesInfo {
p.emailFormat = EmailFormat.HTML_PLAINTEXT;
p.reviewCategoryStrategy = ReviewCategoryStrategy.NONE;
p.downloadScheme = null;
- p.downloadCommand = DownloadCommand.CHECKOUT;
p.dateFormat = DateFormat.STD;
p.timeFormat = TimeFormat.HHMM_12;
p.expandInlineDiffs = false;
diff --git a/java/com/google/gerrit/extensions/client/KeyMapType.java b/java/com/google/gerrit/extensions/client/KeyMapType.java
deleted file mode 100644
index 9c85ac7ec2..0000000000
--- a/java/com/google/gerrit/extensions/client/KeyMapType.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2015 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.client;
-
-public enum KeyMapType {
- DEFAULT,
- EMACS,
- SUBLIME,
- VIM
-}
diff --git a/java/com/google/gerrit/extensions/client/ListAccountsOption.java b/java/com/google/gerrit/extensions/client/ListAccountsOption.java
index d5b0bf0e3b..2274d5dba1 100644
--- a/java/com/google/gerrit/extensions/client/ListAccountsOption.java
+++ b/java/com/google/gerrit/extensions/client/ListAccountsOption.java
@@ -14,11 +14,8 @@
package com.google.gerrit.extensions.client;
-import java.util.EnumSet;
-import java.util.Set;
-
/** Output options available for retrieval of account details. */
-public enum ListAccountsOption {
+public enum ListAccountsOption implements ListOption {
/** Return detailed account properties. */
DETAILS(0),
@@ -31,32 +28,8 @@ public enum ListAccountsOption {
this.value = v;
}
+ @Override
public int getValue() {
return value;
}
-
- public static Set<ListAccountsOption> fromBits(int v) {
- EnumSet<ListAccountsOption> r = EnumSet.noneOf(ListAccountsOption.class);
- for (ListAccountsOption o : ListAccountsOption.values()) {
- if ((v & (1 << o.value)) != 0) {
- r.add(o);
- v &= ~(1 << o.value);
- }
- if (v == 0) {
- return r;
- }
- }
- if (v != 0) {
- throw new IllegalArgumentException("unknown " + Integer.toHexString(v));
- }
- return r;
- }
-
- public static int toBits(Set<ListAccountsOption> set) {
- int r = 0;
- for (ListAccountsOption o : set) {
- r |= 1 << o.value;
- }
- return r;
- }
}
diff --git a/java/com/google/gerrit/extensions/client/ListChangesOption.java b/java/com/google/gerrit/extensions/client/ListChangesOption.java
index 8e607b5154..c842adc61f 100644
--- a/java/com/google/gerrit/extensions/client/ListChangesOption.java
+++ b/java/com/google/gerrit/extensions/client/ListChangesOption.java
@@ -14,11 +14,8 @@
package com.google.gerrit.extensions.client;
-import java.util.EnumSet;
-import java.util.Set;
-
/** Output options available for retrieval of change details. */
-public enum ListChangesOption {
+public enum ListChangesOption implements ListOption {
LABELS(0),
DETAILED_LABELS(8),
@@ -86,32 +83,8 @@ public enum ListChangesOption {
this.value = v;
}
+ @Override
public int getValue() {
return value;
}
-
- public static Set<ListChangesOption> fromBits(int v) {
- EnumSet<ListChangesOption> r = EnumSet.noneOf(ListChangesOption.class);
- for (ListChangesOption o : ListChangesOption.values()) {
- if ((v & (1 << o.value)) != 0) {
- r.add(o);
- v &= ~(1 << o.value);
- }
- if (v == 0) {
- return r;
- }
- }
- if (v != 0) {
- throw new IllegalArgumentException("unknown " + Integer.toHexString(v));
- }
- return r;
- }
-
- public static int toBits(Set<ListChangesOption> set) {
- int r = 0;
- for (ListChangesOption o : set) {
- r |= 1 << o.value;
- }
- return r;
- }
}
diff --git a/java/com/google/gerrit/extensions/client/ListGroupsOption.java b/java/com/google/gerrit/extensions/client/ListGroupsOption.java
index 0d67efc52b..a971226544 100644
--- a/java/com/google/gerrit/extensions/client/ListGroupsOption.java
+++ b/java/com/google/gerrit/extensions/client/ListGroupsOption.java
@@ -14,11 +14,8 @@
package com.google.gerrit.extensions.client;
-import java.util.EnumSet;
-import java.util.Set;
-
/** Output options available when using {@code /groups/} RPCs. */
-public enum ListGroupsOption {
+public enum ListGroupsOption implements ListOption {
/** Return information on the direct group members. */
MEMBERS(0),
@@ -31,32 +28,8 @@ public enum ListGroupsOption {
this.value = v;
}
+ @Override
public int getValue() {
return value;
}
-
- public static Set<ListGroupsOption> fromBits(int v) {
- EnumSet<ListGroupsOption> r = EnumSet.noneOf(ListGroupsOption.class);
- for (ListGroupsOption o : ListGroupsOption.values()) {
- if ((v & (1 << o.value)) != 0) {
- r.add(o);
- v &= ~(1 << o.value);
- }
- if (v == 0) {
- return r;
- }
- }
- if (v != 0) {
- throw new IllegalArgumentException("unknown " + Integer.toHexString(v));
- }
- return r;
- }
-
- public static int toBits(Set<ListGroupsOption> set) {
- int r = 0;
- for (ListGroupsOption o : set) {
- r |= 1 << o.value;
- }
- return r;
- }
}
diff --git a/java/com/google/gerrit/extensions/client/ListOption.java b/java/com/google/gerrit/extensions/client/ListOption.java
new file mode 100644
index 0000000000..e694c0eb6f
--- /dev/null
+++ b/java/com/google/gerrit/extensions/client/ListOption.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.extensions.client;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.EnumSet;
+
+/** Enum that can be expressed as a bitset in query parameters. */
+public interface ListOption {
+ int getValue();
+
+ static <T extends Enum<T> & ListOption> EnumSet<T> fromBits(Class<T> clazz, int v) {
+ EnumSet<T> r = EnumSet.noneOf(clazz);
+ T[] values;
+ try {
+ @SuppressWarnings("unchecked")
+ T[] tmp = (T[]) clazz.getMethod("values").invoke(null);
+ values = tmp;
+ } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ for (T o : values) {
+ if ((v & (1 << o.getValue())) != 0) {
+ r.add(o);
+ v &= ~(1 << o.getValue());
+ }
+ if (v == 0) {
+ return r;
+ }
+ }
+ if (v != 0) {
+ throw new IllegalArgumentException(
+ "unknown " + clazz.getName() + ": " + Integer.toHexString(v));
+ }
+ return r;
+ }
+}
diff --git a/java/com/google/gerrit/extensions/client/MenuItem.java b/java/com/google/gerrit/extensions/client/MenuItem.java
index 8375bba1c1..0c7dd88884 100644
--- a/java/com/google/gerrit/extensions/client/MenuItem.java
+++ b/java/com/google/gerrit/extensions/client/MenuItem.java
@@ -22,11 +22,6 @@ public class MenuItem {
public final String target;
public final String id;
- // Needed for GWT
- public MenuItem() {
- this(null, null, null, null);
- }
-
public MenuItem(String name, String url) {
this(name, url, "_blank");
}
diff --git a/java/com/google/gerrit/extensions/client/Theme.java b/java/com/google/gerrit/extensions/client/Theme.java
deleted file mode 100644
index d7a5b80295..0000000000
--- a/java/com/google/gerrit/extensions/client/Theme.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2014 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.client;
-
-public enum Theme {
- // Light themes
- DEFAULT,
- DAY_3024,
- DUOTONE_LIGHT,
- BASE16_LIGHT,
- ECLIPSE,
- ELEGANT,
- MDN_LIKE,
- NEAT,
- NEO,
- PARAISO_LIGHT,
- SOLARIZED,
- TTCN,
- XQ_LIGHT,
- YETI,
-
- // Dark themes
- NIGHT_3024,
- ABCDEF,
- AMBIANCE,
- BASE16_DARK,
- BESPIN,
- BLACKBOARD,
- COBALT,
- COLORFORTH,
- DRACULA,
- DUOTONE_DARK,
- ERLANG_DARK,
- HOPSCOTCH,
- ICECODER,
- ISOTOPE,
- LESSER_DARK,
- LIQUIBYTE,
- MATERIAL,
- MBO,
- MIDNIGHT,
- MONOKAI,
- NIGHT,
- PARAISO_DARK,
- PASTEL_ON_DARK,
- RAILSCASTS,
- RUBYBLUE,
- SETI,
- THE_MATRIX,
- TOMORROW_NIGHT_BRIGHT,
- TOMORROW_NIGHT_EIGHTIES,
- TWILIGHT,
- VIBRANT_INK,
- XQ_DARK,
- ZENBURN;
-
- public boolean isDark() {
- switch (this) {
- case ABCDEF:
- case AMBIANCE:
- case BASE16_DARK:
- case BESPIN:
- case BLACKBOARD:
- case COBALT:
- case COLORFORTH:
- case DRACULA:
- case DUOTONE_DARK:
- case ERLANG_DARK:
- case HOPSCOTCH:
- case ICECODER:
- case ISOTOPE:
- case LESSER_DARK:
- case LIQUIBYTE:
- case MATERIAL:
- case MBO:
- case MIDNIGHT:
- case MONOKAI:
- case NIGHT:
- case NIGHT_3024:
- case PARAISO_DARK:
- case PASTEL_ON_DARK:
- case RAILSCASTS:
- case RUBYBLUE:
- case SETI:
- case THE_MATRIX:
- case TOMORROW_NIGHT_BRIGHT:
- case TOMORROW_NIGHT_EIGHTIES:
- case TWILIGHT:
- case VIBRANT_INK:
- case XQ_DARK:
- case ZENBURN:
- return true;
- case BASE16_LIGHT:
- case DEFAULT:
- case DAY_3024:
- case DUOTONE_LIGHT:
- case ECLIPSE:
- case ELEGANT:
- case MDN_LIKE:
- case NEAT:
- case NEO:
- case PARAISO_LIGHT:
- case SOLARIZED:
- case TTCN:
- case XQ_LIGHT:
- case YETI:
- default:
- return false;
- }
- }
-}
diff --git a/java/com/google/gerrit/extensions/client/UiType.java b/java/com/google/gerrit/extensions/client/UiType.java
deleted file mode 100644
index 0d9df39431..0000000000
--- a/java/com/google/gerrit/extensions/client/UiType.java
+++ /dev/null
@@ -1,32 +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.extensions.client;
-
-public enum UiType {
- NONE,
- GWT,
- POLYGERRIT;
-
- public static UiType parse(String str) {
- if (str != null) {
- for (UiType type : UiType.values()) {
- if (type.name().equalsIgnoreCase(str)) {
- return type;
- }
- }
- }
- return null;
- }
-}
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 945c239508..9a739efafd 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -46,6 +46,7 @@ public class ChangeInfo {
public Boolean submittable;
public Integer insertions;
public Integer deletions;
+ public Integer totalCommentCount;
public Integer unresolvedCommentCount;
public Boolean isPrivate;
public Boolean workInProgress;
diff --git a/java/com/google/gerrit/extensions/common/GerritInfo.java b/java/com/google/gerrit/extensions/common/GerritInfo.java
index ef1d6795c5..5c462d9224 100644
--- a/java/com/google/gerrit/extensions/common/GerritInfo.java
+++ b/java/com/google/gerrit/extensions/common/GerritInfo.java
@@ -14,9 +14,6 @@
package com.google.gerrit.extensions.common;
-import com.google.gerrit.extensions.client.UiType;
-import java.util.Set;
-
public class GerritInfo {
public String allProjects;
public String allUsers;
@@ -24,7 +21,5 @@ public class GerritInfo {
public String docUrl;
public Boolean editGpgKeys;
public String reportBugUrl;
- public String reportBugText;
- public Set<UiType> webUis;
public String primaryWeblinkName;
}
diff --git a/java/com/google/gerrit/extensions/common/MessageOfTheDayInfo.java b/java/com/google/gerrit/extensions/common/MessageOfTheDayInfo.java
deleted file mode 100644
index f752f86a64..0000000000
--- a/java/com/google/gerrit/extensions/common/MessageOfTheDayInfo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.common;
-
-import java.util.Date;
-
-/** REST API representation of a "message of the day". */
-public class MessageOfTheDayInfo {
- /** The ID of the message. */
- public String id;
- /** The date and time the message will be displayed again after being dismissed by the user. */
- public Date redisplay;
- /** The message in HTML-format. */
- public String html;
-}
diff --git a/java/com/google/gerrit/extensions/common/PluginInfo.java b/java/com/google/gerrit/extensions/common/PluginInfo.java
index 0df6235c9c..47f9b6ac14 100644
--- a/java/com/google/gerrit/extensions/common/PluginInfo.java
+++ b/java/com/google/gerrit/extensions/common/PluginInfo.java
@@ -17,13 +17,21 @@ package com.google.gerrit.extensions.common;
public class PluginInfo {
public final String id;
public final String version;
+ public final String apiVersion;
public final String indexUrl;
public final String filename;
public final Boolean disabled;
- public PluginInfo(String id, String version, String indexUrl, String filename, Boolean disabled) {
+ public PluginInfo(
+ String id,
+ String version,
+ String apiVersion,
+ String indexUrl,
+ String filename,
+ Boolean disabled) {
this.id = id;
this.version = version;
+ this.apiVersion = apiVersion;
this.indexUrl = indexUrl;
this.filename = filename;
this.disabled = disabled;
diff --git a/java/com/google/gerrit/extensions/common/ServerInfo.java b/java/com/google/gerrit/extensions/common/ServerInfo.java
index 27cf529a24..82d5bc8061 100644
--- a/java/com/google/gerrit/extensions/common/ServerInfo.java
+++ b/java/com/google/gerrit/extensions/common/ServerInfo.java
@@ -14,21 +14,16 @@
package com.google.gerrit.extensions.common;
-import java.util.List;
-import java.util.Map;
-
public class ServerInfo {
public AccountsInfo accounts;
public AuthInfo auth;
public ChangeConfigInfo change;
public DownloadInfo download;
public GerritInfo gerrit;
- public List<MessageOfTheDayInfo> messages;
public Boolean noteDbEnabled;
public PluginConfigInfo plugin;
public SshdInfo sshd;
public SuggestInfo suggest;
- public Map<String, String> urlAliases;
public UserConfigInfo user;
public ReceiveInfo receive;
public String defaultTheme;
diff --git a/java/com/google/gerrit/extensions/common/testing/BUILD b/java/com/google/gerrit/extensions/common/testing/BUILD
index 184115201c..9cecb66e0c 100644
--- a/java/com/google/gerrit/extensions/common/testing/BUILD
+++ b/java/com/google/gerrit/extensions/common/testing/BUILD
@@ -8,6 +8,7 @@ java_library(
deps = [
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/truth",
+ "//lib:guava",
"//lib/jgit/org.eclipse.jgit: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 6dd5ce41ae..f0f5516a5e 100644
--- a/java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/CommitInfoSubject.java
@@ -15,18 +15,23 @@
package com.google.gerrit.extensions.common.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.extensions.common.testing.GitPersonSubject.gitPersons;
+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.CommitInfo;
import com.google.gerrit.truth.ListSubject;
public class CommitInfoSubject extends Subject<CommitInfoSubject, CommitInfo> {
public static CommitInfoSubject assertThat(CommitInfo commitInfo) {
- return assertAbout(CommitInfoSubject::new).that(commitInfo);
+ return assertAbout(commits()).that(commitInfo);
+ }
+
+ public static Subject.Factory<CommitInfoSubject, CommitInfo> commits() {
+ return CommitInfoSubject::new;
}
private CommitInfoSubject(FailureMetadata failureMetadata, CommitInfo commitInfo) {
@@ -36,31 +41,30 @@ public class CommitInfoSubject extends Subject<CommitInfoSubject, CommitInfo> {
public StringSubject commit() {
isNotNull();
CommitInfo commitInfo = actual();
- return Truth.assertThat(commitInfo.commit).named("commit");
+ return check("commit()").that(commitInfo.commit);
}
public ListSubject<CommitInfoSubject, CommitInfo> parents() {
isNotNull();
CommitInfo commitInfo = actual();
- return ListSubject.assertThat(commitInfo.parents, CommitInfoSubject::assertThat)
- .named("parents");
+ return check("parents()").about(elements()).thatCustom(commitInfo.parents, commits());
}
public GitPersonSubject committer() {
isNotNull();
CommitInfo commitInfo = actual();
- return GitPersonSubject.assertThat(commitInfo.committer).named("committer");
+ return check("committer()").about(gitPersons()).that(commitInfo.committer);
}
public GitPersonSubject author() {
isNotNull();
CommitInfo commitInfo = actual();
- return GitPersonSubject.assertThat(commitInfo.author).named("author");
+ return check("author()").about(gitPersons()).that(commitInfo.author);
}
public StringSubject message() {
isNotNull();
CommitInfo commitInfo = actual();
- return Truth.assertThat(commitInfo.message).named("message");
+ 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 5fc8ba611a..25750c1e09 100644
--- a/java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/ContentEntrySubject.java
@@ -14,21 +14,27 @@
package com.google.gerrit.extensions.common.testing;
+import static com.google.common.truth.Fact.simpleFact;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.truth.ListSubject.elements;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IntegerSubject;
import com.google.common.truth.IterableSubject;
+import com.google.common.truth.StandardSubjectBuilder;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.common.DiffInfo.ContentEntry;
import com.google.gerrit.truth.ListSubject;
public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEntry> {
public static ContentEntrySubject assertThat(ContentEntry contentEntry) {
- return assertAbout(ContentEntrySubject::new).that(contentEntry);
+ return assertAbout(contentEntries()).that(contentEntry);
+ }
+
+ public static Subject.Factory<ContentEntrySubject, ContentEntry> contentEntries() {
+ return ContentEntrySubject::new;
}
private ContentEntrySubject(FailureMetadata failureMetadata, ContentEntry contentEntry) {
@@ -38,54 +44,54 @@ public class ContentEntrySubject extends Subject<ContentEntrySubject, ContentEnt
public void isDueToRebase() {
isNotNull();
ContentEntry contentEntry = actual();
- Truth.assertWithMessage("Entry should be marked 'dueToRebase'")
- .that(contentEntry.dueToRebase)
- .named("dueToRebase")
- .isTrue();
+ if (contentEntry.dueToRebase == null || !contentEntry.dueToRebase) {
+ failWithActual(simpleFact("expected entry to be marked 'dueToRebase'"));
+ }
}
public void isNotDueToRebase() {
isNotNull();
ContentEntry contentEntry = actual();
- Truth.assertWithMessage("Entry should not be marked 'dueToRebase'")
- .that(contentEntry.dueToRebase)
- .named("dueToRebase")
- .isNull();
+ if (contentEntry.dueToRebase != null && contentEntry.dueToRebase) {
+ failWithActual(simpleFact("expected entry not to be marked 'dueToRebase'"));
+ }
}
public ListSubject<StringSubject, String> commonLines() {
isNotNull();
ContentEntry contentEntry = actual();
- return ListSubject.assertThat(contentEntry.ab, Truth::assertThat).named("common lines");
+ return check("commonLines()")
+ .about(elements())
+ .that(contentEntry.ab, StandardSubjectBuilder::that);
}
public ListSubject<StringSubject, String> linesOfA() {
isNotNull();
ContentEntry contentEntry = actual();
- return ListSubject.assertThat(contentEntry.a, Truth::assertThat).named("lines of 'a'");
+ return check("linesOfA()").about(elements()).that(contentEntry.a, StandardSubjectBuilder::that);
}
public ListSubject<StringSubject, String> linesOfB() {
isNotNull();
ContentEntry contentEntry = actual();
- return ListSubject.assertThat(contentEntry.b, Truth::assertThat).named("lines of 'b'");
+ return check("linesOfB()").about(elements()).that(contentEntry.b, StandardSubjectBuilder::that);
}
public IterableSubject intralineEditsOfA() {
isNotNull();
ContentEntry contentEntry = actual();
- return Truth.assertThat(contentEntry.editA).named("intraline edits of 'a'");
+ return check("intralineEditsOfA()").that(contentEntry.editA);
}
public IterableSubject intralineEditsOfB() {
isNotNull();
ContentEntry contentEntry = actual();
- return Truth.assertThat(contentEntry.editB).named("intraline edits of 'b'");
+ return check("intralineEditsOfB()").that(contentEntry.editB);
}
public IntegerSubject numberOfSkippedLines() {
isNotNull();
ContentEntry contentEntry = actual();
- return Truth.assertThat(contentEntry.skip).named("number of skipped lines");
+ 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 057a1a2918..ee37bde8ee 100644
--- a/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/DiffInfoSubject.java
@@ -15,11 +15,12 @@
package com.google.gerrit.extensions.common.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.extensions.common.testing.FileMetaSubject.fileMetas;
+import static com.google.gerrit.truth.ListSubject.elements;
import com.google.common.truth.ComparableSubject;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.common.ChangeType;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.DiffInfo.ContentEntry;
@@ -38,25 +39,26 @@ public class DiffInfoSubject extends Subject<DiffInfoSubject, DiffInfo> {
public ListSubject<ContentEntrySubject, ContentEntry> content() {
isNotNull();
DiffInfo diffInfo = actual();
- return ListSubject.assertThat(diffInfo.content, ContentEntrySubject::assertThat)
- .named("content");
+ return check("content()")
+ .about(elements())
+ .thatCustom(diffInfo.content, ContentEntrySubject.contentEntries());
}
public ComparableSubject<?, ChangeType> changeType() {
isNotNull();
DiffInfo diffInfo = actual();
- return Truth.assertThat(diffInfo.changeType).named("changeType");
+ return check("changeType()").that(diffInfo.changeType);
}
public FileMetaSubject metaA() {
isNotNull();
DiffInfo diffInfo = actual();
- return FileMetaSubject.assertThat(diffInfo.metaA).named("metaA");
+ return check("metaA()").about(fileMetas()).that(diffInfo.metaA);
}
public FileMetaSubject metaB() {
isNotNull();
DiffInfo diffInfo = actual();
- return FileMetaSubject.assertThat(diffInfo.metaB).named("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 84ad61ce0e..1c99141fdf 100644
--- a/java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/EditInfoSubject.java
@@ -15,11 +15,11 @@
package com.google.gerrit.extensions.common.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.commits;
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.EditInfo;
import com.google.gerrit.truth.OptionalSubject;
import java.util.Optional;
@@ -27,12 +27,16 @@ import java.util.Optional;
public class EditInfoSubject extends Subject<EditInfoSubject, EditInfo> {
public static EditInfoSubject assertThat(EditInfo editInfo) {
- return assertAbout(EditInfoSubject::new).that(editInfo);
+ return assertAbout(edits()).that(editInfo);
+ }
+
+ private static Subject.Factory<EditInfoSubject, EditInfo> edits() {
+ return EditInfoSubject::new;
}
public static OptionalSubject<EditInfoSubject, EditInfo> assertThat(
Optional<EditInfo> editInfoOptional) {
- return OptionalSubject.assertThat(editInfoOptional, EditInfoSubject::assertThat);
+ return OptionalSubject.assertThat(editInfoOptional, edits());
}
private EditInfoSubject(FailureMetadata failureMetadata, EditInfo editInfo) {
@@ -42,12 +46,12 @@ public class EditInfoSubject extends Subject<EditInfoSubject, EditInfo> {
public CommitInfoSubject commit() {
isNotNull();
EditInfo editInfo = actual();
- return CommitInfoSubject.assertThat(editInfo.commit).named("commit");
+ return check("commit()").about(commits()).that(editInfo.commit);
}
public StringSubject baseRevision() {
isNotNull();
EditInfo editInfo = actual();
- return Truth.assertThat(editInfo.baseRevision).named("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 b088016f9b..3ebf8388d9 100644
--- a/java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FileInfoSubject.java
@@ -20,7 +20,6 @@ import com.google.common.truth.ComparableSubject;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IntegerSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.common.FileInfo;
public class FileInfoSubject extends Subject<FileInfoSubject, FileInfo> {
@@ -36,18 +35,18 @@ public class FileInfoSubject extends Subject<FileInfoSubject, FileInfo> {
public IntegerSubject linesInserted() {
isNotNull();
FileInfo fileInfo = actual();
- return Truth.assertThat(fileInfo.linesInserted).named("linesInserted");
+ return check("linesInserted()").that(fileInfo.linesInserted);
}
public IntegerSubject linesDeleted() {
isNotNull();
FileInfo fileInfo = actual();
- return Truth.assertThat(fileInfo.linesDeleted).named("linesDeleted");
+ return check("linesDeleted()").that(fileInfo.linesDeleted);
}
public ComparableSubject<?, Character> status() {
isNotNull();
FileInfo fileInfo = actual();
- return Truth.assertThat(fileInfo.status).named("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 e77eef1311..d1b2031b99 100644
--- a/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FileMetaSubject.java
@@ -19,13 +19,16 @@ import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IntegerSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.common.DiffInfo.FileMeta;
public class FileMetaSubject extends Subject<FileMetaSubject, FileMeta> {
public static FileMetaSubject assertThat(FileMeta fileMeta) {
- return assertAbout(FileMetaSubject::new).that(fileMeta);
+ return assertAbout(fileMetas()).that(fileMeta);
+ }
+
+ public static Subject.Factory<FileMetaSubject, FileMeta> fileMetas() {
+ return FileMetaSubject::new;
}
private FileMetaSubject(FailureMetadata failureMetadata, FileMeta fileMeta) {
@@ -35,6 +38,6 @@ public class FileMetaSubject extends Subject<FileMetaSubject, FileMeta> {
public IntegerSubject totalLineCount() {
isNotNull();
FileMeta fileMeta = actual();
- return Truth.assertThat(fileMeta.lines).named("total line count");
+ 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 b56d3996b7..6ba5f8b291 100644
--- a/java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FixReplacementInfoSubject.java
@@ -15,18 +15,22 @@
package com.google.gerrit.extensions.common.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.extensions.common.testing.RangeSubject.ranges;
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;
public class FixReplacementInfoSubject
extends Subject<FixReplacementInfoSubject, FixReplacementInfo> {
public static FixReplacementInfoSubject assertThat(FixReplacementInfo fixReplacementInfo) {
- return assertAbout(FixReplacementInfoSubject::new).that(fixReplacementInfo);
+ return assertAbout(fixReplacements()).that(fixReplacementInfo);
+ }
+
+ public static Subject.Factory<FixReplacementInfoSubject, FixReplacementInfo> fixReplacements() {
+ return FixReplacementInfoSubject::new;
}
private FixReplacementInfoSubject(
@@ -35,14 +39,17 @@ public class FixReplacementInfoSubject
}
public StringSubject path() {
- return Truth.assertThat(actual().path).named("path");
+ isNotNull();
+ return check("path()").that(actual().path);
}
public RangeSubject range() {
- return RangeSubject.assertThat(actual().range).named("range");
+ isNotNull();
+ return check("range()").about(ranges()).that(actual().range);
}
public StringSubject replacement() {
- return Truth.assertThat(actual().replacement).named("replacement");
+ isNotNull();
+ return check("replacement()").that(actual().replacement);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java b/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java
index 7a6da9cb90..98dac38aa8 100644
--- a/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/FixSuggestionInfoSubject.java
@@ -15,6 +15,8 @@
package com.google.gerrit.extensions.common.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.extensions.common.testing.FixReplacementInfoSubject.fixReplacements;
+import static com.google.gerrit.truth.ListSubject.elements;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.StringSubject;
@@ -27,7 +29,11 @@ import com.google.gerrit.truth.ListSubject;
public class FixSuggestionInfoSubject extends Subject<FixSuggestionInfoSubject, FixSuggestionInfo> {
public static FixSuggestionInfoSubject assertThat(FixSuggestionInfo fixSuggestionInfo) {
- return assertAbout(FixSuggestionInfoSubject::new).that(fixSuggestionInfo);
+ return assertAbout(fixSuggestions()).that(fixSuggestionInfo);
+ }
+
+ public static Subject.Factory<FixSuggestionInfoSubject, FixSuggestionInfo> fixSuggestions() {
+ return FixSuggestionInfoSubject::new;
}
private FixSuggestionInfoSubject(
@@ -40,8 +46,10 @@ public class FixSuggestionInfoSubject extends Subject<FixSuggestionInfoSubject,
}
public ListSubject<FixReplacementInfoSubject, FixReplacementInfo> replacements() {
- return ListSubject.assertThat(actual().replacements, FixReplacementInfoSubject::assertThat)
- .named("replacements");
+ isNotNull();
+ return check("replacements()")
+ .about(elements())
+ .thatCustom(actual().replacements, fixReplacements());
}
public FixReplacementInfoSubject onlyReplacement() {
@@ -49,6 +57,7 @@ public class FixSuggestionInfoSubject extends Subject<FixSuggestionInfoSubject,
}
public StringSubject description() {
- return Truth.assertThat(actual().description).named("description");
+ isNotNull();
+ return check("description()").that(actual().description);
}
}
diff --git a/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java b/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java
index cdbef34afb..dee0636a54 100644
--- a/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/GitPersonSubject.java
@@ -14,6 +14,7 @@
package com.google.gerrit.extensions.common.testing;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.ComparableSubject;
@@ -21,7 +22,6 @@ import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IntegerSubject;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.common.GitPerson;
import java.sql.Timestamp;
import java.util.Date;
@@ -30,7 +30,11 @@ import org.eclipse.jgit.lib.PersonIdent;
public class GitPersonSubject extends Subject<GitPersonSubject, GitPerson> {
public static GitPersonSubject assertThat(GitPerson gitPerson) {
- return assertAbout(GitPersonSubject::new).that(gitPerson);
+ return assertAbout(gitPersons()).that(gitPerson);
+ }
+
+ public static Factory<GitPersonSubject, GitPerson> gitPersons() {
+ return GitPersonSubject::new;
}
private GitPersonSubject(FailureMetadata failureMetadata, GitPerson gitPerson) {
@@ -40,30 +44,30 @@ public class GitPersonSubject extends Subject<GitPersonSubject, GitPerson> {
public StringSubject name() {
isNotNull();
GitPerson gitPerson = actual();
- return Truth.assertThat(gitPerson.name).named("name");
+ return check("name()").that(gitPerson.name);
}
public StringSubject email() {
isNotNull();
GitPerson gitPerson = actual();
- return Truth.assertThat(gitPerson.email).named("email");
+ return check("email()").that(gitPerson.email);
}
public ComparableSubject<?, Timestamp> date() {
isNotNull();
GitPerson gitPerson = actual();
- return Truth.assertThat(gitPerson.date).named("date");
+ return check("date()").that(gitPerson.date);
}
public IntegerSubject tz() {
isNotNull();
GitPerson gitPerson = actual();
- return Truth.assertThat(gitPerson.tz).named("tz");
+ return check("tz()").that(gitPerson.tz);
}
public void hasSameDateAs(GitPerson other) {
+ checkNotNull(other, "'other' GitPerson must not be null");
isNotNull();
- assertThat(other).named("other").isNotNull();
date().isEqualTo(other.date);
tz().isEqualTo(other.tz);
}
@@ -72,9 +76,7 @@ public class GitPersonSubject extends Subject<GitPersonSubject, GitPerson> {
isNotNull();
name().isEqualTo(ident.getName());
email().isEqualTo(ident.getEmailAddress());
- Truth.assertThat(new Date(actual().date.getTime()))
- .named("rounded date")
- .isEqualTo(ident.getWhen());
+ check("roundedDate()").that(new Date(actual().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 db7f0d1782..12acb8d8c7 100644
--- a/java/com/google/gerrit/extensions/common/testing/RangeSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/RangeSubject.java
@@ -14,19 +14,22 @@
package com.google.gerrit.extensions.common.testing;
-import static com.google.common.truth.Fact.fact;
+import static com.google.common.truth.Fact.simpleFact;
import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IntegerSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.client.Comment;
public class RangeSubject extends Subject<RangeSubject, Comment.Range> {
public static RangeSubject assertThat(Comment.Range range) {
- return assertAbout(RangeSubject::new).that(range);
+ return assertAbout(ranges()).that(range);
+ }
+
+ public static Subject.Factory<RangeSubject, Comment.Range> ranges() {
+ return RangeSubject::new;
}
private RangeSubject(FailureMetadata failureMetadata, Comment.Range range) {
@@ -34,32 +37,32 @@ public class RangeSubject extends Subject<RangeSubject, Comment.Range> {
}
public IntegerSubject startLine() {
- return Truth.assertThat(actual().startLine).named("startLine");
+ return check("startLine()").that(actual().startLine);
}
public IntegerSubject startCharacter() {
- return Truth.assertThat(actual().startCharacter).named("startCharacter");
+ return check("startCharacter()").that(actual().startCharacter);
}
public IntegerSubject endLine() {
- return Truth.assertThat(actual().endLine).named("endLine");
+ return check("endLine()").that(actual().endLine);
}
public IntegerSubject endCharacter() {
- return Truth.assertThat(actual().endCharacter).named("endCharacter");
+ return check("endCharacter()").that(actual().endCharacter);
}
public void isValid() {
isNotNull();
if (!actual().isValid()) {
- failWithoutActual(fact("expected", "valid"));
+ failWithActual(simpleFact("expected to be valid"));
}
}
public void isInvalid() {
isNotNull();
if (actual().isValid()) {
- failWithoutActual(fact("expected", "not valid"));
+ 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 c2bed866f7..033f54ba59 100644
--- a/java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java
+++ b/java/com/google/gerrit/extensions/common/testing/RobotCommentInfoSubject.java
@@ -15,6 +15,7 @@
package com.google.gerrit.extensions.common.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.truth.ListSubject.elements;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
@@ -27,12 +28,15 @@ public class RobotCommentInfoSubject extends Subject<RobotCommentInfoSubject, Ro
public static ListSubject<RobotCommentInfoSubject, RobotCommentInfo> assertThatList(
List<RobotCommentInfo> robotCommentInfos) {
- return ListSubject.assertThat(robotCommentInfos, RobotCommentInfoSubject::assertThat)
- .named("robotCommentInfos");
+ return ListSubject.assertThat(robotCommentInfos, robotComments()).named("robotCommentInfos");
}
public static RobotCommentInfoSubject assertThat(RobotCommentInfo robotCommentInfo) {
- return assertAbout(RobotCommentInfoSubject::new).that(robotCommentInfo);
+ return assertAbout(robotComments()).that(robotCommentInfo);
+ }
+
+ private static Factory<RobotCommentInfoSubject, RobotCommentInfo> robotComments() {
+ return RobotCommentInfoSubject::new;
}
private RobotCommentInfoSubject(
@@ -41,8 +45,9 @@ public class RobotCommentInfoSubject extends Subject<RobotCommentInfoSubject, Ro
}
public ListSubject<FixSuggestionInfoSubject, FixSuggestionInfo> fixSuggestions() {
- return ListSubject.assertThat(actual().fixSuggestions, FixSuggestionInfoSubject::assertThat)
- .named("fixSuggestions");
+ return check("fixSuggestions()")
+ .about(elements())
+ .thatCustom(actual().fixSuggestions, FixSuggestionInfoSubject.fixSuggestions());
}
public FixSuggestionInfoSubject onlyFixSuggestion() {
diff --git a/java/com/google/gerrit/extensions/config/CapabilityDefinition.java b/java/com/google/gerrit/extensions/config/CapabilityDefinition.java
index aafb583042..c76ec6c72f 100644
--- a/java/com/google/gerrit/extensions/config/CapabilityDefinition.java
+++ b/java/com/google/gerrit/extensions/config/CapabilityDefinition.java
@@ -18,7 +18,4 @@ import com.google.gerrit.extensions.annotations.ExtensionPoint;
/** Specifies a capability declared by a plugin. */
@ExtensionPoint
-public abstract class CapabilityDefinition {
- /** @return description of the capability. */
- public abstract String getDescription();
-}
+public abstract class CapabilityDefinition implements PluginPermissionDefinition {}
diff --git a/java/com/google/gerrit/extensions/config/PluginPermissionDefinition.java b/java/com/google/gerrit/extensions/config/PluginPermissionDefinition.java
new file mode 100644
index 0000000000..11b198178c
--- /dev/null
+++ b/java/com/google/gerrit/extensions/config/PluginPermissionDefinition.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.extensions.config;
+
+/** Specifies a permission declared by a plugin. */
+public interface PluginPermissionDefinition {
+ /**
+ * Gets the description of a permission declared by a plugin.
+ *
+ * @return description of the permission.
+ */
+ String getDescription();
+}
diff --git a/java/com/google/gerrit/extensions/config/PluginProjectPermissionDefinition.java b/java/com/google/gerrit/extensions/config/PluginProjectPermissionDefinition.java
new file mode 100644
index 0000000000..d1d9f9e21c
--- /dev/null
+++ b/java/com/google/gerrit/extensions/config/PluginProjectPermissionDefinition.java
@@ -0,0 +1,21 @@
+// 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.config;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Specifies a repository permission declared by a plugin. */
+@ExtensionPoint
+public abstract class PluginProjectPermissionDefinition implements PluginPermissionDefinition {}
diff --git a/java/com/google/gerrit/extensions/persistence/DataSourceInterceptor.java b/java/com/google/gerrit/extensions/persistence/DataSourceInterceptor.java
deleted file mode 100644
index 0a83eea3ea..0000000000
--- a/java/com/google/gerrit/extensions/persistence/DataSourceInterceptor.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2014 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.persistence;
-
-import javax.sql.DataSource;
-
-public interface DataSourceInterceptor {
- DataSource intercept(String name, DataSource dataSource);
-}
diff --git a/java/com/google/gerrit/extensions/registration/DynamicItem.java b/java/com/google/gerrit/extensions/registration/DynamicItem.java
index 67982d9040..3f848cb63c 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicItem.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicItem.java
@@ -184,12 +184,7 @@ public class DynamicItem<T> {
}
final Extension<T> defaultItem = old;
- return new RegistrationHandle() {
- @Override
- public void remove() {
- ref.compareAndSet(item, defaultItem);
- }
- };
+ return () -> ref.compareAndSet(item, defaultItem);
}
/**
diff --git a/java/com/google/gerrit/extensions/registration/DynamicSet.java b/java/com/google/gerrit/extensions/registration/DynamicSet.java
index 35f3567b95..b2e871ec4f 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicSet.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -137,7 +137,7 @@ public class DynamicSet<T> implements Iterable<T> {
}
public static <T> DynamicSet<T> emptySet() {
- return new DynamicSet<>(Collections.<AtomicReference<Extension<T>>>emptySet());
+ return new DynamicSet<>(Collections.emptySet());
}
private final CopyOnWriteArrayList<AtomicReference<Extension<T>>> items;
@@ -169,10 +169,8 @@ public class DynamicSet<T> implements Iterable<T> {
public Iterable<Extension<T>> entries() {
final Iterator<AtomicReference<Extension<T>>> itr = items.iterator();
- return new Iterable<Extension<T>>() {
- @Override
- public Iterator<Extension<T>> iterator() {
- return new Iterator<Extension<T>>() {
+ return () ->
+ new Iterator<Extension<T>>() {
private Extension<T> next;
@Override
@@ -201,8 +199,6 @@ public class DynamicSet<T> implements Iterable<T> {
throw new UnsupportedOperationException();
}
};
- }
- };
}
/**
@@ -266,12 +262,9 @@ public class DynamicSet<T> implements Iterable<T> {
final AtomicReference<Extension<T>> ref =
new AtomicReference<>(new Extension<>(pluginName, item));
items.add(ref);
- return new RegistrationHandle() {
- @Override
- public void remove() {
- if (ref.compareAndSet(ref.get(), null)) {
- items.remove(ref);
- }
+ return () -> {
+ if (ref.compareAndSet(ref.get(), null)) {
+ items.remove(ref);
}
};
}
diff --git a/java/com/google/gerrit/extensions/registration/Extension.java b/java/com/google/gerrit/extensions/registration/Extension.java
index aaec201bd7..1031bb0a13 100644
--- a/java/com/google/gerrit/extensions/registration/Extension.java
+++ b/java/com/google/gerrit/extensions/registration/Extension.java
@@ -32,7 +32,7 @@ public class Extension<T> {
private final @Nullable String exportName;
private final Provider<T> provider;
- protected Extension(String pluginName, Provider<T> provider) {
+ public Extension(String pluginName, Provider<T> provider) {
this(pluginName, null, provider);
}
diff --git a/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java b/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java
index cef9e0caaf..fb520b4082 100644
--- a/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java
+++ b/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicMapImpl.java
@@ -36,12 +36,7 @@ public class PrivateInternals_DynamicMapImpl<T> extends DynamicMap<T> {
requireNonNull(item);
final NamePair key = new NamePair(pluginName, exportName);
items.put(key, item);
- return new RegistrationHandle() {
- @Override
- public void remove() {
- items.remove(key, item);
- }
- };
+ return () -> items.remove(key, item);
}
/**
diff --git a/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java b/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java
index 186730824e..510920519a 100644
--- a/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java
+++ b/java/com/google/gerrit/extensions/restapi/testing/BinaryResultSubject.java
@@ -20,7 +20,6 @@ import com.google.common.truth.FailureMetadata;
import com.google.common.truth.PrimitiveByteArraySubject;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.truth.OptionalSubject;
import java.io.ByteArrayOutputStream;
@@ -30,12 +29,16 @@ import java.util.Optional;
public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResult> {
public static BinaryResultSubject assertThat(BinaryResult binaryResult) {
- return assertAbout(BinaryResultSubject::new).that(binaryResult);
+ return assertAbout(binaryResults()).that(binaryResult);
+ }
+
+ private static Subject.Factory<BinaryResultSubject, BinaryResult> binaryResults() {
+ return BinaryResultSubject::new;
}
public static OptionalSubject<BinaryResultSubject, BinaryResult> assertThat(
Optional<BinaryResult> binaryResultOptional) {
- return OptionalSubject.assertThat(binaryResultOptional, BinaryResultSubject::assertThat);
+ return OptionalSubject.assertThat(binaryResultOptional, binaryResults());
}
private BinaryResultSubject(FailureMetadata failureMetadata, BinaryResult binaryResult) {
@@ -48,7 +51,7 @@ public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResu
// be used afterwards. Besides, closing it doesn't have an effect for most
// implementations of a BinaryResult.
BinaryResult binaryResult = actual();
- return Truth.assertThat(binaryResult.asString());
+ return check("asString()").that(binaryResult.asString());
}
public PrimitiveByteArraySubject bytes() throws IOException {
@@ -60,6 +63,6 @@ public class BinaryResultSubject extends Subject<BinaryResultSubject, BinaryResu
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
binaryResult.writeTo(byteArrayOutputStream);
byte[] bytes = byteArrayOutputStream.toByteArray();
- return Truth.assertThat(bytes);
+ return check("bytes()").that(bytes);
}
}
diff --git a/java/com/google/gerrit/extensions/systemstatus/MessageOfTheDay.java b/java/com/google/gerrit/extensions/systemstatus/MessageOfTheDay.java
deleted file mode 100644
index 180a0e6635..0000000000
--- a/java/com/google/gerrit/extensions/systemstatus/MessageOfTheDay.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2014 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.systemstatus;
-
-import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.TimeZone;
-
-/**
- * Supplies a message of the day when the page is first loaded.
- *
- * <pre>
- * DynamicSet.bind(binder(), MessageOfTheDay.class).to(MyMessage.class);
- * </pre>
- */
-@ExtensionPoint
-public abstract class MessageOfTheDay {
- /**
- * Retrieve the message of the day as an HTML fragment.
- *
- * @return message as an HTML fragment; null if no message is available.
- */
- public abstract String getHtmlMessage();
-
- /**
- * Unique identifier for this message.
- *
- * <p>Messages with the same identifier will be hidden from the user until redisplay has occurred.
- *
- * @return unique message identifier. This identifier should be unique within the server.
- */
- public abstract String getMessageId();
-
- /**
- * When should the message be displayed?
- *
- * <p>Default implementation returns {@code tomorrow at 00:00:00 GMT}.
- *
- * @return a future date after which the message should be redisplayed.
- */
- public Date getRedisplay() {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
- cal.set(Calendar.HOUR_OF_DAY, 0);
- cal.set(Calendar.MINUTE, 0);
- cal.set(Calendar.SECOND, 0);
- cal.set(Calendar.MILLISECOND, 0);
- cal.add(Calendar.DAY_OF_MONTH, 1);
- return cal.getTime();
- }
-}
diff --git a/java/com/google/gerrit/extensions/webui/GwtPlugin.java b/java/com/google/gerrit/extensions/webui/GwtPlugin.java
deleted file mode 100644
index e8041c4978..0000000000
--- a/java/com/google/gerrit/extensions/webui/GwtPlugin.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2012 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.webui;
-
-/** Configures a web UI plugin compiled using GWT. */
-public class GwtPlugin extends WebUiPlugin {
- private final String moduleName;
-
- /**
- * @param moduleName name of GWT module. The resource {@code static/$MODULE/$MODULE.nocache.js}
- * will be used.
- */
- public GwtPlugin(String moduleName) {
- this.moduleName = moduleName;
- }
-
- @Override
- public String getJavaScriptResourcePath() {
- return String.format("static/%s/%s.nocache.js", moduleName, moduleName);
- }
-}
diff --git a/java/com/google/gerrit/extensions/webui/WebUiPlugin.java b/java/com/google/gerrit/extensions/webui/WebUiPlugin.java
index 051d336649..2d49e1c8e2 100644
--- a/java/com/google/gerrit/extensions/webui/WebUiPlugin.java
+++ b/java/com/google/gerrit/extensions/webui/WebUiPlugin.java
@@ -34,15 +34,10 @@ import com.google.inject.Inject;
* }
* </pre>
*
- * @see GwtPlugin
* @see JavaScriptPlugin
*/
@ExtensionPoint
public abstract class WebUiPlugin {
- public static final GwtPlugin gwt(String moduleName) {
- return new GwtPlugin(moduleName);
- }
-
public static final JavaScriptPlugin js(String scriptName) {
return new JavaScriptPlugin(scriptName);
}
diff --git a/java/com/google/gerrit/git/BUILD b/java/com/google/gerrit/git/BUILD
new file mode 100644
index 0000000000..4c4d5bcc89
--- /dev/null
+++ b/java/com/google/gerrit/git/BUILD
@@ -0,0 +1,11 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "git",
+ srcs = glob(["*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:guava",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/java/com/google/gerrit/git/LockFailureException.java b/java/com/google/gerrit/git/LockFailureException.java
new file mode 100644
index 0000000000..9e67d700a6
--- /dev/null
+++ b/java/com/google/gerrit/git/LockFailureException.java
@@ -0,0 +1,49 @@
+// 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.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 {
+ private static final long serialVersionUID = 1L;
+
+ private final ImmutableList<String> refs;
+
+ public LockFailureException(String message, RefUpdate refUpdate) {
+ super(message);
+ refs = ImmutableList.of(refUpdate.getName());
+ }
+
+ 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;
+ }
+}
diff --git a/java/com/google/gerrit/git/RefUpdateUtil.java b/java/com/google/gerrit/git/RefUpdateUtil.java
new file mode 100644
index 0000000000..5eaf253f1a
--- /dev/null
+++ b/java/com/google/gerrit/git/RefUpdateUtil.java
@@ -0,0 +1,183 @@
+// 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.git;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** Static utilities for working with JGit's ref update APIs. */
+public class RefUpdateUtil {
+ /**
+ * Execute a batch ref update, throwing a checked exception if not all updates succeeded.
+ *
+ * <p>Creates a new {@link RevWalk} used only for this operation.
+ *
+ * @param bru batch update; should already have been executed.
+ * @param repo repository that created {@code bru}.
+ * @throws LockFailureException if the transaction was aborted due to lock failure; see {@link
+ * #checkResults(BatchRefUpdate)} for details.
+ * @throws IOException if any result was not {@code OK}.
+ */
+ public static void executeChecked(BatchRefUpdate bru, Repository repo) throws IOException {
+ try (RevWalk rw = new RevWalk(repo)) {
+ executeChecked(bru, rw);
+ }
+ }
+
+ /**
+ * Execute a batch ref update, throwing a checked exception if not all updates succeeded.
+ *
+ * @param bru batch update; should already have been executed.
+ * @param rw walk for executing the update.
+ * @throws LockFailureException if the transaction was aborted due to lock failure; see {@link
+ * #checkResults(BatchRefUpdate)} for details.
+ * @throws IOException if any result was not {@code OK}.
+ */
+ public static void executeChecked(BatchRefUpdate bru, RevWalk rw) throws IOException {
+ bru.execute(rw, NullProgressMonitor.INSTANCE);
+ checkResults(bru);
+ }
+
+ /**
+ * Check results of all commands in the update batch, reducing to a single exception if there was
+ * a failure.
+ *
+ * <p>Throws {@link LockFailureException} if at least one command failed with {@code
+ * LOCK_FAILURE}, and the entire transaction was aborted, i.e. any non-{@code LOCK_FAILURE}
+ * results, if there were any, failed with "transaction aborted".
+ *
+ * <p>In particular, if the underlying ref database does not {@link
+ * org.eclipse.jgit.lib.RefDatabase#performsAtomicTransactions() perform atomic transactions},
+ * then a combination of {@code LOCK_FAILURE} on one ref and {@code OK} or another result on other
+ * refs will <em>not</em> throw {@code LockFailureException}.
+ *
+ * @param bru batch update; should already have been executed.
+ * @throws LockFailureException if the transaction was aborted due to lock failure.
+ * @throws IOException if any result was not {@code OK}.
+ */
+ @VisibleForTesting
+ static void checkResults(BatchRefUpdate bru) throws IOException {
+ if (bru.getCommands().isEmpty()) {
+ return;
+ }
+
+ int lockFailure = 0;
+ int aborted = 0;
+ int failure = 0;
+
+ for (ReceiveCommand cmd : bru.getCommands()) {
+ if (cmd.getResult() != ReceiveCommand.Result.OK) {
+ failure++;
+ }
+ if (cmd.getResult() == ReceiveCommand.Result.LOCK_FAILURE) {
+ lockFailure++;
+ } else if (cmd.getResult() == ReceiveCommand.Result.REJECTED_OTHER_REASON
+ && JGitText.get().transactionAborted.equals(cmd.getMessage())) {
+ aborted++;
+ }
+ }
+
+ 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);
+ }
+ }
+
+ /**
+ * Check results of a single ref update, throwing an exception if there was a failure.
+ *
+ * @param ru ref update; must already have been executed.
+ * @throws IllegalArgumentException if the result was {@code NOT_ATTEMPTED}.
+ * @throws LockFailureException if the result was {@code LOCK_FAILURE}.
+ * @throws IOException if the result failed for another reason.
+ */
+ public static void checkResult(RefUpdate ru) throws IOException {
+ RefUpdate.Result result = ru.getResult();
+ switch (result) {
+ case NOT_ATTEMPTED:
+ throw new IllegalArgumentException("Not attempted: " + ru.getName());
+ case NEW:
+ case FORCED:
+ case NO_CHANGE:
+ case FAST_FORWARD:
+ case RENAMED:
+ return;
+ case LOCK_FAILURE:
+ throw new LockFailureException("Failed to update " + ru.getName() + ": " + result, ru);
+ default:
+ case IO_FAILURE:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ throw new IOException("Failed to update " + ru.getName() + ": " + ru.getResult());
+ }
+ }
+
+ /**
+ * Delete a single ref, throwing a checked exception on failure.
+ *
+ * <p>Does not require that the ref have any particular old value. Succeeds as a no-op if the ref
+ * did not exist.
+ *
+ * @param repo repository.
+ * @param refName ref name to delete.
+ * @throws LockFailureException if a low-level lock failure (e.g. compare-and-swap failure)
+ * occurs.
+ * @throws IOException if an error occurred.
+ */
+ public static void deleteChecked(Repository repo, String refName) throws IOException {
+ RefUpdate ru = repo.updateRef(refName);
+ ru.setForceUpdate(true);
+ ru.setCheckConflicting(false);
+ switch (ru.delete()) {
+ case FORCED:
+ // Ref was deleted.
+ return;
+
+ case NEW:
+ // Ref didn't exist (yes, really).
+ return;
+
+ case LOCK_FAILURE:
+ throw new LockFailureException("Failed to delete " + refName + ": " + ru.getResult(), ru);
+
+ // Not really failures, but should not be the result of a deletion, so the best option is to
+ // throw.
+ case NO_CHANGE:
+ case FAST_FORWARD:
+ case RENAMED:
+ case NOT_ATTEMPTED:
+
+ case IO_FAILURE:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ throw new IOException("Failed to delete " + refName + ": " + ru.getResult());
+ }
+ }
+
+ private RefUpdateUtil() {}
+}
diff --git a/java/com/google/gerrit/git/testing/CommitSubject.java b/java/com/google/gerrit/git/testing/CommitSubject.java
new file mode 100644
index 0000000000..08731072da
--- /dev/null
+++ b/java/com/google/gerrit/git/testing/CommitSubject.java
@@ -0,0 +1,97 @@
+// 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.testing;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import java.sql.Timestamp;
+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> {
+
+ /**
+ * Constructs a new subject.
+ *
+ * @param commit the commit.
+ * @return a new subject over the commit.
+ */
+ public static CommitSubject assertThat(RevCommit commit) {
+ return assertAbout(CommitSubject::new).that(commit);
+ }
+
+ /**
+ * Performs some common assertions over a single commit.
+ *
+ * @param commit the commit.
+ * @param expectedCommitMessage exact expected commit message.
+ * @param expectedCommitTimestamp expected commit timestamp, to the tolerance specified in {@link
+ * #hasCommitTimestamp(Timestamp)}.
+ * @param expectedSha1 expected commit SHA-1.
+ */
+ public static void assertCommit(
+ RevCommit commit,
+ String expectedCommitMessage,
+ Timestamp expectedCommitTimestamp,
+ ObjectId expectedSha1) {
+ CommitSubject commitSubject = assertThat(commit);
+ commitSubject.hasCommitMessage(expectedCommitMessage);
+ commitSubject.hasCommitTimestamp(expectedCommitTimestamp);
+ commitSubject.hasSha1(expectedSha1);
+ }
+
+ private CommitSubject(FailureMetadata metadata, RevCommit actual) {
+ super(metadata, actual);
+ }
+
+ /**
+ * Asserts that the commit has the given commit message.
+ *
+ * @param expectedCommitMessage exact expected commit message.
+ */
+ public void hasCommitMessage(String expectedCommitMessage) {
+ isNotNull();
+ RevCommit commit = actual();
+ check("commitMessage()").that(commit.getFullMessage()).isEqualTo(expectedCommitMessage);
+ }
+
+ /**
+ * Asserts that the commit has the given commit message, up to skew of at most 1 second.
+ *
+ * @param expectedCommitTimestamp expected commit timestamp.
+ */
+ 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));
+ }
+
+ /**
+ * Asserts that the commit has the given SHA-1.
+ *
+ * @param expectedSha1 expected commit SHA-1.
+ */
+ 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
new file mode 100644
index 0000000000..5fe91f950d
--- /dev/null
+++ b/java/com/google/gerrit/git/testing/ObjectIdSubject.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.git.testing;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+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 static ObjectIdSubject assertThat(ObjectId objectId) {
+ return assertAbout(objectIds()).that(objectId);
+ }
+
+ public static Factory<ObjectIdSubject, ObjectId> objectIds() {
+ return ObjectIdSubject::new;
+ }
+
+ private ObjectIdSubject(FailureMetadata metadata, ObjectId actual) {
+ super(metadata, actual);
+ }
+
+ public void hasName(String expectedName) {
+ isNotNull();
+ ObjectId objectId = actual();
+ check("name()").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 929e1826e8..f5c9810bb8 100644
--- a/java/com/google/gerrit/git/testing/PushResultSubject.java
+++ b/java/com/google/gerrit/git/testing/PushResultSubject.java
@@ -16,7 +16,6 @@ package com.google.gerrit.git.testing;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.truth.Truth.assertAbout;
-import static java.util.stream.Collectors.joining;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
@@ -25,11 +24,10 @@ import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
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.common.truth.Truth8;
import com.google.gerrit.common.Nullable;
-import java.util.Arrays;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -43,7 +41,8 @@ public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
}
public void hasNoMessages() {
- Truth.assertWithMessage("expected no messages")
+ check("hasNoMessages()")
+ .withMessage("expected no messages")
.that(Strings.nullToEmpty(trimMessages()))
.isEqualTo("");
}
@@ -51,14 +50,14 @@ public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
public void hasMessages(String... expectedLines) {
checkArgument(expectedLines.length > 0, "use hasNoMessages()");
isNotNull();
- Truth.assertThat(trimMessages()).isEqualTo(Arrays.stream(expectedLines).collect(joining("\n")));
+ check("messages()").that(trimMessages()).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());
- Truth.assertThat(got).containsAllIn(expectedLines).inOrder();
+ check("messages()").that(got).containsAtLeastElementsIn(expectedLines).inOrder();
}
private String trimMessages() {
@@ -90,10 +89,7 @@ public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
messages, Throwables.getStackTraceAsString(e));
return;
}
- Truth.assertThat(actual)
- .named("processed commands")
- .containsExactlyEntriesIn(expected)
- .inOrder();
+ check("processedCommands()").that(actual).containsExactlyEntriesIn(expected).inOrder();
}
@VisibleForTesting
@@ -129,7 +125,9 @@ public class PushResultSubject extends Subject<PushResultSubject, PushResult> {
}
public RemoteRefUpdateSubject onlyRef(String refName) {
- Truth8.assertThat(actual().getRemoteUpdates().stream().map(RemoteRefUpdate::getRemoteName))
+ check("setOfRefs()")
+ .about(StreamSubject.streams())
+ .that(actual().getRemoteUpdates().stream().map(RemoteRefUpdate::getRemoteName))
.named("set of refs")
.containsExactly(refName);
return ref(refName);
diff --git a/java/com/google/gerrit/gpg/BUILD b/java/com/google/gerrit/gpg/BUILD
index 113b1845ee..f11b9b92bd 100644
--- a/java/com/google/gerrit/gpg/BUILD
+++ b/java/com/google/gerrit/gpg/BUILD
@@ -5,12 +5,13 @@ 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/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//lib:guava",
- "//lib:gwtorm",
"//lib/bouncycastle:bcpg-neverlink",
"//lib/bouncycastle:bcprov-neverlink",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/gpg/CheckResult.java b/java/com/google/gerrit/gpg/CheckResult.java
index da891aab39..8655b2acef 100644
--- a/java/com/google/gerrit/gpg/CheckResult.java
+++ b/java/com/google/gerrit/gpg/CheckResult.java
@@ -31,14 +31,14 @@ public class CheckResult {
}
static CheckResult trusted() {
- return new CheckResult(Status.TRUSTED, Collections.<String>emptyList());
+ return new CheckResult(Status.TRUSTED, Collections.emptyList());
}
static CheckResult create(Status status, String... problems) {
List<String> problemList =
problems.length > 0
? Collections.unmodifiableList(Arrays.asList(problems))
- : Collections.<String>emptyList();
+ : Collections.emptyList();
return new CheckResult(status, problemList);
}
diff --git a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index b6ecbc5a2f..9c0885707a 100644
--- a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.UrlFormatter;
import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -135,7 +134,7 @@ public class GerritPublicKeyChecker extends PublicKeyChecker {
return checkIdsForExpectedUser(key);
}
return checkIdsForArbitraryUser(key);
- } catch (PGPException | OrmException e) {
+ } catch (PGPException | RuntimeException e) {
String msg = "Error checking user IDs for key";
logger.atWarning().withCause(e).log("%s %s", msg, keyIdToString(key.getKeyID()));
return CheckResult.bad(msg);
@@ -156,7 +155,7 @@ public class GerritPublicKeyChecker extends PublicKeyChecker {
return CheckResult.bad(missingUserIds(allowedUserIds));
}
- private CheckResult checkIdsForArbitraryUser(PGPPublicKey key) throws PGPException, OrmException {
+ private CheckResult checkIdsForArbitraryUser(PGPPublicKey key) throws PGPException {
List<AccountState> accountStates = accountQueryProvider.get().byExternalId(toExtIdKey(key));
if (accountStates.isEmpty()) {
return CheckResult.bad("Key is not associated with any users");
diff --git a/java/com/google/gerrit/gpg/PublicKeyChecker.java b/java/com/google/gerrit/gpg/PublicKeyChecker.java
index 07b42f10b5..27530e7c56 100644
--- a/java/com/google/gerrit/gpg/PublicKeyChecker.java
+++ b/java/com/google/gerrit/gpg/PublicKeyChecker.java
@@ -130,7 +130,7 @@ public class PublicKeyChecker {
if (store == null) {
throw new IllegalStateException("PublicKeyStore is required");
}
- return check(key, 0, true, trusted != null ? new HashSet<Fingerprint>() : null);
+ return check(key, 0, true, trusted != null ? new HashSet<>() : null);
}
/**
diff --git a/java/com/google/gerrit/gpg/PublicKeyStore.java b/java/com/google/gerrit/gpg/PublicKeyStore.java
index 19d503fba1..7dd01d9a12 100644
--- a/java/com/google/gerrit/gpg/PublicKeyStore.java
+++ b/java/com/google/gerrit/gpg/PublicKeyStore.java
@@ -15,8 +15,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.OBJ_BLOB;
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteStreams;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -38,6 +42,8 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
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;
@@ -63,6 +69,8 @@ import org.eclipse.jgit.util.NB;
* matching the ID. Multiple keys are supported because forging a key ID is possible, but such a key
* cannot be used to verify signatures produced with the correct key.
*
+ * <p>Subkeys are mapped to the master GPG key in the same NoteMap.
+ *
* <p>No additional checks are performed on the key after reading; callers should only trust keys
* after checking with a {@link PublicKeyChecker}.
*/
@@ -88,11 +96,19 @@ public class PublicKeyStore implements AutoCloseable {
public static PGPPublicKey getSigner(
Iterable<PGPPublicKeyRing> keyRings, PGPSignature sig, byte[] data) throws PGPException {
for (PGPPublicKeyRing kr : keyRings) {
- PGPPublicKey k = kr.getPublicKey();
+ // Possibly return a signing subkey in case it differs from the master public key
+ PGPPublicKey k = kr.getPublicKey(sig.getKeyID());
+ if (k == null) {
+ throw new IllegalStateException(
+ "No public key found for ID: " + keyIdToString(sig.getKeyID()));
+ }
sig.init(new BcPGPContentVerifierBuilderProvider(), k);
sig.update(data);
if (sig.verify()) {
- return k;
+ // If the signature was made using a subkey, return the main public key.
+ // This enables further validity checks, like user ID checks, that can only
+ // be performed using the master public key.
+ return kr.getPublicKey();
}
}
return null;
@@ -207,19 +223,34 @@ public class PublicKeyStore implements AutoCloseable {
if (notes == null) {
return Collections.emptyList();
}
- Note note = notes.getNote(keyObjectId(keyId));
+
+ return get(keyObjectId(keyId), fp);
+ }
+
+ private List<PGPPublicKeyRing> get(ObjectId keyObjectId, byte[] fp) throws IOException {
+ Note note = notes.getNote(keyObjectId);
if (note == null) {
return Collections.emptyList();
}
+ return readKeysFromNote(note, fp);
+ }
+
+ private List<PGPPublicKeyRing> readKeysFromNote(Note note, byte[] fp)
+ throws IOException, MissingObjectException, IncorrectObjectTypeException {
+ boolean foundAtLeastOneKey = false;
List<PGPPublicKeyRing> keys = new ArrayList<>();
- try (InputStream in = reader.open(note.getData(), OBJ_BLOB).openStream()) {
+ ObjectId data = note.getData();
+ try (InputStream stream = reader.open(data, OBJ_BLOB).openStream()) {
+ byte[] bytes = ByteStreams.toByteArray(stream);
+ InputStream in = new ByteArrayInputStream(bytes);
while (true) {
@SuppressWarnings("unchecked")
Iterator<Object> it = new BcPGPObjectFactory(new ArmoredInputStream(in)).iterator();
if (!it.hasNext()) {
break;
}
+ foundAtLeastOneKey = true;
Object obj = it.next();
if (obj instanceof PGPPublicKeyRing) {
PGPPublicKeyRing kr = (PGPPublicKeyRing) obj;
@@ -229,7 +260,34 @@ public class PublicKeyStore implements AutoCloseable {
}
checkState(!it.hasNext(), "expected one PGP object per ArmoredInputStream");
}
- return keys;
+
+ if (foundAtLeastOneKey) {
+ return keys;
+ }
+
+ // Subkey handling
+ String id = new String(bytes, UTF_8);
+ Preconditions.checkArgument(ObjectId.isId(id), "Not valid SHA1: " + id);
+ return get(ObjectId.fromString(id), fp);
+ }
+ }
+
+ public void rebuildSubkeyMasterKeyMap()
+ throws MissingObjectException, IncorrectObjectTypeException, IOException, PGPException {
+ if (reader == null) {
+ load();
+ }
+ if (notes != null) {
+ try (ObjectInserter ins = repo.newObjectInserter()) {
+ for (Note note : notes) {
+ for (PGPPublicKeyRing keyRing :
+ new PGPPublicKeyRingCollection(readKeysFromNote(note, null))) {
+ long masterKeyId = keyRing.getPublicKey().getKeyID();
+ ObjectId masterKeyObjectId = keyObjectId(masterKeyId);
+ saveSubkeyMapping(ins, keyRing, masterKeyId, masterKeyObjectId);
+ }
+ }
+ }
}
}
@@ -348,8 +406,8 @@ public class PublicKeyStore implements AutoCloseable {
private void saveToNotes(ObjectInserter ins, PGPPublicKeyRing keyRing)
throws PGPException, IOException {
- long keyId = keyRing.getPublicKey().getKeyID();
- PGPPublicKeyRingCollection existing = get(keyId);
+ long masterKeyId = keyRing.getPublicKey().getKeyID();
+ PGPPublicKeyRingCollection existing = get(masterKeyId);
List<PGPPublicKeyRing> toWrite = new ArrayList<>(existing.size() + 1);
boolean replaced = false;
for (PGPPublicKeyRing kr : existing) {
@@ -363,7 +421,37 @@ public class PublicKeyStore implements AutoCloseable {
if (!replaced) {
toWrite.add(keyRing);
}
- notes.set(keyObjectId(keyId), ins.insert(OBJ_BLOB, keysToArmored(toWrite)));
+
+ ObjectId masterKeyObjectId = keyObjectId(masterKeyId);
+ notes.set(masterKeyObjectId, ins.insert(OBJ_BLOB, keysToArmored(toWrite)));
+
+ saveSubkeyMapping(ins, keyRing, masterKeyId, masterKeyObjectId);
+ }
+
+ private void saveSubkeyMapping(
+ ObjectInserter ins, PGPPublicKeyRing keyRing, long masterKeyId, ObjectId masterKeyObjectId)
+ throws IOException {
+ // Subkey handling
+ byte[] masterKeyBytes = masterKeyObjectId.name().getBytes(UTF_8);
+ ObjectId masterKeyObject = null;
+ for (PGPPublicKey key : keyRing) {
+ long subKeyId = key.getKeyID();
+ // Skip master public key
+ if (masterKeyId == subKeyId) {
+ continue;
+ }
+
+ // Insert master key object only once for all subkeys
+ if (masterKeyObject == null) {
+ masterKeyObject = ins.insert(OBJ_BLOB, masterKeyBytes);
+ }
+
+ ObjectId subkeyObjectId = keyObjectId(subKeyId);
+ Preconditions.checkArgument(
+ notes.get(subkeyObjectId) == null || notes.get(subkeyObjectId).equals(masterKeyObject),
+ "Master key differs for subkey: " + subkeyObjectId.name());
+ notes.set(subkeyObjectId, masterKeyObject);
+ }
}
private void deleteFromNotes(ObjectInserter ins, Fingerprint fp)
@@ -378,10 +466,24 @@ public class PublicKeyStore implements AutoCloseable {
}
if (toWrite.size() == existing.size()) {
return;
- } else if (!toWrite.isEmpty()) {
- notes.set(keyObjectId(keyId), ins.insert(OBJ_BLOB, keysToArmored(toWrite)));
+ }
+
+ ObjectId keyObjectId = keyObjectId(keyId);
+ if (!toWrite.isEmpty()) {
+ notes.set(keyObjectId, ins.insert(OBJ_BLOB, keysToArmored(toWrite)));
} else {
- notes.remove(keyObjectId(keyId));
+ PGPPublicKeyRing keyRing = get(fp.get());
+
+ for (PGPPublicKey key : keyRing) {
+ long subKeyId = key.getKeyID();
+ // Skip master public key
+ if (keyId == subKeyId) {
+ continue;
+ }
+ notes.remove(keyObjectId(subKeyId));
+ }
+
+ notes.remove(keyObjectId);
}
}
diff --git a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
index 967259ad89..b7b03db6ca 100644
--- a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -28,7 +28,6 @@ import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.GpgApiAdapter;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -67,7 +66,7 @@ public class GpgApiAdapterImpl implements GpgApiAdapter {
throws RestApiException, GpgException {
try {
return gpgKeys.get().list().apply(account);
- } catch (OrmException | PGPException | IOException e) {
+ } catch (PGPException | IOException e) {
throw new GpgException(e);
}
}
@@ -81,7 +80,7 @@ public class GpgApiAdapterImpl implements GpgApiAdapter {
in.delete = delete;
try {
return postGpgKeys.get().apply(account, in);
- } catch (PGPException | OrmException | IOException | ConfigInvalidException e) {
+ } catch (PGPException | IOException | ConfigInvalidException e) {
throw new GpgException(e);
}
}
@@ -91,7 +90,7 @@ public class GpgApiAdapterImpl implements GpgApiAdapter {
throws RestApiException, GpgException {
try {
return gpgKeyApiFactory.create(gpgKeys.get().parse(account, idStr));
- } catch (PGPException | OrmException | IOException e) {
+ } catch (PGPException | IOException e) {
throw new GpgException(e);
}
}
diff --git a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
index 25b472d9f6..cf09acf470 100644
--- a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
@@ -21,7 +21,6 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.gpg.server.DeleteGpgKey;
import com.google.gerrit.gpg.server.GpgKey;
import com.google.gerrit.gpg.server.GpgKeys;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -57,7 +56,7 @@ public class GpgKeyApiImpl implements GpgKeyApi {
public void delete() throws RestApiException {
try {
delete.apply(rsrc, new Input());
- } catch (PGPException | OrmException | IOException | ConfigInvalidException e) {
+ } catch (PGPException | IOException | ConfigInvalidException e) {
throw new RestApiException("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 b50dfebb46..68bd2d9b4e 100644
--- a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -20,9 +20,9 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GPG
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.BaseEncoding;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.Input;
-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;
@@ -34,7 +34,6 @@ import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.mail.send.DeleteKeySender;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -71,7 +70,7 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
@Override
public Response<?> apply(GpgKey rsrc, Input input)
- throws RestApiException, PGPException, OrmException, IOException, ConfigInvalidException {
+ throws RestApiException, PGPException, IOException, ConfigInvalidException {
PGPPublicKey key = rsrc.getKeyRing().getPublicKey();
String fingerprint = BaseEncoding.base16().encode(key.getFingerprint());
Optional<ExternalId> extId = externalIds.get(ExternalId.Key.create(SCHEME_GPGKEY, fingerprint));
@@ -109,9 +108,9 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
rsrc.getUser().getAccount().getPreferredEmail());
}
break;
+ case LOCK_FAILURE:
case FORCED:
case IO_FAILURE:
- case LOCK_FAILURE:
case NEW:
case NOT_ATTEMPTED:
case REJECTED:
@@ -120,7 +119,7 @@ public class DeleteGpgKey implements RestModifyView<GpgKey, Input> {
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- throw new ResourceConflictException("Failed to delete public key: " + saveResult);
+ throw new StorageException(String.format("Failed to delete public key: %s", saveResult));
}
}
return Response.none();
diff --git a/java/com/google/gerrit/gpg/server/GpgKeys.java b/java/com/google/gerrit/gpg/server/GpgKeys.java
index e555f07554..16592f8b18 100644
--- a/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -39,7 +39,6 @@ import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -86,7 +85,7 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
@Override
public GpgKey parse(AccountResource parent, IdString id)
- throws ResourceNotFoundException, PGPException, OrmException, IOException {
+ throws ResourceNotFoundException, PGPException, IOException {
checkVisible(self, parent);
ExternalId gpgKeyExtId = findGpgKey(id.get(), getGpgExtIds(parent));
@@ -142,7 +141,7 @@ public class GpgKeys implements ChildCollection<AccountResource, GpgKey> {
public class ListGpgKeys implements RestReadView<AccountResource> {
@Override
public Map<String, GpgKeyInfo> apply(AccountResource rsrc)
- throws OrmException, PGPException, IOException, ResourceNotFoundException {
+ throws PGPException, IOException, ResourceNotFoundException {
checkVisible(self, rsrc);
Map<String, GpgKeyInfo> keys = new HashMap<>();
try (PublicKeyStore store = storeProvider.get()) {
diff --git a/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 2a7de58574..0bb4ff7434 100644
--- a/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -27,7 +27,8 @@ 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.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.accounts.GpgKeysInput;
import com.google.gerrit.extensions.common.GpgKeyInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -52,7 +53,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -113,7 +113,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
@Override
public Map<String, GpgKeyInfo> apply(AccountResource rsrc, GpgKeysInput input)
throws ResourceNotFoundException, BadRequestException, ResourceConflictException,
- PGPException, OrmException, IOException, ConfigInvalidException {
+ PGPException, IOException, ConfigInvalidException {
GpgKeys.checkVisible(self, rsrc);
Collection<ExternalId> existingExtIds =
@@ -196,7 +196,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
private void storeKeys(
AccountResource rsrc, List<PGPPublicKeyRing> keyRings, Collection<Fingerprint> toRemove)
- throws BadRequestException, ResourceConflictException, PGPException, IOException {
+ throws BadRequestException, PGPException, IOException {
try (PublicKeyStore store = storeProvider.get()) {
List<String> addedKeys = new ArrayList<>();
IdentifiedUser user = rsrc.getUser();
@@ -249,8 +249,8 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
break;
case NO_CHANGE:
break;
- case IO_FAILURE:
case LOCK_FAILURE:
+ case IO_FAILURE:
case NOT_ATTEMPTED:
case REJECTED:
case REJECTED_CURRENT_BRANCH:
@@ -258,8 +258,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- // TODO(dborowitz): Backoff and retry on LOCK_FAILURE.
- throw new ResourceConflictException("Failed to save public keys: " + saveResult);
+ throw new StorageException(String.format("Failed to save public keys: %s", saveResult));
}
}
}
@@ -268,7 +267,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, GpgKeysInput
return ExternalId.Key.create(SCHEME_GPGKEY, BaseEncoding.base16().encode(fp));
}
- private Account getAccountByExternalId(ExternalId.Key extIdKey) throws OrmException {
+ private Account getAccountByExternalId(ExternalId.Key extIdKey) {
List<AccountState> accountStates = accountQueryProvider.get().byExternalId(extIdKey);
if (accountStates.isEmpty()) {
diff --git a/java/com/google/gerrit/gpg/testing/TestKeys.java b/java/com/google/gerrit/gpg/testing/TestKeys.java
index 00acedb0ad..de668891fe 100644
--- a/java/com/google/gerrit/gpg/testing/TestKeys.java
+++ b/java/com/google/gerrit/gpg/testing/TestKeys.java
@@ -1029,4 +1029,81 @@ public class TestKeys {
+ "=JxsF\n"
+ "-----END PGP PRIVATE KEY BLOCK-----\n");
}
+
+ /**
+ * Master Key without expiration with subkey with expiration.
+ *
+ * <pre>
+ * pub rsa1024 2018-11-17 [C]
+ * 5734 2C37 982A 843B 19C0 622B 6AAF 2D26 B481 02DB
+ * uid [ultimate] Testuser 10 <testuser10@example.com>
+ * sub rsa1024 2018-11-17 [S] [expires: 2065-11-05]
+ * 0A4A 9660 1B96 2DFC E898 E686 4305 C92E 626E B485
+ * </pre>
+ */
+ public static TestKey validKeyWithoutExpirationWithSubkeyWithExpiration() throws Exception {
+ return new TestKey(
+ "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ + "\n"
+ + "mI0EW+98GgEEALLn87xX++daic3AzKwM7nY50Mx2eTaEZPlnaDAVvFlhbZuPG+n5\n"
+ + "g93vYX3wEfnFxI7IBEe7VMT1AyszLZgpFmbzW8eGQxCGpRd1hYrUUlC0IkGAwG9v\n"
+ + "LQB85GDDZUUH4p+A4oHX0yUm8iCpbO9+D82xzNDe/D8Xbw1foWMWGonLABEBAAG0\n"
+ + "JFRlc3R1c2VyIDEwIDx0ZXN0dXNlcjEwQGV4YW1wbGUuY29tPojOBBMBCAA4FiEE\n"
+ + "VzQsN5gqhDsZwGIraq8tJrSBAtsFAlvvfBoCGwEFCwkIBwIGFQoJCAsCBBYCAwEC\n"
+ + "HgECF4AACgkQaq8tJrSBAtvfFAP/VqV77KQZp9rjSGStDpxxlatr4Y5nrRBZfV5v\n"
+ + "jpAjwusIHRjr0OWXLxX7NDLYd+oIjhLFn26Lux1UXOQT+rGRPwnxoJZWrpDQidP7\n"
+ + "fDgfqnNa5UQGvoBPSIVEK1l0DlYAOUuciwz3HdMkeMuvEVEdyg7nOiVd1bF9V9i/\n"
+ + "8v7ABV24jQRb73xXAQQAssv5gwxWx5J0q4gGcqMIaJKzBaHAjiK3ryH6qnFQpsf1\n"
+ + "ODtU+a4NxFJsXGOd6jHEhBEHPgWAaaKZ7PEJVnwA/XOhPG+q9YimAbbZS0qmC/LH\n"
+ + "DpFtFbsJsMKZbIC69j9OcbmalIowspFQBVeAankGFReZVhh99Z/o81Y+Twm9eisA\n"
+ + "EQEAAYkBcQQYAQgAJhYhBFc0LDeYKoQ7GcBiK2qvLSa0gQLbBQJb73xXAhsCBQlY\n"
+ + "WHSAAL8JEGqvLSa0gQLbtCAEGQEIAB0WIQQKSpZgG5Yt/OiY5oZDBckuYm60hQUC\n"
+ + "W+98VwAKCRBDBckuYm60hafuBACSkvwXAYfxvAf7IOK6+Sp3oWkrq6vsjH5K7oup\n"
+ + "TimR1cVgN1CEAWh82UBCg3zR5Q5BAnvnjeugdQVrAY+sftkaoy8qO5YYHCPtHtQK\n"
+ + "mXGWM7Q33hU1E7IfgU06qkFLhIOL3Vwr8jOuOzHv0M3PbLNr7lOCaIJ7uCjPBZuo\n"
+ + "qRwqjHt2BACuXwA8RHbRGAxC65YgoSjGNu/da3q2J26E57KfSFprQ4TzAg33U4Ws\n"
+ + "qdx2vbbJxy1bfTYxb0AYXe/+k23W7EIdBtMGOYwrX01oTfSIKbM+gDrHswSYXdOy\n"
+ + "ziatLaUBSQfyG656lGGZO/aArLZb4dgGeBHBwhXTufFDIYl3X94zfQ==\n"
+ + "=BG9Z\n"
+ + "-----END PGP PUBLIC KEY BLOCK-----",
+ "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
+ + "\n"
+ + "lQIGBFvvfBoBBACy5/O8V/vnWonNwMysDO52OdDMdnk2hGT5Z2gwFbxZYW2bjxvp\n"
+ + "+YPd72F98BH5xcSOyARHu1TE9QMrMy2YKRZm81vHhkMQhqUXdYWK1FJQtCJBgMBv\n"
+ + "by0AfORgw2VFB+KfgOKB19MlJvIgqWzvfg/NsczQ3vw/F28NX6FjFhqJywARAQAB\n"
+ + "/gcDAmvJS+mrsxlf7D58lnhHw8gBjP5JkY9anTgVhIdieJEIlitV4PyRQYPAGZZd\n"
+ + "asUC7bC7WnLCygiU59kXS5z63Ue/RM0tVwWy0FsigpseC90Mtwb4wjL2jTeebszD\n"
+ + "UcM33d6Tg9s4eNnsHzpmlC/CReW6MYJj0/06AsvgUgOxgWXf0YapOLRIr60reTUb\n"
+ + "ovVZtH76rsZXyQvR9qJv11F+BmIDDzg4EsipXDGVuEZ0SXJyq6OLAUPkV2ZdELaT\n"
+ + "P4RWp0Zsn22H8jm4MsZ7la2Ux3jD2AMdy2B9dpwhuxOegDSXUMRfXYwxQ1wioqpA\n"
+ + "pOZ1RjjFsID34XNtxGp3wMYcFleOl3CSpXyW/P1PYTVBQta/Y7xniEetzUk9NHVc\n"
+ + "2jMD8a8767+Tk0SChIPmWOhQYrHS1Ce309SjTRSRVexjKF0Mp5WXHxqz582IlPT9\n"
+ + "jdxvLjVIW4xbtZmnQ2JnPHInWbxoa3exaoPg5osvg8h7QlGxRY6H2/20JFRlc3R1\n"
+ + "c2VyIDEwIDx0ZXN0dXNlcjEwQGV4YW1wbGUuY29tPojOBBMBCAA4FiEEVzQsN5gq\n"
+ + "hDsZwGIraq8tJrSBAtsFAlvvfBoCGwEFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA\n"
+ + "CgkQaq8tJrSBAtvfFAP/VqV77KQZp9rjSGStDpxxlatr4Y5nrRBZfV5vjpAjwusI\n"
+ + "HRjr0OWXLxX7NDLYd+oIjhLFn26Lux1UXOQT+rGRPwnxoJZWrpDQidP7fDgfqnNa\n"
+ + "5UQGvoBPSIVEK1l0DlYAOUuciwz3HdMkeMuvEVEdyg7nOiVd1bF9V9i/8v7ABV2d\n"
+ + "AgYEW+98VwEEALLL+YMMVseSdKuIBnKjCGiSswWhwI4it68h+qpxUKbH9Tg7VPmu\n"
+ + "DcRSbFxjneoxxIQRBz4FgGmimezxCVZ8AP1zoTxvqvWIpgG22UtKpgvyxw6RbRW7\n"
+ + "CbDCmWyAuvY/TnG5mpSKMLKRUAVXgGp5BhUXmVYYffWf6PNWPk8JvXorABEBAAH+\n"
+ + "BwMClgBcY/ItnafslgEWZOSqsxCSJatN72c9zzWZE+zmcx9NbDRuCVxXhTbHJZZs\n"
+ + "Hz44vsKqOKtyhrfr9Oke0mYyzH2CX6tv6ghJyC6znRCGoc/P84uJ1v3ibO/7/p85\n"
+ + "PDHzEEpHmdbef+UymnjZBYKGi45SINy3bLwOa/Vl80Q4wsppPe9oynerq6ig94HR\n"
+ + "e3mNDw/JggtgJA0X2VmmmXG8vHwIB5EziQrH7QGtLyjqdE+w7CLbbvAskL8Uw1qx\n"
+ + "Aowdpb7J8hrUdIDDCr/mlhT17+UM5yOXHKcixyrscqbjlG/nqwPvR10efo7D0rFR\n"
+ + "6tu5OU2y3N2PhGOysDLgupUXBLlpdByF6AYNV9zvU7ipO7QXzrUfYCb/WyAcjl+X\n"
+ + "Yl38sCVTVFGsB2ql9/fzFCxAB3FUNHDlI2sUbkdDPcjgf65SK0GGcckWfntfq9dj\n"
+ + "pQzEVen8X9dT3UhfuvHd98g3n6ju9gh8NucwHM5jITq9ItTY0whb+okBcQQYAQgA\n"
+ + "JhYhBFc0LDeYKoQ7GcBiK2qvLSa0gQLbBQJb73xXAhsCBQlYWHSAAL8JEGqvLSa0\n"
+ + "gQLbtCAEGQEIAB0WIQQKSpZgG5Yt/OiY5oZDBckuYm60hQUCW+98VwAKCRBDBcku\n"
+ + "Ym60hafuBACSkvwXAYfxvAf7IOK6+Sp3oWkrq6vsjH5K7oupTimR1cVgN1CEAWh8\n"
+ + "2UBCg3zR5Q5BAnvnjeugdQVrAY+sftkaoy8qO5YYHCPtHtQKmXGWM7Q33hU1E7If\n"
+ + "gU06qkFLhIOL3Vwr8jOuOzHv0M3PbLNr7lOCaIJ7uCjPBZuoqRwqjHt2BACuXwA8\n"
+ + "RHbRGAxC65YgoSjGNu/da3q2J26E57KfSFprQ4TzAg33U4Wsqdx2vbbJxy1bfTYx\n"
+ + "b0AYXe/+k23W7EIdBtMGOYwrX01oTfSIKbM+gDrHswSYXdOyziatLaUBSQfyG656\n"
+ + "lGGZO/aArLZb4dgGeBHBwhXTufFDIYl3X94zfQ==\n"
+ + "=RbPm\n"
+ + "-----END PGP PRIVATE KEY BLOCK-----");
+ }
}
diff --git a/java/com/google/gerrit/httpd/AllRequestFilter.java b/java/com/google/gerrit/httpd/AllRequestFilter.java
index 9d171d5a54..1c3cafea04 100644
--- a/java/com/google/gerrit/httpd/AllRequestFilter.java
+++ b/java/com/google/gerrit/httpd/AllRequestFilter.java
@@ -18,6 +18,8 @@ import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.plugins.Plugin;
import com.google.gerrit.server.plugins.StopPluginListener;
import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.servlet.ServletModule;
@@ -32,11 +34,15 @@ import javax.servlet.ServletResponse;
/** Filters all HTTP requests passing through the server. */
public abstract class AllRequestFilter implements Filter {
- public static ServletModule module() {
+ public static Module module() {
return new ServletModule() {
@Override
protected void configureServlets() {
DynamicSet.setOf(binder(), AllRequestFilter.class);
+ DynamicSet.bind(binder(), AllRequestFilter.class)
+ .to(AllowRenderInFrameFilter.class)
+ .in(Scopes.SINGLETON);
+
filter("/*").through(FilterProxy.class);
bind(StopPluginListener.class)
diff --git a/java/com/google/gerrit/httpd/AllowRenderInFrameFilter.java b/java/com/google/gerrit/httpd/AllowRenderInFrameFilter.java
new file mode 100644
index 0000000000..b414aad7c1
--- /dev/null
+++ b/java/com/google/gerrit/httpd/AllowRenderInFrameFilter.java
@@ -0,0 +1,59 @@
+// 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.httpd;
+
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.lib.Config;
+
+public class AllowRenderInFrameFilter extends AllRequestFilter {
+ static final String X_FRAME_OPTIONS_HEADER_NAME = "X-Frame-Options";
+
+ public static enum XFrameOption {
+ ALLOW,
+ SAMEORIGIN;
+ }
+
+ private final String xframeOptionString;
+ private final boolean skipXFrameOption;
+
+ @Inject
+ public AllowRenderInFrameFilter(@GerritServerConfig Config cfg) {
+ XFrameOption xframeOption =
+ cfg.getEnum("gerrit", null, "xframeOption", XFrameOption.SAMEORIGIN);
+ boolean canLoadInIFrame = cfg.getBoolean("gerrit", "canLoadInIFrame", false);
+ xframeOptionString = canLoadInIFrame ? xframeOption.name() : "DENY";
+
+ skipXFrameOption = xframeOption.equals(XFrameOption.ALLOW) && canLoadInIFrame;
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ if (skipXFrameOption) {
+ chain.doFilter(request, response);
+ } else {
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ httpResponse.addHeader(X_FRAME_OPTIONS_HEADER_NAME, xframeOptionString);
+ chain.doFilter(request, httpResponse);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index 9f39afa751..1d4189f4bc 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -12,7 +12,10 @@ java_library(
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/git",
+ "//java/com/google/gerrit/json",
"//java/com/google/gerrit/launcher",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
@@ -26,14 +29,11 @@ java_library(
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/http",
- "//java/org/eclipse/jgit:server",
"//lib:args4j",
"//lib:gson",
"//lib:guava",
- "//lib:gwtjsonrpc",
- "//lib:gwtorm",
"//lib:jsch",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib:soy",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/httpd/CacheBasedWebSession.java b/java/com/google/gerrit/httpd/CacheBasedWebSession.java
index ef2ebea9a7..177be75926 100644
--- a/java/com/google/gerrit/httpd/CacheBasedWebSession.java
+++ b/java/com/google/gerrit/httpd/CacheBasedWebSession.java
@@ -19,7 +19,6 @@ 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.common.data.HostPageData;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.httpd.WebSessionManager.Key;
import com.google.gerrit.httpd.WebSessionManager.Val;
@@ -105,7 +104,7 @@ public abstract class CacheBasedWebSession implements WebSession {
private void authFromCookie(String cookie) {
key = new Key(cookie);
val = manager.get(key);
- String token = request.getHeader(HostPageData.XSRF_HEADER_NAME);
+ String token = request.getHeader(XsrfConstants.XSRF_HEADER_NAME);
if (val != null && token != null && token.equals(val.getAuth())) {
okPaths.add(AccessPath.REST_API);
}
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 08ff8a74f7..c97ee1048f 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -14,8 +14,9 @@
package com.google.gerrit.httpd;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
-import com.google.common.collect.ArrayListMultimap;
+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;
@@ -38,6 +39,7 @@ 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.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -51,7 +53,9 @@ import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -60,6 +64,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
@@ -126,6 +131,74 @@ public class GitOverHttpServlet extends GitServlet {
.expireAfterWrite(Duration.ofMinutes(10));
}
});
+
+ // Don't bind Metrics, which is bound in a parent injector in tests.
+ }
+ }
+
+ @VisibleForTesting
+ @Singleton
+ public static class Metrics {
+ // Recording requests separately in this class is only necessary because of a bug in the
+ // implementation of the generic RequestMetricsFilter; see
+ // https://gerrit-review.googlesource.com/c/gerrit/+/211692
+ private final AtomicLong requestsStarted = new AtomicLong();
+
+ void requestStarted() {
+ requestsStarted.incrementAndGet();
+ }
+
+ public long getRequestsStarted() {
+ return requestsStarted.get();
+ }
+ }
+
+ static class HttpServletResponseWithStatusWrapper extends HttpServletResponseWrapper {
+ private int responseStatus;
+
+ HttpServletResponseWithStatusWrapper(HttpServletResponse response) {
+ super(response);
+ /* Even if we could read the status from response, we assume that it is all
+ * fine because we entered the filter without any prior issues.
+ * When Google will have upgraded to Servlet 3.0, we could actually
+ * call response.getStatus() and the code will be clearer.
+ */
+ responseStatus = HttpServletResponse.SC_OK;
+ }
+
+ @Override
+ public void setStatus(int sc) {
+ responseStatus = sc;
+ super.setStatus(sc);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void setStatus(int sc, String sm) {
+ responseStatus = sc;
+ super.setStatus(sc, sm);
+ }
+
+ @Override
+ public void sendError(int sc) throws IOException {
+ this.responseStatus = sc;
+ super.sendError(sc);
+ }
+
+ @Override
+ public void sendError(int sc, String msg) throws IOException {
+ this.responseStatus = sc;
+ super.sendError(sc, msg);
+ }
+
+ @Override
+ public void sendRedirect(String location) throws IOException {
+ this.responseStatus = HttpServletResponse.SC_MOVED_TEMPORARILY;
+ super.sendRedirect(location);
+ }
+
+ public int getResponseStatus() {
+ return responseStatus;
}
}
@@ -155,19 +228,15 @@ public class GitOverHttpServlet extends GitServlet {
}
private static ListMultimap<String, String> extractParameters(HttpServletRequest request) {
-
- ListMultimap<String, String> multiMap = ArrayListMultimap.create();
- if (request.getQueryString() != null) {
- request
- .getParameterMap()
- .forEach(
- (k, v) -> {
- for (int i = 0; i < v.length; i++) {
- multiMap.put(k, v[i]);
- }
- });
+ if (request.getQueryString() == null) {
+ return ImmutableListMultimap.of();
}
- return multiMap;
+ // Explicit cast is required to compile under Servlet API 2.5, where the return type is raw Map.
+ @SuppressWarnings("cast")
+ Map<String, String[]> parameterMap = (Map<String, String[]>) request.getParameterMap();
+ ImmutableListMultimap.Builder<String, String> b = ImmutableListMultimap.builder();
+ parameterMap.forEach(b::putAll);
+ return b.build();
}
static class Resolver implements RepositoryResolver<HttpServletRequest> {
@@ -237,14 +306,14 @@ public class GitOverHttpServlet extends GitServlet {
private final TransferConfig config;
private final DynamicSet<PreUploadHook> preUploadHooks;
private final DynamicSet<PostUploadHook> postUploadHooks;
- private final DynamicSet<UploadPackInitializer> uploadPackInitializers;
+ private final PluginSetContext<UploadPackInitializer> uploadPackInitializers;
@Inject
UploadFactory(
TransferConfig tc,
DynamicSet<PreUploadHook> preUploadHooks,
DynamicSet<PostUploadHook> postUploadHooks,
- DynamicSet<UploadPackInitializer> uploadPackInitializers) {
+ PluginSetContext<UploadPackInitializer> uploadPackInitializers) {
this.config = tc;
this.preUploadHooks = preUploadHooks;
this.postUploadHooks = postUploadHooks;
@@ -259,9 +328,7 @@ public class GitOverHttpServlet extends GitServlet {
up.setPreUploadHook(PreUploadHookChain.newChain(Lists.newArrayList(preUploadHooks)));
up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
- for (UploadPackInitializer initializer : uploadPackInitializers) {
- initializer.init(state.getNameKey(), up);
- }
+ uploadPackInitializers.runEach(initializer -> initializer.init(state.getNameKey(), up));
return up;
}
}
@@ -271,63 +338,74 @@ public class GitOverHttpServlet extends GitServlet {
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> userProvider;
private final GroupAuditService groupAuditService;
+ private final Metrics metrics;
@Inject
UploadFilter(
UploadValidators.Factory uploadValidatorsFactory,
PermissionBackend permissionBackend,
Provider<CurrentUser> userProvider,
- GroupAuditService groupAuditService) {
+ GroupAuditService groupAuditService,
+ Metrics metrics) {
this.uploadValidatorsFactory = uploadValidatorsFactory;
this.permissionBackend = permissionBackend;
this.userProvider = userProvider;
this.groupAuditService = groupAuditService;
+ this.metrics = metrics;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain next)
throws IOException, ServletException {
+ metrics.requestStarted();
// The Resolver above already checked READ access for us.
Repository repo = ServletUtils.getRepository(request);
ProjectState state = (ProjectState) request.getAttribute(ATT_STATE);
UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER);
PermissionBackend.ForProject perm =
permissionBackend.currentUser().project(state.getNameKey());
+ HttpServletResponseWithStatusWrapper responseWrapper =
+ new HttpServletResponseWithStatusWrapper((HttpServletResponse) response);
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ String sessionId = httpRequest.getSession().getId();
+
try {
- perm.check(ProjectPermission.RUN_UPLOAD_PACK);
- } catch (AuthException e) {
- GitSmartHttpTools.sendError(
- (HttpServletRequest) request,
- (HttpServletResponse) response,
- HttpServletResponse.SC_FORBIDDEN,
- "upload-pack not permitted on this server");
- return;
- } catch (PermissionBackendException e) {
- throw new ServletException(e);
+ try {
+ perm.check(ProjectPermission.RUN_UPLOAD_PACK);
+ } catch (AuthException e) {
+ GitSmartHttpTools.sendError(
+ (HttpServletRequest) request,
+ responseWrapper,
+ HttpServletResponse.SC_FORBIDDEN,
+ "upload-pack not permitted on this server");
+ return;
+ } catch (PermissionBackendException e) {
+ responseWrapper.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ throw new ServletException(e);
+ }
+
+ // We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR
+ // may have been overridden by a proxy server -- we'll try to avoid this.
+ UploadValidators uploadValidators =
+ uploadValidatorsFactory.create(state.getProject(), repo, request.getRemoteHost());
+ up.setPreUploadHook(
+ PreUploadHookChain.newChain(
+ Lists.newArrayList(up.getPreUploadHook(), uploadValidators)));
+ up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
+ next.doFilter(httpRequest, responseWrapper);
} finally {
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
groupAuditService.dispatch(
new HttpAuditEvent(
- httpRequest.getSession().getId(),
+ sessionId,
userProvider.get(),
extractWhat(httpRequest),
TimeUtil.nowMs(),
extractParameters(httpRequest),
httpRequest.getMethod(),
httpRequest,
- httpResponse.getStatus(),
- httpResponse));
+ responseWrapper.getResponseStatus(),
+ responseWrapper));
}
-
- // We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR
- // may have been overridden by a proxy server -- we'll try to avoid this.
- UploadValidators uploadValidators =
- uploadValidatorsFactory.create(state.getProject(), repo, request.getRemoteHost());
- up.setPreUploadHook(
- PreUploadHookChain.newChain(Lists.newArrayList(up.getPreUploadHook(), uploadValidators)));
- up.setAdvertiseRefsHook(new DefaultAdvertiseRefsHook(perm, RefFilterOptions.defaults()));
- next.doFilter(request, response);
}
@Override
@@ -378,22 +456,26 @@ public class GitOverHttpServlet extends GitServlet {
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> userProvider;
private final GroupAuditService groupAuditService;
+ private final Metrics metrics;
@Inject
ReceiveFilter(
@Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache,
PermissionBackend permissionBackend,
Provider<CurrentUser> userProvider,
- GroupAuditService groupAuditService) {
+ GroupAuditService groupAuditService,
+ Metrics metrics) {
this.cache = cache;
this.permissionBackend = permissionBackend;
this.userProvider = userProvider;
this.groupAuditService = groupAuditService;
+ this.metrics = metrics;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
+ metrics.requestStarted();
boolean isGet = "GET".equalsIgnoreCase(((HttpServletRequest) request).getMethod());
AsyncReceiveCommits arc = (AsyncReceiveCommits) request.getAttribute(ATT_ARC);
@@ -403,25 +485,28 @@ public class GitOverHttpServlet extends GitServlet {
rp.getAdvertiseRefsHook().advertiseRefs(rp);
ProjectState state = (ProjectState) request.getAttribute(ATT_STATE);
+ HttpServletResponseWithStatusWrapper responseWrapper =
+ new HttpServletResponseWithStatusWrapper((HttpServletResponse) response);
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
Capable canUpload;
try {
- permissionBackend
- .currentUser()
- .project(state.getNameKey())
- .check(ProjectPermission.RUN_RECEIVE_PACK);
- canUpload = arc.canUpload();
- } catch (AuthException e) {
- GitSmartHttpTools.sendError(
- (HttpServletRequest) request,
- (HttpServletResponse) response,
- HttpServletResponse.SC_FORBIDDEN,
- "receive-pack not permitted on this server");
- return;
- } catch (PermissionBackendException e) {
- throw new RuntimeException(e);
+ try {
+ permissionBackend
+ .currentUser()
+ .project(state.getNameKey())
+ .check(ProjectPermission.RUN_RECEIVE_PACK);
+ canUpload = arc.canUpload();
+ } catch (AuthException e) {
+ GitSmartHttpTools.sendError(
+ httpRequest,
+ responseWrapper,
+ HttpServletResponse.SC_FORBIDDEN,
+ "receive-pack not permitted on this server");
+ return;
+ } catch (PermissionBackendException e) {
+ throw new RuntimeException(e);
+ }
} finally {
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
groupAuditService.dispatch(
new HttpAuditEvent(
httpRequest.getSession().getId(),
@@ -431,26 +516,26 @@ public class GitOverHttpServlet extends GitServlet {
extractParameters(httpRequest),
httpRequest.getMethod(),
httpRequest,
- httpResponse.getStatus(),
- httpResponse));
+ responseWrapper.getResponseStatus(),
+ responseWrapper));
}
if (canUpload != Capable.OK) {
GitSmartHttpTools.sendError(
- (HttpServletRequest) request,
- (HttpServletResponse) response,
+ httpRequest,
+ responseWrapper,
HttpServletResponse.SC_FORBIDDEN,
"\n" + canUpload.getMessage());
return;
}
if (!rp.isCheckReferencedObjectsAreReachable()) {
- chain.doFilter(request, response);
+ chain.doFilter(request, responseWrapper);
return;
}
if (!(userProvider.get().isIdentifiedUser())) {
- chain.doFilter(request, response);
+ chain.doFilter(request, responseWrapper);
return;
}
@@ -467,7 +552,7 @@ public class GitOverHttpServlet extends GitServlet {
}
}
- chain.doFilter(request, response);
+ chain.doFilter(request, responseWrapper);
if (isGet) {
cache.put(cacheKey, Collections.unmodifiableSet(new HashSet<>(rp.getAdvertisedObjects())));
diff --git a/java/com/google/gerrit/httpd/GwtCacheControlFilter.java b/java/com/google/gerrit/httpd/GwtCacheControlFilter.java
deleted file mode 100644
index 5ac3d2f710..0000000000
--- a/java/com/google/gerrit/httpd/GwtCacheControlFilter.java
+++ /dev/null
@@ -1,104 +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.httpd;
-
-import com.google.gerrit.util.http.CacheHeaders;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Forces GWT resources to cache for a very long time.
- *
- * <p>GWT compiled JavaScript and ImageBundles can be cached indefinitely by a browser and/or an
- * edge proxy, as they never contain user-specific data and are named by a unique checksum. If their
- * content is ever modified then the URL changes, so user agents would request a different resource.
- * We force these resources to have very long expiration times.
- *
- * <p>To use, add the following block to your {@code web.xml}:
- *
- * <pre>
- * &lt;filter&gt;
- * &lt;filter-name&gt;CacheControl&lt;/filter-name&gt;
- * &lt;filter-class&gt;com.google.gwtexpui.server.CacheControlFilter&lt;/filter-class&gt;
- * &lt;/filter&gt;
- * &lt;filter-mapping&gt;
- * &lt;filter-name&gt;CacheControl&lt;/filter-name&gt;
- * &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
- * &lt;/filter-mapping&gt;
- * </pre>
- */
-@Singleton
-class GwtCacheControlFilter implements Filter {
- @Override
- public void init(FilterConfig config) {}
-
- @Override
- public void destroy() {}
-
- @Override
- public void doFilter(final ServletRequest sreq, ServletResponse srsp, FilterChain chain)
- throws IOException, ServletException {
- final HttpServletRequest req = (HttpServletRequest) sreq;
- final HttpServletResponse rsp = (HttpServletResponse) srsp;
- final String pathInfo = pathInfo(req);
-
- if (cacheForever(pathInfo, req)) {
- CacheHeaders.setCacheable(req, rsp, 365, TimeUnit.DAYS);
- } else if (nocache(pathInfo)) {
- CacheHeaders.setNotCacheable(rsp);
- }
-
- chain.doFilter(req, rsp);
- }
-
- private static boolean cacheForever(String pathInfo, HttpServletRequest req) {
- if (pathInfo.endsWith(".cache.html")
- || pathInfo.endsWith(".cache.gif")
- || pathInfo.endsWith(".cache.png")
- || pathInfo.endsWith(".cache.css")
- || pathInfo.endsWith(".cache.jar")
- || pathInfo.endsWith(".cache.swf")
- || pathInfo.endsWith(".cache.txt")
- || pathInfo.endsWith(".cache.js")) {
- return true;
- } else if (pathInfo.endsWith(".nocache.js")) {
- final String v = req.getParameter("content");
- return v != null && v.length() > 20;
- }
- return false;
- }
-
- private static boolean nocache(String pathInfo) {
- if (pathInfo.endsWith(".nocache.js")) {
- return true;
- }
- return false;
- }
-
- private static String pathInfo(HttpServletRequest req) {
- final String uri = req.getRequestURI();
- final String ctx = req.getContextPath();
- return uri.startsWith(ctx) ? uri.substring(ctx.length()) : uri;
- }
-}
diff --git a/java/com/google/gerrit/httpd/HttpRequestContext.java b/java/com/google/gerrit/httpd/HttpRequestContext.java
index 6cc7a175f6..c30c74ccd2 100644
--- a/java/com/google/gerrit/httpd/HttpRequestContext.java
+++ b/java/com/google/gerrit/httpd/HttpRequestContext.java
@@ -15,30 +15,20 @@
package com.google.gerrit.httpd;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.util.RequestContext;
import com.google.inject.Inject;
-import com.google.inject.Provider;
class HttpRequestContext implements RequestContext {
private final DynamicItem<WebSession> session;
- private final RequestScopedReviewDbProvider provider;
@Inject
- HttpRequestContext(DynamicItem<WebSession> session, RequestScopedReviewDbProvider provider) {
+ HttpRequestContext(DynamicItem<WebSession> session) {
this.session = session;
- this.provider = provider;
}
@Override
public CurrentUser getUser() {
return session.get().getUser();
}
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return provider;
- }
}
diff --git a/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java b/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
new file mode 100644
index 0000000000..d53a5c52a0
--- /dev/null
+++ b/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
@@ -0,0 +1,70 @@
+// 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;
+
+import com.google.gerrit.common.PageLinks;
+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;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/** Redirects {@code domain.tld/123} to {@code domain.tld/c/project/+/123}. */
+@Singleton
+public class NumericChangeIdRedirectServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ private final ChangesCollection changesCollection;
+
+ @Inject
+ NumericChangeIdRedirectServlet(ChangesCollection changesCollection) {
+ this.changesCollection = changesCollection;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
+ String idString = req.getPathInfo();
+ if (idString.endsWith("/")) {
+ idString = idString.substring(0, idString.length() - 1);
+ }
+ Change.Id id;
+ try {
+ id = Change.Id.parse(idString);
+ } catch (IllegalArgumentException e) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ ChangeResource changeResource;
+ try {
+ changeResource = changesCollection.parse(id);
+ } catch (ResourceConflictException | ResourceNotFoundException e) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ } catch (PermissionBackendException | RuntimeException e) {
+ throw new IOException("Unable to lookup change " + id.id, e);
+ }
+ String path =
+ PageLinks.toChange(changeResource.getProject(), changeResource.getChange().getId());
+ UrlModule.toGerrit(path, req, rsp);
+ }
+}
diff --git a/java/com/google/gerrit/httpd/RunAsFilter.java b/java/com/google/gerrit/httpd/RunAsFilter.java
index f3bf5af0a2..15dbcaba7a 100644
--- a/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -21,6 +21,7 @@ import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import com.google.common.flogger.FluentLogger;
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;
@@ -28,7 +29,6 @@ import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.servlet.ServletModule;
@@ -103,19 +103,18 @@ class RunAsFilter implements Filter {
return;
}
- Account target;
+ Account.Id target;
try {
- target = accountResolver.find(runas);
- } catch (OrmException | IOException | ConfigInvalidException e) {
+ target = accountResolver.resolve(runas).asUnique().getAccount().getId();
+ } catch (UnprocessableEntityException e) {
+ replyError(req, res, SC_FORBIDDEN, "no account matches " + RUN_AS, null);
+ return;
+ } catch (IOException | ConfigInvalidException | RuntimeException e) {
logger.atWarning().withCause(e).log("cannot resolve account for %s", RUN_AS);
replyError(req, res, SC_INTERNAL_SERVER_ERROR, "cannot resolve " + RUN_AS, e);
return;
}
- if (target == null) {
- replyError(req, res, SC_FORBIDDEN, "no account matches " + RUN_AS, null);
- return;
- }
- session.get().setUserAccountId(target.getId());
+ session.get().setUserAccountId(target);
}
chain.doFilter(req, res);
diff --git a/java/com/google/gerrit/httpd/UniversalWebLoginFilter.java b/java/com/google/gerrit/httpd/UniversalWebLoginFilter.java
index 48780063ae..107a07e6cd 100644
--- a/java/com/google/gerrit/httpd/UniversalWebLoginFilter.java
+++ b/java/com/google/gerrit/httpd/UniversalWebLoginFilter.java
@@ -14,10 +14,11 @@
package com.google.gerrit.httpd;
-import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.plugincontext.PluginItemContext;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -34,8 +35,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UniversalWebLoginFilter implements Filter {
- private final DynamicItem<WebSession> session;
- private final DynamicSet<WebLoginListener> webLoginListeners;
+ private final PluginItemContext<WebSession> session;
+ private final PluginSetContext<WebLoginListener> webLoginListeners;
private final Provider<CurrentUser> userProvider;
public static ServletModule module() {
@@ -52,8 +53,8 @@ public class UniversalWebLoginFilter implements Filter {
@Inject
public UniversalWebLoginFilter(
- DynamicItem<WebSession> session,
- DynamicSet<WebLoginListener> webLoginListeners,
+ PluginItemContext<WebSession> session,
+ PluginSetContext<WebLoginListener> webLoginListeners,
Provider<CurrentUser> userProvider) {
this.session = session;
this.webLoginListeners = webLoginListeners;
@@ -75,20 +76,18 @@ public class UniversalWebLoginFilter implements Filter {
Optional<IdentifiedUser> loggedInUserAfter = loggedInUser();
if (!loggedInUserBefore.isPresent() && loggedInUserAfter.isPresent()) {
- for (WebLoginListener loginListener : webLoginListeners) {
- loginListener.onLogin(loggedInUserAfter.get(), httpRequest, wrappedResponse);
- }
+ webLoginListeners.runEach(
+ l -> l.onLogin(loggedInUserAfter.get(), httpRequest, wrappedResponse));
} else if (loggedInUserBefore.isPresent() && !loggedInUserAfter.isPresent()) {
- for (WebLoginListener loginListener : webLoginListeners) {
- loginListener.onLogout(loggedInUserBefore.get(), httpRequest, wrappedResponse);
- }
+ webLoginListeners.runEach(
+ l -> l.onLogout(loggedInUserBefore.get(), httpRequest, wrappedResponse));
}
wrappedResponse.play();
}
private Optional<IdentifiedUser> loggedInUser() {
- return session.get().isSignedIn()
+ return session.call(WebSession::isSignedIn)
? Optional.of(userProvider.get().asIdentifiedUser())
: Optional.empty();
}
diff --git a/java/com/google/gerrit/httpd/UrlModule.java b/java/com/google/gerrit/httpd/UrlModule.java
index 94855b96b9..993a0421d8 100644
--- a/java/com/google/gerrit/httpd/UrlModule.java
+++ b/java/com/google/gerrit/httpd/UrlModule.java
@@ -20,8 +20,6 @@ import com.google.common.base.Strings;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.httpd.raw.CatServlet;
-import com.google.gerrit.httpd.raw.HostPageServlet;
-import com.google.gerrit.httpd.raw.LegacyGerritServlet;
import com.google.gerrit.httpd.raw.SshInfoServlet;
import com.google.gerrit.httpd.raw.ToolServlet;
import com.google.gerrit.httpd.restapi.AccessRestApiServlet;
@@ -33,9 +31,7 @@ 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.gerrit.server.config.GerritOptions;
import com.google.inject.Key;
-import com.google.inject.Provider;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.servlet.ServletModule;
import java.io.IOException;
@@ -45,27 +41,14 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Constants;
class UrlModule extends ServletModule {
- private GerritOptions options;
private AuthConfig authConfig;
- UrlModule(GerritOptions options, AuthConfig authConfig) {
- this.options = options;
+ UrlModule(AuthConfig authConfig) {
this.authConfig = authConfig;
}
@Override
protected void configureServlets() {
- filter("/*").through(GwtCacheControlFilter.class);
-
- if (options.enableGwtUi()) {
- filter("/").through(XsrfCookieFilter.class);
- filter("/accounts/self/detail").through(XsrfCookieFilter.class);
- serve("/").with(HostPageServlet.class);
- serve("/Gerrit").with(LegacyGerritServlet.class);
- serve("/Gerrit/*").with(legacyGerritScreen());
- // Forward PolyGerrit URLs to their respective GWT equivalents.
- serveRegex("^/(c|q|x|admin|dashboard|settings)/(.*)").with(gerritUrl());
- }
serve("/cat/*").with(CatServlet.class);
if (authConfig.getAuthType() != AuthType.OAUTH && authConfig.getAuthType() != AuthType.OPENID) {
@@ -87,7 +70,7 @@ class UrlModule extends ServletModule {
serveRegex("^/settings/?$").with(screen(PageLinks.SETTINGS));
serveRegex("^/register$").with(registerScreen(false));
serveRegex("^/register/(.+)$").with(registerScreen(true));
- serveRegex("^/([1-9][0-9]*)/?$").with(directChangeById());
+ serveRegex("^/([1-9][0-9]*)/?$").with(NumericChangeIdRedirectServlet.class);
serveRegex("^/p/(.*)$").with(queryProjectNew());
serveRegex("^/r/(.+)/?$").with(DirectChangeByCommit.class);
@@ -127,19 +110,6 @@ class UrlModule extends ServletModule {
});
}
- private Key<HttpServlet> gerritUrl() {
- return key(
- new HttpServlet() {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- String path = req.getRequestURI().substring(req.getContextPath().length());
- toGerrit(path, req, rsp, true);
- }
- });
- }
-
private Key<HttpServlet> screen(String target) {
return key(
new HttpServlet() {
@@ -152,43 +122,6 @@ class UrlModule extends ServletModule {
});
}
- private Key<HttpServlet> legacyGerritScreen() {
- return key(
- new HttpServlet() {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- final String token = req.getPathInfo().substring(1);
- toGerrit(token, req, rsp);
- }
- });
- }
-
- private Key<HttpServlet> directChangeById() {
- return key(
- new HttpServlet() {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- try {
- String idString = req.getPathInfo();
- if (idString.endsWith("/")) {
- idString = idString.substring(0, idString.length() - 1);
- }
- Change.Id id = Change.Id.parse(idString);
- // User accessed Gerrit with /1234, so we have no project yet.
- // TODO(hiesel) Replace with a preflight request to obtain project before we deprecate
- // the numeric change id.
- toGerrit(PageLinks.toChange(null, id), req, rsp);
- } catch (IllegalArgumentException err) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- }
- }
- });
- }
-
private Key<HttpServlet> queryProjectNew() {
return key(
new HttpServlet() {
@@ -237,15 +170,7 @@ class UrlModule extends ServletModule {
private Key<HttpServlet> key(HttpServlet servlet) {
final Key<HttpServlet> srv = Key.get(HttpServlet.class, UniqueAnnotations.create());
- bind(srv)
- .toProvider(
- new Provider<HttpServlet>() {
- @Override
- public HttpServlet get() {
- return servlet;
- }
- })
- .in(SINGLETON);
+ bind(srv).toProvider(() -> servlet).in(SINGLETON);
return srv;
}
@@ -277,16 +202,8 @@ class UrlModule extends ServletModule {
static void toGerrit(String target, HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
- toGerrit(target, req, rsp, false);
- }
-
- static void toGerrit(String target, HttpServletRequest req, HttpServletResponse rsp, boolean gwt)
- throws IOException {
final StringBuilder url = new StringBuilder();
url.append(req.getContextPath());
- if (gwt) {
- url.append("/#");
- }
url.append(target);
rsp.sendRedirect(url.toString());
}
diff --git a/java/com/google/gerrit/httpd/WebModule.java b/java/com/google/gerrit/httpd/WebModule.java
index d115f43f16..e416075449 100644
--- a/java/com/google/gerrit/httpd/WebModule.java
+++ b/java/com/google/gerrit/httpd/WebModule.java
@@ -21,7 +21,6 @@ import com.google.gerrit.httpd.auth.container.HttpAuthModule;
import com.google.gerrit.httpd.auth.container.HttpsClientSslCertModule;
import com.google.gerrit.httpd.auth.ldap.LdapAuthModule;
import com.google.gerrit.httpd.gitweb.GitwebModule;
-import com.google.gerrit.httpd.rpc.UiRpcModule;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.AuthConfig;
@@ -55,10 +54,7 @@ public class WebModule extends LifecycleModule {
installAuthModule();
if (options.enableMasterFeatures()) {
- install(new UrlModule(options, authConfig));
- if (options.enableGwtUi()) {
- install(new UiRpcModule());
- }
+ install(new UrlModule(authConfig));
}
install(new GerritRequestModule());
install(new GitOverHttpServlet.Module(options.enableMasterFeatures()));
diff --git a/java/com/google/gerrit/httpd/XsrfConstants.java b/java/com/google/gerrit/httpd/XsrfConstants.java
new file mode 100644
index 0000000000..0bbfe34efc
--- /dev/null
+++ b/java/com/google/gerrit/httpd/XsrfConstants.java
@@ -0,0 +1,30 @@
+// 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.httpd;
+
+/** XSRF Constants. */
+public class XsrfConstants {
+ /**
+ * Name of the cookie in which the XSRF token is sent from the server to the client during host
+ * page bootstrapping.
+ */
+ public static final String XSRF_COOKIE_NAME = "XSRF_TOKEN";
+
+ /**
+ * Name of the HTTP header in which the client must send the XSRF token to the server on each
+ * request.
+ */
+ public static final String XSRF_HEADER_NAME = "X-Gerrit-Auth";
+}
diff --git a/java/com/google/gerrit/httpd/XsrfCookieFilter.java b/java/com/google/gerrit/httpd/XsrfCookieFilter.java
index ff64c84298..d15ecacd59 100644
--- a/java/com/google/gerrit/httpd/XsrfCookieFilter.java
+++ b/java/com/google/gerrit/httpd/XsrfCookieFilter.java
@@ -16,7 +16,6 @@ package com.google.gerrit.httpd;
import static com.google.common.base.Strings.nullToEmpty;
-import com.google.gerrit.common.data.HostPageData;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AuthConfig;
@@ -59,7 +58,7 @@ public class XsrfCookieFilter implements Filter {
private void setXsrfTokenCookie(
HttpServletRequest req, HttpServletResponse rsp, WebSession session) {
String v = session != null ? session.getXGerritAuth() : null;
- Cookie c = new Cookie(HostPageData.XSRF_COOKIE_NAME, nullToEmpty(v));
+ Cookie c = new Cookie(XsrfConstants.XSRF_COOKIE_NAME, nullToEmpty(v));
c.setPath("/");
c.setSecure(authConfig.getCookieSecure() && isSecure(req));
c.setMaxAge(
diff --git a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 9cf8b585eb..552e667946 100644
--- a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -24,7 +24,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
@@ -35,8 +34,6 @@ import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.query.account.InternalAccountQuery;
import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -60,7 +57,6 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final DynamicItem<WebSession> webSession;
- private final SchemaFactory<ReviewDb> schema;
private final Accounts accounts;
private final AccountCache accountCache;
private final AccountManager accountManager;
@@ -70,14 +66,12 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
@Inject
BecomeAnyAccountLoginServlet(
DynamicItem<WebSession> ws,
- SchemaFactory<ReviewDb> sf,
Accounts a,
AccountCache ac,
AccountManager am,
SiteHeaderFooter shf,
Provider<InternalAccountQuery> qp) {
webSession = ws;
- schema = sf;
accounts = a;
accountCache = ac;
accountManager = am;
@@ -92,8 +86,7 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
}
@Override
- protected void doPost(HttpServletRequest req, HttpServletResponse rsp)
- throws IOException, ServletException {
+ protected void doPost(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
CacheHeaders.setNotCacheable(rsp);
final AuthResult res;
@@ -111,11 +104,7 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
} else {
byte[] raw;
- try {
- raw = prepareHtmlOutput();
- } catch (OrmException e) {
- throw new ServletException(e);
- }
+ raw = prepareHtmlOutput();
rsp.setContentType("text/html");
rsp.setCharacterEncoding(HtmlDomUtil.ENC.name());
rsp.setContentLength(raw.length);
@@ -151,7 +140,7 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
}
}
- private byte[] prepareHtmlOutput() throws IOException, OrmException {
+ private byte[] prepareHtmlOutput() throws IOException {
final String pageName = "BecomeAnyAccount.html";
Document doc = headers.parse(getClass(), pageName);
if (doc == null) {
@@ -159,30 +148,28 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
}
Element userlistElement = HtmlDomUtil.find(doc, "userlist");
- try (ReviewDb db = schema.open()) {
- for (Account.Id accountId : accounts.firstNIds(100)) {
- Optional<AccountState> accountState = accountCache.get(accountId);
- if (!accountState.isPresent()) {
- continue;
- }
- Account account = accountState.get().getAccount();
- 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();
- } else {
- displayName = accountId.toString();
- }
-
- Element linkElement = doc.createElement("a");
- linkElement.setAttribute("href", "?account_id=" + account.getId().toString());
- linkElement.setTextContent(displayName);
- userlistElement.appendChild(linkElement);
- userlistElement.appendChild(doc.createElement("br"));
+ for (Account.Id accountId : accounts.firstNIds(100)) {
+ Optional<AccountState> accountState = accountCache.get(accountId);
+ if (!accountState.isPresent()) {
+ continue;
+ }
+ Account account = accountState.get().getAccount();
+ 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();
+ } else {
+ displayName = accountId.toString();
}
+
+ Element linkElement = doc.createElement("a");
+ linkElement.setAttribute("href", "?account_id=" + account.getId().toString());
+ linkElement.setTextContent(displayName);
+ userlistElement.appendChild(linkElement);
+ userlistElement.appendChild(doc.createElement("br"));
}
return HtmlDomUtil.toUTF8(doc);
@@ -200,33 +187,20 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
}
private AuthResult byUserName(String userName) {
- try {
- List<AccountState> accountStates =
- queryProvider.get().byExternalId(SCHEME_USERNAME, userName);
- if (accountStates.isEmpty()) {
- getServletContext().log("No accounts with username " + userName + " found");
- return null;
- }
- if (accountStates.size() > 1) {
- getServletContext().log("Multiple accounts with username " + userName + " found");
- return null;
- }
- return auth(accountStates.get(0).getAccount().getId());
- } catch (OrmException e) {
- getServletContext().log("cannot query account index", e);
+ List<AccountState> accountStates = queryProvider.get().byExternalId(SCHEME_USERNAME, userName);
+ if (accountStates.isEmpty()) {
+ getServletContext().log("No accounts with username " + userName + " found");
+ return null;
+ }
+ if (accountStates.size() > 1) {
+ getServletContext().log("Multiple accounts with username " + userName + " found");
return null;
}
+ return auth(accountStates.get(0).getAccount().getId());
}
private Optional<AuthResult> byPreferredEmail(String email) {
- try (ReviewDb db = schema.open()) {
- Optional<AccountState> match =
- queryProvider.get().byPreferredEmail(email).stream().findFirst();
- return auth(match);
- } catch (OrmException e) {
- getServletContext().log("cannot query database", e);
- return Optional.empty();
- }
+ return auth(queryProvider.get().byPreferredEmail(email).stream().findFirst());
}
private Optional<AuthResult> byAccountId(String idStr) {
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java b/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
index 0b3c29d249..509a9f1fbc 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
@@ -25,11 +25,10 @@ import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.httpd.RemoteUserUtil;
import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.httpd.raw.HostPageServlet;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
+import com.google.gerrit.util.http.RequestUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.FileNotFoundException;
@@ -49,10 +48,10 @@ import javax.servlet.http.HttpServletResponse;
* Watches request for the host page and requires login if not yet signed in.
*
* <p>If HTTP authentication has been enabled on this server this filter is bound in front of the
- * {@link HostPageServlet} and redirects users who are not yet signed in to visit {@code /login/},
- * so the web container can force login. This redirect is performed with JavaScript, such that any
- * existing anchor token in the URL can be rewritten and preserved through the authentication
- * process of any enterprise single sign-on solutions.
+ * Gerrit and redirects users who are not yet signed in to visit {@code /login/}, so the web
+ * container can force login. This redirect is performed with JavaScript, such that any existing
+ * anchor token in the URL can be rewritten and preserved through the authentication process of any
+ * enterprise single sign-on solutions.
*/
@Singleton
class HttpAuthFilter implements Filter {
@@ -98,7 +97,7 @@ class HttpAuthFilter implements Filter {
final HttpServletRequest req = (HttpServletRequest) request;
final HttpServletResponse rsp = (HttpServletResponse) response;
final byte[] tosend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ if (RequestUtil.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = signInGzip;
} else {
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java b/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
index fd2f6282bf..1b7e477ae7 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
@@ -31,7 +31,6 @@ import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -128,7 +127,7 @@ class HttpLoginServlet extends HttpServlet {
logger.atFine().log(
"Associating external identity \"%s\" to user \"%s\"", remoteExternalId, user);
updateRemoteExternalId(arsp, remoteExternalId);
- } catch (AccountException | OrmException | ConfigInvalidException e) {
+ } catch (AccountException | ConfigInvalidException e) {
logger.atSevere().withCause(e).log(
"Unable to associate external identity \"%s\" to user \"%s\"", remoteExternalId, user);
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -152,7 +151,7 @@ class HttpLoginServlet extends HttpServlet {
}
private void updateRemoteExternalId(AuthResult arsp, String remoteAuthToken)
- throws AccountException, OrmException, IOException, ConfigInvalidException {
+ throws AccountException, IOException, ConfigInvalidException {
accountManager.updateLink(
arsp.getAccountId(),
new AuthRequest(ExternalId.Key.create(SCHEME_EXTERNAL, remoteAuthToken)));
diff --git a/java/com/google/gerrit/httpd/auth/oauth/BUILD b/java/com/google/gerrit/httpd/auth/oauth/BUILD
index a99d6df173..2d9345ffc3 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/BUILD
+++ b/java/com/google/gerrit/httpd/auth/oauth/BUILD
@@ -8,14 +8,14 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
+ "//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/server",
"//lib:gson",
"//lib:guava",
- "//lib:gwtorm",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/commons:codec",
"//lib/flogger:api",
"//lib/guice",
diff --git a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
index b780fa01e4..0c8a1a10f3 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
+++ b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
@@ -35,7 +35,6 @@ import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.auth.oauth.OAuthTokenCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.servlet.SessionScoped;
@@ -188,7 +187,7 @@ class OAuthSession {
logger.atInfo().log("OAuth2: linking claimed identity to %s", claimedId.get().toString());
try {
accountManager.link(claimedId.get(), req);
- } catch (OrmException | ConfigInvalidException e) {
+ } catch (ConfigInvalidException e) {
logger.atSevere().log(
"Cannot link: %s to user identity:\n Claimed ID: %s is %s",
user.getExternalId(), claimedId.get(), claimedIdentifier);
@@ -203,7 +202,7 @@ class OAuthSession {
throws AccountException, IOException {
try {
accountManager.link(identifiedUser.get().getAccountId(), areq);
- } catch (OrmException | ConfigInvalidException e) {
+ } catch (ConfigInvalidException e) {
logger.atSevere().log(
"Cannot link: %s to user identity: %s",
user.getExternalId(), identifiedUser.get().getAccountId());
diff --git a/java/com/google/gerrit/httpd/auth/openid/BUILD b/java/com/google/gerrit/httpd/auth/openid/BUILD
index 2355833879..2206397619 100644
--- a/java/com/google/gerrit/httpd/auth/openid/BUILD
+++ b/java/com/google/gerrit/httpd/auth/openid/BUILD
@@ -10,14 +10,15 @@ 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/util/http",
"//java/com/google/gerrit/server",
"//lib:guava",
- "//lib:gwtorm",
- "//lib:servlet-api-3_1",
+ "//java/com/google/gwtorm",
+ "//lib:servlet-api",
"//lib/commons:codec",
"//lib/flogger:api",
"//lib/guice",
diff --git a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
index a51a0abedc..08f2d52b57 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
@@ -33,7 +33,6 @@ import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.servlet.SessionScoped;
@@ -164,7 +163,7 @@ class OAuthSessionOverOpenID {
logger.atFine().log("Claimed account already exists: link to it.");
try {
accountManager.link(claimedId.get(), areq);
- } catch (OrmException | ConfigInvalidException e) {
+ } catch (ConfigInvalidException e) {
logger.atSevere().log(
"Cannot link: %s to user identity:\n Claimed ID: %s is %s",
user.getExternalId(), claimedId.get(), claimedIdentifier);
@@ -178,7 +177,7 @@ class OAuthSessionOverOpenID {
try {
logger.atFine().log("Linking \"%s\" to \"%s\"", user.getExternalId(), accountId);
accountManager.link(accountId, areq);
- } catch (OrmException | ConfigInvalidException e) {
+ } catch (ConfigInvalidException e) {
logger.atSevere().log(
"Cannot link: %s to user identity: %s", user.getExternalId(), accountId);
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
diff --git a/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java b/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java
index 2f7f4bd095..864b160c79 100644
--- a/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java
+++ b/java/com/google/gerrit/httpd/auth/openid/XrdsFilter.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2010 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.auth.openid;
import com.google.gerrit.server.config.CanonicalWebUrl;
diff --git a/java/com/google/gerrit/httpd/gitweb/GitwebCssServlet.java b/java/com/google/gerrit/httpd/gitweb/GitwebCssServlet.java
index 0074035572..f7f1bd6bc2 100644
--- a/java/com/google/gerrit/httpd/gitweb/GitwebCssServlet.java
+++ b/java/com/google/gerrit/httpd/gitweb/GitwebCssServlet.java
@@ -21,7 +21,7 @@ import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.server.config.GitwebCgiConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
+import com.google.gerrit.util.http.RequestUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -91,7 +91,7 @@ abstract class GitwebCssServlet extends HttpServlet {
rsp.setContentType("text/css");
rsp.setCharacterEncoding(UTF_8.name());
final byte[] toSend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ if (RequestUtil.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
toSend = gz_css;
} else {
diff --git a/java/com/google/gerrit/httpd/init/BUILD b/java/com/google/gerrit/httpd/init/BUILD
index d3b9e964fd..97475d1503 100644
--- a/java/com/google/gerrit/httpd/init/BUILD
+++ b/java/com/google/gerrit/httpd/init/BUILD
@@ -26,8 +26,7 @@ java_library(
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/sshd",
"//lib:guava",
- "//lib:gwtorm",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-servlet",
diff --git a/java/com/google/gerrit/httpd/init/ReviewDbDataSourceProvider.java b/java/com/google/gerrit/httpd/init/ReviewDbDataSourceProvider.java
deleted file mode 100644
index 6e65780fbd..0000000000
--- a/java/com/google/gerrit/httpd/init/ReviewDbDataSourceProvider.java
+++ /dev/null
@@ -1,77 +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.httpd.init;
-
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.sql.DataSource;
-
-/** Provides access to the {@code ReviewDb} DataSource. */
-@Singleton
-final class ReviewDbDataSourceProvider implements Provider<DataSource>, LifecycleListener {
- private DataSource ds;
-
- @Override
- public synchronized DataSource get() {
- if (ds == null) {
- ds = open();
- }
- return ds;
- }
-
- @Override
- public void start() {}
-
- @Override
- public synchronized void stop() {
- if (ds != null) {
- closeDataSource(ds);
- }
- }
-
- private DataSource open() {
- final String dsName = "java:comp/env/jdbc/ReviewDb";
- try {
- return (DataSource) new InitialContext().lookup(dsName);
- } catch (NamingException namingErr) {
- throw new ProvisionException("No DataSource " + dsName, namingErr);
- }
- }
-
- private void closeDataSource(DataSource ds) {
- try {
- Class<?> type = Class.forName("org.apache.commons.dbcp.BasicDataSource");
- if (type.isInstance(ds)) {
- type.getMethod("close").invoke(ds);
- return;
- }
- } catch (Throwable bad) {
- // Oh well, its not a Commons DBCP pooled connection.
- }
-
- try {
- Class<?> type = Class.forName("com.mchange.v2.c3p0.DataSources");
- if (type.isInstance(ds)) {
- type.getMethod("destroy", DataSource.class).invoke(null, ds);
- }
- } catch (Throwable bad) {
- // Oh well, its not a c3p0 pooled connection.
- }
- }
-}
diff --git a/java/com/google/gerrit/httpd/init/SiteInitializer.java b/java/com/google/gerrit/httpd/init/SiteInitializer.java
index 67510cdae6..04a0ddd356 100644
--- a/java/com/google/gerrit/httpd/init/SiteInitializer.java
+++ b/java/com/google/gerrit/httpd/init/SiteInitializer.java
@@ -47,7 +47,7 @@ public final class SiteInitializer {
if (sitePath != null) {
Path site = Paths.get(sitePath);
logger.atInfo().log("Initializing site at %s", site.toRealPath().normalize());
- new BaseInit(site, false, true, pluginsDistribution, pluginsToInstall).run();
+ new BaseInit(site, false, pluginsDistribution, pluginsToInstall).run();
return;
}
@@ -62,14 +62,7 @@ public final class SiteInitializer {
}
if (site != null) {
logger.atInfo().log("Initializing site at %s", site.toRealPath().normalize());
- new BaseInit(
- site,
- new ReviewDbDataSourceProvider(),
- false,
- false,
- pluginsDistribution,
- pluginsToInstall)
- .run();
+ new BaseInit(site, false, pluginsDistribution, pluginsToInstall).run();
}
} catch (Exception e) {
logger.atSevere().withCause(e).log("Site init failed");
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index c2d4a146ef..bf5cd2a9b8 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -14,7 +14,6 @@
package com.google.gerrit.httpd.init;
-import static com.google.inject.Scopes.SINGLETON;
import static com.google.inject.Stage.PRODUCTION;
import com.google.common.base.Splitter;
@@ -40,11 +39,11 @@ 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.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;
import com.google.gerrit.server.LibModuleLoader;
+import com.google.gerrit.server.LibModuleType;
import com.google.gerrit.server.ModuleOverloader;
import com.google.gerrit.server.StartupChecks;
import com.google.gerrit.server.account.AccountDeactivator;
@@ -82,20 +81,15 @@ import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.server.mail.receive.MailReceiver;
import com.google.gerrit.server.mail.send.SmtpEmailSender;
import com.google.gerrit.server.mime.MimeUtil2Module;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.DiffExecutorModule;
import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
import com.google.gerrit.server.plugins.PluginModule;
import com.google.gerrit.server.project.DefaultProjectNameLockManager;
import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.DataSourceModule;
-import com.google.gerrit.server.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.gerrit.server.schema.DatabaseModule;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
+import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
import com.google.gerrit.server.schema.SchemaModule;
-import com.google.gerrit.server.schema.SchemaVersionCheck;
import com.google.gerrit.server.securestore.SecureStoreClassName;
import com.google.gerrit.server.ssh.NoSshModule;
import com.google.gerrit.server.ssh.SshAddressesModule;
@@ -114,7 +108,6 @@ import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
-import com.google.inject.name.Names;
import com.google.inject.servlet.GuiceFilter;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.spi.Message;
@@ -134,7 +127,6 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
-import javax.sql.DataSource;
import org.eclipse.jgit.lib.Config;
/** Configures the web application environment for Gerrit Code Review. */
@@ -189,7 +181,7 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
}
try {
- dbInjector = createDbInjector();
+ cfgInjector = createCfgInjector();
} catch (CreationException ce) {
final Message first = ce.getErrorMessages().iterator().next();
final StringBuilder buf = new StringBuilder();
@@ -209,7 +201,7 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
throw new CreationException(Collections.singleton(first));
}
- cfgInjector = createCfgInjector();
+ dbInjector = createDbInjector();
initIndexType();
config = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
sysInjector = createSysInjector();
@@ -254,69 +246,33 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
return new SshAddressesModule().getListenAddresses(config).isEmpty();
}
- private Injector createDbInjector() {
+ private Injector createCfgInjector() {
final List<Module> modules = new ArrayList<>();
AbstractModule secureStore = createSecureStoreModule();
modules.add(secureStore);
- if (sitePath != null) {
- Module sitePathModule =
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
- }
- };
- modules.add(sitePathModule);
-
- Module configModule = new GerritServerConfigModule();
- modules.add(configModule);
-
- Injector cfgInjector = Guice.createInjector(sitePathModule, configModule, secureStore);
- Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- String dbType = cfg.getString("database", null, "type");
-
- final DataSourceType dst =
- Guice.createInjector(new DataSourceModule(), configModule, sitePathModule, secureStore)
- .getInstance(Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
- modules.add(
- new LifecycleModule() {
- @Override
- protected void configure() {
- bind(DataSourceType.class).toInstance(dst);
- bind(DataSourceProvider.Context.class)
- .toInstance(DataSourceProvider.Context.MULTI_USER);
- bind(Key.get(DataSource.class, Names.named("ReviewDb")))
- .toProvider(DataSourceProvider.class)
- .in(SINGLETON);
- listener().to(DataSourceProvider.class);
- }
- });
+ Module sitePathModule =
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
+ }
+ };
+ modules.add(sitePathModule);
- } else {
- modules.add(
- new LifecycleModule() {
- @Override
- protected void configure() {
- bind(Key.get(DataSource.class, Names.named("ReviewDb")))
- .toProvider(ReviewDbDataSourceProvider.class)
- .in(SINGLETON);
- listener().to(ReviewDbDataSourceProvider.class);
- }
- });
- modules.add(new GerritServerConfigModule());
- }
- modules.add(new DatabaseModule());
- modules.add(new NotesMigration.Module());
+ Module configModule = new GerritServerConfigModule();
+ modules.add(configModule);
modules.add(new DropWizardMetricMaker.ApiModule());
return Guice.createInjector(PRODUCTION, modules);
}
- private Injector createCfgInjector() {
+ private Injector createDbInjector() {
final List<Module> modules = new ArrayList<>();
modules.add(new SchemaModule());
- modules.add(SchemaVersionCheck.module());
+ modules.add(NoteDbSchemaVersionCheck.module());
modules.add(new AuthConfigModule());
- return dbInjector.createChildInjector(modules);
+ return cfgInjector.createChildInjector(
+ ModuleOverloader.override(
+ modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.DB_MODULE)));
}
private Injector createSysInjector() {
@@ -372,7 +328,7 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
new AbstractModule() {
@Override
protected void configure() {
- bind(GerritOptions.class).toInstance(new GerritOptions(config, false, false, false));
+ bind(GerritOptions.class).toInstance(new GerritOptions(false, false, false));
bind(GerritRuntime.class).toInstance(GerritRuntime.DAEMON);
}
});
@@ -380,8 +336,9 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
modules.add(new ChangeCleanupRunner.Module());
modules.add(new AccountDeactivator.Module());
modules.add(new DefaultProjectNameLockManager.Module());
- return cfgInjector.createChildInjector(
- ModuleOverloader.override(modules, LibModuleLoader.loadModules(cfgInjector)));
+ return dbInjector.createChildInjector(
+ ModuleOverloader.override(
+ modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.SYS_MODULE)));
}
private Module createIndexModule() {
diff --git a/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java b/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
index 937b24ab02..279903c592 100644
--- a/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
+++ b/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
@@ -15,11 +15,9 @@
package com.google.gerrit.httpd.plugins;
import com.google.gerrit.extensions.api.lfs.LfsDefinitions;
-import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.httpd.resources.Resource;
import com.google.gerrit.httpd.resources.ResourceKey;
import com.google.gerrit.httpd.resources.ResourceWeigher;
-import com.google.gerrit.server.DynamicOptions;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.gerrit.server.plugins.ReloadPluginListener;
@@ -65,7 +63,5 @@ public class HttpPluginModule extends ServletModule {
.weigher(ResourceWeigher.class);
}
});
-
- DynamicMap.mapOf(binder(), DynamicOptions.DynamicBean.class);
}
}
diff --git a/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index 74cadd3b9a..43eb3a02f0 100644
--- a/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -35,7 +35,6 @@ import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteStreams;
import com.google.common.net.HttpHeaders;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.httpd.resources.Resource;
import com.google.gerrit.httpd.resources.ResourceKey;
import com.google.gerrit.httpd.resources.SmallResource;
@@ -84,8 +83,6 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -174,13 +171,7 @@ class HttpPluginServlet extends HttpServlet implements StartPluginListener, Relo
GuiceFilter filter = load(plugin);
final String name = plugin.getName();
final PluginHolder holder = new PluginHolder(plugin, filter);
- plugin.add(
- new RegistrationHandle() {
- @Override
- public void remove() {
- plugins.remove(name, holder);
- }
- });
+ plugin.add(() -> plugins.remove(name, holder));
plugins.put(name, holder);
}
@@ -234,12 +225,7 @@ class HttpPluginServlet extends HttpServlet implements StartPluginListener, Relo
HttpServletRequest wr = wrapper.create(req, name);
FilterChain chain =
- new FilterChain() {
- @Override
- public void doFilter(ServletRequest req, ServletResponse res) throws IOException {
- onDefault(holder, (HttpServletRequest) req, (HttpServletResponse) res);
- }
- };
+ (sreq, sres) -> onDefault(holder, (HttpServletRequest) sreq, (HttpServletResponse) sres);
if (holder.filter != null) {
holder.filter.doFilter(wr, res, chain);
} else {
diff --git a/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java b/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java
index 67ee3ba762..fc0ec39fa5 100644
--- a/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java
+++ b/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java
@@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.httpd.resources.Resource;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.plugins.Plugin;
@@ -40,8 +39,6 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -66,12 +63,7 @@ public class LfsPluginServlet extends HttpServlet
LfsPluginServlet(@GerritServerConfig Config cfg) {
this.pluginName = cfg.getString("lfs", null, "plugin");
this.chain =
- new FilterChain() {
- @Override
- public void doFilter(ServletRequest req, ServletResponse res) throws IOException {
- Resource.NOT_FOUND.send((HttpServletRequest) req, (HttpServletResponse) res);
- }
- };
+ (req, res) -> Resource.NOT_FOUND.send((HttpServletRequest) req, (HttpServletResponse) res);
this.filter = new AtomicReference<>();
}
@@ -123,13 +115,7 @@ public class LfsPluginServlet extends HttpServlet
return;
}
final GuiceFilter guiceFilter = load(plugin);
- plugin.add(
- new RegistrationHandle() {
- @Override
- public void remove() {
- filter.compareAndSet(guiceFilter, null);
- }
- });
+ plugin.add(() -> filter.compareAndSet(guiceFilter, null));
filter.set(guiceFilter);
}
diff --git a/java/com/google/gerrit/httpd/raw/BazelBuild.java b/java/com/google/gerrit/httpd/raw/BazelBuild.java
index 3dd46356f5..7677e97458 100644
--- a/java/com/google/gerrit/httpd/raw/BazelBuild.java
+++ b/java/com/google/gerrit/httpd/raw/BazelBuild.java
@@ -148,11 +148,6 @@ public class BazelBuild {
return sourceRoot.resolve("bazel-bin").resolve(l.pkg).resolve(l.name);
}
- /** Label for the agent specific GWT zip. */
- public Label gwtZipLabel(String agent) {
- return new Label("gerrit-gwtui", "ui_" + agent + ".zip");
- }
-
/** Label for the polygerrit component zip. */
public Label polygerritComponents() {
return new Label("polygerrit-ui", "polygerrit_components.bower_components.zip");
diff --git a/java/com/google/gerrit/httpd/raw/CatServlet.java b/java/com/google/gerrit/httpd/raw/CatServlet.java
index 70ae3ae03d..1d0e7d8bb1 100644
--- a/java/com/google/gerrit/httpd/raw/CatServlet.java
+++ b/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -20,7 +20,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
@@ -30,9 +29,7 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Optional;
@@ -53,7 +50,6 @@ import org.eclipse.jgit.lib.ObjectId;
public class CatServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
- private final Provider<ReviewDb> requestDb;
private final ChangeEditUtil changeEditUtil;
private final PatchSetUtil psUtil;
private final ChangeNotes.Factory changeNotesFactory;
@@ -62,13 +58,11 @@ public class CatServlet extends HttpServlet {
@Inject
CatServlet(
- Provider<ReviewDb> sf,
ChangeEditUtil ceu,
PatchSetUtil psu,
ChangeNotes.Factory cnf,
PermissionBackend pb,
ProjectCache pc) {
- requestDb = sf;
changeEditUtil = ceu;
psUtil = psu;
changeNotesFactory = cnf;
@@ -128,11 +122,7 @@ public class CatServlet extends HttpServlet {
String revision;
try {
ChangeNotes notes = changeNotesFactory.createChecked(changeId);
- permissionBackend
- .currentUser()
- .change(notes)
- .database(requestDb)
- .check(ChangePermission.READ);
+ permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
projectCache.checkedGet(notes.getProjectName()).checkStatePermitsRead();
if (patchKey.getParentKey().get() == 0) {
// change edit
@@ -144,7 +134,7 @@ public class CatServlet extends HttpServlet {
return;
}
} else {
- PatchSet patchSet = psUtil.get(requestDb.get(), notes, patchKey.getParentKey());
+ PatchSet patchSet = psUtil.get(notes, patchKey.getParentKey());
if (patchSet == null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
@@ -154,7 +144,7 @@ public class CatServlet extends HttpServlet {
} catch (ResourceConflictException | NoSuchChangeException | AuthException e) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
- } catch (OrmException | PermissionBackendException | IOException e) {
+ } catch (PermissionBackendException | IOException e) {
getServletContext().log("Cannot query database", e);
rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
diff --git a/java/com/google/gerrit/httpd/raw/DirectoryGwtUiServlet.java b/java/com/google/gerrit/httpd/raw/DirectoryGwtUiServlet.java
deleted file mode 100644
index 8ac1601826..0000000000
--- a/java/com/google/gerrit/httpd/raw/DirectoryGwtUiServlet.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2015 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.common.cache.Cache;
-import com.google.gerrit.server.util.time.TimeUtil;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-
-class DirectoryGwtUiServlet extends ResourceServlet {
- private static final long serialVersionUID = 1L;
-
- private static final FileTime NOW = FileTime.fromMillis(TimeUtil.nowMs());
-
- private final Path ui;
-
- DirectoryGwtUiServlet(Cache<Path, Resource> cache, Path unpackedWar, boolean dev)
- throws IOException {
- super(cache, false);
- ui = unpackedWar.resolve("gerrit_ui");
- if (!Files.exists(ui)) {
- Files.createDirectory(ui);
- }
- if (dev) {
- ui.toFile().deleteOnExit();
- }
- }
-
- @Override
- protected Path getResourcePath(String pathInfo) {
- return ui.resolve(pathInfo);
- }
-
- @Override
- protected FileTime getLastModifiedTime(Path p) {
- // Return initialization time of this class, since the GWT outputs from the
- // build process all have mtimes of 1980/1/1.
- return NOW;
- }
-}
diff --git a/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/java/com/google/gerrit/httpd/raw/HostPageServlet.java
deleted file mode 100644
index b593723de5..0000000000
--- a/java/com/google/gerrit/httpd/raw/HostPageServlet.java
+++ /dev/null
@@ -1,411 +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.httpd.raw;
-
-import static com.google.gerrit.common.FileUtil.lastModified;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.base.Strings;
-import com.google.common.flogger.FluentLogger;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
-import com.google.common.primitives.Bytes;
-import com.google.gerrit.common.Version;
-import com.google.gerrit.common.data.HostPageData;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.extensions.systemstatus.MessageOfTheDay;
-import com.google.gerrit.extensions.webui.WebUiPlugin;
-import com.google.gerrit.httpd.HtmlDomUtil;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountResource;
-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.notedb.NotesMigration;
-import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.restapi.account.GetDiffPreferences;
-import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtjsonrpc.server.JsonServlet;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-/** Sends the Gerrit host page to clients. */
-@Singleton
-public class HostPageServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
-
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final String HPD_ID = "gerrit_hostpagedata";
- private static final int DEFAULT_JS_LOAD_TIMEOUT = 5000;
-
- private final Provider<CurrentUser> currentUser;
- private final DynamicSet<WebUiPlugin> plugins;
- private final DynamicSet<MessageOfTheDay> messages;
- private final HostPageData.Theme signedOutTheme;
- private final HostPageData.Theme signedInTheme;
- private final SitePaths site;
- private final Document template;
- private final String noCacheName;
- private final boolean refreshHeaderFooter;
- private final SiteStaticDirectoryServlet staticServlet;
- private final boolean isNoteDbEnabled;
- private final Integer pluginsLoadTimeout;
- private final boolean canLoadInIFrame;
- private final GetDiffPreferences getDiff;
- private volatile Page page;
-
- @Inject
- HostPageServlet(
- Provider<CurrentUser> cu,
- SitePaths sp,
- ThemeFactory themeFactory,
- ServletContext servletContext,
- DynamicSet<WebUiPlugin> webUiPlugins,
- DynamicSet<MessageOfTheDay> motd,
- @GerritServerConfig Config cfg,
- SiteStaticDirectoryServlet ss,
- NotesMigration migration,
- GetDiffPreferences diffPref)
- throws IOException, ServletException {
- currentUser = cu;
- plugins = webUiPlugins;
- messages = motd;
- signedOutTheme = themeFactory.getSignedOutTheme();
- signedInTheme = themeFactory.getSignedInTheme();
- site = sp;
- refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true);
- staticServlet = ss;
- isNoteDbEnabled = migration.readChanges();
- pluginsLoadTimeout = getPluginsLoadTimeout(cfg);
- canLoadInIFrame = cfg.getBoolean("gerrit", "canLoadInIFrame", false);
- getDiff = diffPref;
-
- String pageName = "HostPage.html";
- template = HtmlDomUtil.parseFile(getClass(), pageName);
- if (template == null) {
- throw new FileNotFoundException("No " + pageName + " in webapp");
- }
-
- if (HtmlDomUtil.find(template, "gerrit_module") == null) {
- throw new ServletException("No gerrit_module in " + pageName);
- }
- if (HtmlDomUtil.find(template, HPD_ID) == null) {
- throw new ServletException("No " + HPD_ID + " in " + pageName);
- }
-
- String src = "gerrit_ui/gerrit_ui.nocache.js";
- try (InputStream in = servletContext.getResourceAsStream("/" + src)) {
- if (in != null) {
- Hasher md = Hashing.murmur3_128().newHasher();
- byte[] buf = new byte[1024];
- int n;
- while ((n = in.read(buf)) > 0) {
- md.putBytes(buf, 0, n);
- }
- src += "?content=" + md.hash().toString();
- } else {
- logger.atFine().log("No %s in webapp root; keeping noncache.js URL", src);
- }
- } catch (IOException e) {
- throw new IOException("Failed reading " + src, e);
- }
-
- noCacheName = src;
- page = new Page();
- }
-
- private static int getPluginsLoadTimeout(Config cfg) {
- long cfgValue =
- ConfigUtil.getTimeUnit(
- cfg, "plugins", null, "jsLoadTimeout", DEFAULT_JS_LOAD_TIMEOUT, TimeUnit.MILLISECONDS);
- if (cfgValue < 0) {
- return 0;
- }
- return (int) cfgValue;
- }
-
- private void json(Object data, StringWriter w) {
- JsonServlet.defaultGsonBuilder().create().toJson(data, w);
- }
-
- private Page get() {
- Page p = page;
- try {
- if (refreshHeaderFooter && p.isStale()) {
- p = new Page();
- page = p;
- }
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Cannot refresh site header/footer");
- }
- return p;
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- Page.Content page = select(req);
- StringWriter w = new StringWriter();
- CurrentUser user = currentUser.get();
- if (user.isIdentifiedUser()) {
- w.write(HPD_ID + ".accountDiffPref=");
- json(getDiffPreferences(user.asIdentifiedUser()), w);
- w.write(";");
-
- w.write(HPD_ID + ".theme=");
- json(signedInTheme, w);
- w.write(";");
- } else {
- w.write(HPD_ID + ".theme=");
- json(signedOutTheme, w);
- w.write(";");
- }
- plugins(w);
- messages(w);
-
- byte[] hpd = w.toString().getBytes(UTF_8);
- byte[] raw = Bytes.concat(page.part1, hpd, page.part2);
- byte[] tosend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
- rsp.setHeader("Content-Encoding", "gzip");
- tosend = HtmlDomUtil.compress(raw);
- } else {
- tosend = raw;
- }
-
- CacheHeaders.setNotCacheable(rsp);
- rsp.setContentType("text/html");
- rsp.setCharacterEncoding(HtmlDomUtil.ENC.name());
- rsp.setContentLength(tosend.length);
- try (OutputStream out = rsp.getOutputStream()) {
- out.write(tosend);
- }
- }
-
- private DiffPreferencesInfo getDiffPreferences(IdentifiedUser user) {
- try {
- return getDiff.apply(new AccountResource(user));
- } catch (RestApiException
- | ConfigInvalidException
- | IOException
- | PermissionBackendException e) {
- logger.atWarning().withCause(e).log("Cannot query account diff preferences");
- }
- return DiffPreferencesInfo.defaults();
- }
-
- private void plugins(StringWriter w) {
- List<String> urls = new ArrayList<>();
- for (WebUiPlugin u : plugins) {
- urls.add(String.format("plugins/%s/%s", u.getPluginName(), u.getJavaScriptResourcePath()));
- }
- if (!urls.isEmpty()) {
- w.write(HPD_ID + ".plugins=");
- json(urls, w);
- w.write(";");
- }
- }
-
- private void messages(StringWriter w) {
- List<HostPageData.Message> list = new ArrayList<>(2);
- for (MessageOfTheDay motd : messages) {
- String html = motd.getHtmlMessage();
- if (!Strings.isNullOrEmpty(html)) {
- HostPageData.Message m = new HostPageData.Message();
- m.id = motd.getMessageId();
- m.redisplay = motd.getRedisplay();
- m.html = html;
- list.add(m);
- }
- }
- if (!list.isEmpty()) {
- w.write(HPD_ID + ".messages=");
- json(list, w);
- w.write(";");
- }
- }
-
- private Page.Content select(HttpServletRequest req) {
- Page pg = get();
- if ("1".equals(req.getParameter("dbg"))) {
- return pg.debug;
- }
- return pg.opt;
- }
-
- private void insertETags(Element e) {
- if ("img".equalsIgnoreCase(e.getTagName()) || "script".equalsIgnoreCase(e.getTagName())) {
- String src = e.getAttribute("src");
- if (src != null && src.startsWith("static/")) {
- String name = src.substring("static/".length());
- ResourceServlet.Resource r = staticServlet.getResource(name);
- if (r != null) {
- e.setAttribute("src", src + "?e=" + r.etag);
- }
- }
- }
-
- for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
- if (n instanceof Element) {
- insertETags((Element) n);
- }
- }
- }
-
- private static class FileInfo {
- private final Path path;
- private final long time;
-
- FileInfo(Path p) {
- path = p;
- time = lastModified(path);
- }
-
- boolean isStale() {
- return time != lastModified(path);
- }
- }
-
- private class Page {
- private final FileInfo css;
- private final FileInfo header;
- private final FileInfo footer;
- private final Content opt;
- private final Content debug;
-
- Page() throws IOException {
- Document hostDoc = HtmlDomUtil.clone(template);
-
- css = injectCssFile(hostDoc, "gerrit_sitecss", site.site_css);
- header = injectXmlFile(hostDoc, "gerrit_header", site.site_header);
- footer = injectXmlFile(hostDoc, "gerrit_footer", site.site_footer);
-
- HostPageData pageData = new HostPageData();
- pageData.version = Version.getVersion();
- pageData.isNoteDbEnabled = isNoteDbEnabled;
- pageData.pluginsLoadTimeout = pluginsLoadTimeout;
- pageData.canLoadInIFrame = canLoadInIFrame;
-
- StringWriter w = new StringWriter();
- w.write("var " + HPD_ID + "=");
- json(pageData, w);
- w.write(";");
-
- Element data = HtmlDomUtil.find(hostDoc, HPD_ID);
- asScript(data);
- data.appendChild(hostDoc.createTextNode(w.toString()));
- data.appendChild(hostDoc.createComment(HPD_ID));
-
- Element nocache = HtmlDomUtil.find(hostDoc, "gerrit_module");
- asScript(nocache);
- nocache.removeAttribute("id");
- nocache.setAttribute("src", noCacheName);
- opt = new Content(hostDoc);
-
- nocache.setAttribute("src", "gerrit_ui/dbg_gerrit_ui.nocache.js");
- debug = new Content(hostDoc);
- }
-
- boolean isStale() {
- return css.isStale() || header.isStale() || footer.isStale();
- }
-
- private void asScript(Element scriptNode) {
- scriptNode.setAttribute("type", "text/javascript");
- scriptNode.setAttribute("language", "javascript");
- }
-
- class Content {
- final byte[] part1;
- final byte[] part2;
-
- Content(Document hostDoc) throws IOException {
- String raw = HtmlDomUtil.toString(hostDoc);
- int p = raw.indexOf("<!--" + HPD_ID);
- if (p < 0) {
- throw new IOException("No tag in transformed host page HTML");
- }
- part1 = raw.substring(0, p).getBytes(UTF_8);
- part2 = raw.substring(raw.indexOf('>', p) + 1).getBytes(UTF_8);
- }
- }
-
- private FileInfo injectCssFile(Document hostDoc, String id, Path src) throws IOException {
- FileInfo info = new FileInfo(src);
- Element banner = HtmlDomUtil.find(hostDoc, id);
- if (banner == null) {
- return info;
- }
-
- while (banner.getFirstChild() != null) {
- banner.removeChild(banner.getFirstChild());
- }
-
- String css = HtmlDomUtil.readFile(src.getParent(), src.getFileName().toString());
- if (css == null) {
- return info;
- }
-
- banner.appendChild(hostDoc.createCDATASection("\n" + css + "\n"));
- return info;
- }
-
- private FileInfo injectXmlFile(Document hostDoc, String id, Path src) throws IOException {
- FileInfo info = new FileInfo(src);
- Element banner = HtmlDomUtil.find(hostDoc, id);
- if (banner == null) {
- return info;
- }
-
- while (banner.getFirstChild() != null) {
- banner.removeChild(banner.getFirstChild());
- }
-
- Document html = HtmlDomUtil.parseFile(src);
- if (html == null) {
- return info;
- }
-
- Element content = html.getDocumentElement();
- insertETags(content);
- banner.appendChild(hostDoc.importNode(content, true));
- return info;
- }
- }
-}
diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
index a414e84d8d..b6594bcb10 100644
--- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
+++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
@@ -18,17 +18,18 @@ 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.template.soy.SoyFileSet;
import com.google.template.soy.data.SanitizedContent;
-import com.google.template.soy.data.SoyMapData;
import com.google.template.soy.data.UnsafeSanitizedContentOrdainer;
import com.google.template.soy.tofu.SoyTofu;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Map;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -74,8 +75,8 @@ public class IndexServlet extends HttpServlet {
return uri.getPath().replaceAll("/$", "");
}
- static SoyMapData getTemplateData(String canonicalURL, String cdnPath, String faviconPath)
- throws URISyntaxException {
+ static Map<String, Object> getTemplateData(
+ String canonicalURL, String cdnPath, String faviconPath) throws URISyntaxException {
String canonicalPath = computeCanonicalPath(canonicalURL);
String staticPath = "";
@@ -91,9 +92,16 @@ public class IndexServlet extends HttpServlet {
UnsafeSanitizedContentOrdainer.ordainAsSafe(
staticPath, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
- return new SoyMapData(
- "canonicalPath", canonicalPath,
- "staticResourcePath", sanitizedStaticPath,
- "faviconPath", faviconPath);
+ 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);
+ }
+ return data.build();
}
}
diff --git a/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java b/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
index 7bc56a6fe3..d4647aec54 100644
--- a/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
+++ b/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
@@ -16,7 +16,7 @@ package com.google.gerrit.httpd.raw;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
+import com.google.gerrit.util.http.RequestUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.FileNotFoundException;
@@ -56,7 +56,7 @@ public class LegacyGerritServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
final byte[] tosend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ if (RequestUtil.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = compressed;
} else {
diff --git a/java/com/google/gerrit/httpd/raw/RecompileGwtUiFilter.java b/java/com/google/gerrit/httpd/raw/RecompileGwtUiFilter.java
deleted file mode 100644
index c6c336707b..0000000000
--- a/java/com/google/gerrit/httpd/raw/RecompileGwtUiFilter.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (C) 2015 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 java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-class RecompileGwtUiFilter implements Filter {
- private final boolean gwtuiRecompile =
- System.getProperty("gerrit.disable-gwtui-recompile") == null;
- private final UserAgentRule rule = new UserAgentRule();
- private final Set<String> uaInitialized = new HashSet<>();
- private final Path unpackedWar;
- private final BazelBuild builder;
-
- private String lastAgent;
- private long lastTime;
-
- RecompileGwtUiFilter(BazelBuild builder, Path unpackedWar) {
- this.builder = builder;
- this.unpackedWar = unpackedWar;
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain)
- throws IOException, ServletException {
- String agent = rule.select((HttpServletRequest) request);
- if (unpackedWar != null && (gwtuiRecompile || !uaInitialized.contains(agent))) {
- BazelBuild.Label label = builder.gwtZipLabel(agent);
- File zip = builder.targetPath(label).toFile();
-
- synchronized (this) {
- try {
- builder.build(label);
- } catch (BazelBuild.BuildFailureException e) {
- e.display(label.toString(), (HttpServletResponse) res);
- return;
- }
-
- if (!agent.equals(lastAgent) || lastTime != zip.lastModified()) {
- lastAgent = agent;
- lastTime = zip.lastModified();
- unpack(zip, unpackedWar.toFile());
- }
- }
- uaInitialized.add(agent);
- }
- chain.doFilter(request, res);
- }
-
- @Override
- public void init(FilterConfig config) {}
-
- @Override
- public void destroy() {}
-
- private static void unpack(File srcwar, File dstwar) throws IOException {
- try (ZipFile zf = new ZipFile(srcwar)) {
- final Enumeration<? extends ZipEntry> e = zf.entries();
- while (e.hasMoreElements()) {
- final ZipEntry ze = e.nextElement();
- final String name = ze.getName();
-
- if (ze.isDirectory()
- || name.startsWith("WEB-INF/")
- || name.startsWith("META-INF/")
- || name.startsWith("com/google/gerrit/launcher/")
- || name.equals("Main.class")) {
- continue;
- }
-
- final File rawtmp = new File(dstwar, name);
- mkdir(rawtmp.getParentFile());
- rawtmp.deleteOnExit();
-
- try (OutputStream rawout = Files.newOutputStream(rawtmp.toPath());
- InputStream in = zf.getInputStream(ze)) {
- final byte[] buf = new byte[4096];
- int n;
- while ((n = in.read(buf, 0, buf.length)) > 0) {
- rawout.write(buf, 0, n);
- }
- }
- }
- }
- }
-
- private static void mkdir(File dir) throws IOException {
- if (!dir.isDirectory()) {
- mkdir(dir.getParentFile());
- if (!dir.mkdir()) {
- throw new IOException("Cannot mkdir " + dir.getAbsolutePath());
- }
- dir.deleteOnExit();
- }
- }
-}
diff --git a/java/com/google/gerrit/httpd/raw/ResourceServlet.java b/java/com/google/gerrit/httpd/raw/ResourceServlet.java
index 4b44af45f7..8be4abc03a 100644
--- a/java/com/google/gerrit/httpd/raw/ResourceServlet.java
+++ b/java/com/google/gerrit/httpd/raw/ResourceServlet.java
@@ -33,10 +33,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.httpd.HtmlDomUtil;
-import com.google.gerrit.server.UsedAt;
import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
+import com.google.gerrit.util.http.RequestUtil;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
@@ -182,7 +182,7 @@ public abstract class ResourceServlet extends HttpServlet {
}
byte[] tosend = r.raw;
- if (!r.contentType.equals(JS) && RPCServletUtils.acceptsGzipEncoding(req)) {
+ if (!r.contentType.equals(JS) && RequestUtil.acceptsGzipEncoding(req)) {
byte[] gz = HtmlDomUtil.compress(tosend);
if ((gz.length + 24) < tosend.length) {
rsp.setHeader(CONTENT_ENCODING, "gzip");
@@ -267,7 +267,7 @@ public abstract class ResourceServlet extends HttpServlet {
OutputStream out = rsp.getOutputStream();
GZIPOutputStream gz = null;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ if (RequestUtil.acceptsGzipEncoding(req)) {
rsp.setHeader(CONTENT_ENCODING, "gzip");
gz = new GZIPOutputStream(out);
out = gz;
diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
index 98fbdfe8f4..06ac886079 100644
--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
@@ -22,7 +22,6 @@ 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.client.UiType;
import com.google.gerrit.httpd.XsrfCookieFilter;
import com.google.gerrit.httpd.raw.ResourceServlet.Resource;
import com.google.gerrit.launcher.GerritLauncher;
@@ -51,7 +50,6 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
@@ -62,7 +60,6 @@ public class StaticModule extends ServletModule {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static final String CACHE = "static_content";
- public static final String GERRIT_UI_COOKIE = "GERRIT_UI";
/**
* Paths at which we should serve the main PolyGerrit application {@code index.html}.
@@ -78,6 +75,7 @@ public class StaticModule extends ServletModule {
"/x/*",
"/admin/*",
"/dashboard/*",
+ "/groups/self",
"/settings/*",
"/Documentation/q/*");
// TODO(dborowitz): These fragments conflict with the REST API
@@ -102,12 +100,9 @@ public class StaticModule extends ServletModule {
private static final String DOC_SERVLET = "DocServlet";
private static final String FAVICON_SERVLET = "FaviconServlet";
- private static final String GWT_UI_SERVLET = "GwtUiServlet";
private static final String POLYGERRIT_INDEX_SERVLET = "PolyGerritUiIndexServlet";
private static final String ROBOTS_TXT_SERVLET = "RobotsTxtServlet";
- private static final int GERRIT_UI_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
-
private final GerritOptions options;
private Paths paths;
@@ -144,9 +139,6 @@ public class StaticModule extends ServletModule {
install(new CoreStaticModule());
install(new PolyGerritModule());
}
- if (options.enableGwtUi()) {
- install(new GwtUiModule());
- }
}
@Provides
@@ -219,38 +211,11 @@ public class StaticModule extends ServletModule {
}
}
- private class GwtUiModule extends ServletModule {
- @Override
- public void configureServlets() {
- serveRegex("^/gerrit_ui/(?!rpc/)(.*)$")
- .with(Key.get(HttpServlet.class, Names.named(GWT_UI_SERVLET)));
- Paths p = getPaths();
- if (p.isDev()) {
- filter("/").through(new RecompileGwtUiFilter(p.builder, p.unpackedWar));
- }
- }
-
- @Provides
- @Singleton
- @Named(GWT_UI_SERVLET)
- HttpServlet getGwtUiServlet(@Named(CACHE) Cache<Path, Resource> cache) throws IOException {
- Paths p = getPaths();
- if (p.warFs != null) {
- return new WarGwtUiServlet(cache, p.warFs);
- }
- return new DirectoryGwtUiServlet(cache, p.unpackedWar, p.isDev());
- }
- }
-
private class PolyGerritModule extends ServletModule {
@Override
public void configureServlets() {
for (String p : POLYGERRIT_INDEX_PATHS) {
- // Skip XsrfCookieFilter for /, since that is already done in the GWT UI
- // path (UrlModule) if it is enabled.
- if (!(p.equals("/") && options.enableGwtUi())) {
- filter(p).through(XsrfCookieFilter.class);
- }
+ filter(p).through(XsrfCookieFilter.class);
}
filter("/*").through(PolyGerritFilter.class);
}
@@ -412,7 +377,6 @@ public class StaticModule extends ServletModule {
@Singleton
private static class PolyGerritFilter implements Filter {
- private final GerritOptions options;
private final Paths paths;
private final HttpServlet polyGerritIndex;
private final PolyGerritUiServlet polygerritUI;
@@ -421,14 +385,12 @@ public class StaticModule extends ServletModule {
@Inject
PolyGerritFilter(
- GerritOptions options,
Paths paths,
@Named(POLYGERRIT_INDEX_SERVLET) HttpServlet polyGerritIndex,
PolyGerritUiServlet polygerritUI,
@Nullable BowerComponentsDevServlet bowerComponentServlet,
@Nullable FontsDevServlet fontServlet) {
this.paths = paths;
- this.options = options;
this.polyGerritIndex = polyGerritIndex;
this.polygerritUI = polygerritUI;
this.bowerComponentServlet = bowerComponentServlet;
@@ -446,13 +408,6 @@ public class StaticModule extends ServletModule {
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
- if (handlePolyGerritParam(req, res)) {
- return;
- }
- if (!isPolyGerritEnabled(req)) {
- chain.doFilter(req, res);
- return;
- }
GuiceFilterRequestWrapper reqWrapper = new GuiceFilterRequestWrapper(req);
String path = pathInfo(req);
@@ -491,71 +446,6 @@ public class StaticModule extends ServletModule {
return uri.startsWith(ctx) ? uri.substring(ctx.length()) : uri;
}
- private boolean handlePolyGerritParam(HttpServletRequest req, HttpServletResponse res)
- throws IOException {
- if (!options.enableGwtUi() || !"GET".equals(req.getMethod())) {
- return false;
- }
- boolean redirect = false;
- String param = req.getParameter("polygerrit");
- if ("1".equals(param)) {
- setPolyGerritCookie(req, res, UiType.POLYGERRIT);
- redirect = true;
- } else if ("0".equals(param)) {
- setPolyGerritCookie(req, res, UiType.GWT);
- redirect = true;
- }
- if (redirect) {
- // Strip polygerrit param from URL. This actually strips all params,
- // which is a similar behavior to the JS PolyGerrit redirector code.
- // Stripping just one param is frustratingly difficult without the use
- // of Apache httpclient, which is a dep we don't want here:
- // https://gerrit-review.googlesource.com/#/c/57570/57/gerrit-httpd/BUCK@32
- res.sendRedirect(req.getRequestURL().toString());
- }
- return redirect;
- }
-
- private boolean isPolyGerritEnabled(HttpServletRequest req) {
- return !options.enableGwtUi() || isPolyGerritCookie(req);
- }
-
- private boolean isPolyGerritCookie(HttpServletRequest req) {
- UiType type = UiType.POLYGERRIT;
- Cookie[] all = req.getCookies();
- if (all != null) {
- for (Cookie c : all) {
- if (GERRIT_UI_COOKIE.equals(c.getName())) {
- UiType t = UiType.parse(c.getValue());
- if (t != null) {
- type = t;
- break;
- }
- }
- }
- }
- return type == UiType.POLYGERRIT;
- }
-
- private void setPolyGerritCookie(HttpServletRequest req, HttpServletResponse res, UiType pref) {
- // Only actually set a cookie if GWT UI is enabled in addition to default PG UI;
- // otherwise clear it.
- Cookie cookie = new Cookie(GERRIT_UI_COOKIE, pref.name());
- if (options.enableGwtUi()) {
- cookie.setPath("/");
- cookie.setSecure(isSecure(req));
- cookie.setMaxAge(GERRIT_UI_COOKIE_MAX_AGE);
- } else {
- cookie.setValue("");
- cookie.setMaxAge(0);
- }
- res.addCookie(cookie);
- }
-
- private static boolean isSecure(HttpServletRequest req) {
- return req.isSecure() || "https".equals(req.getScheme());
- }
-
private static boolean isPolyGerritAsset(String path) {
return matchPath(POLYGERRIT_ASSET_PATHS, path);
}
diff --git a/java/com/google/gerrit/httpd/raw/ThemeFactory.java b/java/com/google/gerrit/httpd/raw/ThemeFactory.java
deleted file mode 100644
index 6a75e07a4a..0000000000
--- a/java/com/google/gerrit/httpd/raw/ThemeFactory.java
+++ /dev/null
@@ -1,64 +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.httpd.raw;
-
-import com.google.gerrit.common.data.HostPageData;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class ThemeFactory {
- private final Config cfg;
-
- @Inject
- ThemeFactory(@GerritServerConfig Config cfg) {
- this.cfg = cfg;
- }
-
- HostPageData.Theme getSignedOutTheme() {
- return getTheme("signed-out");
- }
-
- HostPageData.Theme getSignedInTheme() {
- return getTheme("signed-in");
- }
-
- private HostPageData.Theme getTheme(String name) {
- HostPageData.Theme theme = new HostPageData.Theme();
- theme.backgroundColor = color(name, "backgroundColor", "#FFFFFF");
- theme.textColor = color(name, "textColor", "#353535");
- theme.trimColor = color(name, "trimColor", "#EEEEEE");
- theme.selectionColor = color(name, "selectionColor", "#D8EDF9");
- theme.topMenuColor = color(name, "topMenuColor", "#FFFFFF");
- theme.changeTableOutdatedColor = color(name, "changeTableOutdatedColor", "#F08080");
- theme.tableOddRowColor = color(name, "tableOddRowColor", "transparent");
- theme.tableEvenRowColor = color(name, "tableEvenRowColor", "transparent");
- return theme;
- }
-
- private String color(String section, String name, String defaultValue) {
- String v = cfg.getString("theme", section, name);
- if (v == null || v.isEmpty()) {
- v = cfg.getString("theme", null, name);
- if (v == null || v.isEmpty()) {
- v = defaultValue;
- }
- }
- if (!v.startsWith("#") && v.matches("^[0-9a-fA-F]{2,6}$")) {
- v = "#" + v;
- }
- return v;
- }
-}
diff --git a/java/com/google/gerrit/httpd/raw/ToolServlet.java b/java/com/google/gerrit/httpd/raw/ToolServlet.java
index fcdd21d3be..0d707a6fd6 100644
--- a/java/com/google/gerrit/httpd/raw/ToolServlet.java
+++ b/java/com/google/gerrit/httpd/raw/ToolServlet.java
@@ -25,8 +25,7 @@ import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
import com.google.gerrit.common.Version;
import com.google.gerrit.server.tools.ToolsCatalog;
-import com.google.gerrit.server.tools.ToolsCatalog.Entry;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
+import com.google.gerrit.util.http.RequestUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -50,7 +49,7 @@ public class ToolServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- Entry ent = toc.get(req.getPathInfo());
+ ToolsCatalog.Entry ent = toc.get(req.getPathInfo());
if (ent == null) {
rsp.sendError(SC_NOT_FOUND);
return;
@@ -71,7 +70,7 @@ public class ToolServlet extends HttpServlet {
}
}
- private void doGetFile(Entry ent, HttpServletResponse rsp) throws IOException {
+ private void doGetFile(ToolsCatalog.Entry ent, HttpServletResponse rsp) throws IOException {
byte[] tosend = ent.getBytes();
rsp.setDateHeader(HDR_EXPIRES, 0L);
@@ -84,8 +83,8 @@ public class ToolServlet extends HttpServlet {
}
}
- private void doGetDirectory(Entry ent, HttpServletRequest req, HttpServletResponse rsp)
- throws IOException {
+ private void doGetDirectory(
+ ToolsCatalog.Entry ent, HttpServletRequest req, HttpServletResponse rsp) throws IOException {
String path = "/tools/" + ent.getPath();
Document page = newDocument();
@@ -108,9 +107,9 @@ public class ToolServlet extends HttpServlet {
Element ul = page.createElement("ul");
body.appendChild(ul);
- for (Entry e : ent.getChildren()) {
+ for (ToolsCatalog.Entry e : ent.getChildren()) {
String name = e.getName();
- if (e.getType() == Entry.Type.DIR && !name.endsWith("/")) {
+ if (e.getType() == ToolsCatalog.Entry.Type.DIR && !name.endsWith("/")) {
name += "/";
}
@@ -130,7 +129,7 @@ public class ToolServlet extends HttpServlet {
body.appendChild(footer);
byte[] tosend = toUTF8(page);
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ if (RequestUtil.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = compress(tosend);
}
diff --git a/java/com/google/gerrit/httpd/raw/UserAgentRule.java b/java/com/google/gerrit/httpd/raw/UserAgentRule.java
deleted file mode 100644
index 4aac243875..0000000000
--- a/java/com/google/gerrit/httpd/raw/UserAgentRule.java
+++ /dev/null
@@ -1,93 +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.httpd.raw;
-
-import static java.util.regex.Pattern.compile;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Selects the value for the {@code user.agent} property.
- *
- * <p>Examines the {@code User-Agent} HTTP request header, and tries to match it to known {@code
- * user.agent} values.
- *
- * <p>Ported from JavaScript in {@code com.google.gwt.user.UserAgent.gwt.xml}.
- */
-class UserAgentRule {
- private static final Pattern msie = compile(".*msie ([0-11]+)\\.([0-11]+).*");
- private static final Pattern gecko = compile(".*rv:([0-9]+)\\.([0-9]+).*");
-
- String getName() {
- return "user.agent";
- }
-
- String select(HttpServletRequest req) {
- String ua = req.getHeader("User-Agent");
- if (ua == null) {
- return null;
- }
-
- ua = ua.toLowerCase();
-
- if (ua.contains("opera")) {
- return "opera";
-
- } else if (ua.contains("webkit")) {
- return "safari";
-
- } else if (ua.contains("msie")) {
- // GWT 2.0 uses document.documentMode here, which we can't do
- // on the server side.
-
- Matcher m = msie.matcher(ua);
- if (m.matches() && m.groupCount() == 2) {
- int v = makeVersion(m);
- if (v >= 11000) {
- return "ie11";
- }
- if (v >= 10000) {
- return "ie10";
- }
- if (v >= 9000) {
- return "ie9";
- }
- if (v >= 8000) {
- return "ie8";
- }
- }
- return null;
-
- } else if (ua.contains("edge")) {
- return "edge";
- } else if (ua.contains("gecko")) {
- Matcher m = gecko.matcher(ua);
- if (m.matches() && m.groupCount() == 2) {
- if (makeVersion(m) >= 1008) {
- return "gecko1_8";
- }
- }
- return "gecko";
- }
-
- return null;
- }
-
- private int makeVersion(Matcher result) {
- return (Integer.parseInt(result.group(1)) * 1000) + Integer.parseInt(result.group(2));
- }
-}
diff --git a/java/com/google/gerrit/httpd/raw/WarGwtUiServlet.java b/java/com/google/gerrit/httpd/raw/WarGwtUiServlet.java
deleted file mode 100644
index 5fe7054e87..0000000000
--- a/java/com/google/gerrit/httpd/raw/WarGwtUiServlet.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2015 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.common.cache.Cache;
-import com.google.gerrit.server.util.time.TimeUtil;
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-
-class WarGwtUiServlet extends ResourceServlet {
- private static final long serialVersionUID = 1L;
-
- private static final FileTime NOW = FileTime.fromMillis(TimeUtil.nowMs());
-
- private final FileSystem warFs;
-
- WarGwtUiServlet(Cache<Path, Resource> cache, FileSystem warFs) {
- super(cache, false);
- this.warFs = warFs;
- }
-
- @Override
- protected Path getResourcePath(String pathInfo) {
- return warFs.getPath("/gerrit_ui/" + pathInfo);
- }
-
- @Override
- protected FileTime getLastModifiedTime(Path p) {
- // Return initialization time of this class, since the GWT outputs from the
- // build process all have mtimes of 1980/1/1.
- return NOW;
- }
-}
diff --git a/java/com/google/gerrit/httpd/restapi/LogRedactUtil.java b/java/com/google/gerrit/httpd/restapi/LogRedactUtil.java
index 5a2a0336a8..5a37b7bbb4 100644
--- a/java/com/google/gerrit/httpd/restapi/LogRedactUtil.java
+++ b/java/com/google/gerrit/httpd/restapi/LogRedactUtil.java
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// WARNING: NoteDbUpdateManager cares about the package name RestApiServlet lives in.
-
package com.google.gerrit.httpd.restapi;
import static com.google.gerrit.httpd.restapi.RestApiServlet.XD_AUTHORIZATION;
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java b/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java
new file mode 100644
index 0000000000..a3aba6df01
--- /dev/null
+++ b/java/com/google/gerrit/httpd/restapi/RestApiQuotaEnforcer.java
@@ -0,0 +1,83 @@
+// 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.restapi;
+
+import com.google.gerrit.extensions.restapi.RestResource;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.quota.QuotaBackend;
+import com.google.gerrit.server.quota.QuotaException;
+import com.google.gerrit.util.http.RequestUtil;
+import com.google.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Enforces quota on specific REST API endpoints.
+ *
+ * <p>Examples:
+ *
+ * <ul>
+ * <li>GET /a/accounts/self/detail => /restapi/accounts/detail:GET
+ * <li>GET /changes/123/revisions/current/detail => /restapi/changes/revisions/detail:GET
+ * <li>PUT /changes/10/reviewed => /restapi/changes/reviewed:PUT
+ * </ul>
+ *
+ * <p>Adds context (change, project, account) to the quota check if the call is for an existing
+ * entity that was successfully parsed. This quota check is generally enforced after the resource
+ * was parsed, but before the view is executed. If a quota enforcer desires to throttle earlier,
+ * they should consider quota groups in the {@code /http/*} space.
+ */
+public class RestApiQuotaEnforcer {
+ private final QuotaBackend quotaBackend;
+
+ @Inject
+ RestApiQuotaEnforcer(QuotaBackend quotaBackend) {
+ this.quotaBackend = quotaBackend;
+ }
+
+ /** Enforce quota on a request not tied to any {@code RestResource}. */
+ void enforce(HttpServletRequest req) throws QuotaException {
+ String pathForQuotaReporting = RequestUtil.getRestPathWithoutIds(req);
+ quotaBackend
+ .currentUser()
+ .requestToken(quotaGroup(pathForQuotaReporting, req.getMethod()))
+ .throwOnError();
+ }
+
+ /** Enforce quota on a request for a given resource. */
+ void enforce(RestResource rsrc, HttpServletRequest req) throws QuotaException {
+ String pathForQuotaReporting = RequestUtil.getRestPathWithoutIds(req);
+ // Enrich the quota request we are operating on an interesting collection
+ QuotaBackend.WithResource report = quotaBackend.currentUser();
+ if (rsrc instanceof ChangeResource) {
+ ChangeResource changeResource = (ChangeResource) rsrc;
+ report =
+ quotaBackend.currentUser().change(changeResource.getId(), changeResource.getProject());
+ } else if (rsrc instanceof AccountResource) {
+ AccountResource accountResource = (AccountResource) rsrc;
+ report = quotaBackend.currentUser().account(accountResource.getUser().getAccountId());
+ } else if (rsrc instanceof ProjectResource) {
+ ProjectResource projectResource = (ProjectResource) rsrc;
+ report = quotaBackend.currentUser().project(projectResource.getNameKey());
+ }
+
+ report.requestToken(quotaGroup(pathForQuotaReporting, req.getMethod())).throwOnError();
+ }
+
+ private static String quotaGroup(String path, String method) {
+ return "/restapi" + path + ":" + method;
+ }
+}
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 623d31d7d7..c45a1189fb 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -97,23 +97,24 @@ import com.google.gerrit.extensions.restapi.RestResource;
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.git.LockFailureException;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.httpd.restapi.ParameterParser.QueryParams;
+import com.google.gerrit.json.OutputFormat;
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.OutputFormat;
import com.google.gerrit.server.audit.ExtendedHttpAuditEvent;
import com.google.gerrit.server.cache.PerThreadCache;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.LockFailureException;
import com.google.gerrit.server.group.GroupAuditService;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
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.quota.QuotaException;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.util.http.CacheHeaders;
@@ -227,6 +228,7 @@ public class RestApiServlet extends HttpServlet {
final GroupAuditService auditService;
final RestApiMetrics metrics;
final Pattern allowOrigin;
+ final RestApiQuotaEnforcer quotaChecker;
@Inject
Globals(
@@ -236,6 +238,7 @@ public class RestApiServlet extends HttpServlet {
PermissionBackend permissionBackend,
GroupAuditService auditService,
RestApiMetrics metrics,
+ RestApiQuotaEnforcer quotaChecker,
@GerritServerConfig Config cfg) {
this.currentUser = currentUser;
this.webSession = webSession;
@@ -243,6 +246,7 @@ public class RestApiServlet extends HttpServlet {
this.permissionBackend = permissionBackend;
this.auditService = auditService;
this.metrics = metrics;
+ this.quotaChecker = quotaChecker;
allowOrigin = makeAllowOrigin(cfg);
}
@@ -317,6 +321,7 @@ public class RestApiServlet extends HttpServlet {
viewData = new ViewData(null, null);
if (path.isEmpty()) {
+ globals.quotaChecker.enforce(req);
if (rc instanceof NeedsParams) {
((NeedsParams) rc).setParams(qp.params());
}
@@ -339,6 +344,7 @@ public class RestApiServlet extends HttpServlet {
IdString id = path.remove(0);
try {
rsrc = rc.parse(rsrc, id);
+ globals.quotaChecker.enforce(rsrc, req);
if (path.isEmpty()) {
checkPreconditions(req);
}
@@ -346,6 +352,7 @@ public class RestApiServlet extends HttpServlet {
if (!path.isEmpty()) {
throw e;
}
+ globals.quotaChecker.enforce(req);
if (isPost(req) || isPut(req)) {
RestView<RestResource> createView = rc.views().get(PluginName.GERRIT, "CREATE./");
@@ -385,8 +392,12 @@ public class RestApiServlet extends HttpServlet {
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(viewData.pluginName, "POST_ON_COLLECTION./");
+ c.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
@@ -394,7 +405,7 @@ public class RestApiServlet extends HttpServlet {
}
} else if (isDelete(req)) {
RestView<RestResource> restCollectionView =
- c.views().get(viewData.pluginName, "DELETE_ON_COLLECTION./");
+ c.views().get(PluginName.GERRIT, "DELETE_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
@@ -603,6 +614,9 @@ public class RestApiServlet extends HttpServlet {
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);
} catch (Exception e) {
status = SC_INTERNAL_SERVER_ERROR;
responseBytes = handleException(e, req, res);
@@ -1255,6 +1269,8 @@ public class RestApiServlet extends HttpServlet {
return new ViewData(PluginName.GERRIT, core);
}
+ // Check if we want to delegate to a child collection. Child collections are bound with
+ // GET.name so we have to check for this since we haven't found any other views.
core = views.get(PluginName.GERRIT, "GET." + p.get(0));
if (core != null) {
return new ViewData(PluginName.GERRIT, core);
@@ -1268,6 +1284,17 @@ public class RestApiServlet extends HttpServlet {
}
}
+ if (r.isEmpty()) {
+ // Check if we want to delegate to a child collection. Child collections are bound with
+ // GET.name so we have to check for this since we haven't found any other views.
+ for (String plugin : views.plugins()) {
+ RestView<RestResource> action = views.get(plugin, "GET." + p.get(0));
+ if (action != null) {
+ r.put(plugin, action);
+ }
+ }
+ }
+
if (r.size() == 1) {
Map.Entry<String, RestView<RestResource>> entry = Iterables.getOnlyElement(r.entrySet());
return new ViewData(entry.getKey(), entry.getValue());
diff --git a/java/com/google/gerrit/httpd/rpc/AuditedHttpServletResponse.java b/java/com/google/gerrit/httpd/rpc/AuditedHttpServletResponse.java
deleted file mode 100644
index 31819e8af1..0000000000
--- a/java/com/google/gerrit/httpd/rpc/AuditedHttpServletResponse.java
+++ /dev/null
@@ -1,63 +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.httpd.rpc;
-
-import java.io.IOException;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-
-class AuditedHttpServletResponse extends HttpServletResponseWrapper implements HttpServletResponse {
- private int status;
-
- AuditedHttpServletResponse(HttpServletResponse response) {
- super(response);
- }
-
- @SuppressWarnings("all") // @Override for servlet API 3.0+ only.
- public int getStatus() {
- return status;
- }
-
- @Override
- public void setStatus(int sc) {
- super.setStatus(sc);
- this.status = sc;
- }
-
- @Override
- @Deprecated
- public void setStatus(int sc, String sm) {
- super.setStatus(sc, sm);
- this.status = sc;
- }
-
- @Override
- public void sendError(int sc) throws IOException {
- super.sendError(sc);
- this.status = sc;
- }
-
- @Override
- public void sendError(int sc, String msg) throws IOException {
- super.sendError(sc, msg);
- this.status = sc;
- }
-
- @Override
- public void sendRedirect(String location) throws IOException {
- super.sendRedirect(location);
- this.status = SC_MOVED_TEMPORARILY;
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java b/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
deleted file mode 100644
index 10307e6aef..0000000000
--- a/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
+++ /dev/null
@@ -1,289 +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.httpd.rpc;
-
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.audit.Audit;
-import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.common.errors.NotSignedInException;
-import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.server.AccessPath;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.audit.RpcAuditEvent;
-import com.google.gerrit.server.group.GroupAuditService;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gson.GsonBuilder;
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.gwtjsonrpc.server.ActiveCall;
-import com.google.gwtjsonrpc.server.JsonServlet;
-import com.google.gwtjsonrpc.server.MethodHandle;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/** Base JSON servlet to ensure the current user is not forged. */
-final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall> {
- private static final long serialVersionUID = 1L;
-
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final ThreadLocal<GerritCall> currentCall = new ThreadLocal<>();
- private static final ThreadLocal<MethodHandle> currentMethod = new ThreadLocal<>();
- private final DynamicItem<WebSession> session;
- private final RemoteJsonService service;
- private final GroupAuditService audit;
-
- @Inject
- GerritJsonServlet(final DynamicItem<WebSession> w, RemoteJsonService s, GroupAuditService a) {
- session = w;
- service = s;
- audit = a;
- }
-
- @Override
- protected GerritCall createActiveCall(final HttpServletRequest req, HttpServletResponse rsp) {
- final GerritCall call = new GerritCall(session.get(), req, new AuditedHttpServletResponse(rsp));
- currentCall.set(call);
- return call;
- }
-
- @Override
- protected GsonBuilder createGsonBuilder() {
- return gerritDefaultGsonBuilder();
- }
-
- private static GsonBuilder gerritDefaultGsonBuilder() {
- final GsonBuilder g = defaultGsonBuilder();
-
- g.registerTypeAdapter(
- org.eclipse.jgit.diff.Edit.class, new org.eclipse.jgit.diff.EditDeserializer());
-
- return g;
- }
-
- @Override
- protected void preInvoke(GerritCall call) {
- super.preInvoke(call);
-
- if (call.isComplete()) {
- return;
- }
-
- if (call.getMethod().getAnnotation(SignInRequired.class) != null) {
- // If SignInRequired is set on this method we must have both a
- // valid XSRF token *and* have the user signed in. Doing these
- // checks also validates that they agree on the user identity.
- //
- if (!call.requireXsrfValid() || !session.get().isSignedIn()) {
- call.onFailure(new NotSignedInException());
- }
- }
- }
-
- @Override
- protected Object createServiceHandle() {
- return service;
- }
-
- @Override
- protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
- try {
- super.service(req, resp);
- } finally {
- audit();
- currentCall.set(null);
- }
- }
-
- private void audit() {
- try {
- GerritCall call = currentCall.get();
- MethodHandle method = call.getMethod();
- if (method == null) {
- return;
- }
- Audit note = method.getAnnotation(Audit.class);
- if (note != null) {
- String sid = call.getWebSession().getSessionId();
- CurrentUser username = call.getWebSession().getUser();
- ListMultimap<String, ?> args = extractParams(note, call);
- String what = extractWhat(note, call);
- Object result = call.getResult();
-
- audit.dispatch(
- new RpcAuditEvent(
- sid,
- username,
- what,
- call.getWhen(),
- args,
- call.getHttpServletRequest().getMethod(),
- call.getHttpServletRequest().getMethod(),
- ((AuditedHttpServletResponse) (call.getHttpServletResponse())).getStatus(),
- result));
- }
- } catch (Throwable all) {
- logger.atSevere().withCause(all).log("Unable to log the call");
- }
- }
-
- private ListMultimap<String, ?> extractParams(Audit note, GerritCall call) {
- ListMultimap<String, Object> args = MultimapBuilder.hashKeys().arrayListValues().build();
-
- Object[] params = call.getParams();
- for (int i = 0; i < params.length; i++) {
- args.put("$" + i, params[i]);
- }
-
- for (int idx : note.obfuscate()) {
- args.removeAll("$" + idx);
- args.put("$" + idx, "*****");
- }
- return args;
- }
-
- private String extractWhat(Audit note, GerritCall call) {
- Class<?> methodClass = call.getMethodClass();
- String methodClassName = methodClass != null ? methodClass.getName() : "<UNKNOWN_CLASS>";
- methodClassName = methodClassName.substring(methodClassName.lastIndexOf('.') + 1);
- String what = note.action();
- if (what.length() == 0) {
- what = call.getMethod().getName();
- }
-
- return methodClassName + "." + what;
- }
-
- static class GerritCall extends ActiveCall {
- private final WebSession session;
- private final long when;
- private static final Field resultField;
- private static final Field methodField;
-
- // Needed to allow access to non-public result field in GWT/JSON-RPC
- static {
- resultField = getPrivateField(ActiveCall.class, "result");
- methodField = getPrivateField(MethodHandle.class, "method");
- }
-
- private static Field getPrivateField(Class<?> clazz, String fieldName) {
- Field declaredField = null;
- try {
- declaredField = clazz.getDeclaredField(fieldName);
- declaredField.setAccessible(true);
- } catch (Exception e) {
- logger.atSevere().log("Unable to expose RPS/JSON result field");
- }
- return declaredField;
- }
-
- // Surrogate of the missing getMethodClass() in GWT/JSON-RPC
- public Class<?> getMethodClass() {
- if (methodField == null) {
- return null;
- }
-
- try {
- Method method = (Method) methodField.get(this.getMethod());
- return method.getDeclaringClass();
- } catch (IllegalArgumentException e) {
- logger.atSevere().log("Cannot access result field");
- } catch (IllegalAccessException e) {
- logger.atSevere().log("No permissions to access result field");
- }
-
- return null;
- }
-
- // Surrogate of the missing getResult() in GWT/JSON-RPC
- public Object getResult() {
- if (resultField == null) {
- return null;
- }
-
- try {
- return resultField.get(this);
- } catch (IllegalArgumentException e) {
- logger.atSevere().log("Cannot access result field");
- } catch (IllegalAccessException e) {
- logger.atSevere().log("No permissions to access result field");
- }
-
- return null;
- }
-
- GerritCall(WebSession session, HttpServletRequest i, HttpServletResponse o) {
- super(i, o);
- this.session = session;
- this.when = TimeUtil.nowMs();
- }
-
- @Override
- public MethodHandle getMethod() {
- if (currentMethod.get() == null) {
- return super.getMethod();
- }
- return currentMethod.get();
- }
-
- @Override
- public void onFailure(Throwable error) {
- if (error instanceof IllegalArgumentException || error instanceof IllegalStateException) {
- super.onFailure(error);
- } else if (error instanceof OrmException || error instanceof RuntimeException) {
- onInternalFailure(error);
- } else {
- super.onFailure(error);
- }
- }
-
- @Override
- public boolean xsrfValidate() {
- final String keyIn = getXsrfKeyIn();
- if (keyIn == null || "".equals(keyIn)) {
- // Anonymous requests don't need XSRF protection, they shouldn't
- // be able to cause critical state changes.
- //
- return !session.isSignedIn();
-
- } else if (session.isSignedIn() && session.isValidXGerritAuth(keyIn)) {
- // The session must exist, and must be using this token.
- //
- session.getUser().setAccessPath(AccessPath.JSON_RPC);
- return true;
- }
- return false;
- }
-
- public WebSession getWebSession() {
- return session;
- }
-
- public long getWhen() {
- return when;
- }
-
- public long getElapsed() {
- return TimeUtil.nowMs() - when;
- }
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java b/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java
deleted file mode 100644
index b167167d69..0000000000
--- a/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java
+++ /dev/null
@@ -1,47 +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.httpd.rpc;
-
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Provider;
-
-/** Creates {@link GerritJsonServlet} with a {@link RemoteJsonService}. */
-class GerritJsonServletProvider implements Provider<GerritJsonServlet> {
- @Inject private Injector injector;
-
- private final Class<? extends RemoteJsonService> serviceClass;
-
- @Inject
- GerritJsonServletProvider(Class<? extends RemoteJsonService> c) {
- serviceClass = c;
- }
-
- @Override
- public GerritJsonServlet get() {
- final RemoteJsonService srv = injector.getInstance(serviceClass);
- return injector
- .createChildInjector(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(RemoteJsonService.class).toInstance(srv);
- }
- })
- .getInstance(GerritJsonServlet.class);
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/Handler.java b/java/com/google/gerrit/httpd/rpc/Handler.java
deleted file mode 100644
index ae20571747..0000000000
--- a/java/com/google/gerrit/httpd/rpc/Handler.java
+++ /dev/null
@@ -1,92 +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.httpd.rpc;
-
-import com.google.gerrit.common.errors.NoSuchEntityException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.NoSuchRefException;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.VoidResult;
-import com.google.gwtorm.server.OrmException;
-import java.util.concurrent.Callable;
-
-/**
- * Base class for RPC service implementations.
- *
- * <p>Typically an RPC service implementation will extend this class and use Guice injection to
- * manage its state. For example:
- *
- * <pre>
- * class Foo extends Handler&lt;Result&gt; {
- * interface Factory {
- * Foo create(... args ...);
- * }
- * &#064;Inject
- * Foo(state, @Assisted args) { ... }
- * Result get() throws Exception { ... }
- * }
- * </pre>
- *
- * @param <T> type of result for {@link AsyncCallback#onSuccess(Object)} if the operation completed
- * successfully.
- */
-public abstract class Handler<T> implements Callable<T> {
- public static <T> Handler<T> wrap(Callable<T> r) {
- return new Handler<T>() {
- @Override
- public T call() throws Exception {
- return r.call();
- }
- };
- }
-
- /**
- * Run the operation and pass the result to the callback.
- *
- * @param callback callback to receive the result of {@link #call()}.
- */
- public final void to(AsyncCallback<T> callback) {
- try {
- final T r = call();
- if (r != null) {
- callback.onSuccess(r);
- }
- } catch (NoSuchProjectException | NoSuchChangeException | NoSuchRefException e) {
- callback.onFailure(new NoSuchEntityException());
-
- } catch (OrmException e) {
- if (e.getCause() instanceof NoSuchEntityException) {
- callback.onFailure(e.getCause());
-
- } else {
- callback.onFailure(e);
- }
- } catch (Exception e) {
- callback.onFailure(e);
- }
- }
-
- /**
- * Compute the operation result.
- *
- * @return the result of the operation. Return {@link VoidResult#INSTANCE} if there is no
- * meaningful return value for the operation.
- * @throws Exception the operation failed. The caller will log the exception and the stack trace,
- * if it is worth logging on the server side.
- */
- @Override
- public abstract T call() throws Exception;
-}
diff --git a/java/com/google/gerrit/httpd/rpc/RpcServletModule.java b/java/com/google/gerrit/httpd/rpc/RpcServletModule.java
deleted file mode 100644
index b03609ebf8..0000000000
--- a/java/com/google/gerrit/httpd/rpc/RpcServletModule.java
+++ /dev/null
@@ -1,48 +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.httpd.rpc;
-
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.inject.Key;
-import com.google.inject.Scopes;
-import com.google.inject.internal.UniqueAnnotations;
-import com.google.inject.servlet.ServletModule;
-
-/** Binds {@link RemoteJsonService} implementations to a JSON servlet. */
-public abstract class RpcServletModule extends ServletModule {
- public static final String PREFIX = "/gerrit_ui/rpc/";
-
- private final String prefix;
-
- protected RpcServletModule(String pathPrefix) {
- prefix = pathPrefix;
- }
-
- protected void rpc(Class<? extends RemoteJsonService> clazz) {
- String name = clazz.getSimpleName();
- if (name.endsWith("Impl")) {
- name = name.substring(0, name.length() - 4);
- }
- rpc(name, clazz);
- }
-
- protected void rpc(String name, Class<? extends RemoteJsonService> clazz) {
- final Key<GerritJsonServlet> srv = Key.get(GerritJsonServlet.class, UniqueAnnotations.create());
- final GerritJsonServletProvider provider = new GerritJsonServletProvider(clazz);
- bind(clazz);
- serve(prefix + name).with(srv);
- bind(srv).toProvider(provider).in(Scopes.SINGLETON);
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java b/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java
deleted file mode 100644
index 634e8d8502..0000000000
--- a/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java
+++ /dev/null
@@ -1,68 +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.httpd.rpc;
-
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.SshHostKey;
-import com.google.gerrit.common.data.SystemInfoService;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.VoidResult;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.jcraft.jsch.HostKey;
-import com.jcraft.jsch.JSch;
-import java.util.ArrayList;
-import java.util.List;
-import javax.servlet.http.HttpServletRequest;
-
-class SystemInfoServiceImpl implements SystemInfoService {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final JSch JSCH = new JSch();
-
- private final List<HostKey> hostKeys;
- private final Provider<HttpServletRequest> httpRequest;
-
- @Inject
- SystemInfoServiceImpl(SshInfo daemon, Provider<HttpServletRequest> hsr) {
- hostKeys = daemon.getHostKeys();
- httpRequest = hsr;
- }
-
- @Override
- public void daemonHostKeys(AsyncCallback<List<SshHostKey>> callback) {
- final ArrayList<SshHostKey> r = new ArrayList<>(hostKeys.size());
- for (HostKey hk : hostKeys) {
- String host = hk.getHost();
- if (host.startsWith("*:")) {
- final String port = host.substring(2);
- host = "[" + httpRequest.get().getServerName() + "]:" + port;
- }
- final String fp = hk.getFingerPrint(JSCH);
- r.add(new SshHostKey(host, hk.getType() + " " + hk.getKey(), fp));
- }
- callback.onSuccess(r);
- }
-
- @Override
- public void clientError(String message, AsyncCallback<VoidResult> callback) {
- HttpServletRequest r = httpRequest.get();
- String ua = r.getHeader("User-Agent");
- message = message.replaceAll("\n", "\n ");
- logger.atSevere().log("Client UI JavaScript error: User-Agent=%s: %s", ua, message);
- callback.onSuccess(VoidResult.INSTANCE);
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/UiRpcModule.java b/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
deleted file mode 100644
index 9aab920a62..0000000000
--- a/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
+++ /dev/null
@@ -1,31 +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.httpd.rpc;
-
-import com.google.gerrit.httpd.rpc.project.ProjectModule;
-
-/** Registers servlets to answer RPCs from client UI. */
-public class UiRpcModule extends RpcServletModule {
- public UiRpcModule() {
- super(PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- rpc(SystemInfoServiceImpl.class);
-
- install(new ProjectModule());
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
deleted file mode 100644
index 24efb86592..0000000000
--- a/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2010 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.rpc.project;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.ProjectAccess;
-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.CreateGroupPermissionSyncer;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-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.project.ContributorAgreementsChecker;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.restapi.project.SetParent;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-
-class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
- interface Factory {
- ChangeProjectAccess create(
- @Assisted("projectName") Project.NameKey projectName,
- @Nullable @Assisted ObjectId base,
- @Assisted List<AccessSection> sectionList,
- @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
- @Nullable @Assisted String message);
- }
-
- private final GitReferenceUpdated gitRefUpdated;
- private final ProjectAccessFactory.Factory projectAccessFactory;
- private final ProjectCache projectCache;
- private final CreateGroupPermissionSyncer createGroupPermissionSyncer;
-
- @Inject
- ChangeProjectAccess(
- ProjectAccessFactory.Factory projectAccessFactory,
- ProjectCache projectCache,
- GroupBackend groupBackend,
- MetaDataUpdate.User metaDataUpdateFactory,
- AllProjectsName allProjects,
- Provider<SetParent> setParent,
- GitReferenceUpdated gitRefUpdated,
- ContributorAgreementsChecker contributorAgreements,
- Provider<CurrentUser> user,
- PermissionBackend permissionBackend,
- CreateGroupPermissionSyncer createGroupPermissionSyncer,
- @Assisted("projectName") Project.NameKey projectName,
- @Nullable @Assisted ObjectId base,
- @Assisted List<AccessSection> sectionList,
- @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
- @Nullable @Assisted String message) {
- super(
- groupBackend,
- metaDataUpdateFactory,
- allProjects,
- setParent,
- user.get(),
- projectName,
- base,
- sectionList,
- parentProjectName,
- message,
- contributorAgreements,
- permissionBackend,
- true);
- this.projectAccessFactory = projectAccessFactory;
- this.projectCache = projectCache;
- this.gitRefUpdated = gitRefUpdated;
- this.createGroupPermissionSyncer = createGroupPermissionSyncer;
- }
-
- @Override
- protected ProjectAccess updateProjectConfig(
- ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
- throws IOException, NoSuchProjectException, ConfigInvalidException,
- PermissionBackendException, ResourceConflictException {
- RevCommit commit = config.commit(md);
-
- gitRefUpdated.fire(
- config.getProject().getNameKey(),
- RefNames.REFS_CONFIG,
- base,
- commit.getId(),
- user.asIdentifiedUser().state());
-
- projectCache.evict(config.getProject());
- createGroupPermissionSyncer.syncIfNeeded();
- return projectAccessFactory.create(projectName).call();
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
deleted file mode 100644
index 3afb0e2004..0000000000
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
+++ /dev/null
@@ -1,295 +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.httpd.rpc.project;
-
-import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER;
-import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
-import static com.google.gerrit.server.permissions.RefPermission.READ;
-import static com.google.gerrit.server.permissions.RefPermission.WRITE_CONFIG;
-
-import com.google.common.collect.Maps;
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupInfo;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.data.ProjectAccess;
-import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gerrit.common.data.WebLinkInfoCommon;
-import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.httpd.rpc.Handler;
-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.WebLinks;
-import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.config.AllProjectsName;
-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.ProjectPermission;
-import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-class ProjectAccessFactory extends Handler<ProjectAccess> {
- interface Factory {
- ProjectAccessFactory create(@Assisted Project.NameKey name);
- }
-
- private final GroupBackend groupBackend;
- private final ProjectCache projectCache;
- private final PermissionBackend permissionBackend;
- private final GroupControl.Factory groupControlFactory;
- private final MetaDataUpdate.Server metaDataUpdateFactory;
- private final AllProjectsName allProjectsName;
-
- private final Project.NameKey projectName;
- private WebLinks webLinks;
-
- @Inject
- ProjectAccessFactory(
- GroupBackend groupBackend,
- ProjectCache projectCache,
- PermissionBackend permissionBackend,
- GroupControl.Factory groupControlFactory,
- MetaDataUpdate.Server metaDataUpdateFactory,
- AllProjectsName allProjectsName,
- WebLinks webLinks,
- @Assisted final Project.NameKey name) {
- this.groupBackend = groupBackend;
- this.projectCache = projectCache;
- this.permissionBackend = permissionBackend;
- this.groupControlFactory = groupControlFactory;
- this.metaDataUpdateFactory = metaDataUpdateFactory;
- this.allProjectsName = allProjectsName;
- this.webLinks = webLinks;
-
- this.projectName = name;
- }
-
- @Override
- public ProjectAccess call()
- throws NoSuchProjectException, IOException, ConfigInvalidException,
- PermissionBackendException, ResourceConflictException {
- ProjectState projectState = checkProjectState();
-
- // Load the current configuration from the repository, ensuring its the most
- // recent version available. If it differs from what was in the project
- // state, force a cache flush now.
- //
- ProjectConfig config;
- try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
- config = ProjectConfig.read(md);
- if (config.updateGroupNames(groupBackend)) {
- md.setMessage("Update group names\n");
- config.commit(md);
- projectCache.evict(config.getProject());
- projectState = checkProjectState();
- } else if (config.getRevision() != null
- && !config.getRevision().equals(projectState.getConfig().getRevision())) {
- projectCache.evict(config.getProject());
- projectState = checkProjectState();
- }
- }
-
- // The following implementation must match the GetAccess REST API endpoint.
-
- List<AccessSection> local = new ArrayList<>();
- Set<String> ownerOf = new HashSet<>();
- Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
- PermissionBackend.ForProject perm = permissionBackend.currentUser().project(projectName);
- boolean checkReadConfig = check(perm, RefNames.REFS_CONFIG, READ);
- boolean canWriteProjectConfig = true;
- try {
- perm.check(ProjectPermission.WRITE_CONFIG);
- } catch (AuthException e) {
- canWriteProjectConfig = false;
- }
-
- for (AccessSection section : config.getAccessSections()) {
- String name = section.getName();
- if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (canWriteProjectConfig) {
- local.add(section);
- ownerOf.add(name);
-
- } else if (checkReadConfig) {
- local.add(section);
- }
-
- } else if (RefConfigSection.isValid(name)) {
- if (check(perm, name, WRITE_CONFIG)) {
- local.add(section);
- ownerOf.add(name);
-
- } else if (checkReadConfig) {
- local.add(section);
-
- } else if (check(perm, name, READ)) {
- // Filter the section to only add rules describing groups that
- // are visible to the current-user. This includes any group the
- // user is a member of, as well as groups they own or that
- // are visible to all users.
-
- AccessSection dst = null;
- for (Permission srcPerm : section.getPermissions()) {
- Permission dstPerm = null;
-
- for (PermissionRule srcRule : srcPerm.getRules()) {
- AccountGroup.UUID group = srcRule.getGroup().getUUID();
- if (group == null) {
- continue;
- }
-
- Boolean canSeeGroup = visibleGroups.get(group);
- if (canSeeGroup == null) {
- try {
- canSeeGroup = groupControlFactory.controlFor(group).isVisible();
- } catch (NoSuchGroupException e) {
- canSeeGroup = Boolean.FALSE;
- }
- visibleGroups.put(group, canSeeGroup);
- }
-
- if (canSeeGroup) {
- if (dstPerm == null) {
- if (dst == null) {
- dst = new AccessSection(name);
- local.add(dst);
- }
- dstPerm = dst.getPermission(srcPerm.getName(), true);
- }
- dstPerm.add(srcRule);
- }
- }
- }
- }
- }
- }
-
- if (ownerOf.isEmpty() && isAdmin()) {
- // Special case: If the section list is empty, this project has no current
- // access control information. Fall back to site administrators.
- ownerOf.add(AccessSection.ALL);
- }
-
- final ProjectAccess detail = new ProjectAccess();
- detail.setProjectName(projectName);
-
- if (config.getRevision() != null) {
- detail.setRevision(config.getRevision().name());
- }
-
- detail.setInheritsFrom(config.getProject().getParent(allProjectsName));
-
- if (projectName.equals(allProjectsName)
- && permissionBackend.currentUser().testOrFalse(ADMINISTRATE_SERVER)) {
- ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
- }
-
- detail.setLocal(local);
- detail.setOwnerOf(ownerOf);
- detail.setCanUpload(
- canWriteProjectConfig
- || (checkReadConfig
- && perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE)
- && projectState.statePermitsWrite()));
- detail.setConfigVisible(canWriteProjectConfig || checkReadConfig);
- detail.setGroupInfo(buildGroupInfo(local));
- detail.setLabelTypes(projectState.getLabelTypes());
- detail.setFileHistoryLinks(getConfigFileLogLinks(projectName.get()));
- return detail;
- }
-
- private List<WebLinkInfoCommon> getConfigFileLogLinks(String projectName) {
- List<WebLinkInfoCommon> links =
- webLinks.getFileHistoryLinks(
- projectName, RefNames.REFS_CONFIG, ProjectConfig.PROJECT_CONFIG);
- return links.isEmpty() ? null : links;
- }
-
- private Map<AccountGroup.UUID, GroupInfo> buildGroupInfo(List<AccessSection> local) {
- Map<AccountGroup.UUID, GroupInfo> infos = new HashMap<>();
- for (AccessSection section : local) {
- for (Permission permission : section.getPermissions()) {
- for (PermissionRule rule : permission.getRules()) {
- if (rule.getGroup() != null) {
- AccountGroup.UUID uuid = rule.getGroup().getUUID();
- if (uuid != null && !infos.containsKey(uuid)) {
- GroupDescription.Basic group = groupBackend.get(uuid);
- infos.put(uuid, group != null ? new GroupInfo(group) : null);
- }
- }
- }
- }
- }
- return Maps.filterEntries(infos, in -> in.getValue() != null);
- }
-
- private ProjectState checkProjectState()
- throws NoSuchProjectException, IOException, PermissionBackendException,
- ResourceConflictException {
- ProjectState state = projectCache.checkedGet(projectName);
- // Hidden projects(permitsRead = false) should only be accessible by the project owners.
- // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
- // be allowed for other users). Allowing project owners to access here will help them to view
- // and update the config of hidden projects easily.
- ProjectPermission permissionToCheck =
- state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG;
- try {
- permissionBackend.currentUser().project(projectName).check(permissionToCheck);
- } catch (AuthException e) {
- throw new NoSuchProjectException(projectName, e);
- }
- state.checkStatePermitsRead();
- return state;
- }
-
- private static boolean check(PermissionBackend.ForProject ctx, String ref, RefPermission perm)
- throws PermissionBackendException {
- try {
- ctx.ref(ref).check(perm);
- return true;
- } catch (AuthException denied) {
- return false;
- }
- }
-
- private boolean isAdmin() throws PermissionBackendException {
- try {
- permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
- return true;
- } catch (AuthException e) {
- return false;
- }
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
deleted file mode 100644
index 552e712f6c..0000000000
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright (C) 2012 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.rpc.project;
-
-import static com.google.gerrit.common.ProjectAccessUtil.mergeSections;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.MoreObjects;
-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.common.errors.InvalidNameException;
-import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.common.errors.UpdateParentFailedException;
-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.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.account.GroupBackends;
-import com.google.gerrit.server.config.AllProjectsName;
-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.permissions.RefPermission;
-import com.google.gerrit.server.project.ContributorAgreementsChecker;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gerrit.server.restapi.project.SetParent;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
-
-public abstract class ProjectAccessHandler<T> extends Handler<T> {
-
- protected final GroupBackend groupBackend;
- protected final Project.NameKey projectName;
- protected final ObjectId base;
- protected final CurrentUser user;
-
- private final MetaDataUpdate.User metaDataUpdateFactory;
- private final AllProjectsName allProjects;
- private final Provider<SetParent> setParent;
- private final ContributorAgreementsChecker contributorAgreements;
- private final PermissionBackend permissionBackend;
- private final Project.NameKey parentProjectName;
-
- protected String message;
-
- private List<AccessSection> sectionList;
- private boolean checkIfOwner;
- private Boolean canWriteConfig;
-
- protected ProjectAccessHandler(
- GroupBackend groupBackend,
- MetaDataUpdate.User metaDataUpdateFactory,
- AllProjectsName allProjects,
- Provider<SetParent> setParent,
- CurrentUser user,
- Project.NameKey projectName,
- ObjectId base,
- List<AccessSection> sectionList,
- Project.NameKey parentProjectName,
- String message,
- ContributorAgreementsChecker contributorAgreements,
- PermissionBackend permissionBackend,
- boolean checkIfOwner) {
- this.groupBackend = groupBackend;
- this.metaDataUpdateFactory = metaDataUpdateFactory;
- this.allProjects = allProjects;
- this.setParent = setParent;
- this.user = user;
-
- this.projectName = projectName;
- this.base = base;
- this.sectionList = sectionList;
- this.parentProjectName = parentProjectName;
- this.message = message;
- this.contributorAgreements = contributorAgreements;
- this.permissionBackend = permissionBackend;
- this.checkIfOwner = checkIfOwner;
- }
-
- @Override
- public final T call()
- throws NoSuchProjectException, IOException, ConfigInvalidException, InvalidNameException,
- NoSuchGroupException, OrmException, UpdateParentFailedException, AuthException,
- PermissionBackendException, ResourceConflictException {
- contributorAgreements.check(projectName, user);
-
- try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
- ProjectConfig config = ProjectConfig.read(md, base);
- Set<String> toDelete = scanSectionNames(config);
- PermissionBackend.ForProject forProject = permissionBackend.user(user).project(projectName);
-
- for (AccessSection section : mergeSections(sectionList)) {
- String name = section.getName();
-
- if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (checkIfOwner && !canWriteConfig()) {
- continue;
- }
- replace(config, toDelete, section);
-
- } else if (AccessSection.isValid(name)) {
- if (checkIfOwner) {
- try {
- forProject.ref(name).check(RefPermission.WRITE_CONFIG);
- } catch (AuthException e) {
- continue;
- }
- }
-
- RefPattern.validate(name);
-
- replace(config, toDelete, section);
- }
- }
-
- for (String name : toDelete) {
- if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (!checkIfOwner || canWriteConfig()) {
- config.remove(config.getAccessSection(name));
- }
-
- } else if (!checkIfOwner) {
- config.remove(config.getAccessSection(name));
- } else {
- try {
- forProject.ref(name).check(RefPermission.WRITE_CONFIG);
- config.remove(config.getAccessSection(name));
- } catch (AuthException e) {
- // Do nothing.
- }
- }
- }
-
- boolean parentProjectUpdate = false;
- if (!config.getProject().getNameKey().equals(allProjects)
- && !config.getProject().getParent(allProjects).equals(parentProjectName)) {
- parentProjectUpdate = true;
- try {
- setParent
- .get()
- .validateParentUpdate(
- projectName,
- user.asIdentifiedUser(),
- MoreObjects.firstNonNull(parentProjectName, allProjects).get(),
- checkIfOwner);
- } catch (AuthException e) {
- throw new UpdateParentFailedException(
- "You are not allowed to change the parent project since you are "
- + "not an administrator. You may save the modifications for review "
- + "so that an administrator can approve them.",
- e);
- } catch (ResourceConflictException | UnprocessableEntityException | BadRequestException e) {
- throw new UpdateParentFailedException(e.getMessage(), e);
- }
- config.getProject().setParentName(parentProjectName);
- }
-
- if (message != null && !message.isEmpty()) {
- if (!message.endsWith("\n")) {
- message += "\n";
- }
- md.setMessage(message);
- } else {
- md.setMessage("Modify access rules\n");
- }
-
- return updateProjectConfig(config, md, parentProjectUpdate);
- } catch (RepositoryNotFoundException notFound) {
- throw new NoSuchProjectException(projectName, notFound);
- }
- }
-
- protected abstract T updateProjectConfig(
- ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
- throws IOException, NoSuchProjectException, ConfigInvalidException, OrmException,
- AuthException, PermissionBackendException, ResourceConflictException;
-
- private void replace(ProjectConfig config, Set<String> toDelete, AccessSection section)
- throws NoSuchGroupException {
- for (Permission permission : section.getPermissions()) {
- for (PermissionRule rule : permission.getRules()) {
- lookupGroup(rule);
- }
- }
- config.replace(section);
- toDelete.remove(section.getName());
- }
-
- private static Set<String> scanSectionNames(ProjectConfig config) {
- Set<String> names = new HashSet<>();
- for (AccessSection section : config.getAccessSections()) {
- names.add(section.getName());
- }
- return names;
- }
-
- private void lookupGroup(PermissionRule rule) throws NoSuchGroupException {
- GroupReference ref = rule.getGroup();
- if (ref.getUUID() == null) {
- final GroupReference group = GroupBackends.findBestSuggestion(groupBackend, ref.getName());
- if (group == null) {
- throw new NoSuchGroupException(ref.getName());
- }
- ref.setUUID(group.getUUID());
- }
- }
-
- /** Provide a local cache for {@code ProjectPermission.WRITE_CONFIG} capability. */
- private boolean canWriteConfig() throws PermissionBackendException {
- requireNonNull(user);
-
- if (canWriteConfig != null) {
- return canWriteConfig;
- }
- try {
- permissionBackend.user(user).project(projectName).check(ProjectPermission.WRITE_CONFIG);
- canWriteConfig = true;
- } catch (AuthException e) {
- canWriteConfig = false;
- }
- return canWriteConfig;
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
deleted file mode 100644
index da471c3f4b..0000000000
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
+++ /dev/null
@@ -1,80 +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.httpd.rpc.project;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.ProjectAccess;
-import com.google.gerrit.common.data.ProjectAdminService;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.inject.Inject;
-import java.util.List;
-import org.eclipse.jgit.lib.ObjectId;
-
-class ProjectAdminServiceImpl implements ProjectAdminService {
- private final ChangeProjectAccess.Factory changeProjectAccessFactory;
- private final ReviewProjectAccess.Factory reviewProjectAccessFactory;
- private final ProjectAccessFactory.Factory projectAccessFactory;
-
- @Inject
- ProjectAdminServiceImpl(
- final ChangeProjectAccess.Factory changeProjectAccessFactory,
- final ReviewProjectAccess.Factory reviewProjectAccessFactory,
- final ProjectAccessFactory.Factory projectAccessFactory) {
- this.changeProjectAccessFactory = changeProjectAccessFactory;
- this.reviewProjectAccessFactory = reviewProjectAccessFactory;
- this.projectAccessFactory = projectAccessFactory;
- }
-
- @Override
- public void projectAccess(
- final Project.NameKey projectName, AsyncCallback<ProjectAccess> callback) {
- projectAccessFactory.create(projectName).to(callback);
- }
-
- private static ObjectId getBase(String baseRevision) {
- if (baseRevision != null && !baseRevision.isEmpty()) {
- return ObjectId.fromString(baseRevision);
- }
- return null;
- }
-
- @Override
- public void changeProjectAccess(
- Project.NameKey projectName,
- String baseRevision,
- String msg,
- List<AccessSection> sections,
- Project.NameKey parentProjectName,
- AsyncCallback<ProjectAccess> cb) {
- changeProjectAccessFactory
- .create(projectName, getBase(baseRevision), sections, parentProjectName, msg)
- .to(cb);
- }
-
- @Override
- public void reviewProjectAccess(
- Project.NameKey projectName,
- String baseRevision,
- String msg,
- List<AccessSection> sections,
- Project.NameKey parentProjectName,
- AsyncCallback<Change.Id> cb) {
- reviewProjectAccessFactory
- .create(projectName, getBase(baseRevision), sections, parentProjectName, msg)
- .to(cb);
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
deleted file mode 100644
index 3d7d80fea2..0000000000
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
+++ /dev/null
@@ -1,39 +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.httpd.rpc.project;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.httpd.rpc.RpcServletModule;
-import com.google.gerrit.httpd.rpc.UiRpcModule;
-
-public class ProjectModule extends RpcServletModule {
- public ProjectModule() {
- super(UiRpcModule.PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- install(
- new FactoryModule() {
- @Override
- protected void configure() {
- factory(ChangeProjectAccess.Factory.class);
- factory(ReviewProjectAccess.Factory.class);
- factory(ProjectAccessFactory.Factory.class);
- }
- });
- rpc(ProjectAdminServiceImpl.class);
- }
-}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
deleted file mode 100644
index 6a43a1d1d3..0000000000
--- a/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright (C) 2012 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.rpc.project;
-
-import com.google.common.base.Throwables;
-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.PermissionRule;
-import com.google.gerrit.extensions.api.changes.AddReviewerInput;
-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.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.change.ChangeInserter;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-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.permissions.RefPermission;
-import com.google.gerrit.server.project.ContributorAgreementsChecker;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.restapi.change.ChangesCollection;
-import com.google.gerrit.server.restapi.change.PostReviewers;
-import com.google.gerrit.server.restapi.project.SetParent;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gerrit.server.update.UpdateException;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-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.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
- interface Factory {
- ReviewProjectAccess create(
- @Assisted("projectName") Project.NameKey projectName,
- @Nullable @Assisted ObjectId base,
- @Assisted List<AccessSection> sectionList,
- @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
- @Nullable @Assisted String message);
- }
-
- private final ReviewDb db;
- private final PermissionBackend permissionBackend;
- private final Sequences seq;
- private final Provider<PostReviewers> reviewersProvider;
- private final ProjectCache projectCache;
- private final ChangesCollection changes;
- private final ChangeInserter.Factory changeInserterFactory;
- private final BatchUpdate.Factory updateFactory;
- private final boolean allowProjectOwnersToChangeParent;
-
- @Inject
- ReviewProjectAccess(
- PermissionBackend permissionBackend,
- GroupBackend groupBackend,
- MetaDataUpdate.User metaDataUpdateFactory,
- ReviewDb db,
- Provider<PostReviewers> reviewersProvider,
- ProjectCache projectCache,
- AllProjectsName allProjects,
- ChangesCollection changes,
- ChangeInserter.Factory changeInserterFactory,
- BatchUpdate.Factory updateFactory,
- Provider<SetParent> setParent,
- Sequences seq,
- ContributorAgreementsChecker contributorAgreements,
- Provider<CurrentUser> user,
- @GerritServerConfig Config config,
- @Assisted("projectName") Project.NameKey projectName,
- @Nullable @Assisted ObjectId base,
- @Assisted List<AccessSection> sectionList,
- @Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
- @Nullable @Assisted String message) {
- super(
- groupBackend,
- metaDataUpdateFactory,
- allProjects,
- setParent,
- user.get(),
- projectName,
- base,
- sectionList,
- parentProjectName,
- message,
- contributorAgreements,
- permissionBackend,
- false);
- this.db = db;
- this.permissionBackend = permissionBackend;
- this.seq = seq;
- this.reviewersProvider = reviewersProvider;
- this.projectCache = projectCache;
- this.changes = changes;
- this.changeInserterFactory = changeInserterFactory;
- this.updateFactory = updateFactory;
- this.allowProjectOwnersToChangeParent =
- config.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
- }
-
- // TODO(dborowitz): Hack MetaDataUpdate so it can be created within a BatchUpdate and we can avoid
- // calling setUpdateRef(false).
- @SuppressWarnings("deprecation")
- @Override
- protected Change.Id updateProjectConfig(
- ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
- throws IOException, OrmException, AuthException, PermissionBackendException,
- ConfigInvalidException, ResourceConflictException {
- PermissionBackend.ForProject perm = permissionBackend.user(user).project(config.getName());
- if (!check(perm, ProjectPermission.READ_CONFIG)) {
- throw new AuthException(RefNames.REFS_CONFIG + " not visible");
- }
-
- if (!check(perm, ProjectPermission.WRITE_CONFIG)
- && !check(perm.ref(RefNames.REFS_CONFIG), RefPermission.CREATE_CHANGE)) {
- throw new AuthException("cannot create change for " + RefNames.REFS_CONFIG);
- }
-
- projectCache.checkedGet(config.getName()).checkStatePermitsWrite();
-
- md.setInsertChangeId(true);
- Change.Id changeId = new Change.Id(seq.nextChangeId());
- RevCommit commit =
- config.commitToNewRef(
- md, new PatchSet.Id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
- if (commit.getId().equals(base)) {
- return null;
- }
-
- try (ObjectInserter objInserter = md.getRepository().newObjectInserter();
- ObjectReader objReader = objInserter.newReader();
- RevWalk rw = new RevWalk(objReader);
- BatchUpdate bu =
- updateFactory.create(db, config.getProject().getNameKey(), user, TimeUtil.nowTs())) {
- bu.setRepository(md.getRepository(), rw, objInserter);
- bu.insertChange(
- changeInserterFactory
- .create(changeId, commit, RefNames.REFS_CONFIG)
- .setValidate(false)
- .setUpdateRef(false)); // Created by commitToNewRef.
- bu.execute();
- } catch (UpdateException | RestApiException e) {
- throw new IOException(e);
- }
-
- ChangeResource rsrc;
- try {
- rsrc = changes.parse(changeId);
- } catch (RestApiException e) {
- throw new IOException(e);
- }
- addProjectOwnersAsReviewers(rsrc);
- if (parentProjectUpdate && !allowProjectOwnersToChangeParent) {
- addAdministratorsAsReviewers(rsrc);
- }
- return changeId;
- }
-
- private void addProjectOwnersAsReviewers(ChangeResource rsrc) {
- final String projectOwners = groupBackend.get(SystemGroupBackend.PROJECT_OWNERS).getName();
- try {
- AddReviewerInput input = new AddReviewerInput();
- input.reviewer = projectOwners;
- reviewersProvider.get().apply(rsrc, input);
- } catch (Exception e) {
- // one of the owner groups is not visible to the user and this it why it
- // can't be added as reviewer
- Throwables.throwIfUnchecked(e);
- }
- }
-
- private void addAdministratorsAsReviewers(ChangeResource rsrc) {
- List<PermissionRule> adminRules =
- projectCache
- .getAllProjects()
- .getConfig()
- .getAccessSection(AccessSection.GLOBAL_CAPABILITIES)
- .getPermission(GlobalCapability.ADMINISTRATE_SERVER)
- .getRules();
- for (PermissionRule r : adminRules) {
- try {
- AddReviewerInput input = new AddReviewerInput();
- input.reviewer = r.getGroup().getUUID().get();
- reviewersProvider.get().apply(rsrc, input);
- } catch (Exception e) {
- // ignore
- Throwables.throwIfUnchecked(e);
- }
- }
- }
-
- private boolean check(PermissionBackend.ForRef perm, RefPermission p)
- throws PermissionBackendException {
- try {
- perm.check(p);
- return true;
- } catch (AuthException denied) {
- return false;
- }
- }
-
- private boolean check(PermissionBackend.ForProject perm, ProjectPermission p)
- throws PermissionBackendException {
- try {
- perm.check(p);
- return true;
- } catch (AuthException denied) {
- return false;
- }
- }
-}
diff --git a/java/com/google/gerrit/index/BUILD b/java/com/google/gerrit/index/BUILD
index 13f07e1ca4..55c7746924 100644
--- a/java/com/google/gerrit/index/BUILD
+++ b/java/com/google/gerrit/index/BUILD
@@ -22,13 +22,13 @@ java_library(
":query_exception",
"//antlr3:query_parser",
"//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
+ "//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:gwtjsonrpc",
- "//lib:gwtorm",
"//lib/antlr:java-runtime",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/index/FieldDef.java b/java/com/google/gerrit/index/FieldDef.java
index beb9c07ede..fb48104626 100644
--- a/java/com/google/gerrit/index/FieldDef.java
+++ b/java/com/google/gerrit/index/FieldDef.java
@@ -18,7 +18,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import com.google.common.base.CharMatcher;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
import java.io.IOException;
import java.sql.Timestamp;
@@ -60,7 +61,8 @@ public final class FieldDef<I, T> {
@FunctionalInterface
public interface Getter<I, T> {
- T get(I input) throws OrmException, IOException;
+ @Nullable
+ T get(I input) throws IOException;
}
public static class Builder<T> {
@@ -131,13 +133,13 @@ public final class FieldDef<I, T> {
*
* @param input input object.
* @return the field value(s) to index.
- * @throws OrmException
*/
- public T get(I input) throws OrmException {
+ @Nullable
+ public T get(I input) {
try {
return getter.get(input);
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/index/Index.java b/java/com/google/gerrit/index/Index.java
index 2d7e31e182..44f8b4236e 100644
--- a/java/com/google/gerrit/index/Index.java
+++ b/java/com/google/gerrit/index/Index.java
@@ -14,14 +14,13 @@
package com.google.gerrit.index;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.index.query.IndexPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryParseException;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.util.List;
import java.util.Optional;
/**
@@ -48,24 +47,18 @@ public interface Index<K, V> {
* searchers, but should be visible within a reasonable amount of time.
*
* @param obj document object
- * @throws IOException
*/
- void replace(V obj) throws IOException;
+ void replace(V obj);
/**
* Delete a document from the index by key.
*
* @param key document key
- * @throws IOException
*/
- void delete(K key) throws IOException;
+ void delete(K key);
- /**
- * Delete all documents from the index.
- *
- * @throws IOException
- */
- void deleteAll() throws IOException;
+ /** Delete all documents from the index. */
+ void deleteAll();
/**
* Convert the given operator predicate into a source searching the index and returning only the
@@ -91,20 +84,17 @@ public interface Index<K, V> {
* @param opts query options. Options that do not make sense in the context of a single document,
* such as start, will be ignored.
* @return a single document if present.
- * @throws IOException
*/
- default Optional<V> get(K key, QueryOptions opts) throws IOException {
+ default Optional<V> get(K key, QueryOptions opts) {
opts = opts.withStart(0).withLimit(2);
- List<V> results;
+ ImmutableList<V> results;
try {
results = getSource(keyPredicate(key), opts).read().toList();
} catch (QueryParseException e) {
- throw new IOException("Unexpected QueryParseException during get()", e);
- } catch (OrmException e) {
- throw new IOException(e);
+ throw new StorageException("Unexpected QueryParseException during get()", e);
}
if (results.size() > 1) {
- throw new IOException("Multiple results found in index for key " + key + ": " + results);
+ throw new StorageException("Multiple results found in index for key " + key + ": " + results);
}
return results.stream().findFirst();
}
@@ -116,20 +106,17 @@ public interface Index<K, V> {
* @param opts query options. Options that do not make sense in the context of a single document,
* such as start, will be ignored.
* @return an abstraction of a raw index document to retrieve fields from.
- * @throws IOException
*/
- default Optional<FieldBundle> getRaw(K key, QueryOptions opts) throws IOException {
+ default Optional<FieldBundle> getRaw(K key, QueryOptions opts) {
opts = opts.withStart(0).withLimit(2);
- List<FieldBundle> results;
+ ImmutableList<FieldBundle> results;
try {
results = getSource(keyPredicate(key), opts).readRaw().toList();
} catch (QueryParseException e) {
- throw new IOException("Unexpected QueryParseException during get()", e);
- } catch (OrmException e) {
- throw new IOException(e);
+ throw new StorageException("Unexpected QueryParseException during get()", e);
}
if (results.size() > 1) {
- throw new IOException("Multiple results found in index for key " + key + ": " + results);
+ throw new StorageException("Multiple results found in index for key " + key + ": " + results);
}
return results.stream().findFirst();
}
@@ -146,7 +133,6 @@ public interface Index<K, V> {
* Mark whether this index is up-to-date and ready to serve reads.
*
* @param ready whether the index is ready
- * @throws IOException
*/
- void markReady(boolean ready) throws IOException;
+ void markReady(boolean ready);
}
diff --git a/java/com/google/gerrit/index/IndexedQuery.java b/java/com/google/gerrit/index/IndexedQuery.java
deleted file mode 100644
index 143cc26a0e..0000000000
--- a/java/com/google/gerrit/index/IndexedQuery.java
+++ /dev/null
@@ -1,133 +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.index;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.index.query.DataSource;
-import com.google.gerrit.index.query.FieldBundle;
-import com.google.gerrit.index.query.IndexPredicate;
-import com.google.gerrit.index.query.Paginated;
-import com.google.gerrit.index.query.Predicate;
-import com.google.gerrit.index.query.QueryParseException;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Wrapper combining an {@link IndexPredicate} together with a {@link DataSource} that returns
- * matching results from the index.
- *
- * <p>Appropriate to return as the rootmost predicate that can be processed using the secondary
- * index; such predicates must also implement {@link DataSource} to be chosen by the query
- * processor.
- *
- * @param <I> The type of the IDs by which the entities are stored in the index.
- * @param <T> The type of the entities that are stored in the index.
- */
-public class IndexedQuery<I, T> extends Predicate<T> implements DataSource<T>, Paginated<T> {
- protected final Index<I, T> index;
-
- private QueryOptions opts;
- private final Predicate<T> pred;
- protected DataSource<T> source;
-
- public IndexedQuery(Index<I, T> index, Predicate<T> pred, QueryOptions opts)
- throws QueryParseException {
- this.index = index;
- this.opts = opts;
- this.pred = pred;
- this.source = index.getSource(pred, this.opts);
- }
-
- @Override
- public int getChildCount() {
- return 1;
- }
-
- @Override
- public Predicate<T> getChild(int i) {
- if (i == 0) {
- return pred;
- }
- throw new ArrayIndexOutOfBoundsException(i);
- }
-
- @Override
- public List<Predicate<T>> getChildren() {
- return ImmutableList.of(pred);
- }
-
- @Override
- public QueryOptions getOptions() {
- return opts;
- }
-
- @Override
- public int getCardinality() {
- return source != null ? source.getCardinality() : opts.limit();
- }
-
- @Override
- public ResultSet<T> read() throws OrmException {
- return source.read();
- }
-
- @Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
- return source.readRaw();
- }
-
- @Override
- public ResultSet<T> restart(int start) throws OrmException {
- opts = opts.withStart(start);
- try {
- source = index.getSource(pred, opts);
- } catch (QueryParseException e) {
- // Don't need to show this exception to the user; the only thing that
- // changed about pred was its start, and any other QPEs that might happen
- // should have already thrown from the constructor.
- throw new OrmException(e);
- }
- // Don't convert start to a limit, since the caller of this method (see
- // AndSource) has calculated the actual number to skip.
- return read();
- }
-
- @Override
- public Predicate<T> copy(Collection<? extends Predicate<T>> children) {
- return this;
- }
-
- @Override
- public int hashCode() {
- return pred.hashCode();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == null || getClass() != other.getClass()) {
- return false;
- }
- IndexedQuery<?, ?> o = (IndexedQuery<?, ?>) other;
- return pred.equals(o.pred) && opts.equals(o.opts);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper("index").add("p", pred).add("opts", opts).toString();
- }
-}
diff --git a/java/com/google/gerrit/index/Schema.java b/java/com/google/gerrit/index/Schema.java
index e5829c0aea..f65d03e438 100644
--- a/java/com/google/gerrit/index/Schema.java
+++ b/java/com/google/gerrit/index/Schema.java
@@ -21,7 +21,7 @@ import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.exceptions.StorageException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -168,13 +168,12 @@ public class Schema<T> {
Object v;
try {
v = f.get(obj);
- } catch (OrmException e) {
- // OrmException is thrown when the object is not found. On this case,
+ } catch (StorageException e) {
+ // StorageException is thrown when the object is not found. On this case,
// it is pointless to make further attempts for each field, so propagate
// the exception to return an empty list.
logger.atSevere().withCause(e).log("error getting field %s of %s", f.getName(), obj);
- throw new RuntimeException(
- e); // work around throwing checked exceptions from methods used in Streams
+ throw e;
} catch (RuntimeException e) {
logger.atSevere().withCause(e).log("error getting field %s of %s", f.getName(), obj);
return null;
@@ -202,11 +201,8 @@ public class Schema<T> {
.map(f -> fieldValues(obj, f))
.filter(Objects::nonNull)
.collect(toImmutableList());
- } catch (RuntimeException e) {
- if (e.getCause().getClass().equals(OrmException.class)) {
- return ImmutableList.of();
- }
- throw e;
+ } catch (StorageException e) {
+ return ImmutableList.of();
}
}
diff --git a/java/com/google/gerrit/index/project/IndexedProjectQuery.java b/java/com/google/gerrit/index/project/IndexedProjectQuery.java
index 4409ccbc34..383ba1c75d 100644
--- a/java/com/google/gerrit/index/project/IndexedProjectQuery.java
+++ b/java/com/google/gerrit/index/project/IndexedProjectQuery.java
@@ -15,9 +15,9 @@
package com.google.gerrit.index.project;
import com.google.gerrit.index.Index;
-import com.google.gerrit.index.IndexedQuery;
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;
diff --git a/java/com/google/gerrit/index/project/ProjectField.java b/java/com/google/gerrit/index/project/ProjectField.java
index 3bc028d1b8..119980c104 100644
--- a/java/com/google/gerrit/index/project/ProjectField.java
+++ b/java/com/google/gerrit/index/project/ProjectField.java
@@ -49,7 +49,7 @@ public class ProjectField {
exact("state").stored().build(p -> p.getProject().getState().name());
public static final FieldDef<ProjectData, Iterable<String>> ANCESTOR_NAME =
- exact("ancestor_name").buildRepeatable(p -> p.getParentNames());
+ exact("ancestor_name").buildRepeatable(ProjectData::getParentNames);
/**
* All values of all refs that were used in the course of indexing this document. This covers
diff --git a/java/com/google/gerrit/index/project/ProjectIndexer.java b/java/com/google/gerrit/index/project/ProjectIndexer.java
index 44dccfe8fa..1ca29f5580 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexer.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexer.java
@@ -15,7 +15,6 @@
package com.google.gerrit.index.project;
import com.google.gerrit.reviewdb.client.Project;
-import java.io.IOException;
public interface ProjectIndexer {
@@ -24,5 +23,5 @@ public interface ProjectIndexer {
*
* @param nameKey name key of project to index.
*/
- void index(Project.NameKey nameKey) throws IOException;
+ void index(Project.NameKey nameKey);
}
diff --git a/java/com/google/gerrit/index/query/AndPredicate.java b/java/com/google/gerrit/index/query/AndPredicate.java
index 7fba05fccf..ae13fb3f8b 100644
--- a/java/com/google/gerrit/index/query/AndPredicate.java
+++ b/java/com/google/gerrit/index/query/AndPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkState;
-import com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -82,7 +81,7 @@ public class AndPredicate<T> extends Predicate<T> implements Matchable<T> {
}
@Override
- public boolean match(T object) throws OrmException {
+ public boolean match(T object) {
for (Predicate<T> c : children) {
checkState(
c.isMatchable(),
diff --git a/java/com/google/gerrit/index/query/AndSource.java b/java/com/google/gerrit/index/query/AndSource.java
index d1e1c30b09..7d817d2b44 100644
--- a/java/com/google/gerrit/index/query/AndSource.java
+++ b/java/com/google/gerrit/index/query/AndSource.java
@@ -17,14 +17,10 @@ package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import com.google.gwtorm.server.ListResultSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.gwtorm.server.ResultSet;
+import com.google.gerrit.exceptions.StorageException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
@@ -78,26 +74,9 @@ public class AndSource<T> extends AndPredicate<T>
}
@Override
- public ResultSet<T> read() throws OrmException {
- try {
- return readImpl();
- } catch (OrmRuntimeException err) {
- if (err.getCause() != null) {
- Throwables.throwIfInstanceOf(err.getCause(), OrmException.class);
- }
- throw new OrmException(err);
- }
- }
-
- @Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
- // TOOD(hiesel): Implement
- throw new UnsupportedOperationException("not implemented");
- }
-
- private ResultSet<T> readImpl() throws OrmException {
+ public ResultSet<T> read() {
if (source == null) {
- throw new OrmException("No DataSource: " + this);
+ throw new StorageException("No DataSource: " + this);
}
List<T> r = new ArrayList<>();
T last = null;
@@ -144,12 +123,18 @@ public class AndSource<T> extends AndPredicate<T>
}
@Override
+ public ResultSet<FieldBundle> readRaw() {
+ // TOOD(hiesel): Implement
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
public boolean isMatchable() {
return isVisibleToPredicate != null || super.isMatchable();
}
@Override
- public boolean match(T object) throws OrmException {
+ public boolean match(T object) {
if (isVisibleToPredicate != null && !isVisibleToPredicate.match(object)) {
return false;
}
@@ -166,7 +151,7 @@ public class AndSource<T> extends AndPredicate<T>
.transformAndConcat(this::transformBuffer);
}
- protected List<T> transformBuffer(List<T> buffer) throws OrmRuntimeException {
+ protected List<T> transformBuffer(List<T> buffer) {
return buffer;
}
diff --git a/java/com/google/gerrit/index/query/DataSource.java b/java/com/google/gerrit/index/query/DataSource.java
index 88cc0e3c0e..2c2ba532df 100644
--- a/java/com/google/gerrit/index/query/DataSource.java
+++ b/java/com/google/gerrit/index/query/DataSource.java
@@ -14,16 +14,13 @@
package com.google.gerrit.index.query;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-
public interface DataSource<T> {
/** @return an estimate of the number of results from {@link #read()}. */
int getCardinality();
/** @return read from the database and return the results. */
- ResultSet<T> read() throws OrmException;
+ ResultSet<T> read();
/** @return read from the database and return the raw results. */
- ResultSet<FieldBundle> readRaw() throws OrmException;
+ ResultSet<FieldBundle> readRaw();
}
diff --git a/java/com/google/gerrit/index/query/IndexedQuery.java b/java/com/google/gerrit/index/query/IndexedQuery.java
new file mode 100644
index 0000000000..d9e33ea2dc
--- /dev/null
+++ b/java/com/google/gerrit/index/query/IndexedQuery.java
@@ -0,0 +1,128 @@
+// 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.index.query;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.index.Index;
+import com.google.gerrit.index.QueryOptions;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Wrapper combining an {@link IndexPredicate} together with a {@link DataSource} that returns
+ * matching results from the index.
+ *
+ * <p>Appropriate to return as the rootmost predicate that can be processed using the secondary
+ * index; such predicates must also implement {@link DataSource} to be chosen by the query
+ * processor.
+ *
+ * @param <I> The type of the IDs by which the entities are stored in the index.
+ * @param <T> The type of the entities that are stored in the index.
+ */
+public class IndexedQuery<I, T> extends Predicate<T> implements DataSource<T>, Paginated<T> {
+ protected final Index<I, T> index;
+
+ private QueryOptions opts;
+ private final Predicate<T> pred;
+ protected DataSource<T> source;
+
+ public IndexedQuery(Index<I, T> index, Predicate<T> pred, QueryOptions opts)
+ throws QueryParseException {
+ this.index = index;
+ this.opts = opts;
+ this.pred = pred;
+ this.source = index.getSource(pred, this.opts);
+ }
+
+ @Override
+ public int getChildCount() {
+ return 1;
+ }
+
+ @Override
+ public Predicate<T> getChild(int i) {
+ if (i == 0) {
+ return pred;
+ }
+ throw new ArrayIndexOutOfBoundsException(i);
+ }
+
+ @Override
+ public List<Predicate<T>> getChildren() {
+ return ImmutableList.of(pred);
+ }
+
+ @Override
+ public QueryOptions getOptions() {
+ return opts;
+ }
+
+ @Override
+ public int getCardinality() {
+ return source != null ? source.getCardinality() : opts.limit();
+ }
+
+ @Override
+ public ResultSet<T> read() {
+ return source.read();
+ }
+
+ @Override
+ public ResultSet<FieldBundle> readRaw() {
+ return source.readRaw();
+ }
+
+ @Override
+ public ResultSet<T> restart(int start) {
+ opts = opts.withStart(start);
+ try {
+ source = index.getSource(pred, opts);
+ } catch (QueryParseException e) {
+ // Don't need to show this exception to the user; the only thing that
+ // changed about pred was its start, and any other QPEs that might happen
+ // should have already thrown from the constructor.
+ throw new StorageException(e);
+ }
+ // Don't convert start to a limit, since the caller of this method (see
+ // AndSource) has calculated the actual number to skip.
+ return read();
+ }
+
+ @Override
+ public Predicate<T> copy(Collection<? extends Predicate<T>> children) {
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return pred.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ IndexedQuery<?, ?> o = (IndexedQuery<?, ?>) other;
+ return pred.equals(o.pred) && opts.equals(o.opts);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper("index").add("p", pred).add("opts", opts).toString();
+ }
+}
diff --git a/java/com/google/gerrit/index/query/IntegerRangePredicate.java b/java/com/google/gerrit/index/query/IntegerRangePredicate.java
index 66351a8f45..6780867fae 100644
--- a/java/com/google/gerrit/index/query/IntegerRangePredicate.java
+++ b/java/com/google/gerrit/index/query/IntegerRangePredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.index.query;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.query.RangeUtil.Range;
-import com.google.gwtorm.server.OrmException;
public abstract class IntegerRangePredicate<T> extends IndexPredicate<T> {
private final Range range;
@@ -30,9 +29,9 @@ public abstract class IntegerRangePredicate<T> extends IndexPredicate<T> {
}
}
- protected abstract Integer getValueInt(T object) throws OrmException;
+ protected abstract Integer getValueInt(T object);
- public boolean match(T object) throws OrmException {
+ public boolean match(T object) {
Integer valueInt = getValueInt(object);
if (valueInt == null) {
return false;
diff --git a/java/com/google/gerrit/index/query/InternalQuery.java b/java/com/google/gerrit/index/query/InternalQuery.java
index 3a4b372e36..48e214ee69 100644
--- a/java/com/google/gerrit/index/query/InternalQuery.java
+++ b/java/com/google/gerrit/index/query/InternalQuery.java
@@ -17,16 +17,18 @@ package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.stream.Collectors.toSet;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexCollection;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.Schema;
-import com.google.gwtorm.server.OrmException;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Supplier;
/**
* Execute a single query over a secondary index, for use by Gerrit internals.
@@ -38,7 +40,7 @@ import java.util.List;
* <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
* holding on to a single instance.
*/
-public class InternalQuery<T> {
+public class InternalQuery<T, Q extends InternalQuery<T, Q>> {
private final QueryProcessor<T> queryProcessor;
private final IndexCollection<?, T, ? extends Index<?, T>> indexes;
@@ -53,34 +55,48 @@ public class InternalQuery<T> {
this.indexConfig = indexConfig;
}
- public InternalQuery<T> setLimit(int n) {
+ @SuppressWarnings("unchecked")
+ protected final Q self() {
+ return (Q) this;
+ }
+
+ final Q setStart(int start) {
+ queryProcessor.setStart(start);
+ return self();
+ }
+
+ public final Q setLimit(int n) {
queryProcessor.setUserProvidedLimit(n);
- return this;
+ return self();
}
- public InternalQuery<T> enforceVisibility(boolean enforce) {
+ public final Q enforceVisibility(boolean enforce) {
queryProcessor.enforceVisibility(enforce);
- return this;
+ return self();
}
- @SuppressWarnings("unchecked") // Can't set @SafeVarargs on a non-final method.
- public InternalQuery<T> setRequestedFields(FieldDef<T, ?>... fields) {
+ @SafeVarargs
+ public final Q setRequestedFields(FieldDef<T, ?>... fields) {
checkArgument(fields.length > 0, "requested field list is empty");
queryProcessor.setRequestedFields(
Arrays.stream(fields).map(FieldDef::getName).collect(toSet()));
- return this;
+ return self();
}
- public InternalQuery<T> noFields() {
+ public final Q noFields() {
queryProcessor.setRequestedFields(ImmutableSet.of());
- return this;
+ return self();
}
- public List<T> query(Predicate<T> p) throws OrmException {
+ public final List<T> query(Predicate<T> p) {
+ return queryResults(p).entities();
+ }
+
+ final QueryResult<T> queryResults(Predicate<T> p) {
try {
- return queryProcessor.query(p).entities();
+ return queryProcessor.query(p);
} catch (QueryParseException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
@@ -94,16 +110,58 @@ public class InternalQuery<T> {
* @return results of the queries, one list of results per input query, in the same order as the
* input.
*/
- public List<List<T>> query(List<Predicate<T>> queries) throws OrmException {
+ public final List<List<T>> query(List<Predicate<T>> queries) {
try {
return Lists.transform(queryProcessor.query(queries), QueryResult::entities);
} catch (QueryParseException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
- protected Schema<T> schema() {
+ protected final Schema<T> schema() {
Index<?, T> index = indexes != null ? indexes.getSearchIndex() : null;
return index != null ? index.getSchema() : null;
}
+
+ /**
+ * Query a predicate repeatedly until all results are exhausted.
+ *
+ * <p>Capable of iterating through all results regardless of limits. The passed {@code
+ * querySupplier} may choose to pre-set limits or not; this only affects the number of queries
+ * that may be issued, not the size of the final results.
+ *
+ * <p>Since multiple queries may be issued, this method is subject to races when the result set
+ * changes mid-iteration. This may result in skipped results, if an entity gets modified to jump
+ * to the front of the list after this method has passed it. It may also result in duplicate
+ * results, if an entity at the end of one batch of results gets pushed back further, putting it
+ * at the beginning of the next batch. This race cannot be avoided unless we change the underlying
+ * index interface to support true continuation tokens.
+ *
+ * @param querySupplier supplier for queries. Callers will generally pass a lambda that invokes an
+ * underlying {@code Provider<InternalFooQuery>}, since the instances are not reusable. The
+ * lambda may also call additional methods on the newly-created query, such as {@link
+ * #enforceVisibility(boolean)}.
+ * @param predicate predicate to search for.
+ * @param <T> result type.
+ * @return exhaustive list of results, subject to the race condition described above.
+ */
+ protected static <T> ImmutableList<T> queryExhaustively(
+ Supplier<? extends InternalQuery<T, ?>> querySupplier, Predicate<T> predicate) {
+ ImmutableList.Builder<T> b = null;
+ int start = 0;
+ while (true) {
+ QueryResult<T> qr = querySupplier.get().setStart(start).queryResults(predicate);
+ if (b == null) {
+ if (!qr.more()) {
+ return qr.entities();
+ }
+ b = ImmutableList.builder();
+ }
+ b.addAll(qr.entities());
+ if (!qr.more()) {
+ return b.build();
+ }
+ start += qr.entities().size();
+ }
+ }
}
diff --git a/java/com/google/gerrit/index/query/ListResultSet.java b/java/com/google/gerrit/index/query/ListResultSet.java
new file mode 100644
index 0000000000..4cf48c8f1b
--- /dev/null
+++ b/java/com/google/gerrit/index/query/ListResultSet.java
@@ -0,0 +1,47 @@
+// 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.gerrit.index.query;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ListResultSet<T> implements ResultSet<T> {
+ private ImmutableList<T> items;
+
+ public ListResultSet(List<T> r) {
+ items = ImmutableList.copyOf(r);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return toList().iterator();
+ }
+
+ @Override
+ public ImmutableList<T> toList() {
+ if (items == null) {
+ throw new IllegalStateException("Results already obtained");
+ }
+ ImmutableList<T> r = items;
+ items = null;
+ return r;
+ }
+
+ @Override
+ public void close() {
+ items = null;
+ }
+}
diff --git a/java/com/google/gerrit/index/query/Matchable.java b/java/com/google/gerrit/index/query/Matchable.java
index 3d07943874..7a16ae8639 100644
--- a/java/com/google/gerrit/index/query/Matchable.java
+++ b/java/com/google/gerrit/index/query/Matchable.java
@@ -14,15 +14,9 @@
package com.google.gerrit.index.query;
-import com.google.gwtorm.server.OrmException;
-
public interface Matchable<T> {
- /**
- * Does this predicate match this object?
- *
- * @throws OrmException
- */
- boolean match(T object) throws OrmException;
+ /** Does this predicate match this object? */
+ boolean match(T object);
/** @return a cost estimate to run this predicate, higher figures cost more. */
int getCost();
diff --git a/java/com/google/gerrit/index/query/NotPredicate.java b/java/com/google/gerrit/index/query/NotPredicate.java
index 750759d24c..14cb740554 100644
--- a/java/com/google/gerrit/index/query/NotPredicate.java
+++ b/java/com/google/gerrit/index/query/NotPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkState;
-import com.google.gwtorm.server.OrmException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -64,7 +63,7 @@ public class NotPredicate<T> extends Predicate<T> implements Matchable<T> {
}
@Override
- public boolean match(T object) throws OrmException {
+ public boolean match(T object) {
checkState(
that.isMatchable(),
"match invoked, but child predicate %s doesn't implement %s",
diff --git a/java/com/google/gerrit/index/query/OrPredicate.java b/java/com/google/gerrit/index/query/OrPredicate.java
index 8c3ed1c596..9bc3769366 100644
--- a/java/com/google/gerrit/index/query/OrPredicate.java
+++ b/java/com/google/gerrit/index/query/OrPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkState;
-import com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -82,7 +81,7 @@ public class OrPredicate<T> extends Predicate<T> implements Matchable<T> {
}
@Override
- public boolean match(T object) throws OrmException {
+ public boolean match(T object) {
for (Predicate<T> c : children) {
checkState(
c.isMatchable(),
diff --git a/java/com/google/gerrit/index/query/Paginated.java b/java/com/google/gerrit/index/query/Paginated.java
index 20f65dcf06..e61dd53234 100644
--- a/java/com/google/gerrit/index/query/Paginated.java
+++ b/java/com/google/gerrit/index/query/Paginated.java
@@ -15,11 +15,9 @@
package com.google.gerrit.index.query;
import com.google.gerrit.index.QueryOptions;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
public interface Paginated<T> {
QueryOptions getOptions();
- ResultSet<T> restart(int start) throws OrmException;
+ ResultSet<T> restart(int start);
}
diff --git a/java/com/google/gerrit/index/query/Predicate.java b/java/com/google/gerrit/index/query/Predicate.java
index 53c92c9b37..b5ed82da81 100644
--- a/java/com/google/gerrit/index/query/Predicate.java
+++ b/java/com/google/gerrit/index/query/Predicate.java
@@ -14,6 +14,7 @@
package com.google.gerrit.index.query;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.Iterables;
@@ -82,6 +83,8 @@ public abstract class Predicate<T> {
/** Invert the passed node. */
public static <T> Predicate<T> not(Predicate<T> that) {
+ checkArgument(
+ !(that instanceof Any), "negating any() is unsafe because it post-filters all results");
if (that instanceof NotPredicate) {
// Negate of a negate is the original predicate.
//
diff --git a/java/com/google/gerrit/index/query/QueryBuilder.java b/java/com/google/gerrit/index/query/QueryBuilder.java
index c6c39c3fe0..d24cfebe2f 100644
--- a/java/com/google/gerrit/index/query/QueryBuilder.java
+++ b/java/com/google/gerrit/index/query/QueryBuilder.java
@@ -14,10 +14,12 @@
package com.google.gerrit.index.query;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.index.query.Predicate.and;
import static com.google.gerrit.index.query.Predicate.not;
import static com.google.gerrit.index.query.Predicate.or;
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;
import static com.google.gerrit.index.query.QueryParser.EXACT_PHRASE;
import static com.google.gerrit.index.query.QueryParser.FIELD_NAME;
@@ -25,7 +27,13 @@ import static com.google.gerrit.index.query.QueryParser.NOT;
import static com.google.gerrit.index.query.QueryParser.OR;
import static com.google.gerrit.index.query.QueryParser.SINGLE_WORD;
+import com.google.common.base.Ascii;
+import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -34,9 +42,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import org.antlr.runtime.tree.Tree;
/**
@@ -68,45 +74,60 @@ import org.antlr.runtime.tree.Tree;
* <p>Subclasses may also declare a handler for values which appear without operator by overriding
* {@link #defaultField(String)}.
*
+ * <p>Instances are non-singletons and should only be used once, in order to rescan the {@code
+ * DynamicMap} of plugin-provided operators on each query invocation.
+ *
* @param <T> type of object the predicates can evaluate in memory.
*/
-public abstract class QueryBuilder<T> {
+public abstract class QueryBuilder<T, Q extends QueryBuilder<T, Q>> {
/** Converts a value string passed to an operator into a {@link Predicate}. */
- public interface OperatorFactory<T, Q extends QueryBuilder<T>> {
+ public interface OperatorFactory<T, Q extends QueryBuilder<T, Q>> {
Predicate<T> create(Q builder, String value) throws QueryParseException;
}
/**
* Defines the operators known by a QueryBuilder.
*
- * <p>This class is thread-safe and may be reused or cached.
+ * <p>Operators are discovered by scanning for methods annotated with {@link Operator}. Operator
+ * methods must be public, non-abstract, return a {@code Predicate}, and take a single string as
+ * an argument.
+ *
+ * <p>This class is deeply immutable.
*
- * @param <T> type of object the predicates can evaluate in memory.
+ * @param <T> type of object the predicates can evaluate.
* @param <Q> type of the query builder subclass.
*/
- public static class Definition<T, Q extends QueryBuilder<T>> {
- private final Map<String, OperatorFactory<T, Q>> opFactories = new HashMap<>();
+ public static class Definition<T, Q extends QueryBuilder<T, Q>> {
+ private final ImmutableMap<String, OperatorFactory<T, Q>> opFactories;
- public Definition(Class<Q> clazz) {
- // Guess at the supported operators by scanning methods.
- //
+ public Definition(Class<? extends Q> clazz) {
+ ImmutableMap.Builder<String, OperatorFactory<T, Q>> b = ImmutableMap.builder();
Class<?> c = clazz;
while (c != QueryBuilder.class) {
for (Method method : c.getDeclaredMethods()) {
- if (method.getAnnotation(Operator.class) != null
- && Predicate.class.isAssignableFrom(method.getReturnType())
- && method.getParameterTypes().length == 1
- && method.getParameterTypes()[0] == String.class
- && (method.getModifiers() & Modifier.ABSTRACT) == 0
- && (method.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC) {
- final String name = method.getName().toLowerCase();
- if (!opFactories.containsKey(name)) {
- opFactories.put(name, new ReflectionFactory<T, Q>(name, method));
- }
+ if (method.getAnnotation(Operator.class) == null) {
+ continue;
}
+ checkArgument(
+ CharMatcher.ascii().matchesAllOf(method.getName()),
+ "method name must be ASCII: %s",
+ method.getName());
+ checkArgument(
+ Predicate.class.isAssignableFrom(method.getReturnType())
+ && method.getParameterTypes().length == 1
+ && method.getParameterTypes()[0] == String.class
+ && Modifier.isPublic(method.getModifiers())
+ && !Modifier.isAbstract(method.getModifiers()),
+ "method must be of the form \"@%s public Predicate<T> %s(String value)\": %s",
+ Operator.class.getSimpleName(),
+ method.getName(),
+ method);
+ String name = Ascii.toLowerCase(method.getName());
+ b.put(name, new ReflectionFactory<>(name, method));
}
c = c.getSuperclass();
}
+ opFactories = b.build();
}
}
@@ -161,14 +182,26 @@ public abstract class QueryBuilder<T> {
return null;
}
- protected final Definition<T, ? extends QueryBuilder<T>> builderDef;
+ protected final Definition<T, Q> builderDef;
+ private final ImmutableMap<String, OperatorFactory<T, Q>> opFactories;
- protected final Map<String, OperatorFactory<?, ?>> opFactories;
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- protected QueryBuilder(Definition<T, ? extends QueryBuilder<T>> def) {
+ protected QueryBuilder(
+ Definition<T, Q> def,
+ @Nullable DynamicMap<? extends OperatorFactory<T, Q>> dynamicOpFactories) {
builderDef = def;
- opFactories = (Map) def.opFactories;
+
+ if (dynamicOpFactories != null) {
+ ImmutableMap.Builder<String, OperatorFactory<T, Q>> opFactoriesBuilder =
+ ImmutableMap.builder();
+ opFactoriesBuilder.putAll(def.opFactories);
+ for (Extension<? extends OperatorFactory<T, Q>> e : dynamicOpFactories) {
+ String name = e.getExportName() + "_" + e.getPluginName();
+ opFactoriesBuilder.put(name, e.getProvider().get());
+ }
+ opFactories = opFactoriesBuilder.build();
+ } else {
+ opFactories = def.opFactories;
+ }
}
/**
@@ -214,44 +247,44 @@ public abstract class QueryBuilder<T> {
return not(toPredicate(onlyChildOf(r)));
case DEFAULT_FIELD:
- return defaultField(onlyChildOf(r));
+ return defaultField(concatenateChildText(r));
case FIELD_NAME:
- return operator(r.getText(), onlyChildOf(r));
+ return operator(r.getText(), concatenateChildText(r));
default:
throw error("Unsupported operator: " + r);
}
}
- private Predicate<T> operator(String name, Tree val) throws QueryParseException {
- switch (val.getType()) {
- // Expand multiple values, "foo:(a b c)", as though they were written
- // out with the longer form, "foo:a foo:b foo:c".
- //
- case AND:
- case OR:
- {
- List<Predicate<T>> p = new ArrayList<>(val.getChildCount());
- for (int i = 0; i < val.getChildCount(); i++) {
- final Tree c = val.getChild(i);
- if (c.getType() != DEFAULT_FIELD) {
- throw error("Nested operator not expected: " + c);
- }
- p.add(operator(name, onlyChildOf(c)));
- }
- return val.getType() == AND ? and(p) : or(p);
- }
+ private static String concatenateChildText(Tree r) throws QueryParseException {
+ if (r.getChildCount() == 0) {
+ throw error("Expected children under: " + r);
+ }
+ if (r.getChildCount() == 1) {
+ return getFieldValue(r.getChild(0));
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < r.getChildCount(); i++) {
+ sb.append(getFieldValue(r.getChild(i)));
+ }
+ return sb.toString();
+ }
+ private static String getFieldValue(Tree r) throws QueryParseException {
+ if (r.getChildCount() != 0) {
+ throw error("Expected no children under: " + r);
+ }
+ switch (r.getType()) {
case SINGLE_WORD:
+ case COLON:
case EXACT_PHRASE:
- if (val.getChildCount() != 0) {
- throw error("Expected no children under: " + val);
- }
- return operator(name, val.getText());
-
+ return r.getText();
default:
- throw error("Unsupported node in operator " + name + ": " + val);
+ throw error(
+ String.format(
+ "Unsupported %s node in operator %s: %s",
+ QueryParser.tokenNames[r.getType()], r.getParent(), r));
}
}
@@ -265,20 +298,6 @@ public abstract class QueryBuilder<T> {
return f.create(this, value);
}
- private Predicate<T> defaultField(Tree r) throws QueryParseException {
- switch (r.getType()) {
- case SINGLE_WORD:
- case EXACT_PHRASE:
- if (r.getChildCount() != 0) {
- throw error("Expected no children under: " + r);
- }
- return defaultField(r.getText());
-
- default:
- throw error("Unsupported node: " + r);
- }
- }
-
/**
* Handle a value present outside of an operator.
*
@@ -322,7 +341,7 @@ public abstract class QueryBuilder<T> {
@Target(ElementType.METHOD)
protected @interface Operator {}
- private static class ReflectionFactory<T, Q extends QueryBuilder<T>>
+ private static class ReflectionFactory<T, Q extends QueryBuilder<T, Q>>
implements OperatorFactory<T, Q> {
private final String name;
private final Method method;
diff --git a/java/com/google/gerrit/index/query/QueryProcessor.java b/java/com/google/gerrit/index/query/QueryProcessor.java
index 64d8742ab1..70772455f5 100644
--- a/java/com/google/gerrit/index/query/QueryProcessor.java
+++ b/java/com/google/gerrit/index/query/QueryProcessor.java
@@ -26,11 +26,11 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexCollection;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.IndexRewriter;
-import com.google.gerrit.index.IndexedQuery;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.SchemaDefinitions;
import com.google.gerrit.metrics.Description;
@@ -38,9 +38,6 @@ 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.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.gwtorm.server.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -91,6 +88,7 @@ public abstract class QueryProcessor<T> {
private boolean enforceVisibility = true;
private int userProvidedLimit;
+ private boolean isNoLimit;
private Set<String> requestedFields;
protected QueryProcessor(
@@ -157,6 +155,11 @@ public abstract class QueryProcessor<T> {
return this;
}
+ public QueryProcessor<T> setNoLimit(boolean isNoLimit) {
+ this.isNoLimit = isNoLimit;
+ return this;
+ }
+
public QueryProcessor<T> setRequestedFields(Set<String> fields) {
requestedFields = fields;
return this;
@@ -169,7 +172,7 @@ public abstract class QueryProcessor<T> {
* @param query the query.
* @return results of the query.
*/
- public QueryResult<T> query(Predicate<T> query) throws OrmException, QueryParseException {
+ public QueryResult<T> query(Predicate<T> query) throws QueryParseException {
return query(ImmutableList.of(query)).get(0);
}
@@ -184,13 +187,10 @@ public abstract class QueryProcessor<T> {
* @return results of the queries, one QueryResult per input query, in the same order as the
* input.
*/
- public List<QueryResult<T>> query(List<Predicate<T>> queries)
- throws OrmException, QueryParseException {
+ public List<QueryResult<T>> query(List<Predicate<T>> queries) throws QueryParseException {
try {
return query(null, queries);
- } catch (OrmRuntimeException e) {
- throw new OrmException(e.getMessage(), e);
- } catch (OrmException e) {
+ } catch (StorageException e) {
if (e.getCause() != null) {
Throwables.throwIfInstanceOf(e.getCause(), QueryParseException.class);
}
@@ -199,8 +199,7 @@ public abstract class QueryProcessor<T> {
}
private List<QueryResult<T>> query(
- @Nullable List<String> queryStrings, List<Predicate<T>> queries)
- throws OrmException, QueryParseException {
+ @Nullable List<String> queryStrings, List<Predicate<T>> queries) throws QueryParseException {
long startNanos = System.nanoTime();
checkState(!used.getAndSet(true), "%s has already been used", getClass().getSimpleName());
int cnt = queries.size();
@@ -268,7 +267,7 @@ public abstract class QueryProcessor<T> {
out = new ArrayList<>(cnt);
for (int i = 0; i < cnt; i++) {
- List<T> matchesList = matches.get(i).toList();
+ ImmutableList<T> matchesList = matches.get(i).toList();
logger.atFine().log(
"Matches[%d]:\n%s",
i, lazy(() -> matchesList.stream().map(this::formatForLogging).collect(toSet())));
@@ -283,7 +282,7 @@ public abstract class QueryProcessor<T> {
// Only measure successful queries that actually touched the index.
metrics.executionTime.record(
schemaDef.getName(), System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
- } catch (OrmException | OrmRuntimeException e) {
+ } catch (StorageException e) {
Optional<QueryParseException> qpe = findQueryParseException(e);
if (qpe.isPresent()) {
throw new QueryParseException(qpe.get().getMessage(), e);
@@ -325,7 +324,7 @@ public abstract class QueryProcessor<T> {
return requestedFields;
}
Index<?, T> index = indexes.getSearchIndex();
- return index != null ? index.getSchema().getStoredFields().keySet() : ImmutableSet.<String>of();
+ return index != null ? index.getSchema().getStoredFields().keySet() : ImmutableSet.of();
}
/**
@@ -354,6 +353,9 @@ public abstract class QueryProcessor<T> {
}
private int getEffectiveLimit(Predicate<T> p) {
+ if (isNoLimit == true) {
+ return Integer.MAX_VALUE;
+ }
List<Integer> possibleLimits = new ArrayList<>(4);
possibleLimits.add(getBackendSupportedLimit());
possibleLimits.add(getPermittedLimit());
diff --git a/java/com/google/gerrit/index/query/QueryResult.java b/java/com/google/gerrit/index/query/QueryResult.java
index 341e2b60f7..33fcef0364 100644
--- a/java/com/google/gerrit/index/query/QueryResult.java
+++ b/java/com/google/gerrit/index/query/QueryResult.java
@@ -15,6 +15,7 @@
package com.google.gerrit.index.query;
import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
import java.util.List;
@@ -22,15 +23,15 @@ import java.util.List;
@AutoValue
public abstract class QueryResult<T> {
public static <T> QueryResult<T> create(
- @Nullable String query, Predicate<T> predicate, int limit, List<T> entites) {
+ @Nullable String query, Predicate<T> predicate, int limit, List<T> entities) {
boolean more;
- if (entites.size() > limit) {
+ if (entities.size() > limit) {
more = true;
- entites = entites.subList(0, limit);
+ entities = entities.subList(0, limit);
} else {
more = false;
}
- return new AutoValue_QueryResult<>(query, predicate, entites, more);
+ return new AutoValue_QueryResult<>(query, predicate, ImmutableList.copyOf(entities), more);
}
/** @return the original query string, or null if the query was created programmatically. */
@@ -41,7 +42,7 @@ public abstract class QueryResult<T> {
public abstract Predicate<T> predicate();
/** @return the query results. */
- public abstract List<T> entities();
+ public abstract ImmutableList<T> entities();
/**
* @return whether the query could be retried with a higher start/limit to produce more results.
diff --git a/java/com/google/gerrit/index/query/ResultSet.java b/java/com/google/gerrit/index/query/ResultSet.java
new file mode 100644
index 0000000000..65fcd4593c
--- /dev/null
+++ b/java/com/google/gerrit/index/query/ResultSet.java
@@ -0,0 +1,52 @@
+// 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.gerrit.index.query;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+
+/**
+ * Result from any data store query function.
+ *
+ * @param <T> type of entity being returned by the query.
+ */
+public interface ResultSet<T> extends Iterable<T> {
+ /**
+ * Obtain an iterator to loop through the results.
+ *
+ * <p>The iterator can be obtained only once. When the iterator completes ( <code>hasNext()</code>
+ * returns false) {@link #close()} will be automatically called.
+ */
+ @Override
+ Iterator<T> iterator();
+
+ /**
+ * Materialize all results as a single list.
+ *
+ * <p>Prior to returning {@link #close()} is invoked. This method must not be combined with {@link
+ * #iterator()} on the same instance.
+ *
+ * @return immutable list of the complete results.
+ */
+ ImmutableList<T> toList();
+
+ /**
+ * Close the result, discarding any further results.
+ *
+ * <p>This method may be invoked more than once. Its main use is to stop obtaining results before
+ * the iterator has finished.
+ */
+ void close();
+}
diff --git a/java/com/google/gerrit/index/query/TimestampRangePredicate.java b/java/com/google/gerrit/index/query/TimestampRangePredicate.java
index edc21205c3..38b2b73e66 100644
--- a/java/com/google/gerrit/index/query/TimestampRangePredicate.java
+++ b/java/com/google/gerrit/index/query/TimestampRangePredicate.java
@@ -15,7 +15,7 @@
package com.google.gerrit.index.query;
import com.google.gerrit.index.FieldDef;
-import com.google.gwtjsonrpc.common.JavaSqlTimestampHelper;
+import com.google.gerrit.json.JavaSqlTimestampHelper;
import java.sql.Timestamp;
import java.util.Date;
diff --git a/java/com/google/gerrit/index/query/testing/BUILD b/java/com/google/gerrit/index/query/testing/BUILD
new file mode 100644
index 0000000000..1785f49c3b
--- /dev/null
+++ b/java/com/google/gerrit/index/query/testing/BUILD
@@ -0,0 +1,17 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+ default_testonly = True,
+ default_visibility = ["//visibility:public"],
+)
+
+java_library(
+ name = "testing",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//antlr3:query_parser",
+ "//lib:guava",
+ "//lib/antlr:java-runtime",
+ "//lib/truth",
+ ],
+)
diff --git a/java/com/google/gerrit/index/query/testing/TreeSubject.java b/java/com/google/gerrit/index/query/testing/TreeSubject.java
new file mode 100644
index 0000000000..c60b36394a
--- /dev/null
+++ b/java/com/google/gerrit/index/query/testing/TreeSubject.java
@@ -0,0 +1,73 @@
+// 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.testing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.truth.Truth.assertAbout;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.truth.FailureMetadata;
+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 static TreeSubject assertThat(Tree actual) {
+ return assertAbout(TreeSubject::new).that(actual);
+ }
+
+ private TreeSubject(FailureMetadata failureMetadata, Tree tree) {
+ super(failureMetadata, tree);
+ }
+
+ public void hasType(int expectedType) {
+ isNotNull();
+ check("getType()").that(typeName(actual().getType())).isEqualTo(typeName(expectedType));
+ }
+
+ public void hasText(String expectedText) {
+ requireNonNull(expectedText);
+ isNotNull();
+ check("getText()").that(actual().getText()).isEqualTo(expectedText);
+ }
+
+ public void hasNoChildren() {
+ isNotNull();
+ check("getChildCount()").that(actual().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);
+ }
+
+ public TreeSubject child(int childIndex) {
+ isNotNull();
+ return check("getChild(%s)", childIndex)
+ .about(TreeSubject::new)
+ .that(actual().getChild(childIndex));
+ }
+
+ private static String typeName(int type) {
+ checkArgument(
+ type >= 0 && type < QueryParser.tokenNames.length,
+ "invalid token type %s, max is %s",
+ type,
+ QueryParser.tokenNames.length - 1);
+ return QueryParser.tokenNames[type];
+ }
+}
diff --git a/java/com/google/gerrit/jgit/BUILD b/java/com/google/gerrit/jgit/BUILD
new file mode 100644
index 0000000000..e67ebfe1ff
--- /dev/null
+++ b/java/com/google/gerrit/jgit/BUILD
@@ -0,0 +1,13 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "jgit",
+ srcs = [
+ "diff/ReplaceEdit.java",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:gson",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/java/com/google/gerrit/jgit/diff/ReplaceEdit.java b/java/com/google/gerrit/jgit/diff/ReplaceEdit.java
new file mode 100644
index 0000000000..45bfad2852
--- /dev/null
+++ b/java/com/google/gerrit/jgit/diff/ReplaceEdit.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2010 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.jgit.diff;
+
+import java.util.List;
+import org.eclipse.jgit.diff.Edit;
+
+public class ReplaceEdit extends Edit {
+ private List<Edit> internalEdit;
+
+ public ReplaceEdit(int as, int ae, int bs, int be, List<Edit> internal) {
+ super(as, ae, bs, be);
+ internalEdit = internal;
+ }
+
+ public ReplaceEdit(Edit orig, List<Edit> internal) {
+ super(orig.getBeginA(), orig.getEndA(), orig.getBeginB(), orig.getEndB());
+ internalEdit = internal;
+ }
+
+ public List<Edit> getInternalEdits() {
+ return internalEdit;
+ }
+}
diff --git a/java/com/google/gerrit/json/BUILD b/java/com/google/gerrit/json/BUILD
new file mode 100644
index 0000000000..d9cec456fc
--- /dev/null
+++ b/java/com/google/gerrit/json/BUILD
@@ -0,0 +1,10 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "json",
+ srcs = glob(["*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:gson",
+ ],
+)
diff --git a/java/com/google/gerrit/json/JavaSqlTimestampHelper.java b/java/com/google/gerrit/json/JavaSqlTimestampHelper.java
new file mode 100644
index 0000000000..b59cbd0d0b
--- /dev/null
+++ b/java/com/google/gerrit/json/JavaSqlTimestampHelper.java
@@ -0,0 +1,108 @@
+// 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.gerrit.json;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+/** Utility to parse Timestamp from a string. */
+public class JavaSqlTimestampHelper {
+ /**
+ * Parse a string into a timestamp.
+ *
+ * <p>Note that {@link Timestamp}s have no timezone, so the result is relative to the UTC epoch.
+ *
+ * <p>Supports the format {@code yyyy-MM-dd[ HH:mm:ss[.SSS][ Z]]} where {@code Z} is a 4-digit
+ * offset with sign, e.g. {@code -0500}.
+ *
+ * @param s input string.
+ * @return resulting timestamp.
+ */
+ public static Timestamp parseTimestamp(String s) {
+ String[] components = s.split(" ");
+ if (components.length < 1 || components.length > 3) {
+ throw new IllegalArgumentException("Expected date and optional time: " + s);
+ }
+ String date = components[0];
+ String time = components.length >= 2 ? components[1] : null;
+ int off = components.length == 3 ? parseTimeZone(components[2]) : 0;
+ String[] dSplit = date.split("-");
+ if (dSplit.length != 3) {
+ throw new IllegalArgumentException("Invalid date format: " + date);
+ }
+ int yy, mm, dd;
+ try {
+ yy = Integer.parseInt(dSplit[0]) - 1900;
+ mm = Integer.parseInt(dSplit[1]) - 1;
+ dd = Integer.parseInt(dSplit[2]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid date format: " + date, e);
+ }
+
+ int hh, mi, ss, ns;
+ if (time != null) {
+ int p = time.indexOf('.');
+ String t;
+ double f;
+ try {
+ if (p >= 0) {
+ t = time.substring(0, p);
+ f = Double.parseDouble("0." + time.substring(p + 1));
+ } else {
+ t = time;
+ f = 0;
+ }
+ String[] tSplit = t.split(":");
+ if (tSplit.length != 3) {
+ throw new IllegalArgumentException("Invalid time format: " + time);
+ }
+ hh = Integer.parseInt(tSplit[0]);
+ mi = Integer.parseInt(tSplit[1]);
+ ss = Integer.parseInt(tSplit[2]);
+ ns = (int) Math.round(f * 1e9);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid time format: " + time, e);
+ }
+ } else {
+ hh = 0;
+ mi = 0;
+ ss = 0;
+ ns = 0;
+ }
+ @SuppressWarnings("deprecation")
+ Timestamp result = new Timestamp(Date.UTC(yy, mm, dd, hh, mi, ss) - off);
+ result.setNanos(ns);
+ return result;
+ }
+
+ private static int parseTimeZone(String s) {
+ if (s.length() != 5 || (s.charAt(0) != '-' && s.charAt(0) != '+')) {
+ throw new IllegalArgumentException("Invalid time zone: " + s);
+ }
+ for (int i = 1; i < s.length(); i++) {
+ if (s.charAt(i) < '0' || s.charAt(i) > '9') {
+ throw new IllegalArgumentException("Invalid time zone: " + s);
+ }
+ }
+ int off =
+ (s.charAt(0) == '-' ? -1 : 1)
+ * 60
+ * 1000
+ * ((60 * Integer.parseInt(s.substring(1, 3))) + Integer.parseInt(s.substring(3, 5)));
+ return off;
+ }
+
+ private JavaSqlTimestampHelper() {}
+}
diff --git a/java/com/google/gerrit/json/OutputFormat.java b/java/com/google/gerrit/json/OutputFormat.java
new file mode 100644
index 0000000000..a2d174f4cc
--- /dev/null
+++ b/java/com/google/gerrit/json/OutputFormat.java
@@ -0,0 +1,69 @@
+// Copyright (C) 2012 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.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.sql.Timestamp;
+
+/** Standard output format used by an API call. */
+public enum OutputFormat {
+ /**
+ * The output is a human readable text format. It may also be regular enough to be machine
+ * readable. Whether or not the text format is machine readable and will be committed to as a long
+ * term format that tools can build upon is specific to each API call.
+ */
+ TEXT,
+
+ /**
+ * Pretty-printed JSON format. This format uses whitespace to make the output readable by a human,
+ * but is also machine readable with a JSON library. The structure of the output is a long term
+ * format that tools can rely upon.
+ */
+ JSON,
+
+ /**
+ * Same as {@link #JSON}, but with unnecessary whitespace removed to save generation time and copy
+ * costs. Typically JSON_COMPACT format is used by a browser based HTML client running over the
+ * network.
+ */
+ JSON_COMPACT;
+
+ /** @return true when the format is either JSON or JSON_COMPACT. */
+ public boolean isJson() {
+ return this == JSON_COMPACT || this == JSON;
+ }
+
+ /** @return a new Gson instance configured according to the format. */
+ public GsonBuilder newGsonBuilder() {
+ if (!isJson()) {
+ throw new IllegalStateException(String.format("%s is not JSON", this));
+ }
+ GsonBuilder gb =
+ new GsonBuilder()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .registerTypeAdapter(Timestamp.class, new SqlTimestampDeserializer());
+ if (this == OutputFormat.JSON) {
+ gb.setPrettyPrinting();
+ }
+ return gb;
+ }
+
+ /** @return a new Gson instance configured according to the format. */
+ public Gson newGson() {
+ return newGsonBuilder().create();
+ }
+}
diff --git a/java/com/google/gerrit/json/SqlTimestampDeserializer.java b/java/com/google/gerrit/json/SqlTimestampDeserializer.java
new file mode 100644
index 0000000000..0148226284
--- /dev/null
+++ b/java/com/google/gerrit/json/SqlTimestampDeserializer.java
@@ -0,0 +1,64 @@
+// 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.gerrit.json;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+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;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+class SqlTimestampDeserializer implements JsonDeserializer<Timestamp>, JsonSerializer<Timestamp> {
+ private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
+ @Override
+ public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ if (json.isJsonNull()) {
+ return null;
+ }
+ if (!json.isJsonPrimitive()) {
+ throw new JsonParseException("Expected string for timestamp type");
+ }
+ JsonPrimitive p = (JsonPrimitive) json;
+ if (!p.isString()) {
+ throw new JsonParseException("Expected string for timestamp type");
+ }
+
+ return JavaSqlTimestampHelper.parseTimestamp(p.getAsString());
+ }
+
+ @Override
+ public JsonElement serialize(Timestamp src, Type typeOfSrc, JsonSerializationContext context) {
+ if (src == null) {
+ return JsonNull.INSTANCE;
+ }
+ return new JsonPrimitive(newFormat().format(src) + "000000");
+ }
+
+ private static SimpleDateFormat newFormat() {
+ SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ f.setTimeZone(UTC);
+ f.setLenient(true);
+ return f;
+ }
+}
diff --git a/java/com/google/gerrit/launcher/GerritLauncher.java b/java/com/google/gerrit/launcher/GerritLauncher.java
index 60d21f9f15..f6c395e7b6 100644
--- a/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -93,7 +93,6 @@ public final class GerritLauncher {
System.err.println(" init Initialize a Gerrit installation");
System.err.println(" reindex Rebuild the secondary index");
System.err.println(" daemon Run the Gerrit network daemons");
- System.err.println(" gsql Run the interactive query console");
System.err.println(" version Display the build version number");
System.err.println(" passwd Set or change password in secure.config");
@@ -324,7 +323,7 @@ public final class GerritLauncher {
}
String name = ze.getName();
- jars.put(name.substring(name.lastIndexOf('/'), name.length()), tmp.toURI().toURL());
+ jars.put(name.substring(name.lastIndexOf('/')), tmp.toURI().toURL());
}
private static void move(SortedMap<String, URL> jars, String prefix, List<URL> extapi) {
diff --git a/java/com/google/gerrit/lifecycle/LifecycleModule.java b/java/com/google/gerrit/lifecycle/LifecycleModule.java
index bfb61d2eda..0fb4653611 100644
--- a/java/com/google/gerrit/lifecycle/LifecycleModule.java
+++ b/java/com/google/gerrit/lifecycle/LifecycleModule.java
@@ -1,3 +1,17 @@
+// 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.lifecycle;
import com.google.gerrit.extensions.config.FactoryModule;
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index cb26488e3b..7a0430c701 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -20,6 +20,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
@@ -30,6 +31,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.FieldType;
import com.google.gerrit.index.Index;
@@ -38,18 +40,14 @@ import com.google.gerrit.index.Schema;
import com.google.gerrit.index.Schema.Values;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.FieldBundle;
+import com.google.gerrit.index.query.ListResultSet;
+import com.google.gerrit.index.query.ResultSet;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import java.io.IOException;
import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -215,7 +213,7 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
}
@Override
- public void markReady(boolean ready) throws IOException {
+ public void markReady(boolean ready) {
IndexUtils.setReady(sitePaths, name, schema.getVersion(), ready);
}
@@ -289,8 +287,12 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
}
@Override
- public void deleteAll() throws IOException {
- writer.deleteAll();
+ public void deleteAll() {
+ try {
+ writer.deleteAll();
+ } catch (IOException e) {
+ throw new StorageException(e);
+ }
}
public IndexWriter getWriter() {
@@ -488,49 +490,33 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
}
@Override
- public ResultSet<V> read() throws OrmException {
+ public ResultSet<V> read() {
return readImpl(AbstractLuceneIndex.this::fromDocument);
}
@Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
+ public ResultSet<FieldBundle> readRaw() {
return readImpl(AbstractLuceneIndex.this::toFieldBundle);
}
- private <T> ResultSet<T> readImpl(Function<Document, T> mapper) throws OrmException {
+ private <T> ResultSet<T> readImpl(Function<Document, T> mapper) {
IndexSearcher searcher = null;
try {
searcher = acquire();
int realLimit = opts.start() + opts.limit();
TopFieldDocs docs = searcher.search(query, realLimit, sort);
- List<T> result = new ArrayList<>(docs.scoreDocs.length);
+ ImmutableList.Builder<T> b = ImmutableList.builderWithExpectedSize(docs.scoreDocs.length);
for (int i = opts.start(); i < docs.scoreDocs.length; i++) {
ScoreDoc sd = docs.scoreDocs[i];
Document doc = searcher.doc(sd.doc, opts.fields());
T mapperResult = mapper.apply(doc);
if (mapperResult != null) {
- result.add(mapperResult);
+ b.add(mapperResult);
}
}
- final List<T> r = Collections.unmodifiableList(result);
- return new ResultSet<T>() {
- @Override
- public Iterator<T> iterator() {
- return r.iterator();
- }
-
- @Override
- public List<T> toList() {
- return r;
- }
-
- @Override
- public void close() {
- // Do nothing.
- }
- };
+ return new ListResultSet<>(b.build());
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
} finally {
if (searcher != null) {
try {
diff --git a/java/com/google/gerrit/lucene/BUILD b/java/com/google/gerrit/lucene/BUILD
index 0eef397f7e..40e3157b48 100644
--- a/java/com/google/gerrit/lucene/BUILD
+++ b/java/com/google/gerrit/lucene/BUILD
@@ -10,7 +10,6 @@ java_library(
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//lib:guava",
- "//lib:gwtorm",
"//lib/lucene:lucene-core-and-backward-codecs",
],
)
@@ -25,15 +24,19 @@ java_library(
deps = [
":query_builder",
"//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/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:gwtorm",
+ "//lib:protobuf",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/lucene/ChangeSubIndex.java b/java/com/google/gerrit/lucene/ChangeSubIndex.java
index 7d7cbef64f..98424b5e1c 100644
--- a/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -71,12 +71,12 @@ public class ChangeSubIndex extends AbstractLuceneIndex<Change.Id, ChangeData>
}
@Override
- public void replace(ChangeData obj) throws IOException {
+ public void replace(ChangeData obj) {
throw new UnsupportedOperationException("don't use ChangeSubIndex directly");
}
@Override
- public void delete(Change.Id key) throws IOException {
+ public void delete(Change.Id key) {
throw new UnsupportedOperationException("don't use ChangeSubIndex directly");
}
diff --git a/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index 86a21115c5..0b787b6e18 100644
--- a/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -19,6 +19,7 @@ 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.PREFERRED_EMAIL_EXACT;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
@@ -120,20 +121,20 @@ public class LuceneAccountIndex extends AbstractLuceneIndex<Account.Id, AccountS
}
@Override
- public void replace(AccountState as) throws IOException {
+ public void replace(AccountState as) {
try {
replace(idTerm(as), toDocument(as)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
@Override
- public void delete(Account.Id key) throws IOException {
+ public void delete(Account.Id key) {
try {
delete(idTerm(key)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 5194e5d25a..87f9396a90 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -14,10 +14,8 @@
package com.google.gerrit.lucene;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.lucene.AbstractLuceneIndex.sortFieldName;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.APPROVAL_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.CHANGE_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.PATCH_SET_CODEC;
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.PROJECT;
@@ -30,6 +28,7 @@ import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
@@ -37,16 +36,22 @@ 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.exceptions.StorageException;
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.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.server.ReviewDb;
+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;
@@ -58,24 +63,20 @@ import com.google.gerrit.server.index.change.ChangeIndexRewriter;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
+import com.google.protobuf.MessageLite;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.function.Consumer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexableField;
@@ -129,6 +130,7 @@ public class LuceneChangeIndex implements ChangeIndex {
ChangeField.STORED_SUBMIT_RECORD_LENIENT.getName();
private static final String SUBMIT_RECORD_STRICT_FIELD =
ChangeField.STORED_SUBMIT_RECORD_STRICT.getName();
+ private static final String TOTAL_COMMENT_COUNT_FIELD = ChangeField.TOTAL_COMMENT_COUNT.getName();
private static final String UNRESOLVED_COMMENT_COUNT_FIELD =
ChangeField.UNRESOLVED_COMMENT_COUNT.getName();
@@ -141,7 +143,6 @@ public class LuceneChangeIndex implements ChangeIndex {
}
private final ListeningExecutorService executor;
- private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final Schema<ChangeData> schema;
private final QueryBuilder<ChangeData> queryBuilder;
@@ -153,12 +154,10 @@ public class LuceneChangeIndex implements ChangeIndex {
@GerritServerConfig Config cfg,
SitePaths sitePaths,
@IndexExecutor(INTERACTIVE) ListeningExecutorService executor,
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
@Assisted Schema<ChangeData> schema)
throws IOException {
this.executor = executor;
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.schema = schema;
@@ -201,34 +200,34 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public void replace(ChangeData cd) throws IOException {
+ public void replace(ChangeData cd) {
Term id = LuceneChangeIndex.idTerm(cd);
// toDocument is essentially static and doesn't depend on the specific
// sub-index, so just pick one.
Document doc = openIndex.toDocument(cd);
try {
- if (cd.change().getStatus().isOpen()) {
+ if (cd.change().isNew()) {
Futures.allAsList(closedIndex.delete(id), openIndex.replace(id, doc)).get();
} else {
Futures.allAsList(openIndex.delete(id), closedIndex.replace(id, doc)).get();
}
- } catch (OrmException | ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ } catch (ExecutionException | InterruptedException e) {
+ throw new StorageException(e);
}
}
@Override
- public void delete(Change.Id id) throws IOException {
+ public void delete(Change.Id id) {
Term idTerm = LuceneChangeIndex.idTerm(id);
try {
Futures.allAsList(openIndex.delete(idTerm), closedIndex.delete(idTerm)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
@Override
- public void deleteAll() throws IOException {
+ public void deleteAll() {
openIndex.deleteAll();
closedIndex.deleteAll();
}
@@ -248,7 +247,7 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public void markReady(boolean ready) throws IOException {
+ public void markReady(boolean ready) {
// Arbitrary done on open index, as ready bit is set
// per index and not sub index
openIndex.markReady(ready);
@@ -303,10 +302,10 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public ResultSet<ChangeData> read() throws OrmException {
+ public ResultSet<ChangeData> read() {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
- throw new OrmException("interrupted");
+ throw new StorageException("interrupted");
}
final Set<String> fields = IndexUtils.changeFields(opts);
@@ -327,14 +326,15 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
+ public ResultSet<FieldBundle> readRaw() {
List<Document> documents;
try {
documents = doRead(IndexUtils.changeFields(opts));
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
- List<FieldBundle> fieldBundles = documents.stream().map(rawDocumentMapper).collect(toList());
+ ImmutableList<FieldBundle> fieldBundles =
+ documents.stream().map(rawDocumentMapper).collect(toImmutableList());
return new ResultSet<FieldBundle>() {
@Override
public Iterator<FieldBundle> iterator() {
@@ -342,7 +342,7 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public List<FieldBundle> toList() {
+ public ImmutableList<FieldBundle> toList() {
return fieldBundles;
}
@@ -402,21 +402,22 @@ public class LuceneChangeIndex implements ChangeIndex {
}
@Override
- public List<ChangeData> toList() {
+ public ImmutableList<ChangeData> toList() {
try {
List<Document> docs = future.get();
- List<ChangeData> result = new ArrayList<>(docs.size());
+ 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));
}
- return result;
+ return result.build();
} catch (InterruptedException e) {
close();
- throw new OrmRuntimeException(e);
+ throw new StorageException(e);
} catch (ExecutionException e) {
Throwables.throwIfUnchecked(e.getCause());
- throw new OrmRuntimeException(e.getCause());
+ throw new StorageException(e.getCause());
}
}
@@ -446,15 +447,13 @@ public class LuceneChangeIndex implements ChangeIndex {
IndexableField cb = Iterables.getFirst(doc.get(CHANGE_FIELD), null);
if (cb != null) {
BytesRef proto = cb.binaryValue();
- cd =
- changeDataFactory.create(
- db.get(), CHANGE_CODEC.decode(proto.bytes, proto.offset, proto.length));
+ 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(db.get(), new Project.NameKey(project.stringValue()), id);
+ cd = changeDataFactory.create(new Project.NameKey(project.stringValue()), id);
}
// Any decoding that is done here must also be done in {@link ElasticChangeIndex}.
@@ -504,11 +503,12 @@ public class LuceneChangeIndex implements ChangeIndex {
}
decodeUnresolvedCommentCount(doc, cd);
+ decodeTotalCommentCount(doc, cd);
return cd;
}
private void decodePatchSets(ListMultimap<String, IndexableField> doc, ChangeData cd) {
- List<PatchSet> patchSets = decodeProtos(doc, PATCH_SET_FIELD, PATCH_SET_CODEC);
+ List<PatchSet> patchSets = decodeProtos(doc, PATCH_SET_FIELD, PatchSetProtoConverter.INSTANCE);
if (!patchSets.isEmpty()) {
// Will be an empty list for schemas prior to when this field was stored;
// this cannot be valid since a change needs at least one patch set.
@@ -517,7 +517,8 @@ public class LuceneChangeIndex implements ChangeIndex {
}
private void decodeApprovals(ListMultimap<String, IndexableField> doc, ChangeData cd) {
- cd.setCurrentApprovals(decodeProtos(doc, APPROVAL_FIELD, APPROVAL_CODEC));
+ cd.setCurrentApprovals(
+ decodeProtos(doc, APPROVAL_FIELD, PatchSetApprovalProtoConverter.INSTANCE));
}
private void decodeChangedLines(ListMultimap<String, IndexableField> doc, ChangeData cd) {
@@ -633,25 +634,35 @@ public class LuceneChangeIndex implements ChangeIndex {
private void decodeUnresolvedCommentCount(
ListMultimap<String, IndexableField> doc, ChangeData cd) {
- IndexableField f = Iterables.getFirst(doc.get(UNRESOLVED_COMMENT_COUNT_FIELD), null);
+ decodeIntField(doc, UNRESOLVED_COMMENT_COUNT_FIELD, cd::setUnresolvedCommentCount);
+ }
+
+ private void decodeTotalCommentCount(ListMultimap<String, IndexableField> doc, ChangeData cd) {
+ decodeIntField(doc, TOTAL_COMMENT_COUNT_FIELD, cd::setTotalCommentCount);
+ }
+
+ private static void decodeIntField(
+ ListMultimap<String, IndexableField> doc, String fieldName, Consumer<Integer> consumer) {
+ IndexableField f = Iterables.getFirst(doc.get(fieldName), null);
if (f != null && f.numericValue() != null) {
- cd.setUnresolvedCommentCount(f.numericValue().intValue());
+ consumer.accept(f.numericValue().intValue());
}
}
private static <T> List<T> decodeProtos(
- ListMultimap<String, IndexableField> doc, String fieldName, ProtobufCodec<T> codec) {
- Collection<IndexableField> fields = doc.get(fieldName);
- if (fields.isEmpty()) {
- return Collections.emptyList();
- }
-
- List<T> result = new ArrayList<>(fields.size());
- for (IndexableField f : fields) {
- BytesRef r = f.binaryValue();
- result.add(codec.decode(r.bytes, r.offset, r.length));
- }
- return result;
+ ListMultimap<String, IndexableField> doc, String fieldName, ProtoConverter<?, T> converter) {
+ return doc.get(fieldName).stream()
+ .map(IndexableField::binaryValue)
+ .map(bytesRef -> parseProtoFrom(bytesRef, converter))
+ .collect(toImmutableList());
+ }
+
+ private static <P extends MessageLite, T> T parseProtoFrom(
+ BytesRef bytesRef, ProtoConverter<P, T> converter) {
+ P message =
+ Protos.parseUnchecked(
+ converter.getParser(), bytesRef.bytes, bytesRef.offset, bytesRef.length);
+ return converter.fromProto(message);
}
private static List<byte[]> copyAsBytes(Collection<IndexableField> fields) {
diff --git a/java/com/google/gerrit/lucene/LuceneGroupIndex.java b/java/com/google/gerrit/lucene/LuceneGroupIndex.java
index 95e2ab903e..0fdef77b96 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.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
@@ -110,20 +111,20 @@ public class LuceneGroupIndex extends AbstractLuceneIndex<AccountGroup.UUID, Int
}
@Override
- public void replace(InternalGroup group) throws IOException {
+ public void replace(InternalGroup group) {
try {
replace(idTerm(group), toDocument(group)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
@Override
- public void delete(AccountGroup.UUID key) throws IOException {
+ public void delete(AccountGroup.UUID key) {
try {
delete(idTerm(key)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/lucene/LuceneProjectIndex.java b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
index 02d86552f8..950e2060ca 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.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
@@ -110,20 +111,20 @@ public class LuceneProjectIndex extends AbstractLuceneIndex<Project.NameKey, Pro
}
@Override
- public void replace(ProjectData projectState) throws IOException {
+ public void replace(ProjectData projectState) {
try {
replace(idTerm(projectState), toDocument(projectState)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
@Override
- public void delete(Project.NameKey nameKey) throws IOException {
+ public void delete(Project.NameKey nameKey) {
try {
delete(idTerm(nameKey)).get();
} catch (ExecutionException | InterruptedException e) {
- throw new IOException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/lucene/LuceneVersionManager.java b/java/com/google/gerrit/lucene/LuceneVersionManager.java
index 63abea84da..f3ba73ded9 100644
--- a/java/com/google/gerrit/lucene/LuceneVersionManager.java
+++ b/java/com/google/gerrit/lucene/LuceneVersionManager.java
@@ -16,7 +16,6 @@ package com.google.gerrit.lucene;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.Schema;
@@ -25,6 +24,7 @@ import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.GerritIndexStatus;
import com.google.gerrit.server.index.OnlineUpgradeListener;
import com.google.gerrit.server.index.VersionManager;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -47,7 +47,7 @@ public class LuceneVersionManager extends VersionManager {
LuceneVersionManager(
@GerritServerConfig Config cfg,
SitePaths sitePaths,
- DynamicSet<OnlineUpgradeListener> listeners,
+ PluginSetContext<OnlineUpgradeListener> listeners,
Collection<IndexDefinition<?, ?, ?>> defs) {
super(sitePaths, listeners, defs, VersionManager.getOnlineUpgrade(cfg));
}
@@ -81,7 +81,7 @@ public class LuceneVersionManager extends VersionManager {
continue;
}
if (!versions.containsKey(v)) {
- versions.put(v, new Version<V>(null, v, true, cfg.getReady(def.getName(), v)));
+ versions.put(v, new Version<>(null, v, true, cfg.getReady(def.getName(), v)));
}
}
} catch (IOException e) {
diff --git a/java/com/google/gerrit/mail/MailHeaderParser.java b/java/com/google/gerrit/mail/MailHeaderParser.java
index 8eb4d97047..a4a6a037db 100644
--- a/java/com/google/gerrit/mail/MailHeaderParser.java
+++ b/java/com/google/gerrit/mail/MailHeaderParser.java
@@ -103,6 +103,6 @@ public class MailHeaderParser {
}
private static String extractFooter(String key, String line) {
- return line.substring(line.indexOf(key) + key.length(), line.length()).trim();
+ return line.substring(line.indexOf(key) + key.length()).trim();
}
}
diff --git a/java/com/google/gerrit/metrics/DisabledMetricMaker.java b/java/com/google/gerrit/metrics/DisabledMetricMaker.java
index bc88a60d6f..adf6a66d5e 100644
--- a/java/com/google/gerrit/metrics/DisabledMetricMaker.java
+++ b/java/com/google/gerrit/metrics/DisabledMetricMaker.java
@@ -187,9 +187,6 @@ public class DisabledMetricMaker extends MetricMaker {
@Override
public RegistrationHandle newTrigger(Set<CallbackMetric<?>> metrics, Runnable trigger) {
- return new RegistrationHandle() {
- @Override
- public void remove() {}
- };
+ return () -> {};
}
}
diff --git a/java/com/google/gerrit/metrics/MetricMaker.java b/java/com/google/gerrit/metrics/MetricMaker.java
index 401a6d6bd1..42ec8a09a9 100644
--- a/java/com/google/gerrit/metrics/MetricMaker.java
+++ b/java/com/google/gerrit/metrics/MetricMaker.java
@@ -136,7 +136,7 @@ public abstract class MetricMaker {
* @return registration handle
*/
public RegistrationHandle newTrigger(CallbackMetric<?> metric1, Runnable trigger) {
- return newTrigger(ImmutableSet.<CallbackMetric<?>>of(metric1), trigger);
+ return newTrigger(ImmutableSet.of(metric1), trigger);
}
public RegistrationHandle newTrigger(
diff --git a/java/com/google/gerrit/metrics/dropwizard/BucketedCallback.java b/java/com/google/gerrit/metrics/dropwizard/BucketedCallback.java
index c1c319c822..b3860f72ab 100644
--- a/java/com/google/gerrit/metrics/dropwizard/BucketedCallback.java
+++ b/java/com/google/gerrit/metrics/dropwizard/BucketedCallback.java
@@ -128,7 +128,7 @@ abstract class BucketedCallback<V> implements BucketedMetric {
@Override
public Map<Object, Metric> getCells() {
- return Maps.transformValues(cells, in -> (Metric) in);
+ return Maps.transformValues(cells, in -> in);
}
final class ValueGauge implements Gauge<V> {
diff --git a/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl0.java b/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl0.java
index 5e25651ff8..6e7e7d94fc 100644
--- a/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl0.java
+++ b/java/com/google/gerrit/metrics/dropwizard/CallbackMetricImpl0.java
@@ -14,6 +14,7 @@
package com.google.gerrit.metrics.dropwizard;
+import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
import com.google.gerrit.metrics.CallbackMetric0;
@@ -71,12 +72,10 @@ class CallbackMetricImpl0<V> extends CallbackMetric0<V> implements CallbackMetri
public void register(Runnable trigger) {
registry.register(
name,
- new com.codahale.metrics.Gauge<V>() {
- @Override
- public V getValue() {
- trigger.run();
- return value;
- }
- });
+ (Gauge<V>)
+ () -> {
+ trigger.run();
+ return value;
+ });
}
}
diff --git a/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java b/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
index b57ea920ef..fcba0eeb3c 100644
--- a/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
+++ b/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
@@ -305,14 +305,7 @@ public class DropWizardMetricMaker extends MetricMaker {
}
trigger.run();
- return new RegistrationHandle() {
- @Override
- public void remove() {
- for (CallbackMetricGlue m : all) {
- m.remove();
- }
- }
- };
+ return () -> all.forEach(CallbackMetricGlue::remove);
}
synchronized void remove(String name) {
diff --git a/java/com/google/gerrit/metrics/dropwizard/MetricJson.java b/java/com/google/gerrit/metrics/dropwizard/MetricJson.java
index 20806239e7..20f4fa33a3 100644
--- a/java/com/google/gerrit/metrics/dropwizard/MetricJson.java
+++ b/java/com/google/gerrit/metrics/dropwizard/MetricJson.java
@@ -137,7 +137,7 @@ class MetricJson {
p99_9 = s.get999thPercentile();
min = (double) s.getMin();
- avg = (double) s.getMean();
+ avg = s.getMean();
max = (double) s.getMax();
sum = s.getMean() * m.getCount();
std_dev = s.getStdDev();
diff --git a/java/com/google/gerrit/metrics/proc/ProcMetricModule.java b/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
index 2395e3d622..2c29882b65 100644
--- a/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
+++ b/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
@@ -15,10 +15,8 @@
package com.google.gerrit.metrics.proc;
import com.google.common.base.Strings;
-import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Version;
-import com.google.gerrit.metrics.CallbackMetric;
import com.google.gerrit.metrics.CallbackMetric0;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Description;
@@ -77,12 +75,7 @@ public class ProcMetricModule extends MetricModule {
"proc/cpu/usage",
Double.class,
new Description("CPU time used by the process").setCumulative().setUnit(Units.SECONDS),
- new Supplier<Double>() {
- @Override
- public Double get() {
- return provider.getProcessCpuTime() / 1e9;
- }
- });
+ () -> provider.getProcessCpuTime() / 1e9);
}
if (provider.getOpenFileDescriptorCount() != -1) {
@@ -154,7 +147,7 @@ public class ProcMetricModule extends MetricModule {
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
metrics.newTrigger(
- ImmutableSet.<CallbackMetric<?>>of(
+ ImmutableSet.of(
heapCommitted, heapUsed, nonHeapCommitted, nonHeapUsed, objectPendingFinalizationCount),
() -> {
try {
diff --git a/java/com/google/gerrit/pgm/BUILD b/java/com/google/gerrit/pgm/BUILD
index c6cef19988..a8690b908c 100644
--- a/java/com/google/gerrit/pgm/BUILD
+++ b/java/com/google/gerrit/pgm/BUILD
@@ -20,6 +20,7 @@ java_library(
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/elasticsearch",
+ "//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/gpg",
"//java/com/google/gerrit/httpd",
@@ -30,6 +31,7 @@ java_library(
"//java/com/google/gerrit/launcher",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/lucene",
+ "//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/metrics/dropwizard",
"//java/com/google/gerrit/pgm/http/jetty",
"//java/com/google/gerrit/pgm/init",
@@ -49,9 +51,8 @@ java_library(
"//java/com/google/gerrit/sshd",
"//lib:args4j",
"//lib:guava",
- "//lib:gwtorm",
"//lib:protobuf",
- "//lib:servlet-api-3_1-without-neverlink",
+ "//lib:servlet-api-without-neverlink",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 65707fa012..a5c6dacbc4 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -15,9 +15,7 @@
package com.google.gerrit.pgm;
import static com.google.gerrit.common.Version.getVersion;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
@@ -55,6 +53,7 @@ import com.google.gerrit.pgm.util.LogFileCompressor;
import com.google.gerrit.pgm.util.RuntimeShutdown;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.server.LibModuleLoader;
+import com.google.gerrit.server.LibModuleType;
import com.google.gerrit.server.ModuleOverloader;
import com.google.gerrit.server.StartupChecks;
import com.google.gerrit.server.account.AccountDeactivator;
@@ -91,18 +90,14 @@ import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.server.mail.receive.MailReceiver;
import com.google.gerrit.server.mail.send.SmtpEmailSender;
import com.google.gerrit.server.mime.MimeUtil2Module;
-import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
-import com.google.gerrit.server.notedb.rebuild.OnlineNoteDbMigrator;
import com.google.gerrit.server.patch.DiffExecutorModule;
import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
import com.google.gerrit.server.plugins.PluginModule;
import com.google.gerrit.server.project.DefaultProjectNameLockManager;
import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.InMemoryAccountPatchReviewStore;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
-import com.google.gerrit.server.schema.SchemaVersionCheck;
+import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
import com.google.gerrit.server.securestore.DefaultSecureStore;
import com.google.gerrit.server.securestore.SecureStore;
import com.google.gerrit.server.securestore.SecureStoreClassName;
@@ -125,7 +120,6 @@ import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Stage;
import java.io.IOException;
-import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -134,7 +128,6 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jgit.lib.Config;
import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
/** Run SSH daemon portions of Gerrit. */
public class Daemon extends SiteProgram {
@@ -183,15 +176,6 @@ public class Daemon extends SiteProgram {
@Option(name = "--stop-only", usage = "Stop the daemon", hidden = true)
private boolean stopOnly;
- @Option(
- name = "--migrate-to-note-db",
- usage = "Automatically migrate changes to NoteDb",
- handler = ExplicitBooleanOptionHandler.class)
- private boolean migrateToNoteDb;
-
- @Option(name = "--trial", usage = "(With --migrate-to-note-db) " + MigrateToNoteDb.TRIAL_USAGE)
- private boolean trial;
-
private final LifecycleManager manager = new LifecycleManager();
private Injector dbInjector;
private Injector cfgInjector;
@@ -205,6 +189,7 @@ public class Daemon extends SiteProgram {
private AbstractModule luceneModule;
private Module emailModule;
private List<Module> testSysModules = new ArrayList<>();
+ private List<Module> testSshModules = new ArrayList<>();
private Module auditEventModule;
private Runnable serverStarted;
@@ -251,12 +236,7 @@ public class Daemon extends SiteProgram {
}
mustHaveValidSite();
Thread.setDefaultUncaughtExceptionHandler(
- new UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- logger.atSevere().withCause(e).log("Thread %s threw exception", t.getName());
- }
- });
+ (t, e) -> logger.atSevere().withCause(e).log("Thread %s threw exception", t.getName()));
if (runId != null) {
runFile = getSitePath().resolve("logs").resolve("gerrit.run");
@@ -295,8 +275,6 @@ public class Daemon extends SiteProgram {
if (inspector) {
JythonShell shell = new JythonShell();
shell.set("m", manager);
- shell.set("ds", dbInjector.getInstance(DataSourceProvider.class));
- shell.set("schk", dbInjector.getInstance(SchemaVersionCheck.class));
shell.set("d", this);
shell.run();
} else {
@@ -343,9 +321,14 @@ public class Daemon extends SiteProgram {
}
@VisibleForTesting
+ public void addAdditionalSshModuleForTesting(@Nullable Module... modules) {
+ testSshModules.addAll(Arrays.asList(modules));
+ }
+
+ @VisibleForTesting
public void start() throws IOException {
if (dbInjector == null) {
- dbInjector = createDbInjector(true /* enableMetrics */, MULTI_USER);
+ dbInjector = createDbInjector(true /* enableMetrics */);
}
cfgInjector = createCfgInjector();
config = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
@@ -354,9 +337,7 @@ public class Daemon extends SiteProgram {
sysInjector.getInstance(PluginGuiceEnvironment.class).setDbCfgInjector(dbInjector, cfgInjector);
manager.add(dbInjector, cfgInjector, sysInjector);
- if (!consoleLog) {
- manager.add(ErrorLogFile.start(getSitePath(), config));
- }
+ manager.add(ErrorLogFile.start(getSitePath(), config, consoleLog));
sshd &= !sshdOff();
if (sshd) {
@@ -411,7 +392,7 @@ public class Daemon extends SiteProgram {
private Injector createSysInjector() {
final List<Module> modules = new ArrayList<>();
- modules.add(SchemaVersionCheck.module());
+ modules.add(NoteDbSchemaVersionCheck.module());
modules.add(new DropWizardMetricMaker.RestModule());
modules.add(new LogFileCompressor.Module());
@@ -422,10 +403,7 @@ public class Daemon extends SiteProgram {
modules.add(new WorkQueue.Module());
modules.add(new StreamEventsApiListener.Module());
modules.add(new EventBroker.Module());
- modules.add(
- inMemoryTest
- ? new InMemoryAccountPatchReviewStore.Module()
- : new JdbcAccountPatchReviewStore.Module(config));
+ modules.add(new JdbcAccountPatchReviewStore.Module(config));
modules.add(new SysExecutorModule());
modules.add(new DiffExecutorModule());
modules.add(new MimeUtil2Module());
@@ -451,9 +429,7 @@ public class Daemon extends SiteProgram {
}
modules.add(new SignedTokenEmailTokenVerifier.Module());
modules.add(new PluginModule());
- if (VersionManager.getOnlineUpgrade(config)
- // Schema upgrade is handled by OnlineNoteDbMigrator in this case.
- && !migrateToNoteDb()) {
+ if (VersionManager.getOnlineUpgrade(config)) {
modules.add(new OnlineUpgrader.Module());
}
modules.add(new RestApiModule());
@@ -487,8 +463,7 @@ public class Daemon extends SiteProgram {
new AbstractModule() {
@Override
protected void configure() {
- bind(GerritOptions.class)
- .toInstance(new GerritOptions(config, headless, slave, polyGerritDev));
+ bind(GerritOptions.class).toInstance(new GerritOptions(headless, slave, polyGerritDev));
if (inMemoryTest) {
bind(String.class)
.annotatedWith(SecureStoreClassName.class)
@@ -504,18 +479,12 @@ public class Daemon extends SiteProgram {
modules.add(new AccountDeactivator.Module());
modules.add(new ChangeCleanupRunner.Module());
}
- if (migrateToNoteDb()) {
- modules.add(new OnlineNoteDbMigrator.Module(trial));
- }
modules.addAll(testSysModules);
modules.add(new LocalMergeSuperSetComputation.Module());
modules.add(new DefaultProjectNameLockManager.Module());
return cfgInjector.createChildInjector(
- ModuleOverloader.override(modules, LibModuleLoader.loadModules(cfgInjector)));
- }
-
- private boolean migrateToNoteDb() {
- return migrateToNoteDb || NoteDbMigrator.getAutoMigrate(requireNonNull(config));
+ ModuleOverloader.override(
+ modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.SYS_MODULE)));
}
private Module createIndexModule() {
@@ -560,6 +529,8 @@ public class Daemon extends SiteProgram {
slave,
sysInjector.getInstance(DownloadConfig.class),
sysInjector.getInstance(LfsPluginAuthCommand.Module.class)));
+
+ modules.addAll(testSshModules);
if (!slave) {
modules.add(new IndexCommandsModule(sysInjector));
}
diff --git a/java/com/google/gerrit/pgm/DeleteZombieDrafts.java b/java/com/google/gerrit/pgm/DeleteZombieDrafts.java
index 82c260674a..c08e9999db 100644
--- a/java/com/google/gerrit/pgm/DeleteZombieDrafts.java
+++ b/java/com/google/gerrit/pgm/DeleteZombieDrafts.java
@@ -14,6 +14,8 @@
package com.google.gerrit.pgm;
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.server.config.GerritServerConfigModule;
@@ -22,7 +24,6 @@ import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs;
import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs.Factory;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.server.securestore.SecureStoreClassName;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -49,7 +50,7 @@ public class DeleteZombieDrafts extends SiteProgram {
private Integer cleanupPercentage = 100;
@Override
- public int run() throws IOException, OrmException {
+ public int run() throws IOException {
mustHaveValidSite();
Injector sysInjector = getSysInjector();
DeleteZombieCommentsRefs cleanup =
@@ -69,6 +70,7 @@ public class DeleteZombieDrafts extends SiteProgram {
bind(String.class)
.annotatedWith(SecureStoreClassName.class)
.toProvider(Providers.of(getConfiguredSecureStoreClass()));
+ bind(MetricMaker.class).to(DisabledMetricMaker.class);
install(new FactoryModuleBuilder().build(DeleteZombieCommentsRefs.Factory.class));
}
});
diff --git a/java/com/google/gerrit/pgm/Gsql.java b/java/com/google/gerrit/pgm/Gsql.java
deleted file mode 100644
index 004486bd01..0000000000
--- a/java/com/google/gerrit/pgm/Gsql.java
+++ /dev/null
@@ -1,77 +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.pgm;
-
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.pgm.util.RuntimeShutdown;
-import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.sshd.commands.QueryShell;
-import com.google.gerrit.sshd.commands.QueryShell.Factory;
-import com.google.inject.Injector;
-import java.io.IOException;
-import org.kohsuke.args4j.Option;
-
-/** Run Gerrit's SQL query tool */
-public class Gsql extends SiteProgram {
- private final LifecycleManager manager = new LifecycleManager();
- private Injector dbInjector;
-
- @Option(name = "--format", usage = "Set output format")
- private QueryShell.OutputFormat format = QueryShell.OutputFormat.PRETTY;
-
- @Option(name = "-c", metaVar = "SQL QUERY", usage = "Query to execute")
- private String query;
-
- @Override
- public int run() throws Exception {
- mustHaveValidSite();
-
- dbInjector = createDbInjector(SINGLE_USER);
- manager.add(dbInjector);
- manager.start();
- RuntimeShutdown.add(
- () -> {
- try {
- System.in.close();
- } catch (IOException e) {
- // Ignored
- }
- manager.stop();
- });
- final QueryShell shell = shellFactory().create(System.in, System.out);
- shell.setOutputFormat(format);
- if (query != null) {
- shell.execute(query);
- } else {
- shell.run();
- }
- return 0;
- }
-
- private Factory shellFactory() {
- return dbInjector
- .createChildInjector(
- new FactoryModule() {
- @Override
- protected void configure() {
- factory(QueryShell.Factory.class);
- }
- })
- .getInstance(QueryShell.Factory.class);
- }
-}
diff --git a/java/com/google/gerrit/pgm/Init.java b/java/com/google/gerrit/pgm/Init.java
index 4ea31da540..6d9d00318d 100644
--- a/java/com/google/gerrit/pgm/Init.java
+++ b/java/com/google/gerrit/pgm/Init.java
@@ -22,6 +22,7 @@ import com.google.common.collect.Sets;
import com.google.gerrit.common.IoUtil;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.PluginData;
+import com.google.gerrit.index.SchemaDefinitions;
import com.google.gerrit.index.project.ProjectSchemaDefinitions;
import com.google.gerrit.pgm.init.BaseInit;
import com.google.gerrit.pgm.init.Browser;
@@ -30,6 +31,10 @@ import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.util.ErrorLogFile;
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
+import com.google.gerrit.server.index.GerritIndexStatus;
+import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
+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.inject.AbstractModule;
@@ -59,9 +64,6 @@ public class Init extends BaseInit {
@Option(name = "--no-auto-start", usage = "Don't automatically start daemon after init")
private boolean noAutoStart;
- @Option(name = "--no-reindex", usage = "Don't automatically reindex any entities")
- private boolean noReindex;
-
@Option(name = "--skip-plugins", usage = "Don't install plugins")
private boolean skipPlugins;
@@ -90,18 +92,21 @@ public class Init extends BaseInit {
@Inject Browser browser;
+ private GerritIndexStatus indexStatus;
+
public Init() {
super(new WarDistribution(), null);
}
public Init(Path sitePath) {
- super(sitePath, true, true, new WarDistribution(), null);
+ super(sitePath, true, new WarDistribution(), null);
batchMode = true;
noAutoStart = true;
}
@Override
protected boolean beforeInit(SiteInit init) throws Exception {
+ indexStatus = new GerritIndexStatus(init.site);
ErrorLogFile.errorOnlyConsole();
if (!skipPlugins) {
@@ -130,6 +135,12 @@ public class Init extends BaseInit {
@Override
protected void afterInit(SiteRun run) throws Exception {
+ List<SchemaDefinitions<?>> schemaDefs =
+ ImmutableList.of(
+ AccountSchemaDefinitions.INSTANCE,
+ ChangeSchemaDefinitions.INSTANCE,
+ GroupSchemaDefinitions.INSTANCE,
+ ProjectSchemaDefinitions.INSTANCE);
List<Module> modules = new ArrayList<>();
modules.add(
new AbstractModule() {
@@ -144,7 +155,13 @@ public class Init extends BaseInit {
});
modules.add(new GerritServerConfigModule());
Guice.createInjector(modules).injectMembers(this);
- reindexProjects();
+ if (!run.flags.cfg.getBoolean("container", "slave", false)) {
+ for (SchemaDefinitions<?> schemaDef : schemaDefs) {
+ if (!indexStatus.exists(schemaDef.getName())) {
+ reindex(schemaDef);
+ }
+ }
+ }
start(run);
}
@@ -190,7 +207,7 @@ public class Init extends BaseInit {
@Override
protected List<String> getSkippedDownloads() {
- return skippedDownloads != null ? skippedDownloads : Collections.<String>emptyList();
+ return skippedDownloads != null ? skippedDownloads : Collections.emptyList();
}
@Override
@@ -256,11 +273,7 @@ public class Init extends BaseInit {
}
}
- private void reindexProjects() throws Exception {
- if (noReindex) {
- return;
- }
- // Reindex all projects, so that we bootstrap the project index for new installations
+ private void reindex(SchemaDefinitions<?> schemaDef) throws Exception {
List<String> reindexArgs =
ImmutableList.of(
"--site-path",
@@ -268,8 +281,9 @@ public class Init extends BaseInit {
"--threads",
Integer.toString(1),
"--index",
- ProjectSchemaDefinitions.NAME);
- getConsoleUI().message("Init complete, reindexing projects with:");
+ schemaDef.getName());
+ getConsoleUI()
+ .message(String.format("Init complete, reindexing %s with:", schemaDef.getName()));
getConsoleUI().message(" reindex " + reindexArgs.stream().collect(joining(" ")));
Reindex reindexPgm = new Reindex();
reindexPgm.main(reindexArgs.stream().toArray(String[]::new));
diff --git a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
index 5bfc00f783..e6e091c360 100644
--- a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
+++ b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
@@ -15,8 +15,8 @@
package com.google.gerrit.pgm;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
+import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.util.SiteProgram;
@@ -29,8 +29,7 @@ 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.server.index.account.AccountSchemaDefinitions;
-import com.google.gerrit.server.schema.SchemaVersionCheck;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
@@ -54,8 +53,8 @@ public class LocalUsernamesToLowerCase extends SiteProgram {
@Override
public int run() throws Exception {
- Injector dbInjector = createDbInjector(MULTI_USER);
- manager.add(dbInjector, dbInjector.createChildInjector(SchemaVersionCheck.module()));
+ Injector dbInjector = createDbInjector();
+ manager.add(dbInjector, dbInjector.createChildInjector(NoteDbSchemaVersionCheck.module()));
manager.start();
dbInjector
.createChildInjector(
@@ -97,7 +96,7 @@ public class LocalUsernamesToLowerCase extends SiteProgram {
}
private void convertLocalUserToLowerCase(ExternalIdNotes extIdNotes, ExternalId extId)
- throws OrmDuplicateKeyException, IOException {
+ throws DuplicateKeyException, IOException {
if (extId.isScheme(SCHEME_GERRIT)) {
String localUser = extId.key().id();
String localUserLowerCase = localUser.toLowerCase(Locale.US);
diff --git a/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java b/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java
index 4ace62b77f..e12bde2afd 100644
--- a/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java
+++ b/java/com/google/gerrit/pgm/MigrateAccountPatchReviewDb.java
@@ -21,7 +21,6 @@ import com.google.gerrit.pgm.util.SiteProgram;
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.schema.DataSourceProvider;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
import com.google.inject.Injector;
import com.google.inject.Key;
@@ -49,7 +48,7 @@ public class MigrateAccountPatchReviewDb extends SiteProgram {
@Override
public int run() throws Exception {
- Injector dbInjector = createDbInjector(DataSourceProvider.Context.SINGLE_USER);
+ Injector dbInjector = createDbInjector();
SitePaths sitePaths = new SitePaths(getSitePath());
ThreadSettingsConfig threadSettingsConfig = dbInjector.getInstance(ThreadSettingsConfig.class);
Config fakeCfg = new Config();
diff --git a/java/com/google/gerrit/pgm/MigrateToNoteDb.java b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
deleted file mode 100644
index 247aaf9f64..0000000000
--- a/java/com/google/gerrit/pgm/MigrateToNoteDb.java
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (C) 2014 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.pgm;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
-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.collect.ImmutableList;
-import com.google.gerrit.common.Die;
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.pgm.util.BatchProgramModule;
-import com.google.gerrit.pgm.util.RuntimeShutdown;
-import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.pgm.util.ThreadLimiter;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.git.GarbageCollection;
-import com.google.gerrit.server.index.DummyIndexModule;
-import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
-import com.google.gerrit.server.notedb.rebuild.GcAllUsers;
-import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
-import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Provider;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
-
-public class MigrateToNoteDb extends SiteProgram {
- static final String TRIAL_USAGE =
- "Trial mode: migrate changes and turn on reading from NoteDb, but leave ReviewDb as the"
- + " source of truth";
-
- private static final int ISSUE_8022_THREAD_LIMIT = 4;
-
- @Option(name = "--threads", usage = "Number of threads to use for rebuilding NoteDb")
- private Integer threads;
-
- @Option(
- name = "--project",
- usage =
- "Only rebuild these projects, do no other migration; incompatible with --change"
- + " and --skip-project; recommended for debugging only")
- private List<String> projects = new ArrayList<>();
-
- @Option(
- name = "--skip-project",
- usage = "Rebuild all projects except these; incompatible with the --project and --change")
- private List<String> skipProjects = new ArrayList<>();
-
- @Option(
- name = "--change",
- usage =
- "Only rebuild these changes, do no other migration; incompatible with --project and"
- + " --skip-project; recommended for debugging only")
- private List<Integer> changes = new ArrayList<>();
-
- @Option(
- name = "--force",
- usage =
- "Force rebuilding changes where ReviewDb is still the source of truth, even if they"
- + " were previously migrated")
- private boolean force;
-
- @Option(
- name = "--force-state-change-with-skip",
- usage = "Force state change of the migration if projects are skipped")
- private boolean forceStateChangeWithSkip;
-
- @Option(name = "--trial", usage = TRIAL_USAGE)
- private boolean trial;
-
- @Option(
- name = "--sequence-gap",
- usage =
- "gap in change sequence numbers between last ReviewDb number and first NoteDb number;"
- + " negative indicates using the value of noteDb.changes.initialSequenceGap (default"
- + " 1000)")
- private int sequenceGap;
-
- @Option(
- name = "--reindex",
- usage =
- "Reindex all changes after migration; defaults to false in trial mode, true otherwise",
- handler = ExplicitBooleanOptionHandler.class)
- private Boolean reindex;
-
- @Option(name = "--verbose", usage = "Output more detailed information when reindexing")
- private boolean verbose;
-
- private Injector dbInjector;
- private Injector sysInjector;
- private LifecycleManager dbManager;
- private LifecycleManager sysManager;
-
- @Inject private GcAllUsers gcAllUsers;
- @Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
-
- @Override
- public int run() throws Exception {
- RuntimeShutdown.add(this::stop);
- try {
- mustHaveValidSite();
- dbInjector = createDbInjector(MULTI_USER);
-
- dbManager = new LifecycleManager();
- dbManager.add(dbInjector);
- dbManager.start();
-
- threads = limitThreads();
-
- sysInjector = createSysInjector();
- sysInjector.injectMembers(this);
- sysManager = new LifecycleManager();
- sysManager.add(sysInjector);
- sysInjector
- .getInstance(PluginGuiceEnvironment.class)
- .setDbCfgInjector(dbInjector, dbInjector);
- sysManager.start();
-
- try (NoteDbMigrator migrator =
- migratorBuilderProvider
- .get()
- .setThreads(threads)
- .setProgressOut(System.err)
- .setProjects(projects.stream().map(Project.NameKey::new).collect(toList()))
- .setSkipProjects(skipProjects.stream().map(Project.NameKey::new).collect(toList()))
- .setChanges(changes.stream().map(Change.Id::new).collect(toList()))
- .setTrialMode(trial)
- .setForceRebuild(force)
- .setForceStateChangeWithSkip(forceStateChangeWithSkip)
- .setSequenceGap(sequenceGap)
- .setVerbose(verbose)
- .build()) {
- if (!projects.isEmpty()
- || !changes.isEmpty()
- || (!forceStateChangeWithSkip && !skipProjects.isEmpty())) {
- migrator.rebuild();
- } else {
- migrator.migrate();
- }
- }
-
- PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out, UTF_8), true);
- gcAllUsers.run(w);
- // No closing of the PrintWriter here, as it would cascade down and eventually close
- // System.out and thereby swallow further output.
- } catch (Throwable e) {
- throw new Die(e.getMessage(), e);
- } finally {
- stop();
- }
-
- System.out.println("NoteDB Migration complete.");
- boolean reindex = firstNonNull(this.reindex, !trial);
- if (!reindex) {
- System.out.println("Reindexing changes is skipped (either because of trial mode, or ");
- System.out.println("because you requested so). If needed, reindex changes manually.");
- return 0;
- }
- // Reindex all indices, to save the user from having to run yet another program by hand while
- // their server is offline.
- ImmutableList.Builder<String> reindexArgsBuilder = ImmutableList.builder();
- reindexArgsBuilder.add(
- "--site-path",
- getSitePath().toString(),
- "--threads",
- Integer.toString(threads),
- "--index",
- ChangeSchemaDefinitions.NAME);
- if (verbose) {
- reindexArgsBuilder.add("--verbose");
- }
- List<String> reindexArgs = reindexArgsBuilder.build();
- System.out.println("Reindexing changes with:");
- System.out.println(" reindex " + reindexArgs.stream().collect(joining(" ")));
- Reindex reindexPgm = new Reindex();
- return reindexPgm.main(reindexArgs.stream().toArray(String[]::new));
- }
-
- private int limitThreads() {
- if (threads != null) {
- return threads;
- }
- int actualThreads;
- int procs = Runtime.getRuntime().availableProcessors();
- DataSourceType dsType = dbInjector.getInstance(DataSourceType.class);
- if (dsType.getDriver().equals("org.h2.Driver") && procs > ISSUE_8022_THREAD_LIMIT) {
- System.out.println(
- "Not using more than "
- + ISSUE_8022_THREAD_LIMIT
- + " threads due to http://crbug.com/gerrit/8022");
- System.out.println("Can be increased by passing --threads, but may cause errors");
- actualThreads = ISSUE_8022_THREAD_LIMIT;
- } else {
- actualThreads = procs;
- }
- actualThreads = ThreadLimiter.limitThreads(dbInjector, actualThreads);
- return actualThreads;
- }
-
- private Injector createSysInjector() {
- return dbInjector.createChildInjector(
- new FactoryModule() {
- @Override
- public void configure() {
- install(dbInjector.getInstance(BatchProgramModule.class));
- install(new DummyIndexModule());
- factory(ChangeResource.Factory.class);
- factory(GarbageCollection.Factory.class);
- }
- });
- }
-
- private void stop() {
- try {
- LifecycleManager m = sysManager;
- sysManager = null;
- if (m != null) {
- m.stop();
- }
- } finally {
- LifecycleManager m = dbManager;
- dbManager = null;
- if (m != null) {
- m.stop();
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/pgm/Passwd.java b/java/com/google/gerrit/pgm/Passwd.java
index f63d2f4520..10ed07d864 100644
--- a/java/com/google/gerrit/pgm/Passwd.java
+++ b/java/com/google/gerrit/pgm/Passwd.java
@@ -78,7 +78,7 @@ public class Passwd extends SiteProgram {
bind(Boolean.class).annotatedWith(InstallAllPlugins.class).toInstance(Boolean.FALSE);
bind(new TypeLiteral<List<String>>() {})
.annotatedWith(InstallPlugins.class)
- .toInstance(new ArrayList<String>());
+ .toInstance(new ArrayList<>());
bind(String.class)
.annotatedWith(SecureStoreClassName.class)
.toProvider(Providers.of(getConfiguredSecureStoreClass()));
diff --git a/java/com/google/gerrit/pgm/PrologShell.java b/java/com/google/gerrit/pgm/PrologShell.java
index 5decd68118..2780f84db0 100644
--- a/java/com/google/gerrit/pgm/PrologShell.java
+++ b/java/com/google/gerrit/pgm/PrologShell.java
@@ -30,9 +30,14 @@ public class PrologShell extends AbstractProgram {
@Option(name = "-s", metaVar = "FILE.pl", usage = "file to load")
private List<String> fileName = new ArrayList<>();
+ @Option(name = "-q", usage = "quiet mode without banner")
+ private boolean quiet;
+
@Override
public int run() {
- banner();
+ if (!quiet) {
+ banner();
+ }
BufferingPrologControl pcl = new BufferingPrologControl();
pcl.setPrologClassLoader(new PrologClassLoader(getClass().getClassLoader()));
@@ -55,7 +60,9 @@ public class PrologShell extends AbstractProgram {
try {
pcl.execute(Prolog.BUILTIN, "cafeteria");
- write("% halt\n");
+ if (!quiet) {
+ write("% halt\n");
+ }
return 0;
} catch (HaltException halt) {
write("% halt(" + halt.getStatus() + ")\n");
diff --git a/java/com/google/gerrit/pgm/ProtobufImport.java b/java/com/google/gerrit/pgm/ProtobufImport.java
deleted file mode 100644
index 0179b1d3ca..0000000000
--- a/java/com/google/gerrit/pgm/ProtobufImport.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2015 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.pgm;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-import static java.util.Objects.requireNonNull;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.Iterables;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.pgm.util.RuntimeShutdown;
-import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.protobuf.CodecFactory;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import com.google.gwtorm.schema.RelationModel;
-import com.google.gwtorm.schema.java.JavaSchemaModel;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.protobuf.ByteString;
-import com.google.protobuf.Parser;
-import com.google.protobuf.UnknownFieldSet;
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.file.Files;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.kohsuke.args4j.Option;
-
-/**
- * Import data from a protocol buffer dump into the database.
- *
- * <p>Takes as input a file containing protocol buffers concatenated together with varint length
- * encoding, as in {@link Parser#parseDelimitedFrom(InputStream)}. Each message contains a single
- * field with a tag corresponding to the relation ID in the {@link
- * com.google.gwtorm.server.Relation} annotation.
- *
- * <p><strong>Warning</strong>: This method blindly upserts data into the database. It should only
- * be used to restore a protobuf-formatted backup into a new, empty site.
- */
-public class ProtobufImport extends SiteProgram {
- @Option(
- name = "--file",
- aliases = {"-f"},
- required = true,
- metaVar = "FILE",
- usage = "File to import from")
- private File file;
-
- private final LifecycleManager manager = new LifecycleManager();
- private final Map<Integer, Relation> relations = new HashMap<>();
-
- @Inject private SchemaFactory<ReviewDb> schemaFactory;
-
- @Override
- public int run() throws Exception {
- mustHaveValidSite();
-
- Injector dbInjector = createDbInjector(SINGLE_USER);
- manager.add(dbInjector);
- manager.start();
- RuntimeShutdown.add(manager::stop);
- dbInjector.injectMembers(this);
-
- ProgressMonitor progress = new TextProgressMonitor();
- progress.beginTask("Importing entities", ProgressMonitor.UNKNOWN);
- try (ReviewDb db = schemaFactory.open()) {
- for (RelationModel model : new JavaSchemaModel(ReviewDb.class).getRelations()) {
- relations.put(model.getRelationID(), Relation.create(model, db));
- }
-
- Parser<UnknownFieldSet> parser = UnknownFieldSet.getDefaultInstance().getParserForType();
- try (InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
- UnknownFieldSet msg;
- while ((msg = parser.parseDelimitedFrom(in)) != null) {
- Map.Entry<Integer, UnknownFieldSet.Field> e =
- Iterables.getOnlyElement(msg.asMap().entrySet());
- Relation rel =
- requireNonNull(
- relations.get(e.getKey()),
- String.format("unknown relation ID %s in message: %s", e.getKey(), msg));
- List<ByteString> values = e.getValue().getLengthDelimitedList();
- checkState(values.size() == 1, "expected one string field in message: %s", msg);
- upsert(rel, values.get(0));
- progress.update(1);
- }
- }
- progress.endTask();
- }
-
- return 0;
- }
-
- @SuppressWarnings({"rawtypes", "unchecked"})
- private static void upsert(Relation rel, ByteString s) throws OrmException {
- Collection ents = Collections.singleton(rel.codec().decode(s));
- try {
- // Not all relations support update; fall back manually.
- rel.access().insert(ents);
- } catch (OrmDuplicateKeyException e) {
- rel.access().delete(ents);
- rel.access().insert(ents);
- }
- }
-
- @AutoValue
- abstract static class Relation {
- private static Relation create(RelationModel model, ReviewDb db)
- throws IllegalAccessException, InvocationTargetException, NoSuchMethodException,
- ClassNotFoundException {
- Method m = db.getClass().getMethod(model.getMethodName());
- Class<?> clazz = Class.forName(model.getEntityTypeClassName());
- return new AutoValue_ProtobufImport_Relation(
- (Access<?, ?>) m.invoke(db), CodecFactory.encoder(clazz));
- }
-
- abstract Access<?, ?> access();
-
- abstract ProtobufCodec<?> codec();
- }
-}
diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java
index 85e860966f..6fd542406c 100644
--- a/java/com/google/gerrit/pgm/Reindex.java
+++ b/java/com/google/gerrit/pgm/Reindex.java
@@ -14,7 +14,6 @@
package com.google.gerrit.pgm;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
@@ -29,7 +28,6 @@ import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.pgm.util.BatchProgramModule;
import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.pgm.util.ThreadLimiter;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.IndexModule;
@@ -40,7 +38,6 @@ import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -81,10 +78,9 @@ public class Reindex extends SiteProgram {
@Override
public int run() throws Exception {
mustHaveValidSite();
- dbInjector = createDbInjector(MULTI_USER);
+ dbInjector = createDbInjector();
cfgInjector = dbInjector.createChildInjector();
globalConfig = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- threads = ThreadLimiter.limitThreads(dbInjector, threads);
overrideConfig();
LifecycleManager dbManager = new LifecycleManager();
dbManager.add(dbInjector);
@@ -117,7 +113,7 @@ public class Reindex extends SiteProgram {
return true;
}
- private boolean reindex() throws IOException {
+ private boolean reindex() {
boolean ok = true;
for (IndexDefinition<?, ?, ?> def : indexDefs) {
if (indices.isEmpty() || indices.contains(def.getName())) {
@@ -163,7 +159,7 @@ public class Reindex extends SiteProgram {
throw new IllegalStateException("unsupported index.type");
}
modules.add(indexModule);
- modules.add(dbInjector.getInstance(BatchProgramModule.class));
+ modules.add(new BatchProgramModule());
modules.add(
new FactoryModule() {
@Override
@@ -189,8 +185,7 @@ public class Reindex extends SiteProgram {
globalConfig.setBoolean("index", null, "autoReindexIfStale", false);
}
- private <K, V, I extends Index<K, V>> boolean reindex(IndexDefinition<K, V, I> def)
- throws IOException {
+ private <K, V, I extends Index<K, V>> boolean reindex(IndexDefinition<K, V, I> def) {
I index = def.getIndexCollection().getSearchIndex();
requireNonNull(
index, () -> String.format("no active search index configured for %s", def.getName()));
diff --git a/java/com/google/gerrit/pgm/Rulec.java b/java/com/google/gerrit/pgm/Rulec.java
index add06ef11d..aa72ae00b9 100644
--- a/java/com/google/gerrit/pgm/Rulec.java
+++ b/java/com/google/gerrit/pgm/Rulec.java
@@ -14,8 +14,6 @@
package com.google.gerrit.pgm;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.rules.PrologCompiler;
@@ -60,7 +58,7 @@ public class Rulec extends SiteProgram {
@Override
public int run() throws Exception {
- dbInjector = createDbInjector(SINGLE_USER);
+ dbInjector = createDbInjector();
manager.add(dbInjector);
manager.start();
dbInjector
diff --git a/java/com/google/gerrit/pgm/SwitchSecureStore.java b/java/com/google/gerrit/pgm/SwitchSecureStore.java
index 3f10130f7c..733c9d1453 100644
--- a/java/com/google/gerrit/pgm/SwitchSecureStore.java
+++ b/java/com/google/gerrit/pgm/SwitchSecureStore.java
@@ -14,8 +14,6 @@
package com.google.gerrit.pgm;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
-
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
@@ -86,7 +84,7 @@ public class SwitchSecureStore extends SiteProgram {
logger.atInfo().log(
"Current secureStoreClass property (%s) will be replaced with %s",
currentSecureStoreName, newSecureStore);
- Injector dbInjector = createDbInjector(SINGLE_USER);
+ Injector dbInjector = createDbInjector();
SecureStore currentStore = getSecureStore(currentSecureStoreName, dbInjector);
SecureStore newStore = getSecureStore(newSecureStore, dbInjector);
diff --git a/java/com/google/gerrit/pgm/http/jetty/BUILD b/java/com/google/gerrit/pgm/http/jetty/BUILD
index e1e1a0b74e..9d65ded896 100644
--- a/java/com/google/gerrit/pgm/http/jetty/BUILD
+++ b/java/com/google/gerrit/pgm/http/jetty/BUILD
@@ -16,7 +16,7 @@ java_library(
"//java/com/google/gerrit/util/logging",
"//lib:gson",
"//lib:guava",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLog.java b/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
index 978d490da7..4e4c93b57b 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
@@ -48,6 +48,7 @@ class HttpLog extends AbstractLifeCycle implements RequestLog {
protected static final String P_PROTOCOL = "Version";
protected static final String P_STATUS = "Status";
protected static final String P_CONTENT_LENGTH = "Content-Length";
+ protected static final String P_LATENCY = "Latency";
protected static final String P_REFERER = "Referer";
protected static final String P_USER_AGENT = "User-Agent";
@@ -109,6 +110,7 @@ class HttpLog extends AbstractLifeCycle implements RequestLog {
set(event, P_PROTOCOL, req.getProtocol());
set(event, P_STATUS, rsp.getStatus());
set(event, P_CONTENT_LENGTH, rsp.getContentCount());
+ set(event, P_LATENCY, System.currentTimeMillis() - req.getTimeStamp());
set(event, P_REFERER, req.getHeader("Referer"));
set(event, P_USER_AGENT, req.getHeader("User-Agent"));
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java b/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
index 73d9ee45c7..77726609a1 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
@@ -16,6 +16,7 @@ package com.google.gerrit.pgm.http.jetty;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_CONTENT_LENGTH;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_HOST;
+import static com.google.gerrit.pgm.http.jetty.HttpLog.P_LATENCY;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_METHOD;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_PROTOCOL;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_REFERER;
@@ -52,6 +53,7 @@ public class HttpLogJsonLayout extends JsonLayout {
public String protocol;
public String status;
public String contentLength;
+ public String latency;
public String referer;
public String userAgent;
@@ -65,6 +67,7 @@ public class HttpLogJsonLayout extends JsonLayout {
this.protocol = getMdcString(event, P_PROTOCOL);
this.status = getMdcString(event, P_STATUS);
this.contentLength = getMdcString(event, P_CONTENT_LENGTH);
+ this.latency = getMdcString(event, P_LATENCY);
this.referer = getMdcString(event, P_REFERER);
this.userAgent = getMdcString(event, P_USER_AGENT);
}
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java b/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
index 9f240ac7c9..4b52c6f0fa 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
@@ -72,6 +72,9 @@ public final class HttpLogLayout extends Layout {
opt(buf, event, HttpLog.P_CONTENT_LENGTH);
buf.append(' ');
+ opt(buf, event, HttpLog.P_LATENCY);
+
+ buf.append(' ');
dq_opt(buf, event, HttpLog.P_REFERER);
buf.append(' ');
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index ae27cbcb17..0bbb51d198 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -50,7 +50,6 @@ import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@@ -330,13 +329,9 @@ public class JettyServer {
defaultPort = 8080;
config.addCustomizer(new ForwardedRequestCustomizer());
config.addCustomizer(
- new HttpConfiguration.Customizer() {
- @Override
- public void customize(
- Connector connector, HttpConfiguration channelConfig, Request request) {
- request.setScheme(HttpScheme.HTTPS.asString());
- request.setSecure(true);
- }
+ (connector, channelConfig, request) -> {
+ request.setScheme(HttpScheme.HTTPS.asString());
+ request.setSecure(true);
});
c = newServerConnector(server, acceptors, config);
@@ -439,7 +434,7 @@ public class JettyServer {
maxThreads,
minThreads,
idleTimeout,
- new BlockingArrayQueue<Runnable>(
+ new BlockingArrayQueue<>(
minThreads, // capacity,
minThreads, // growBy,
maxCapacity // maxCapacity
diff --git a/java/com/google/gerrit/pgm/init/BUILD b/java/com/google/gerrit/pgm/init/BUILD
index 9298dd0d25..eb0d49e36a 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/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/launcher",
@@ -23,8 +24,6 @@ java_library(
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
"//lib:guava",
- "//lib:gwtjsonrpc",
- "//lib:gwtorm",
"//lib:h2",
"//lib/commons:validator",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/pgm/init/BaseInit.java b/java/com/google/gerrit/pgm/init/BaseInit.java
index 234abfdded..0bc1860fcd 100644
--- a/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -14,8 +14,6 @@
package com.google.gerrit.pgm.init;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
-import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
import static com.google.inject.Scopes.SINGLETON;
import static com.google.inject.Stage.PRODUCTION;
@@ -24,6 +22,7 @@ import com.google.common.base.Strings;
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.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.pgm.init.api.ConsoleUI;
@@ -35,31 +34,23 @@ import com.google.gerrit.pgm.init.index.IndexManagerOnInit;
import com.google.gerrit.pgm.init.index.elasticsearch.ElasticIndexModuleOnInit;
import com.google.gerrit.pgm.init.index.lucene.LuceneIndexModuleOnInit;
import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.plugins.JarScanner;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.server.schema.SchemaUpdater;
+import com.google.gerrit.server.schema.NoteDbSchemaUpdater;
import com.google.gerrit.server.schema.UpdateUI;
import com.google.gerrit.server.securestore.SecureStore;
import com.google.gerrit.server.securestore.SecureStoreClassName;
import com.google.gerrit.server.securestore.SecureStoreProvider;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.gwtorm.server.StatementExecutor;
import com.google.inject.AbstractModule;
import com.google.inject.CreationException;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Module;
-import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.Message;
import com.google.inject.util.Providers;
@@ -75,14 +66,12 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import javax.sql.DataSource;
/** Initialize a new Gerrit installation. */
public class BaseInit extends SiteProgram {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final boolean standalone;
- private final boolean initDb;
protected final PluginsDistribution pluginsDistribution;
private final List<String> pluginsToInstall;
@@ -90,7 +79,6 @@ public class BaseInit extends SiteProgram {
protected BaseInit(PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) {
this.standalone = true;
- this.initDb = true;
this.pluginsDistribution = pluginsDistribution;
this.pluginsToInstall = pluginsToInstall;
}
@@ -98,22 +86,10 @@ public class BaseInit extends SiteProgram {
public BaseInit(
Path sitePath,
boolean standalone,
- boolean initDb,
PluginsDistribution pluginsDistribution,
List<String> pluginsToInstall) {
- this(sitePath, null, standalone, initDb, pluginsDistribution, pluginsToInstall);
- }
-
- public BaseInit(
- Path sitePath,
- final Provider<DataSource> dsProvider,
- boolean standalone,
- boolean initDb,
- PluginsDistribution pluginsDistribution,
- List<String> pluginsToInstall) {
- super(sitePath, dsProvider);
+ super(sitePath);
this.standalone = standalone;
- this.initDb = initDb;
this.pluginsDistribution = pluginsDistribution;
this.pluginsToInstall = pluginsToInstall;
}
@@ -143,7 +119,7 @@ public class BaseInit extends SiteProgram {
run = createSiteRun(init);
try {
run.upgradeSchema();
- } catch (OrmException e) {
+ } catch (StorageException e) {
String msg = "Couldn't upgrade schema. Expected if slave and read-only database";
System.err.println(msg);
logger.atSevere().withCause(e).log(msg);
@@ -262,15 +238,14 @@ public class BaseInit extends SiteProgram {
}
m.add(new GerritServerConfigModule());
- m.add(new InitModule(standalone, initDb));
+ m.add(new InitModule(standalone));
m.add(
new AbstractModule() {
@Override
protected void configure() {
bind(ConsoleUI.class).toInstance(ui);
bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
- List<String> plugins =
- MoreObjects.firstNonNull(getInstallPlugins(), new ArrayList<String>());
+ List<String> plugins = MoreObjects.firstNonNull(getInstallPlugins(), new ArrayList<>());
bind(new TypeLiteral<List<String>>() {})
.annotatedWith(InstallPlugins.class)
.toInstance(plugins);
@@ -364,8 +339,7 @@ public class BaseInit extends SiteProgram {
public final ConsoleUI ui;
public final SitePaths site;
public final InitFlags flags;
- final SchemaUpdater schemaUpdater;
- final SchemaFactory<ReviewDb> schema;
+ final NoteDbSchemaUpdater noteDbSchemaUpdater;
final GitRepositoryManager repositoryManager;
@Inject
@@ -373,80 +347,50 @@ public class BaseInit extends SiteProgram {
ConsoleUI ui,
SitePaths site,
InitFlags flags,
- SchemaUpdater schemaUpdater,
- @ReviewDbFactory SchemaFactory<ReviewDb> schema,
+ NoteDbSchemaUpdater noteDbSchemaUpdater,
GitRepositoryManager repositoryManager) {
this.ui = ui;
this.site = site;
this.flags = flags;
- this.schemaUpdater = schemaUpdater;
- this.schema = schema;
+ this.noteDbSchemaUpdater = noteDbSchemaUpdater;
this.repositoryManager = repositoryManager;
}
- void upgradeSchema() throws OrmException {
- final List<String> pruneList = new ArrayList<>();
- schemaUpdater.update(
- new UpdateUI() {
- @Override
- public void message(String message) {
- System.err.println(message);
- System.err.flush();
- }
-
- @Override
- public boolean yesno(boolean defaultValue, String message) {
- return ui.yesno(defaultValue, message);
- }
+ void upgradeSchema() {
+ noteDbSchemaUpdater.update(new UpdateUIImpl(ui));
+ }
- @Override
- public void waitForUser() {
- ui.waitForUser();
- }
+ private static class UpdateUIImpl implements UpdateUI {
+ private final ConsoleUI consoleUi;
- @Override
- public String readString(
- String defaultValue, Set<String> allowedValues, String message) {
- return ui.readString(defaultValue, allowedValues, message);
- }
+ UpdateUIImpl(ConsoleUI consoleUi) {
+ this.consoleUi = consoleUi;
+ }
- @Override
- public boolean isBatch() {
- return ui.isBatch();
- }
+ @Override
+ public void message(String message) {
+ System.err.println(message);
+ System.err.flush();
+ }
- @Override
- public void pruneSchema(StatementExecutor e, List<String> prune) {
- for (String p : prune) {
- if (!pruneList.contains(p)) {
- pruneList.add(p);
- }
- }
- }
- });
+ @Override
+ public boolean yesno(boolean defaultValue, String message) {
+ return consoleUi.yesno(defaultValue, message);
+ }
- if (!pruneList.isEmpty()) {
- StringBuilder msg = new StringBuilder();
- msg.append("Execute the following SQL to drop unused objects:\n");
- msg.append("\n");
- for (String sql : pruneList) {
- msg.append(" ");
- msg.append(sql);
- msg.append(";\n");
- }
+ @Override
+ public void waitForUser() {
+ consoleUi.waitForUser();
+ }
- if (ui.isBatch()) {
- System.err.print(msg);
- System.err.flush();
+ @Override
+ public String readString(String defaultValue, Set<String> allowedValues, String message) {
+ return consoleUi.readString(defaultValue, allowedValues, message);
+ }
- } else if (ui.yesno(true, "%s\nExecute now", msg)) {
- try (JdbcSchema db = (JdbcSchema) unwrapDb(schema.open());
- JdbcExecutor e = new JdbcExecutor(db)) {
- for (String sql : pruneList) {
- e.execute(sql);
- }
- }
- }
+ @Override
+ public boolean isBatch() {
+ return consoleUi.isBatch();
}
}
}
@@ -466,7 +410,7 @@ public class BaseInit extends SiteProgram {
bind(InitFlags.class).toInstance(init.flags);
}
});
- Injector dbInjector = createDbInjector(SINGLE_USER);
+ Injector dbInjector = createDbInjector();
switch (IndexModule.getIndexType(dbInjector)) {
case LUCENE:
diff --git a/java/com/google/gerrit/pgm/init/DB2Initializer.java b/java/com/google/gerrit/pgm/init/DB2Initializer.java
deleted file mode 100644
index 9dc1088b6f..0000000000
--- a/java/com/google/gerrit/pgm/init/DB2Initializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2015 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class DB2Initializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "50001";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, false);
- databaseSection.string("Database name", "database", "gerrit");
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java b/java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java
deleted file mode 100644
index 27019576ae..0000000000
--- a/java/com/google/gerrit/pgm/init/DatabaseConfigInitializer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2012 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.pgm.init;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-/** Abstraction of initializer for the database section */
-interface DatabaseConfigInitializer {
-
- /**
- * Performs database platform specific configuration steps and writes configuration parameters
- * into the given database section
- */
- void initConfig(Section databaseSection);
-}
diff --git a/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java b/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
deleted file mode 100644
index 44f883ad25..0000000000
--- a/java/com/google/gerrit/pgm/init/DatabaseConfigModule.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2012 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.pgm.init;
-
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.AbstractModule;
-import com.google.inject.name.Names;
-
-public class DatabaseConfigModule extends AbstractModule {
-
- private final SitePaths site;
-
- public DatabaseConfigModule(SitePaths site) {
- this.site = site;
- }
-
- @Override
- protected void configure() {
- bind(SitePaths.class).toInstance(site);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("db2"))
- .to(DB2Initializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("derby"))
- .to(DerbyInitializer.class);
- bind(DatabaseConfigInitializer.class).annotatedWith(Names.named("h2")).to(H2Initializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("jdbc"))
- .to(JDBCInitializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("mariadb"))
- .to(MariaDbInitializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("mysql"))
- .to(MySqlInitializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("oracle"))
- .to(OracleInitializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("postgresql"))
- .to(PostgreSQLInitializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("maxdb"))
- .to(MaxDbInitializer.class);
- bind(DatabaseConfigInitializer.class)
- .annotatedWith(Names.named("hana"))
- .to(HANAInitializer.class);
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/DerbyInitializer.java b/java/com/google/gerrit/pgm/init/DerbyInitializer.java
deleted file mode 100644
index 3aad0f4aa9..0000000000
--- a/java/com/google/gerrit/pgm/init/DerbyInitializer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.die;
-
-import com.google.gerrit.common.FileUtil;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.nio.file.Path;
-
-class DerbyInitializer implements DatabaseConfigInitializer {
-
- private final SitePaths site;
-
- @Inject
- DerbyInitializer(SitePaths site) {
- this.site = site;
- }
-
- @Override
- public void initConfig(Section databaseSection) {
- String path = databaseSection.get("database");
- Path db;
- if (path == null) {
- db = site.resolve("db").resolve("ReviewDB");
- databaseSection.set("database", db.toString());
- } else {
- db = site.resolve(path);
- }
- if (db == null) {
- throw die("database.database must be supplied for Derby");
- }
- db = db.getParent();
- FileUtil.mkdirsOrDie(db, "cannot create database.database");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java b/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
index 6336c939fa..9519653cab 100644
--- a/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
@@ -23,7 +23,6 @@ import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
@@ -49,7 +48,7 @@ public class ExternalIdsOnInit {
}
public synchronized void insert(String commitMessage, Collection<ExternalId> extIds)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
File path = getPath();
if (path != null) {
try (Repository allUsersRepo = new FileRepository(path)) {
diff --git a/java/com/google/gerrit/pgm/init/GroupsOnInit.java b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
index 8e06aa16e2..273ebfb9e0 100644
--- a/java/com/google/gerrit/pgm/init/GroupsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
@@ -20,12 +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.common.errors.NoSuchGroupException;
+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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerIdProvider;
@@ -37,7 +36,6 @@ import com.google.gerrit.server.group.db.AuditLogFormatter;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
@@ -54,9 +52,8 @@ import org.eclipse.jgit.util.FS;
/**
* A database accessor for calls related to groups.
*
- * <p>All calls which read or write group related details to the database <strong>during
- * init</strong> (either ReviewDb or NoteDb) are gathered here. For non-init cases, use {@code
- * Groups} or {@code GroupsUpdate} instead.
+ * <p>All calls which read or write group related details to the NoteDb <strong>during init</strong>
+ * are gathered here. For non-init cases, use {@code Groups} or {@code GroupsUpdate} instead.
*
* <p>All methods of this class refer to <em>internal</em> groups.
*/
@@ -76,16 +73,14 @@ public class GroupsOnInit {
/**
* Returns the {@code AccountGroup} for the specified {@code GroupReference}.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @param groupReference the {@code GroupReference} of the group
* @return the {@code InternalGroup} represented by the {@code GroupReference}
- * @throws OrmException if the group couldn't be retrieved from ReviewDb
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the data in NoteDb is in an incorrect format
* @throws NoSuchGroupException if a group with such a name doesn't exist
*/
- public InternalGroup getExistingGroup(ReviewDb db, GroupReference groupReference)
- throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+ public InternalGroup getExistingGroup(GroupReference groupReference)
+ throws NoSuchGroupException, IOException, ConfigInvalidException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
@@ -102,14 +97,11 @@ public class GroupsOnInit {
/**
* Returns {@code GroupReference}s for all internal groups.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @return a stream of the {@code GroupReference}s of all internal groups
- * @throws OrmException if an error occurs while reading from ReviewDb
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the data in NoteDb is in an incorrect format
*/
- public Stream<GroupReference> getAllGroupReferences(ReviewDb db)
- throws OrmException, IOException, ConfigInvalidException {
+ public Stream<GroupReference> getAllGroupReferences() throws IOException, ConfigInvalidException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
@@ -126,14 +118,12 @@ public class GroupsOnInit {
* <p><strong>Note</strong>: This method doesn't check whether the account exists! It also doesn't
* update the account index!
*
- * @param db the {@code ReviewDb} instance to update
* @param groupUuid the UUID of the group
* @param account the account to add
- * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
* @throws NoSuchGroupException if the specified group doesn't exist
*/
- public void addGroupMember(ReviewDb db, AccountGroup.UUID groupUuid, Account account)
- throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+ public void addGroupMember(AccountGroup.UUID groupUuid, Account account)
+ throws NoSuchGroupException, IOException, ConfigInvalidException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository repository = new FileRepository(allUsersRepoPath)) {
diff --git a/java/com/google/gerrit/pgm/init/H2Initializer.java b/java/com/google/gerrit/pgm/init/H2Initializer.java
deleted file mode 100644
index 63aa6ec9b7..0000000000
--- a/java/com/google/gerrit/pgm/init/H2Initializer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2012 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.die;
-
-import com.google.gerrit.common.FileUtil;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.nio.file.Path;
-
-class H2Initializer implements DatabaseConfigInitializer {
-
- private final SitePaths site;
-
- @Inject
- H2Initializer(SitePaths site) {
- this.site = site;
- }
-
- @Override
- public void initConfig(Section databaseSection) {
- String path = databaseSection.get("database");
- Path db;
- if (path == null) {
- db = site.resolve("db").resolve("ReviewDB");
- databaseSection.set("database", db.toString());
- } else {
- db = site.resolve(path);
- }
- if (db == null) {
- throw die("database.database must be supplied for H2");
- }
- db = db.getParent();
- FileUtil.mkdirsOrDie(db, "cannot create database.database");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/HANAInitializer.java b/java/com/google/gerrit/pgm/init/HANAInitializer.java
deleted file mode 100644
index 713392d547..0000000000
--- a/java/com/google/gerrit/pgm/init/HANAInitializer.java
+++ /dev/null
@@ -1,32 +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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class HANAInitializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "(hana default)";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, true);
- databaseSection.string("Database name", "database", null);
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/InitAdminUser.java b/java/com/google/gerrit/pgm/init/InitAdminUser.java
index f12fa50507..27e6ce90ab 100644
--- a/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -18,7 +18,7 @@ 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.common.errors.NoSuchGroupException;
+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;
@@ -26,7 +26,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountSshKey;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -37,7 +36,6 @@ import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
@@ -57,7 +55,6 @@ public class InitAdminUser implements InitStep {
private final ExternalIdsOnInit externalIds;
private final SequencesOnInit sequencesOnInit;
private final GroupsOnInit groupsOnInit;
- private SchemaFactory<ReviewDb> dbFactory;
private AccountIndexCollection accountIndexCollection;
private GroupIndexCollection groupIndexCollection;
@@ -84,11 +81,6 @@ public class InitAdminUser implements InitStep {
@Override
public void run() {}
- @Inject(optional = true)
- void set(SchemaFactory<ReviewDb> dbFactory) {
- this.dbFactory = dbFactory;
- }
-
@Inject
void set(AccountIndexCollection accountIndexCollection) {
this.accountIndexCollection = accountIndexCollection;
@@ -106,58 +98,56 @@ public class InitAdminUser implements InitStep {
return;
}
- try (ReviewDb db = dbFactory.open()) {
- if (!accounts.hasAnyAccount()) {
- ui.header("Gerrit Administrator");
- if (ui.yesno(true, "Create administrator user")) {
- Account.Id id = new Account.Id(sequencesOnInit.nextAccountId(db));
- String username = ui.readString("admin", "username");
- String name = ui.readString("Administrator", "name");
- String httpPassword = ui.readString("secret", "HTTP password");
- AccountSshKey sshKey = readSshKey(id);
- String email = readEmail(sshKey);
-
- List<ExternalId> extIds = new ArrayList<>(2);
- extIds.add(ExternalId.createUsername(username, id, httpPassword));
-
- if (email != null) {
- extIds.add(ExternalId.createEmail(id, email));
- }
- 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);
-
- // Only two groups should exist at this point in time and hence iterating over all of them
- // is cheap.
- Optional<GroupReference> adminGroupReference =
- groupsOnInit
- .getAllGroupReferences(db)
- .filter(group -> group.getName().equals("Administrators"))
- .findAny();
- if (!adminGroupReference.isPresent()) {
- throw new NoSuchGroupException("Administrators");
- }
- GroupReference adminGroup = adminGroupReference.get();
- groupsOnInit.addGroupMember(db, adminGroup.getUUID(), a);
-
- if (sshKey != null) {
- VersionedAuthorizedKeysOnInit authorizedKeys = authorizedKeysFactory.create(id).load();
- authorizedKeys.addKey(sshKey.sshPublicKey());
- authorizedKeys.save("Add SSH key for initial admin user\n");
- }
-
- AccountState as = AccountState.forAccount(new AllUsersName(allUsers.get()), a, extIds);
- for (AccountIndex accountIndex : accountIndexCollection.getWriteIndexes()) {
- accountIndex.replace(as);
- }
-
- InternalGroup adminInternalGroup = groupsOnInit.getExistingGroup(db, adminGroup);
- for (GroupIndex groupIndex : groupIndexCollection.getWriteIndexes()) {
- groupIndex.replace(adminInternalGroup);
- }
+ if (!accounts.hasAnyAccount()) {
+ ui.header("Gerrit Administrator");
+ if (ui.yesno(true, "Create administrator user")) {
+ Account.Id id = new Account.Id(sequencesOnInit.nextAccountId());
+ String username = ui.readString("admin", "username");
+ String name = ui.readString("Administrator", "name");
+ String httpPassword = ui.readString("secret", "HTTP password");
+ AccountSshKey sshKey = readSshKey(id);
+ String email = readEmail(sshKey);
+
+ List<ExternalId> extIds = new ArrayList<>(2);
+ extIds.add(ExternalId.createUsername(username, id, httpPassword));
+
+ if (email != null) {
+ extIds.add(ExternalId.createEmail(id, email));
+ }
+ 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);
+
+ // Only two groups should exist at this point in time and hence iterating over all of them
+ // is cheap.
+ Optional<GroupReference> adminGroupReference =
+ groupsOnInit
+ .getAllGroupReferences()
+ .filter(group -> group.getName().equals("Administrators"))
+ .findAny();
+ if (!adminGroupReference.isPresent()) {
+ throw new NoSuchGroupException("Administrators");
+ }
+ GroupReference adminGroup = adminGroupReference.get();
+ groupsOnInit.addGroupMember(adminGroup.getUUID(), a);
+
+ if (sshKey != null) {
+ VersionedAuthorizedKeysOnInit authorizedKeys = authorizedKeysFactory.create(id).load();
+ authorizedKeys.addKey(sshKey.sshPublicKey());
+ authorizedKeys.save("Add SSH key for initial admin user\n");
+ }
+
+ AccountState as = AccountState.forAccount(new AllUsersName(allUsers.get()), a, extIds);
+ for (AccountIndex accountIndex : accountIndexCollection.getWriteIndexes()) {
+ accountIndex.replace(as);
+ }
+
+ InternalGroup adminInternalGroup = groupsOnInit.getExistingGroup(adminGroup);
+ for (GroupIndex groupIndex : groupIndexCollection.getWriteIndexes()) {
+ groupIndex.replace(adminInternalGroup);
}
}
}
diff --git a/java/com/google/gerrit/pgm/init/InitAuth.java b/java/com/google/gerrit/pgm/init/InitAuth.java
index a52d8ba096..c15cff386f 100644
--- a/java/com/google/gerrit/pgm/init/InitAuth.java
+++ b/java/com/google/gerrit/pgm/init/InitAuth.java
@@ -26,7 +26,7 @@ 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.Section;
-import com.google.gwtjsonrpc.server.SignedToken;
+import com.google.gerrit.server.mail.SignedToken;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.EnumSet;
diff --git a/java/com/google/gerrit/pgm/init/InitDatabase.java b/java/com/google/gerrit/pgm/init/InitDatabase.java
deleted file mode 100644
index 558716caf8..0000000000
--- a/java/com/google/gerrit/pgm/init/InitDatabase.java
+++ /dev/null
@@ -1,140 +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.pgm.init;
-
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.inject.Stage.PRODUCTION;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
-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.Section;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.inject.Binding;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
-import java.lang.annotation.Annotation;
-import java.util.List;
-import java.util.Set;
-import org.eclipse.jgit.lib.Config;
-
-/** Initialize the {@code database} configuration section. */
-@Singleton
-class InitDatabase implements InitStep {
- private final ConsoleUI ui;
- private final SitePaths site;
- private final Libraries libraries;
- private final InitFlags flags;
- private final Section database;
- private final Section idSection;
- private final Section noteDbChanges;
-
- @Inject
- InitDatabase(
- ConsoleUI ui,
- SitePaths site,
- Libraries libraries,
- InitFlags flags,
- Section.Factory sections) {
- this.ui = ui;
- this.site = site;
- this.libraries = libraries;
- this.flags = flags; // Don't grab any flags yet; they aren't initialized until BaseInit#run.
- this.database = sections.get("database", null);
- this.idSection = sections.get(GerritServerIdProvider.SECTION, null);
- this.noteDbChanges = sections.get(SECTION_NOTE_DB, CHANGES.key());
- }
-
- @Override
- public void run() {
- initSqlDb();
- if (flags.isNew) {
- initNoteDb();
- }
- }
-
- private void initSqlDb() {
- ui.header("SQL Database");
-
- Set<String> allowedValues = Sets.newTreeSet();
- Injector i = Guice.createInjector(PRODUCTION, new DatabaseConfigModule(site));
- List<Binding<DatabaseConfigInitializer>> dbConfigBindings =
- i.findBindingsByType(new TypeLiteral<DatabaseConfigInitializer>() {});
- for (Binding<DatabaseConfigInitializer> binding : dbConfigBindings) {
- Annotation annotation = binding.getKey().getAnnotation();
- if (annotation instanceof Named) {
- allowedValues.add(((Named) annotation).value());
- }
- }
-
- if (!Strings.isNullOrEmpty(database.get("url"))
- && Strings.isNullOrEmpty(database.get("type"))) {
- database.set("type", "jdbc");
- }
-
- String dbType = database.select("Database server type", "type", "h2", allowedValues);
-
- DatabaseConfigInitializer dci =
- i.getInstance(Key.get(DatabaseConfigInitializer.class, Names.named(dbType.toLowerCase())));
-
- if (dci instanceof MySqlInitializer) {
- libraries.mysqlDriver.downloadRequired();
- } else if (dci instanceof MariaDbInitializer) {
- libraries.mariadbDriver.downloadRequired();
- } else if (dci instanceof OracleInitializer) {
- libraries.oracleDriver.downloadRequired();
- } else if (dci instanceof DB2Initializer) {
- libraries.db2Driver.downloadRequired();
- } else if (dci instanceof HANAInitializer) {
- libraries.hanaDriver.downloadRequired();
- }
-
- dci.initConfig(database);
-
- // Initialize UUID for NoteDb on first init.
- String id = idSection.get(GerritServerIdProvider.KEY);
- if (Strings.isNullOrEmpty(id)) {
- idSection.set(GerritServerIdProvider.KEY, GerritServerIdProvider.generate());
- }
- }
-
- private void initNoteDb() {
- ui.header("NoteDb Database");
- ui.message(
- "Use NoteDb for change metadata?\n"
- + " See documentation:\n"
- + " https://gerrit-review.googlesource.com/Documentation/note-db.html\n");
- if (!ui.yesno(true, "Enable")) {
- return;
- }
-
- Config defaultConfig = new Config();
- NotesMigrationState.FINAL.setConfigValues(defaultConfig);
- for (String name : defaultConfig.getNames(SECTION_NOTE_DB, CHANGES.key())) {
- noteDbChanges.set(name, defaultConfig.getString(SECTION_NOTE_DB, CHANGES.key(), name));
- }
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/InitHttpd.java b/java/com/google/gerrit/pgm/init/InitHttpd.java
index ada4ac5ddc..b9fe874ad8 100644
--- a/java/com/google/gerrit/pgm/init/InitHttpd.java
+++ b/java/com/google/gerrit/pgm/init/InitHttpd.java
@@ -25,7 +25,7 @@ import com.google.gerrit.pgm.init.api.InitFlags;
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.gwtjsonrpc.server.SignedToken;
+import com.google.gerrit.server.mail.SignedToken;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
diff --git a/java/com/google/gerrit/pgm/init/InitModule.java b/java/com/google/gerrit/pgm/init/InitModule.java
index d61e72ce27..b65867538b 100644
--- a/java/com/google/gerrit/pgm/init/InitModule.java
+++ b/java/com/google/gerrit/pgm/init/InitModule.java
@@ -26,27 +26,20 @@ import java.lang.annotation.Annotation;
public class InitModule extends FactoryModule {
private final boolean standalone;
- private final boolean initDb;
- public InitModule(boolean standalone, boolean initDb) {
+ public InitModule(boolean standalone) {
this.standalone = standalone;
- this.initDb = initDb;
}
@Override
protected void configure() {
bind(SitePaths.class);
- bind(Libraries.class);
- bind(LibraryDownloader.class);
factory(Section.Factory.class);
factory(VersionedAuthorizedKeysOnInit.Factory.class);
// Steps are executed in the order listed here.
//
step().to(InitGitManager.class);
- if (initDb) {
- step().to(InitDatabase.class);
- }
step().to(InitLogging.class);
step().to(InitIndex.class);
step().to(InitAuth.class);
diff --git a/java/com/google/gerrit/pgm/init/JDBCInitializer.java b/java/com/google/gerrit/pgm/init/JDBCInitializer.java
deleted file mode 100644
index e3a1d666a0..0000000000
--- a/java/com/google/gerrit/pgm/init/JDBCInitializer.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2012 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.pgm.init.api.Section;
-
-class JDBCInitializer implements DatabaseConfigInitializer {
- @Override
- public void initConfig(Section database) {
- boolean hasUrl = Strings.emptyToNull(database.get("url")) != null;
- database.string("URL", "url", null);
- guessDriver(database);
- database.string("Driver class name", "driver", null);
- database.string("Database username", "username", hasUrl ? null : username());
- database.password("username", "password");
- }
-
- private void guessDriver(Section database) {
- String url = Strings.emptyToNull(database.get("url"));
- if (url != null && Strings.isNullOrEmpty(database.get("driver"))) {
- if (url.startsWith("jdbc:derby:")) {
- database.set("driver", "org.apache.derby.jdbc.EmbeddedDriver");
- } else if (url.startsWith("jdbc:h2:")) {
- database.set("driver", "org.h2.Driver");
- } else if (url.startsWith("jdbc:mariadb:")) {
- database.set("driver", "org.mariadb.jdbc.Driver");
- } else if (url.startsWith("jdbc:mysql:")) {
- database.set("driver", "com.mysql.jdbc.Driver");
- } else if (url.startsWith("jdbc:postgresql:")) {
- database.set("driver", "org.postgresql.Driver");
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/Libraries.java b/java/com/google/gerrit/pgm/init/Libraries.java
deleted file mode 100644
index 826a79b2a2..0000000000
--- a/java/com/google/gerrit/pgm/init/Libraries.java
+++ /dev/null
@@ -1,141 +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.pgm.init;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.pgm.init.api.LibraryDownload;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.List;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-
-/** Standard {@link LibraryDownloader} instances derived from configuration. */
-@Singleton
-class Libraries {
- private static final String RESOURCE_FILE = "com/google/gerrit/pgm/init/libraries.config";
-
- private final Provider<LibraryDownloader> downloadProvider;
- private final List<String> skippedDownloads;
- private final boolean skipAllDownloads;
-
- /* final */ LibraryDownloader db2Driver;
- /* final */ LibraryDownloader db2DriverLicense;
- /* final */ LibraryDownloader hanaDriver;
- /* final */ LibraryDownloader mariadbDriver;
- /* final */ LibraryDownloader mysqlDriver;
- /* final */ LibraryDownloader oracleDriver;
-
- @Inject
- Libraries(
- final Provider<LibraryDownloader> downloadProvider,
- @LibraryDownload List<String> skippedDownloads,
- @LibraryDownload Boolean skipAllDownloads) {
- this.downloadProvider = downloadProvider;
- this.skippedDownloads = skippedDownloads;
- this.skipAllDownloads = skipAllDownloads;
- init();
- }
-
- private void init() {
- final Config cfg = new Config();
- try {
- cfg.fromText(read(RESOURCE_FILE));
- } catch (IOException | ConfigInvalidException e) {
- throw new RuntimeException(e.getMessage(), e);
- }
-
- for (Field f : Libraries.class.getDeclaredFields()) {
- if ((f.getModifiers() & Modifier.STATIC) == 0 && f.getType() == LibraryDownloader.class) {
- try {
- f.set(this, downloadProvider.get());
- } catch (IllegalArgumentException | IllegalAccessException e) {
- throw new IllegalStateException("Cannot initialize " + f.getName(), e);
- }
- }
- }
-
- for (Field f : Libraries.class.getDeclaredFields()) {
- if ((f.getModifiers() & Modifier.STATIC) == 0 && f.getType() == LibraryDownloader.class) {
- try {
- init(f, cfg);
- } catch (IllegalArgumentException
- | IllegalAccessException
- | NoSuchFieldException
- | SecurityException e) {
- throw new IllegalStateException("Cannot configure " + f.getName(), e);
- }
- }
- }
- }
-
- private void init(Field field, Config cfg)
- throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
- SecurityException {
- String n = field.getName();
- LibraryDownloader dl = (LibraryDownloader) field.get(this);
- dl.setName(get(cfg, n, "name"));
- dl.setJarUrl(get(cfg, n, "url"));
- dl.setSHA1(getOptional(cfg, n, "sha1"));
- dl.setRemove(get(cfg, n, "remove"));
- for (String d : cfg.getStringList("library", n, "needs")) {
- dl.addNeeds((LibraryDownloader) getClass().getDeclaredField(d).get(this));
- }
- dl.setSkipDownload(skipAllDownloads || skippedDownloads.contains(n));
- }
-
- private static String getOptional(Config cfg, String name, String key) {
- return doGet(cfg, name, key, false);
- }
-
- private static String get(Config cfg, String name, String key) {
- return doGet(cfg, name, key, true);
- }
-
- private static String doGet(Config cfg, String name, String key, boolean required) {
- String val = cfg.getString("library", name, key);
- if ((val == null || val.isEmpty()) && required) {
- throw new IllegalStateException(
- "Variable library." + name + "." + key + " is required within " + RESOURCE_FILE);
- }
- return val;
- }
-
- private static String read(String p) throws IOException {
- try (InputStream in = Libraries.class.getClassLoader().getResourceAsStream(p)) {
- if (in == null) {
- throw new FileNotFoundException("Cannot load resource " + p);
- }
- try (Reader r = new InputStreamReader(in, UTF_8)) {
- final StringBuilder buf = new StringBuilder();
- final char[] tmp = new char[512];
- int n;
- while (0 < (n = r.read(tmp))) {
- buf.append(tmp, 0, n);
- }
- return buf.toString();
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/LibraryDownloader.java b/java/com/google/gerrit/pgm/init/LibraryDownloader.java
deleted file mode 100644
index 0b31ee22ab..0000000000
--- a/java/com/google/gerrit/pgm/init/LibraryDownloader.java
+++ /dev/null
@@ -1,316 +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.pgm.init;
-
-import com.google.common.hash.Funnels;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
-import com.google.common.io.ByteStreams;
-import com.google.gerrit.common.Die;
-import com.google.gerrit.common.IoUtil;
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.Proxy;
-import java.net.ProxySelector;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import org.eclipse.jgit.util.HttpSupport;
-
-/** Get optional or required 3rd party library files into $site_path/lib. */
-class LibraryDownloader {
- private final ConsoleUI ui;
- private final Path lib_dir;
- private final StaleLibraryRemover remover;
-
- private boolean required;
- private String name;
- private String jarUrl;
- private String sha1;
- private String remove;
- private List<LibraryDownloader> needs;
- private LibraryDownloader neededBy;
- private Path dst;
- private boolean download; // download or copy
- private boolean exists;
- private boolean skipDownload;
-
- @Inject
- LibraryDownloader(ConsoleUI ui, SitePaths site, StaleLibraryRemover remover) {
- this.ui = ui;
- this.lib_dir = site.lib_dir;
- this.remover = remover;
- this.needs = new ArrayList<>(2);
- }
-
- void setName(String name) {
- this.name = name;
- }
-
- void setJarUrl(String url) {
- this.jarUrl = url;
- download = jarUrl.startsWith("http");
- }
-
- void setSHA1(String sha1) {
- this.sha1 = sha1;
- }
-
- void setRemove(String remove) {
- this.remove = remove;
- }
-
- void addNeeds(LibraryDownloader lib) {
- needs.add(lib);
- }
-
- void setSkipDownload(boolean skipDownload) {
- this.skipDownload = skipDownload;
- }
-
- void downloadRequired() {
- setRequired(true);
- download();
- }
-
- void downloadOptional() {
- required = false;
- download();
- }
-
- private void setRequired(boolean r) {
- required = r;
- for (LibraryDownloader d : needs) {
- d.setRequired(r);
- }
- }
-
- private void download() {
- if (skipDownload) {
- return;
- }
-
- if (jarUrl == null || !jarUrl.contains("/")) {
- throw new IllegalStateException("Invalid JarUrl for " + name);
- }
-
- final String jarName = jarUrl.substring(jarUrl.lastIndexOf('/') + 1);
- if (jarName.contains("/") || jarName.contains("\\")) {
- throw new IllegalStateException("Invalid JarUrl: " + jarUrl);
- }
-
- if (name == null) {
- name = jarName;
- }
-
- dst = lib_dir.resolve(jarName);
- if (Files.exists(dst)) {
- exists = true;
- } else if (shouldGet()) {
- doGet();
- }
-
- if (exists) {
- for (LibraryDownloader d : needs) {
- d.neededBy = this;
- d.downloadRequired();
- }
- }
- }
-
- private boolean shouldGet() {
- if (ui.isBatch()) {
- return required;
- }
- final StringBuilder msg = new StringBuilder();
- msg.append("\n");
- msg.append("Gerrit Code Review is not shipped with %s\n");
- if (neededBy != null) {
- msg.append(String.format("** This library is required by %s. **\n", neededBy.name));
- } else if (required) {
- msg.append("** This library is required for your configuration. **\n");
- } else {
- msg.append(" If available, Gerrit can take advantage of features\n");
- msg.append(" in the library, but will also function without it.\n");
- }
- msg.append(String.format("%s and install it now", download ? "Download" : "Copy"));
- return ui.yesno(true, msg.toString(), name);
- }
-
- private void doGet() {
- if (!Files.exists(lib_dir)) {
- try {
- Files.createDirectories(lib_dir);
- } catch (IOException e) {
- throw new Die("Cannot create " + lib_dir, e);
- }
- }
-
- try {
- remover.remove(remove);
- if (download) {
- doGetByHttp();
- } else {
- doGetByLocalCopy();
- }
- verifyFileChecksum();
- } catch (IOException err) {
- try {
- Files.delete(dst);
- } catch (IOException e) {
- // Delete failed; leave alone.
- }
-
- if (ui.isBatch()) {
- throw new Die("error: Cannot get " + jarUrl, err);
- }
-
- System.err.println();
- System.err.println();
- System.err.println("error: " + err.getMessage());
- System.err.println("Please download:");
- System.err.println();
- System.err.println(" " + jarUrl);
- System.err.println();
- System.err.println("and save as:");
- System.err.println();
- System.err.println(" " + dst.toAbsolutePath());
- System.err.println();
- System.err.flush();
-
- ui.waitForUser();
-
- if (Files.exists(dst)) {
- verifyFileChecksum();
-
- } else if (!ui.yesno(!required, "Continue without this library")) {
- throw new Die("aborted by user");
- }
- }
-
- if (Files.exists(dst)) {
- exists = true;
- IoUtil.loadJARs(dst);
- }
- }
-
- private void doGetByLocalCopy() throws IOException {
- System.err.print("Copying " + jarUrl + " ...");
- Path p = url2file(jarUrl);
- if (!Files.exists(p)) {
- StringBuilder msg =
- new StringBuilder()
- .append("\n")
- .append("Can not find the %s at this location: %s\n")
- .append("Please provide alternative URL");
- p = url2file(ui.readString(null, msg.toString(), name, jarUrl));
- }
- Files.copy(p, dst);
- }
-
- private static Path url2file(String urlString) throws IOException {
- final URL url = new URL(urlString);
- try {
- return Paths.get(url.toURI());
- } catch (URISyntaxException e) {
- return Paths.get(url.getPath());
- }
- }
-
- private void doGetByHttp() throws IOException {
- System.err.print("Downloading " + jarUrl + " ...");
- System.err.flush();
- try (InputStream in = openHttpStream(jarUrl);
- OutputStream out = Files.newOutputStream(dst)) {
- ByteStreams.copy(in, out);
- System.err.println(" OK");
- System.err.flush();
- } catch (IOException err) {
- deleteDst();
- System.err.println(" !! FAIL !!");
- System.err.println(err);
- System.err.flush();
- throw err;
- }
- }
-
- private static InputStream openHttpStream(String urlStr) throws IOException {
- ProxySelector proxySelector = ProxySelector.getDefault();
- URL url = new URL(urlStr);
- Proxy proxy = HttpSupport.proxyFor(proxySelector, url);
- HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy);
-
- switch (HttpSupport.response(c)) {
- case HttpURLConnection.HTTP_OK:
- return c.getInputStream();
-
- case HttpURLConnection.HTTP_NOT_FOUND:
- throw new FileNotFoundException(url.toString());
-
- default:
- throw new IOException(
- url.toString() + ": " + HttpSupport.response(c) + " " + c.getResponseMessage());
- }
- }
-
- @SuppressWarnings("deprecation") // Use Hashing.sha1 for compatibility.
- private void verifyFileChecksum() {
- if (sha1 == null) {
- System.err.println();
- System.err.flush();
- return;
- }
- Hasher h = Hashing.sha1().newHasher();
- try (InputStream in = Files.newInputStream(dst);
- OutputStream out = Funnels.asOutputStream(h)) {
- ByteStreams.copy(in, out);
- } catch (IOException e) {
- deleteDst();
- throw new Die("cannot checksum " + dst, e);
- }
- if (sha1.equals(h.hash().toString())) {
- System.err.println("Checksum " + dst.getFileName() + " OK");
- System.err.flush();
- } else if (ui.isBatch()) {
- deleteDst();
- throw new Die(dst + " SHA-1 checksum does not match");
-
- } else if (!ui.yesno(
- null /* force an answer */,
- "error: SHA-1 checksum does not match\nUse %s anyway", //
- dst.getFileName())) {
- deleteDst();
- throw new Die("aborted by user");
- }
- }
-
- private void deleteDst() {
- try {
- Files.delete(dst);
- } catch (IOException e) {
- System.err.println(" Failed to clean up lib: " + dst);
- }
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/MariaDbInitializer.java b/java/com/google/gerrit/pgm/init/MariaDbInitializer.java
deleted file mode 100644
index db32113358..0000000000
--- a/java/com/google/gerrit/pgm/init/MariaDbInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-class MariaDbInitializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "(mariadb default)";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, true);
- databaseSection.string("Database name", "database", "reviewdb");
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/MaxDbInitializer.java b/java/com/google/gerrit/pgm/init/MaxDbInitializer.java
deleted file mode 100644
index 0f696b731a..0000000000
--- a/java/com/google/gerrit/pgm/init/MaxDbInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2014 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class MaxDbInitializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "(maxdb default)";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, true);
- databaseSection.string("Database name", "database", "reviewdb");
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/MySqlInitializer.java b/java/com/google/gerrit/pgm/init/MySqlInitializer.java
deleted file mode 100644
index 037b52be74..0000000000
--- a/java/com/google/gerrit/pgm/init/MySqlInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2012 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-class MySqlInitializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "(mysql default)";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, true);
- databaseSection.string("Database name", "database", "reviewdb");
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/OracleInitializer.java b/java/com/google/gerrit/pgm/init/OracleInitializer.java
deleted file mode 100644
index ffbaf34212..0000000000
--- a/java/com/google/gerrit/pgm/init/OracleInitializer.java
+++ /dev/null
@@ -1,32 +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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-public class OracleInitializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "1521";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, false);
- databaseSection.string("Instance name", "instance", "xe");
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java b/java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java
deleted file mode 100644
index 65a66de779..0000000000
--- a/java/com/google/gerrit/pgm/init/PostgreSQLInitializer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2012 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.pgm.init;
-
-import static com.google.gerrit.pgm.init.api.InitUtil.username;
-
-import com.google.gerrit.pgm.init.api.Section;
-
-class PostgreSQLInitializer implements DatabaseConfigInitializer {
-
- @Override
- public void initConfig(Section databaseSection) {
- final String defPort = "(postgresql default)";
- databaseSection.string("Server hostname", "hostname", "localhost");
- databaseSection.string("Server port", "port", defPort, true);
- databaseSection.string("Database name", "database", "reviewdb");
- databaseSection.string("Database username", "username", username());
- databaseSection.password("username", "password");
- }
-}
diff --git a/java/com/google/gerrit/pgm/init/StaleLibraryRemover.java b/java/com/google/gerrit/pgm/init/StaleLibraryRemover.java
index c454cce134..d0db8ab382 100644
--- a/java/com/google/gerrit/pgm/init/StaleLibraryRemover.java
+++ b/java/com/google/gerrit/pgm/init/StaleLibraryRemover.java
@@ -39,12 +39,7 @@ public class StaleLibraryRemover {
public void remove(String pattern) {
if (!Strings.isNullOrEmpty(pattern)) {
DirectoryStream.Filter<Path> filter =
- new DirectoryStream.Filter<Path>() {
- @Override
- public boolean accept(Path entry) {
- return entry.getFileName().toString().matches("^" + pattern + "$");
- }
- };
+ entry -> entry.getFileName().toString().matches("^" + pattern + "$");
try (DirectoryStream<Path> paths = Files.newDirectoryStream(lib_dir, filter)) {
for (Path p : paths) {
String old = p.getFileName().toString();
diff --git a/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
index 9fd3f16e43..20e7ba24b4 100644
--- a/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
+++ b/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -15,8 +15,10 @@
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.server.config.AllProjectsName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.project.GroupList;
import com.google.gerrit.server.project.ProjectConfig;
@@ -27,16 +29,21 @@ import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.lib.StoredConfig;
public class AllProjectsConfig extends VersionedMetaDataOnInit {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ @Nullable private final StoredConfig baseConfig;
private Config cfg;
private GroupList groupList;
@Inject
AllProjectsConfig(AllProjectsNameOnInitProvider allProjects, SitePaths site, InitFlags flags) {
super(flags, site, allProjects.get(), RefNames.REFS_CONFIG);
+ this.baseConfig =
+ ProjectConfig.Factory.getBaseConfig(
+ site, new AllProjectsName(allProjects.get()), new Project.NameKey(allProjects.get()));
}
public Config getConfig() {
@@ -55,8 +62,11 @@ public class AllProjectsConfig extends VersionedMetaDataOnInit {
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
+ if (baseConfig != null) {
+ baseConfig.load();
+ }
groupList = readGroupList();
- cfg = readConfig(ProjectConfig.PROJECT_CONFIG);
+ cfg = readConfig(ProjectConfig.PROJECT_CONFIG, baseConfig);
}
private GroupList readGroupList() throws IOException {
diff --git a/java/com/google/gerrit/pgm/init/api/BUILD b/java/com/google/gerrit/pgm/init/api/BUILD
index aa989907d6..19203fcc48 100644
--- a/java/com/google/gerrit/pgm/init/api/BUILD
+++ b/java/com/google/gerrit/pgm/init/api/BUILD
@@ -7,10 +7,10 @@ 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/server",
"//lib:guava",
- "//lib:gwtorm",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/pgm/init/api/ConsoleUI.java b/java/com/google/gerrit/pgm/init/api/ConsoleUI.java
index 444f64fe7a..ea39a44556 100644
--- a/java/com/google/gerrit/pgm/init/api/ConsoleUI.java
+++ b/java/com/google/gerrit/pgm/init/api/ConsoleUI.java
@@ -224,7 +224,7 @@ public abstract class ConsoleUI {
@Override
public void header(String fmt, Object... args) {
- fmt = fmt.replaceAll("\n", "\n*** ");
+ fmt = fmt.replace("\n", "\n*** ");
console.printf("\n*** " + fmt + "\n*** \n\n", args);
}
diff --git a/java/com/google/gerrit/pgm/init/api/InitUtil.java b/java/com/google/gerrit/pgm/init/api/InitUtil.java
index 656f53aef6..d038de74ad 100644
--- a/java/com/google/gerrit/pgm/init/api/InitUtil.java
+++ b/java/com/google/gerrit/pgm/init/api/InitUtil.java
@@ -97,7 +97,7 @@ public class InitUtil {
p = name.indexOf(".");
if (0 < p) {
name = name.substring(p + 1);
- name = "DC=" + name.replaceAll("\\.", ",DC=");
+ name = "DC=" + name.replace(".", ",DC=");
} else {
name = null;
}
diff --git a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
index c9c3a64c27..d3d22cba69 100644
--- a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
+++ b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
@@ -15,12 +15,10 @@
package com.google.gerrit.pgm.init.api;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -35,16 +33,14 @@ public class SequencesOnInit {
this.allUsersName = allUsersName;
}
- public int nextAccountId(ReviewDb db) throws OrmException {
- @SuppressWarnings("deprecation")
- RepoSequence.Seed accountSeed = db::nextAccountId;
+ public int nextAccountId() {
RepoSequence accountSeq =
new RepoSequence(
repoManager,
GitReferenceUpdated.DISABLED,
new Project.NameKey(allUsersName.get()),
Sequences.NAME_ACCOUNTS,
- accountSeed,
+ () -> Sequences.FIRST_ACCOUNT_ID,
1);
return accountSeq.next();
}
diff --git a/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java b/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java
index b417d05452..80de1e5413 100644
--- a/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java
+++ b/java/com/google/gerrit/pgm/init/index/IndexModuleOnInit.java
@@ -30,7 +30,6 @@ import com.google.gerrit.server.index.account.AccountIndexDefinition;
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
import com.google.gerrit.server.index.account.AllAccountsIndexer;
import com.google.gerrit.server.index.group.AllGroupsIndexer;
-import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.index.group.GroupIndexDefinition;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
import com.google.inject.AbstractModule;
@@ -47,8 +46,7 @@ public class IndexModuleOnInit extends AbstractModule {
static final String INDEX_MANAGER = "IndexModuleOnInit/IndexManager";
private static final ImmutableCollection<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
- ImmutableList.<SchemaDefinitions<?>>of(
- AccountSchemaDefinitions.INSTANCE, GroupSchemaDefinitions.INSTANCE);
+ ImmutableList.of(AccountSchemaDefinitions.INSTANCE, GroupSchemaDefinitions.INSTANCE);
@Override
protected void configure() {
@@ -75,11 +73,9 @@ public class IndexModuleOnInit extends AbstractModule {
// during init, hence we don't need AllGroupsIndexer.
bind(AllGroupsIndexer.class).toProvider(Providers.of(null));
- bind(GroupIndexCollection.class);
-
bind(new TypeLiteral<Map<String, Integer>>() {})
.annotatedWith(Names.named(SingleVersionModule.SINGLE_VERSIONS))
- .toInstance(ImmutableMap.<String, Integer>of());
+ .toInstance(ImmutableMap.of());
bind(LifecycleListener.class)
.annotatedWith(Names.named(INDEX_MANAGER))
.to(SingleVersionListener.class);
@@ -88,8 +84,7 @@ public class IndexModuleOnInit extends AbstractModule {
@Provides
Collection<IndexDefinition<?, ?, ?>> getIndexDefinitions(
AccountIndexDefinition accounts, GroupIndexDefinition groups) {
- Collection<IndexDefinition<?, ?, ?>> result =
- ImmutableList.<IndexDefinition<?, ?, ?>>of(accounts, groups);
+ Collection<IndexDefinition<?, ?, ?>> result = ImmutableList.of(accounts, groups);
Set<String> expected =
FluentIterable.from(ALL_SCHEMA_DEFS).transform(SchemaDefinitions::getName).toSet();
Set<String> actual = FluentIterable.from(result).transform(IndexDefinition::getName).toSet();
diff --git a/java/com/google/gerrit/pgm/util/AbstractProgram.java b/java/com/google/gerrit/pgm/util/AbstractProgram.java
index fca5551ecd..96b042aada 100644
--- a/java/com/google/gerrit/pgm/util/AbstractProgram.java
+++ b/java/com/google/gerrit/pgm/util/AbstractProgram.java
@@ -66,9 +66,9 @@ public abstract class AbstractProgram {
final Throwable cause = err.getCause();
final String diemsg = err.getMessage();
if (cause != null && !cause.getMessage().equals(diemsg)) {
- System.err.println("fatal: " + cause.getMessage().replaceAll("\n", "\nfatal: "));
+ System.err.println("fatal: " + cause.getMessage().replace("\n", "\nfatal: "));
}
- System.err.println("fatal: " + diemsg.replaceAll("\n", "\nfatal: "));
+ System.err.println("fatal: " + diemsg.replace("\n", "\nfatal: "));
}
return 128;
}
diff --git a/java/com/google/gerrit/pgm/util/BUILD b/java/com/google/gerrit/pgm/util/BUILD
index 526a9324ad..60fb5e4faa 100644
--- a/java/com/google/gerrit/pgm/util/BUILD
+++ b/java/com/google/gerrit/pgm/util/BUILD
@@ -19,8 +19,6 @@ java_library(
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
- "//lib:gwtorm",
- "//lib/commons:dbcp",
"//lib/flogger:api",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 540c5f35d7..8e2a24410a 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -39,6 +39,7 @@ import com.google.gerrit.server.account.externalids.ExternalIdModule;
import com.google.gerrit.server.cache.CacheRemovalListener;
import com.google.gerrit.server.cache.h2.H2CacheModule;
import com.google.gerrit.server.cache.mem.DefaultMemoryCacheModule;
+import com.google.gerrit.server.change.ChangeAttributeFactory;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeKindCacheImpl;
import com.google.gerrit.server.change.MergeabilityCacheImpl;
@@ -48,9 +49,8 @@ import com.google.gerrit.server.config.AdministrateServerGroups;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
import com.google.gerrit.server.config.DefaultUrlFormatter;
-import com.google.gerrit.server.config.DisableReverseDnsLookup;
-import com.google.gerrit.server.config.DisableReverseDnsLookupProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.EnableReverseDnsLookup;
+import com.google.gerrit.server.config.EnableReverseDnsLookupProvider;
import com.google.gerrit.server.config.GitReceivePackGroups;
import com.google.gerrit.server.config.GitUploadPackGroups;
import com.google.gerrit.server.config.SysExecutorModule;
@@ -58,6 +58,7 @@ import com.google.gerrit.server.extensions.events.EventUtil;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.extensions.events.RevisionCreated;
import com.google.gerrit.server.git.MergeUtil;
+import com.google.gerrit.server.git.PureRevertCache;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.mail.send.ReplacePatchSetSender;
@@ -73,42 +74,24 @@ import com.google.gerrit.server.project.ProjectCacheImpl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.ChangeQueryProcessor;
+import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.SubmitRule;
import com.google.gerrit.server.update.BatchUpdate;
-import com.google.inject.Inject;
-import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Providers;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import org.eclipse.jgit.lib.Config;
-/**
- * Module for programs that perform batch operations on a site.
- *
- * <p>Any program that requires this module likely also requires using {@link ThreadLimiter} to
- * limit the number of threads accessing the database concurrently.
- */
+/** Module for programs that perform batch operations on a site. */
public class BatchProgramModule extends FactoryModule {
- private final Config cfg;
- private final Module reviewDbModule;
-
- @Inject
- BatchProgramModule(@GerritServerConfig Config cfg, PerThreadReviewDbModule reviewDbModule) {
- this.cfg = cfg;
- this.reviewDbModule = reviewDbModule;
- }
-
@SuppressWarnings("rawtypes")
@Override
protected void configure() {
- install(reviewDbModule);
install(new DiffExecutorModule());
install(new SysExecutorModule());
install(BatchUpdate.module());
@@ -126,28 +109,25 @@ public class BatchProgramModule extends FactoryModule {
// We're just running through each change
// once, so don't worry about cache removal.
- bind(new TypeLiteral<DynamicSet<CacheRemovalListener>>() {})
- .toInstance(DynamicSet.<CacheRemovalListener>emptySet());
- bind(new TypeLiteral<DynamicMap<Cache<?, ?>>>() {})
- .toInstance(DynamicMap.<Cache<?, ?>>emptyMap());
+ bind(new TypeLiteral<DynamicSet<CacheRemovalListener>>() {}).toInstance(DynamicSet.emptySet());
+ bind(new TypeLiteral<DynamicMap<Cache<?, ?>>>() {}).toInstance(DynamicMap.emptyMap());
bind(new TypeLiteral<List<CommentLinkInfo>>() {})
.toProvider(CommentLinkProvider.class)
.in(SINGLETON);
- bind(new TypeLiteral<DynamicMap<ChangeQueryProcessor.ChangeAttributeFactory>>() {})
- .toInstance(DynamicMap.<ChangeQueryProcessor.ChangeAttributeFactory>emptyMap());
+ bind(new TypeLiteral<DynamicSet<ChangeAttributeFactory>>() {})
+ .toInstance(DynamicSet.emptySet());
bind(new TypeLiteral<DynamicMap<RestView<CommitResource>>>() {})
- .toInstance(DynamicMap.<RestView<CommitResource>>emptyMap());
+ .toInstance(DynamicMap.emptyMap());
bind(String.class)
.annotatedWith(CanonicalWebUrl.class)
.toProvider(CanonicalWebUrlProvider.class);
bind(Boolean.class)
- .annotatedWith(DisableReverseDnsLookup.class)
- .toProvider(DisableReverseDnsLookupProvider.class)
+ .annotatedWith(EnableReverseDnsLookup.class)
+ .toProvider(EnableReverseDnsLookupProvider.class)
.in(SINGLETON);
bind(Realm.class).to(FakeRealm.class);
- bind(IdentifiedUser.class).toProvider(Providers.<IdentifiedUser>of(null));
- bind(ReplacePatchSetSender.Factory.class)
- .toProvider(Providers.<ReplacePatchSetSender.Factory>of(null));
+ bind(IdentifiedUser.class).toProvider(Providers.of(null));
+ bind(ReplacePatchSetSender.Factory.class).toProvider(Providers.of(null));
bind(CurrentUser.class).to(IdentifiedUser.class);
factory(MergeUtil.Factory.class);
factory(PatchSetInserter.Factory.class);
@@ -155,17 +135,17 @@ public class BatchProgramModule extends FactoryModule {
// As Reindex is a batch program, don't assume the index is available for
// the change cache.
- bind(SearchingChangeCacheImpl.class).toProvider(Providers.<SearchingChangeCacheImpl>of(null));
+ bind(SearchingChangeCacheImpl.class).toProvider(Providers.of(null));
bind(new TypeLiteral<ImmutableSet<GroupReference>>() {})
.annotatedWith(AdministrateServerGroups.class)
- .toInstance(ImmutableSet.<GroupReference>of());
+ .toInstance(ImmutableSet.of());
bind(new TypeLiteral<Set<AccountGroup.UUID>>() {})
.annotatedWith(GitUploadPackGroups.class)
- .toInstance(Collections.<AccountGroup.UUID>emptySet());
+ .toInstance(Collections.emptySet());
bind(new TypeLiteral<Set<AccountGroup.UUID>>() {})
.annotatedWith(GitReceivePackGroups.class)
- .toInstance(Collections.<AccountGroup.UUID>emptySet());
+ .toInstance(Collections.emptySet());
install(new BatchGitModule());
install(new DefaultPermissionBackendModule());
@@ -173,7 +153,7 @@ public class BatchProgramModule extends FactoryModule {
install(new H2CacheModule());
install(new ExternalIdModule());
install(new GroupModule());
- install(new NoteDbModule(cfg));
+ install(new NoteDbModule());
install(AccountCacheImpl.module());
install(GroupCacheImpl.module());
install(GroupIncludeCacheImpl.module());
@@ -182,8 +162,10 @@ public class BatchProgramModule extends FactoryModule {
install(ChangeKindCacheImpl.module());
install(MergeabilityCacheImpl.module());
install(TagCache.module());
+ install(PureRevertCache.module());
factory(CapabilityCollection.Factory.class);
factory(ChangeData.AssistedFactory.class);
+ factory(ChangeIsVisibleToPredicate.Factory.class);
factory(ProjectState.Factory.class);
// Submit rules
@@ -193,8 +175,8 @@ public class BatchProgramModule extends FactoryModule {
install(new DefaultSubmitRule.Module());
install(new IgnoreSelfApprovalRule.Module());
- bind(ChangeJson.Factory.class).toProvider(Providers.<ChangeJson.Factory>of(null));
- bind(EventUtil.class).toProvider(Providers.<EventUtil>of(null));
+ bind(ChangeJson.Factory.class).toProvider(Providers.of(null));
+ bind(EventUtil.class).toProvider(Providers.of(null));
bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
bind(RevisionCreated.class).toInstance(RevisionCreated.DISABLED);
bind(AccountVisibility.class).toProvider(AccountVisibilityProvider.class).in(SINGLETON);
diff --git a/java/com/google/gerrit/pgm/util/ErrorLogFile.java b/java/com/google/gerrit/pgm/util/ErrorLogFile.java
index 227719ab79..8eae82ac92 100644
--- a/java/com/google/gerrit/pgm/util/ErrorLogFile.java
+++ b/java/com/google/gerrit/pgm/util/ErrorLogFile.java
@@ -49,11 +49,12 @@ public class ErrorLogFile {
root.addAppender(dst);
}
- public static LifecycleListener start(Path sitePath, Config config) throws IOException {
+ public static LifecycleListener start(Path sitePath, Config config, boolean consoleLog)
+ throws IOException {
Path logdir =
FileUtil.mkdirsOrDie(new SitePaths(sitePath).logs_dir, "Cannot create log directory");
if (SystemLog.shouldConfigure()) {
- initLogSystem(logdir, config);
+ initLogSystem(logdir, config, consoleLog);
}
return new LifecycleListener() {
@@ -67,18 +68,28 @@ public class ErrorLogFile {
};
}
- private static void initLogSystem(Path logdir, Config config) {
+ private static void initLogSystem(Path logdir, Config config, boolean consoleLog) {
Logger root = LogManager.getRootLogger();
root.removeAllAppenders();
+ PatternLayout errorLogLayout = new PatternLayout("[%d] [%t] %-5p %c %x: %m%n");
+
+ if (consoleLog) {
+ ConsoleAppender dst = new ConsoleAppender();
+ dst.setLayout(errorLogLayout);
+ dst.setTarget("System.err");
+ dst.setThreshold(Level.INFO);
+ dst.activateOptions();
+
+ root.addAppender(dst);
+ }
+
boolean json = config.getBoolean("log", "jsonLogging", false);
- boolean text = config.getBoolean("log", "textLogging", true) || !json;
+ boolean text = config.getBoolean("log", "textLogging", true) || !(json || consoleLog);
boolean rotate = config.getBoolean("log", "rotate", true);
if (text) {
- root.addAppender(
- SystemLog.createAppender(
- logdir, LOG_NAME, new PatternLayout("[%d] [%t] %-5p %c %x: %m%n"), rotate));
+ root.addAppender(SystemLog.createAppender(logdir, LOG_NAME, errorLogLayout, rotate));
}
if (json) {
diff --git a/java/com/google/gerrit/pgm/util/PerThreadReviewDbModule.java b/java/com/google/gerrit/pgm/util/PerThreadReviewDbModule.java
deleted file mode 100644
index bdd939f959..0000000000
--- a/java/com/google/gerrit/pgm/util/PerThreadReviewDbModule.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2014 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.pgm.util;
-
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Module to bind a single {@link ReviewDb} instance per thread.
- *
- * <p>New instances are opened on demand, but are closed only at shutdown.
- */
-class PerThreadReviewDbModule extends LifecycleModule {
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- PerThreadReviewDbModule(SchemaFactory<ReviewDb> schema) {
- this.schema = schema;
- }
-
- @Override
- protected void configure() {
- final List<ReviewDb> dbs = Collections.synchronizedList(new ArrayList<ReviewDb>());
- final ThreadLocal<ReviewDb> localDb = new ThreadLocal<>();
-
- bind(ReviewDb.class)
- .toProvider(
- new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- ReviewDb db = localDb.get();
- if (db == null) {
- try {
- db = schema.open();
- dbs.add(db);
- localDb.set(db);
- } catch (OrmException e) {
- throw new ProvisionException("unable to open ReviewDb", e);
- }
- }
- return db;
- }
- });
- listener()
- .toInstance(
- new LifecycleListener() {
- @Override
- public void start() {
- // Do nothing.
- }
-
- @Override
- public void stop() {
- for (ReviewDb db : dbs) {
- db.close();
- }
- }
- });
- }
-}
diff --git a/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java b/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
deleted file mode 100644
index 1c7dc52576..0000000000
--- a/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
+++ /dev/null
@@ -1,56 +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.pgm.util;
-
-import com.google.gerrit.common.SiteLibraryLoaderUtil;
-import com.google.gerrit.metrics.MetricMaker;
-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.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.nio.file.Path;
-import javax.sql.DataSource;
-import org.eclipse.jgit.lib.Config;
-
-/** Loads the site library if not yet loaded. */
-@Singleton
-public class SiteLibraryBasedDataSourceProvider extends DataSourceProvider {
- private final Path libdir;
- private boolean init;
-
- @Inject
- SiteLibraryBasedDataSourceProvider(
- SitePaths site,
- @GerritServerConfig Config cfg,
- MetricMaker metrics,
- ThreadSettingsConfig tsc,
- DataSourceProvider.Context ctx,
- DataSourceType dst) {
- super(cfg, metrics, tsc, ctx, dst);
- libdir = site.lib_dir;
- }
-
- @Override
- public synchronized DataSource get() {
- if (!init) {
- SiteLibraryLoaderUtil.loadSiteLib(libdir);
- init = true;
- }
- return super.get();
- }
-}
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index 5930c6c831..eed307fd47 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -15,57 +15,36 @@
package com.google.gerrit.pgm.util;
import static com.google.gerrit.server.config.GerritServerConfigModule.getSecureStoreClassName;
-import static com.google.inject.Scopes.SINGLETON;
import static com.google.inject.Stage.PRODUCTION;
import com.google.gerrit.common.Die;
-import com.google.gerrit.extensions.events.LifecycleListener;
-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;
+import com.google.gerrit.server.LibModuleLoader;
+import com.google.gerrit.server.LibModuleType;
+import com.google.gerrit.server.ModuleOverloader;
import com.google.gerrit.server.config.GerritRuntime;
-import com.google.gerrit.server.config.GerritServerConfig;
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.notedb.NotesMigration;
-import com.google.gerrit.server.schema.DataSourceModule;
-import com.google.gerrit.server.schema.DataSourceProvider;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.gerrit.server.schema.DatabaseModule;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.server.securestore.SecureStoreClassName;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.AbstractModule;
-import com.google.inject.Binding;
import com.google.inject.CreationException;
import com.google.inject.Guice;
import com.google.inject.Injector;
-import com.google.inject.Key;
import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
import com.google.inject.spi.Message;
import com.google.inject.util.Providers;
-import java.lang.annotation.Annotation;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.sql.Connection;
-import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
-import javax.sql.DataSource;
-import org.eclipse.jgit.lib.Config;
import org.kohsuke.args4j.Option;
public abstract class SiteProgram extends AbstractProgram {
- private static final String CONNECTION_ERROR = "Cannot connect to SQL database";
-
@Option(
name = "--site-path",
aliases = {"-d"},
@@ -74,19 +53,12 @@ public abstract class SiteProgram extends AbstractProgram {
sitePath = Paths.get(path).normalize();
}
- protected Provider<DataSource> dsProvider;
-
private Path sitePath = Paths.get(".");
protected SiteProgram() {}
- protected SiteProgram(Path sitePath, Provider<DataSource> dsProvider) {
- this.sitePath = sitePath.normalize();
- this.dsProvider = dsProvider;
- }
-
protected SiteProgram(Path sitePath) {
- this(sitePath, null);
+ this.sitePath = sitePath.normalize();
}
/** @return the site path specified on the command line. */
@@ -102,12 +74,12 @@ public abstract class SiteProgram extends AbstractProgram {
}
/** @return provides database connectivity and site path. */
- protected Injector createDbInjector(DataSourceProvider.Context context) {
- return createDbInjector(false, context);
+ protected Injector createDbInjector() {
+ return createDbInjector(false);
}
/** @return provides database connectivity and site path. */
- protected Injector createDbInjector(boolean enableMetrics, DataSourceProvider.Context context) {
+ protected Injector createDbInjector(boolean enableMetrics) {
List<Module> modules = new ArrayList<>();
Module sitePathModule =
@@ -134,26 +106,6 @@ public abstract class SiteProgram extends AbstractProgram {
});
}
- modules.add(
- new LifecycleModule() {
- @Override
- protected void configure() {
- bind(DataSourceProvider.Context.class).toInstance(context);
- if (dsProvider != null) {
- bind(Key.get(DataSource.class, Names.named("ReviewDb")))
- .toProvider(dsProvider)
- .in(SINGLETON);
- if (LifecycleListener.class.isAssignableFrom(dsProvider.getClass())) {
- listener().toInstance((LifecycleListener) dsProvider);
- }
- } else {
- bind(Key.get(DataSource.class, Names.named("ReviewDb")))
- .toProvider(SiteLibraryBasedDataSourceProvider.class)
- .in(SINGLETON);
- listener().to(SiteLibraryBasedDataSourceProvider.class);
- }
- }
- });
Module configModule = new GerritServerConfigModule();
modules.add(configModule);
modules.add(
@@ -164,53 +116,19 @@ public abstract class SiteProgram extends AbstractProgram {
}
});
Injector cfgInjector = Guice.createInjector(sitePathModule, configModule);
- Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
- String dbType;
- if (dsProvider != null) {
- dbType = getDbType(dsProvider);
- } else {
- dbType = cfg.getString("database", null, "type");
- }
-
- if (dbType == null) {
- throw new ProvisionException("database.type must be defined");
- }
-
- DataSourceType dst =
- Guice.createInjector(new DataSourceModule(), configModule, sitePathModule)
- .getInstance(Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
- modules.add(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(DataSourceType.class).toInstance(dst);
- }
- });
- modules.add(new DatabaseModule());
modules.add(new SchemaModule());
modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class));
- modules.add(new NotesMigration.Module());
try {
- return Guice.createInjector(PRODUCTION, modules);
+ return Guice.createInjector(
+ PRODUCTION,
+ ModuleOverloader.override(
+ modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.DB_MODULE)));
} catch (CreationException ce) {
Message first = ce.getErrorMessages().iterator().next();
Throwable why = first.getCause();
- if (why instanceof SQLException) {
- throw die(CONNECTION_ERROR, why);
- }
- if (why instanceof OrmException
- && why.getCause() != null
- && "Unable to determine driver URL".equals(why.getMessage())) {
- why = why.getCause();
- if (isCannotCreatePoolException(why)) {
- throw die(CONNECTION_ERROR, why.getCause());
- }
- throw die(CONNECTION_ERROR, why);
- }
-
StringBuilder buf = new StringBuilder();
if (why != null) {
buf.append(why.getMessage());
@@ -235,46 +153,4 @@ public abstract class SiteProgram extends AbstractProgram {
protected final String getConfiguredSecureStoreClass() {
return getSecureStoreClassName(sitePath);
}
-
- private String getDbType(Provider<DataSource> dsProvider) {
- String dbProductName;
- try (Connection conn = dsProvider.get().getConnection()) {
- dbProductName = conn.getMetaData().getDatabaseProductName().toLowerCase();
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
-
- List<Module> modules = new ArrayList<>();
- modules.add(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath());
- }
- });
- modules.add(new GerritServerConfigModule());
- modules.add(new DataSourceModule());
- Injector i = Guice.createInjector(modules);
- List<Binding<DataSourceType>> dsTypeBindings =
- i.findBindingsByType(new TypeLiteral<DataSourceType>() {});
- for (Binding<DataSourceType> binding : dsTypeBindings) {
- Annotation annotation = binding.getKey().getAnnotation();
- if (annotation instanceof Named) {
- Named named = (Named) annotation;
- if (named.value().toLowerCase().contains(dbProductName)) {
- return named.value();
- }
- }
- }
- throw new IllegalStateException(
- String.format(
- "Cannot guess database type from the database product name '%s'", dbProductName));
- }
-
- @SuppressWarnings("deprecation")
- private static boolean isCannotCreatePoolException(Throwable why) {
- return why instanceof org.apache.commons.dbcp.SQLNestedException
- && why.getCause() != null
- && why.getMessage().startsWith("Cannot create PoolableConnectionFactory");
- }
}
diff --git a/java/com/google/gerrit/pgm/util/ThreadLimiter.java b/java/com/google/gerrit/pgm/util/ThreadLimiter.java
deleted file mode 100644
index 64f703bd22..0000000000
--- a/java/com/google/gerrit/pgm/util/ThreadLimiter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2014 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.pgm.util;
-
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import org.eclipse.jgit.lib.Config;
-
-// TODO(dborowitz): Not necessary once we switch to NoteDb.
-/** Utility to limit threads used by a batch program. */
-public class ThreadLimiter {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public static int limitThreads(Injector dbInjector, int threads) {
- return limitThreads(
- dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)),
- dbInjector.getInstance(DataSourceType.class),
- dbInjector.getInstance(ThreadSettingsConfig.class),
- threads);
- }
-
- private static int limitThreads(
- Config cfg, DataSourceType dst, ThreadSettingsConfig threadSettingsConfig, int threads) {
- boolean usePool = cfg.getBoolean("database", "connectionpool", dst.usePool());
- int poolLimit = threadSettingsConfig.getDatabasePoolLimit();
- if (usePool && threads > poolLimit) {
- logger.atWarning().log("Limiting program to %d threads due to database.poolLimit", poolLimit);
- return poolLimit;
- }
- return threads;
- }
-
- private ThreadLimiter() {}
-}
diff --git a/java/com/google/gerrit/prettify/BUILD b/java/com/google/gerrit/prettify/BUILD
index 09237a0ada..76afbe77b8 100644
--- a/java/com/google/gerrit/prettify/BUILD
+++ b/java/com/google/gerrit/prettify/BUILD
@@ -1,30 +1,12 @@
load("@rules_java//java:defs.bzl", "java_library")
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "client",
- srcs = glob(["common/**/*.java"]),
- exported_deps = [
- "//java/com/google/gerrit/extensions:client",
- "//java/com/google/gerrit/reviewdb:client",
- "//java/com/google/gwtexpui/safehtml",
- "//java/org/eclipse/jgit:Edit",
- "//java/org/eclipse/jgit:client",
- "//lib:gwtjsonrpc",
- "//lib:gwtjsonrpc_src",
- ],
- gwt_xml = "PrettyFormatter.gwt.xml",
- visibility = ["//visibility:public"],
- deps = ["//lib/gwt:user-neverlink"],
-)
java_library(
name = "server",
srcs = glob(["common/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/reviewdb:server",
"//lib:guava",
- "//lib:gwtjsonrpc",
"//lib/jgit/org.eclipse.jgit:jgit",
],
)
diff --git a/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml b/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml
deleted file mode 100644
index 06035d275b..0000000000
--- a/java/com/google/gerrit/prettify/PrettyFormatter.gwt.xml
+++ /dev/null
@@ -1,27 +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.
--->
-<module>
- <replace-with class='com.google.gerrit.prettify.client.PrivateScopeImplIE8'>
- <when-type-is class='com.google.gerrit.prettify.client.PrivateScopeImpl'/>
- <any>
- <when-property-is name="user.agent" value="ie8" />
- </any>
- </replace-with>
-
- <inherits name='com.google.gwt.resources.Resources'/>
- <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
- <source path='common' />
-</module>
diff --git a/java/com/google/gerrit/prettify/common/EditList.java b/java/com/google/gerrit/prettify/common/EditList.java
index 61c807c7fc..172a346752 100644
--- a/java/com/google/gerrit/prettify/common/EditList.java
+++ b/java/com/google/gerrit/prettify/common/EditList.java
@@ -36,10 +36,8 @@ public class EditList {
}
public Iterable<Hunk> getHunks() {
- return new Iterable<Hunk>() {
- @Override
- public Iterator<Hunk> iterator() {
- return new Iterator<Hunk>() {
+ return () ->
+ new Iterator<Hunk>() {
private int curIdx;
@Override
@@ -60,8 +58,6 @@ public class EditList {
throw new UnsupportedOperationException();
}
};
- }
- };
}
private int findCombinedEnd(int i) {
diff --git a/java/com/google/gerrit/proto/BUILD b/java/com/google/gerrit/proto/BUILD
index 0e9d3cd712..98558c54f0 100644
--- a/java/com/google/gerrit/proto/BUILD
+++ b/java/com/google/gerrit/proto/BUILD
@@ -1,16 +1,10 @@
-load("@rules_java//java:defs.bzl", "java_binary")
+load("@rules_java//java:defs.bzl", "java_library")
-java_binary(
- name = "ProtoGen",
- srcs = ["ProtoGen.java"],
- resource_strip_prefix = "resources",
- resources = ["//resources/com/google/gerrit/proto"],
- visibility = ["//proto:__pkg__"],
+java_library(
+ name = "proto",
+ srcs = ["Protos.java"],
+ visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/reviewdb:server",
- "//lib:args4j",
- "//lib:guava",
- "//lib:gwtorm",
- "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib:protobuf",
],
)
diff --git a/java/com/google/gerrit/proto/ProtoGen.java b/java/com/google/gerrit/proto/ProtoGen.java
deleted file mode 100644
index c1302416e3..0000000000
--- a/java/com/google/gerrit/proto/ProtoGen.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2010 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.proto;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.schema.java.JavaSchemaModel;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import org.eclipse.jgit.internal.storage.file.LockFile;
-import org.eclipse.jgit.util.IO;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.ParserProperties;
-
-public class ProtoGen {
- @Option(
- name = "--output",
- aliases = {"-o"},
- required = true,
- metaVar = "FILE",
- usage = "File to write .proto into")
- private File file;
-
- public static void main(String[] argv) throws Exception {
- System.exit(new ProtoGen().run(argv));
- }
-
- private int run(String[] argv) throws Exception {
- CmdLineParser parser = new CmdLineParser(this, ParserProperties.defaults().withAtSyntax(false));
- try {
- parser.parseArgument(argv);
- } catch (CmdLineException e) {
- System.err.println(e.getMessage());
- System.err.println(getClass().getSimpleName() + " -o output.proto");
- parser.printUsage(System.err);
- return 1;
- }
-
- LockFile lock = new LockFile(file.getAbsoluteFile());
- checkState(lock.lock(), "cannot lock %s", file);
- try {
- JavaSchemaModel jsm = new JavaSchemaModel(ReviewDb.class);
- try (OutputStream o = lock.getOutputStream();
- PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(o, UTF_8)))) {
- String header;
- try (InputStream in = getClass().getResourceAsStream("ProtoGenHeader.txt")) {
- ByteBuffer buf = IO.readWholeStream(in, 1024);
- int ptr = buf.arrayOffset() + buf.position();
- int len = buf.remaining();
- header = new String(buf.array(), ptr, len, UTF_8);
- }
-
- out.write(header);
- jsm.generateProto(out);
- out.flush();
- }
- checkState(lock.commit(), "Could not write to %s", file);
- } finally {
- lock.unlock();
- }
- return 0;
- }
-}
diff --git a/java/com/google/gerrit/proto/Protos.java b/java/com/google/gerrit/proto/Protos.java
new file mode 100644
index 0000000000..b4f0b55741
--- /dev/null
+++ b/java/com/google/gerrit/proto/Protos.java
@@ -0,0 +1,119 @@
+// 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.proto;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.CodedOutputStream;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+import java.io.IOException;
+
+/** Static utilities for dealing with protobuf-based objects. */
+public class Protos {
+ /**
+ * Serializes a proto to a byte array.
+ *
+ * <p>Guarantees deterministic serialization. No matter whether the use case cares about
+ * determinism or not, always use this method in preference to {@link MessageLite#toByteArray()},
+ * which is not guaranteed deterministic.
+ *
+ * @param message the proto message to serialize.
+ * @return a byte array with the message contents.
+ */
+ public static byte[] toByteArray(MessageLite message) {
+ byte[] bytes = new byte[message.getSerializedSize()];
+ CodedOutputStream cout = CodedOutputStream.newInstance(bytes);
+ cout.useDeterministicSerialization();
+ try {
+ message.writeTo(cout);
+ cout.checkNoSpaceLeft();
+ return bytes;
+ } catch (IOException e) {
+ throw new IllegalStateException("exception writing to byte array", e);
+ }
+ }
+
+ /**
+ * Serializes a proto to a {@code ByteString}.
+ *
+ * <p>Guarantees deterministic serialization. No matter whether the use case cares about
+ * determinism or not, always use this method in preference to {@link MessageLite#toByteString()},
+ * which is not guaranteed deterministic.
+ *
+ * @param message the proto message to serialize
+ * @return a {@code ByteString} with the message contents
+ */
+ public static ByteString toByteString(MessageLite message) {
+ try (ByteString.Output bout = ByteString.newOutput(message.getSerializedSize())) {
+ CodedOutputStream outputStream = CodedOutputStream.newInstance(bout);
+ outputStream.useDeterministicSerialization();
+ message.writeTo(outputStream);
+ outputStream.flush();
+ return bout.toByteString();
+ } catch (IOException e) {
+ throw new IllegalStateException("exception writing to ByteString", e);
+ }
+ }
+
+ /**
+ * Parses a byte array to a protobuf message.
+ *
+ * @param parser parser for the proto type.
+ * @param in byte array with the message contents.
+ * @return parsed proto.
+ */
+ public static <M extends MessageLite> M parseUnchecked(Parser<M> parser, byte[] in) {
+ try {
+ return parser.parseFrom(in);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("exception parsing byte array to proto", e);
+ }
+ }
+
+ /**
+ * Parses a specific segment of a byte array to a protobuf message.
+ *
+ * @param parser parser for the proto type
+ * @param in byte array with the message contents
+ * @param offset offset in the byte array to start reading from
+ * @param length amount of read bytes
+ * @return parsed proto
+ */
+ public static <M extends MessageLite> M parseUnchecked(
+ Parser<M> parser, byte[] in, int offset, int length) {
+ try {
+ return parser.parseFrom(in, offset, length);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("exception parsing byte array to proto", e);
+ }
+ }
+
+ /**
+ * Parses a {@code ByteString} to a protobuf message.
+ *
+ * @param parser parser for the proto type
+ * @param byteString {@code ByteString} with the message contents
+ * @return parsed proto
+ */
+ public static <M extends MessageLite> M parseUnchecked(Parser<M> parser, ByteString byteString) {
+ try {
+ return parser.parseFrom(byteString);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("exception parsing ByteString to proto", e);
+ }
+ }
+
+ private Protos() {}
+}
diff --git a/java/com/google/gerrit/proto/testing/BUILD b/java/com/google/gerrit/proto/testing/BUILD
new file mode 100644
index 0000000000..acfa8f06ee
--- /dev/null
+++ b/java/com/google/gerrit/proto/testing/BUILD
@@ -0,0 +1,15 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_testonly = True)
+
+java_library(
+ name = "testing",
+ 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
new file mode 100644
index 0000000000..546ff89bf2
--- /dev/null
+++ b/java/com/google/gerrit/proto/testing/SerializedClassSubject.java
@@ -0,0 +1,110 @@
+// 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.proto.testing;
+
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.truth.Fact.simpleFact;
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Map;
+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
+ * 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
+ * implementation.
+ *
+ * <p>Changing fields of a serialized class (or abstract methods, in the case of {@code @AutoValue}
+ * classes) will likely require changes to the serializer implementation, and may require bumping
+ * the {@link com.google.gerrit.server.cache.PersistentCacheBinding#version(int) version} in the
+ * cache binding, in case the representation has changed in such a way that old serialized data
+ * becomes unreadable.
+ *
+ * <p>Changes to a serialized class such as adding or removing fields generally requires a change to
+ * 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 static SerializedClassSubject assertThatSerializedClass(Class<?> actual) {
+ // This formulation fails in Eclipse 4.7.3a with "The type
+ // SerializedClassSubject does not define SerializedClassSubject() that is
+ // applicable here", due to
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=534694 or a similar bug:
+ // return assertAbout(SerializedClassSubject::new).that(actual);
+ Subject.Factory<SerializedClassSubject, Class<?>> factory =
+ (m, a) -> new SerializedClassSubject(m, a);
+ return assertAbout(factory).that(actual);
+ }
+
+ private SerializedClassSubject(FailureMetadata metadata, Class<?> actual) {
+ super(metadata, actual);
+ }
+
+ public void isAbstract() {
+ isNotNull();
+ if (!Modifier.isAbstract(actual().getModifiers())) {
+ failWithActual(simpleFact("expected class to be abstract"));
+ }
+ }
+
+ public void isConcrete() {
+ isNotNull();
+ if (Modifier.isAbstract(actual().getModifiers())) {
+ failWithActual(simpleFact("expected class to be concrete"));
+ }
+ }
+
+ public void hasFields(Map<String, Type> expectedFields) {
+ isConcrete();
+ check("fields()")
+ .that(
+ FieldUtils.getAllFieldsList(actual()).stream()
+ .filter(f -> !Modifier.isStatic(f.getModifiers()))
+ .collect(toImmutableMap(Field::getName, Field::getGenericType)))
+ .containsExactlyEntriesIn(expectedFields);
+ }
+
+ 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())
+ .that(
+ Arrays.stream(actual().getDeclaredMethods())
+ .filter(m -> !Modifier.isStatic(m.getModifiers()))
+ .filter(m -> Modifier.isAbstract(m.getModifiers()))
+ .filter(m -> m.getParameters().length == 0)
+ .collect(toImmutableMap(Method::getName, Method::getGenericReturnType)))
+ .isEqualTo(expectedMethods);
+ }
+
+ public void extendsClass(Type superclassType) {
+ isNotNull();
+ check("superclass(%s)", actual().getName())
+ .that(actual().getGenericSuperclass())
+ .isEqualTo(superclassType);
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/BUILD b/java/com/google/gerrit/reviewdb/BUILD
index 05fad6694e..3bc6528d5d 100644
--- a/java/com/google/gerrit/reviewdb/BUILD
+++ b/java/com/google/gerrit/reviewdb/BUILD
@@ -1,29 +1,18 @@
load("@rules_java//java:defs.bzl", "java_library")
-load("//tools/bzl:gwt.bzl", "gwt_module")
package(
default_visibility = ["//visibility:public"],
)
-gwt_module(
- name = "client",
- srcs = glob(["client/**/*.java"]),
- gwt_xml = "ReviewDB.gwt.xml",
- deps = [
- "//java/com/google/gerrit/extensions:client",
- "//lib:gwtorm-client",
- "//lib:gwtorm-client_src",
- ],
-)
-
java_library(
name = "server",
srcs = glob(["**/*.java"]),
- resource_strip_prefix = "resources",
- resources = ["//resources/com/google/gerrit/reviewdb"],
deps = [
+ "//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gwtorm",
"//lib:guava",
- "//lib:gwtorm",
+ "//lib:protobuf",
+ "//proto:entities_java_proto",
],
)
diff --git a/java/com/google/gerrit/reviewdb/ReviewDB.gwt.xml b/java/com/google/gerrit/reviewdb/ReviewDB.gwt.xml
deleted file mode 100644
index 4402826e85..0000000000
--- a/java/com/google/gerrit/reviewdb/ReviewDB.gwt.xml
+++ /dev/null
@@ -1,18 +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.
--->
-<module>
- <inherits name='com.google.gwtorm.GWTORM'/>
-</module>
diff --git a/java/com/google/gerrit/reviewdb/client/Account.java b/java/com/google/gerrit/reviewdb/client/Account.java
index 961c098afe..fa3abedca1 100644
--- a/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/java/com/google/gerrit/reviewdb/client/Account.java
@@ -18,8 +18,8 @@ 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 com.google.common.primitives.Ints;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.IntKey;
import java.sql.Timestamp;
import java.util.Optional;
@@ -46,18 +46,14 @@ import java.util.Optional;
* </ul>
*/
public final class Account {
- /**
- * Key local to Gerrit to identify a user.
- *
- * <p>Fields in this type must be annotated with {@link Column} so that account IDs can be
- * converted into protos (protobuf requires the {@link Column} annotations for decoding/encoding).
- * We need to be able to store account IDs as protos because we store change protos in the change
- * index and a change references account IDs for the change owner and the assignee.
- */
+ public static Id id(int id) {
+ return new 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;
- @Column(id = 1)
protected int id;
protected Id() {}
@@ -78,11 +74,7 @@ public final class Account {
/** Parse an Account.Id out of a string representation. */
public static Optional<Id> tryParse(String str) {
- try {
- return Optional.of(new Id(Integer.parseInt(str)));
- } catch (NumberFormatException e) {
- return Optional.empty();
- }
+ return Optional.ofNullable(Ints.tryParse(str)).map(Id::new);
}
public static Id fromRef(String name) {
@@ -151,10 +143,7 @@ public final class Account {
/** The user-settable status of this account (e.g. busy, OOO, available) */
private String status;
- /**
- * ID of the user branch from which the account was read, {@code null} if the account was read
- * from ReviewDb.
- */
+ /** ID of the user branch from which the account was read. */
private String metaId;
protected Account() {}
@@ -162,7 +151,7 @@ public final class Account {
/**
* Create a new account.
*
- * @param newId unique id, see {@link com.google.gerrit.server.Sequences#nextAccountId()}.
+ * @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) {
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroup.java b/java/com/google/gerrit/reviewdb/client/AccountGroup.java
index c7dc420fe8..0db7bbd83f 100644
--- a/java/com/google/gerrit/reviewdb/client/AccountGroup.java
+++ b/java/com/google/gerrit/reviewdb/client/AccountGroup.java
@@ -14,10 +14,11 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
+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;
/** Named group of one or more accounts, typically used for access controls. */
@@ -26,19 +27,20 @@ public final class AccountGroup {
* Time when the audit subsystem was implemented, used as the default value for {@link #createdOn}
* when one couldn't be determined from the audit log.
*/
- // Can't use Instant here because GWT. This is verified against a readable time in the tests,
- // which don't need to compile under GWT.
- private static final long AUDIT_CREATION_INSTANT_MS = 1244489460000L;
+ private static final Instant AUDIT_CREATION_INSTANT_MS = Instant.ofEpochMilli(1244489460000L);
public static Timestamp auditCreationInstantTs() {
- return new Timestamp(AUDIT_CREATION_INSTANT_MS);
+ return Timestamp.from(AUDIT_CREATION_INSTANT_MS);
+ }
+
+ public static NameKey nameKey(String n) {
+ return new NameKey(n);
}
/** Group name key */
public static class NameKey extends StringKey<com.google.gwtorm.client.Key<?>> {
private static final long serialVersionUID = 1L;
- @Column(id = 1)
protected String name;
protected NameKey() {}
@@ -58,11 +60,14 @@ public final class AccountGroup {
}
}
+ public static UUID uuid(String n) {
+ return new UUID(n);
+ }
+
/** Globally unique identifier. */
public static class UUID extends StringKey<com.google.gwtorm.client.Key<?>> {
private static final long serialVersionUID = 1L;
- @Column(id = 1)
protected String uuid;
protected UUID() {}
@@ -116,11 +121,14 @@ public final class AccountGroup {
return uuid.get().matches("^[0-9a-f]{40}$");
}
+ public static Id id(int id) {
+ return new 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;
- @Column(id = 1)
protected int id;
protected Id() {}
@@ -148,29 +156,24 @@ public final class AccountGroup {
}
/** Unique name of this group within the system. */
- @Column(id = 1)
protected NameKey name;
/** Unique identity, to link entities as {@link #name} can change. */
- @Column(id = 2)
protected Id groupId;
// DELETED: id = 3 (ownerGroupId)
/** A textual description of the group's purpose. */
- @Column(id = 4, length = Integer.MAX_VALUE, notNull = false)
- protected String description;
+ @Nullable protected String description;
// DELETED: id = 5 (groupType)
// DELETED: id = 6 (externalName)
- @Column(id = 7)
protected boolean visibleToAll;
// DELETED: id = 8 (emailOnlyAuthors)
/** Globally unique identifier name for this group. */
- @Column(id = 9)
protected UUID groupUUID;
/**
@@ -178,11 +181,9 @@ public final class AccountGroup {
*
* <p>This can be a self-reference to indicate the group's members manage itself.
*/
- @Column(id = 10)
protected UUID ownerGroupUUID;
- @Column(id = 11, notNull = false)
- protected Timestamp createdOn;
+ @Nullable protected Timestamp createdOn;
protected AccountGroup() {}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupById.java b/java/com/google/gerrit/reviewdb/client/AccountGroupById.java
index 17a205e1fc..578865c1a7 100644
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupById.java
+++ b/java/com/google/gerrit/reviewdb/client/AccountGroupById.java
@@ -14,19 +14,20 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
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;
- @Column(id = 1)
protected AccountGroup.Id groupId;
- @Column(id = 2)
protected AccountGroup.UUID includeUUID;
protected Key() {
@@ -48,17 +49,24 @@ public final class AccountGroupById {
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};
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
protected AccountGroupById() {}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java b/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
index 759e4f6a4d..308e1e1dcc 100644
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
+++ b/java/com/google/gerrit/reviewdb/client/AccountGroupByIdAud.java
@@ -14,23 +14,24 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
+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;
- @Column(id = 1)
protected AccountGroup.Id groupId;
- @Column(id = 2)
protected AccountGroup.UUID includeUUID;
- @Column(id = 3)
protected Timestamp addedOn;
protected Key() {
@@ -49,14 +50,26 @@ public final class AccountGroupByIdAud {
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};
@@ -75,17 +88,13 @@ public final class AccountGroupByIdAud {
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
- @Column(id = 2)
protected Account.Id addedBy;
- @Column(id = 3, notNull = false)
- protected Account.Id removedBy;
+ @Nullable protected Account.Id removedBy;
- @Column(id = 4, notNull = false)
- protected Timestamp removedOn;
+ @Nullable protected Timestamp removedOn;
protected AccountGroupByIdAud() {}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java b/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java
index e1e075414d..dfa7d24426 100644
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java
+++ b/java/com/google/gerrit/reviewdb/client/AccountGroupMember.java
@@ -14,19 +14,20 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
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;
- @Column(id = 1)
protected Account.Id accountId;
- @Column(id = 2)
protected AccountGroup.Id groupId;
protected Key() {
@@ -44,17 +45,24 @@ public final class AccountGroupMember {
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};
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
protected AccountGroupMember() {}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java b/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
index fc7b2d86f2..5d43b4a915 100644
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
+++ b/java/com/google/gerrit/reviewdb/client/AccountGroupMemberAudit.java
@@ -14,23 +14,24 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
+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;
- @Column(id = 1)
protected Account.Id accountId;
- @Column(id = 2)
protected AccountGroup.Id groupId;
- @Column(id = 3)
protected Timestamp addedOn;
protected Key() {
@@ -49,14 +50,26 @@ public final class AccountGroupMemberAudit {
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};
@@ -75,17 +88,13 @@ public final class AccountGroupMemberAudit {
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
- @Column(id = 2)
protected Account.Id addedBy;
- @Column(id = 3, notNull = false)
- protected Account.Id removedBy;
+ @Nullable protected Account.Id removedBy;
- @Column(id = 4, notNull = false)
- protected Timestamp removedOn;
+ @Nullable protected Timestamp removedOn;
protected AccountGroupMemberAudit() {}
diff --git a/java/com/google/gerrit/reviewdb/client/AccountGroupName.java b/java/com/google/gerrit/reviewdb/client/AccountGroupName.java
deleted file mode 100644
index 924f45718d..0000000000
--- a/java/com/google/gerrit/reviewdb/client/AccountGroupName.java
+++ /dev/null
@@ -1,53 +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.gwtorm.client.Column;
-
-/** Unique name of an {@link AccountGroup}. */
-public class AccountGroupName {
- @Column(id = 1)
- protected AccountGroup.NameKey name;
-
- @Column(id = 2)
- protected AccountGroup.Id groupId;
-
- protected AccountGroupName() {}
-
- public AccountGroupName(AccountGroup.NameKey name, AccountGroup.Id groupId) {
- this.name = name;
- this.groupId = groupId;
- }
-
- public AccountGroupName(AccountGroup group) {
- this(group.getNameKey(), group.getId());
- }
-
- public String getName() {
- return getNameKey().get();
- }
-
- public AccountGroup.NameKey getNameKey() {
- return name;
- }
-
- public AccountGroup.Id getId() {
- return groupId;
- }
-
- public void setId(AccountGroup.Id id) {
- groupId = id;
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/client/Branch.java b/java/com/google/gerrit/reviewdb/client/Branch.java
index fd8bbfd92e..4ea49b7637 100644
--- a/java/com/google/gerrit/reviewdb/client/Branch.java
+++ b/java/com/google/gerrit/reviewdb/client/Branch.java
@@ -14,19 +14,24 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
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;
- @Column(id = 1)
protected Project.NameKey projectName;
- @Column(id = 2)
protected String branchName;
protected NameKey() {
@@ -47,6 +52,10 @@ public final class Branch {
return branchName;
}
+ public String branch() {
+ return get();
+ }
+
@Override
protected void set(String newValue) {
branchName = RefNames.fullName(newValue);
@@ -57,6 +66,10 @@ public final class Branch {
return projectName;
}
+ public Project.NameKey project() {
+ return getParentKey();
+ }
+
public String getShortName() {
return RefNames.shortName(get());
}
diff --git a/java/com/google/gerrit/reviewdb/client/Change.java b/java/com/google/gerrit/reviewdb/client/Change.java
index 8d4de0526f..79739dc948 100644
--- a/java/com/google/gerrit/reviewdb/client/Change.java
+++ b/java/com/google/gerrit/reviewdb/client/Change.java
@@ -16,10 +16,9 @@ package com.google.gerrit.reviewdb.client;
import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.RowVersion;
import com.google.gwtorm.client.StringKey;
import java.sql.Timestamp;
import java.util.Arrays;
@@ -94,10 +93,13 @@ 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;
- @Column(id = 1)
public int id;
protected Id() {}
@@ -253,6 +255,10 @@ public final class Change {
}
}
+ public static Key key(String key) {
+ return new 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.
@@ -260,7 +266,6 @@ public final class Change {
public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
private static final long serialVersionUID = 1L;
- @Column(id = 1, length = 60)
protected String id;
protected Key() {}
@@ -428,20 +433,15 @@ public final class Change {
}
/** Locally assigned unique identifier of the change */
- @Column(id = 1)
protected Id changeId;
/** Globally assigned unique identifier of the change */
- @Column(id = 2)
protected Key changeKey;
/** optimistic locking */
- @Column(id = 3)
- @RowVersion
protected int rowVersion;
/** When this change was first introduced into the database. */
- @Column(id = 4)
protected Timestamp createdOn;
/**
@@ -449,37 +449,30 @@ public final class Change {
*
* <p>Note, this update timestamp includes its children.
*/
- @Column(id = 5)
protected Timestamp lastUpdatedOn;
// DELETED: id = 6 (sortkey)
- @Column(id = 7, name = "owner_account_id")
protected Account.Id owner;
/** The branch (and project) this change merges into. */
- @Column(id = 8)
protected Branch.NameKey dest;
// DELETED: id = 9 (open)
/** Current state code; see {@link Status}. */
- @Column(id = 10)
protected char status;
// DELETED: id = 11 (nbrPatchSets)
/** The current patch set. */
- @Column(id = 12)
protected int currentPatchSetId;
/** Subject from the current patch set. */
- @Column(id = 13)
protected String subject;
/** Topic name assigned by the user, if any. */
- @Column(id = 14, notNull = false)
- protected String topic;
+ @Nullable protected String topic;
// DELETED: id = 15 (lastSha1MergeTested)
// DELETED: id = 16 (mergeable)
@@ -490,39 +483,28 @@ public final class Change {
* <p>Unlike {@link #subject}, this string does not change if future patch sets change the first
* line.
*/
- @Column(id = 17, notNull = false)
- protected String originalSubject;
+ @Nullable protected String originalSubject;
/**
* Unique id for the changes submitted together assigned during merging. Only set if the status is
* MERGED.
*/
- @Column(id = 18, notNull = false)
- protected String submissionId;
+ @Nullable protected String submissionId;
/** Allows assigning a change to a user. */
- @Column(id = 19, notNull = false)
- protected Account.Id assignee;
+ @Nullable protected Account.Id assignee;
/** Whether the change is private. */
- @Column(id = 20)
protected boolean isPrivate;
/** Whether the change is work in progress. */
- @Column(id = 21)
protected boolean workInProgress;
/** Whether the change has started review. */
- @Column(id = 22)
protected boolean reviewStarted;
/** References a change that this change reverts. */
- @Column(id = 23, notNull = false)
- protected Id revertOf;
-
- /** @see com.google.gerrit.server.notedb.NoteDbChangeState */
- @Column(id = 101, notNull = false, length = Integer.MAX_VALUE)
- protected String noteDbState;
+ @Nullable protected Id revertOf;
protected Change() {}
@@ -559,7 +541,6 @@ public final class Change {
isPrivate = other.isPrivate;
workInProgress = other.workInProgress;
reviewStarted = other.reviewStarted;
- noteDbState = other.noteDbState;
revertOf = other.revertOf;
}
@@ -698,6 +679,22 @@ public final class Change {
status = newStatus.getCode();
}
+ public boolean isNew() {
+ return getStatus().equals(Status.NEW);
+ }
+
+ public boolean isMerged() {
+ return getStatus().equals(Status.MERGED);
+ }
+
+ public boolean isAbandoned() {
+ return getStatus().equals(Status.ABANDONED);
+ }
+
+ public boolean isClosed() {
+ return isAbandoned() || isMerged();
+ }
+
public String getTopic() {
return topic;
}
@@ -738,14 +735,6 @@ public final class Change {
return this.revertOf;
}
- public String getNoteDbState() {
- return noteDbState;
- }
-
- public void setNoteDbState(String state) {
- noteDbState = state;
- }
-
@Override
public String toString() {
return new StringBuilder(getClass().getSimpleName())
diff --git a/java/com/google/gerrit/reviewdb/client/ChangeMessage.java b/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
index 8e397f0db6..de318bd271 100644
--- a/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
+++ b/java/com/google/gerrit/reviewdb/client/ChangeMessage.java
@@ -14,20 +14,22 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
+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);
+ }
+
public static class Key extends StringKey<Change.Id> {
private static final long serialVersionUID = 1L;
- @Column(id = 1)
protected Change.Id changeId;
- @Column(id = 2, length = 40)
protected String uuid;
protected Key() {
@@ -44,43 +46,44 @@ public final class ChangeMessage {
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;
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
/** Who wrote this comment; null if it was written by the Gerrit system. */
- @Column(id = 2, name = "author_id", notNull = false)
- protected Account.Id author;
+ @Nullable protected Account.Id author;
/** When this comment was drafted. */
- @Column(id = 3)
protected Timestamp writtenOn;
/** The text left by the user. */
- @Column(id = 4, notNull = false, length = Integer.MAX_VALUE)
- protected String message;
+ @Nullable protected String message;
/** Which patchset (if any) was this message generated from? */
- @Column(id = 5, notNull = false)
- protected PatchSet.Id patchset;
+ @Nullable protected PatchSet.Id patchset;
/** Tag associated with change message */
- @Column(id = 6, notNull = false)
- protected String tag;
+ @Nullable protected String tag;
/** Real user that added this message on behalf of the user recorded in {@link #author}. */
- @Column(id = 7, notNull = false)
- protected Account.Id realAuthor;
+ @Nullable protected Account.Id realAuthor;
protected ChangeMessage() {}
diff --git a/java/com/google/gerrit/reviewdb/client/Comment.java b/java/com/google/gerrit/reviewdb/client/Comment.java
index 207643ec43..e03d0fa24e 100644
--- a/java/com/google/gerrit/reviewdb/client/Comment.java
+++ b/java/com/google/gerrit/reviewdb/client/Comment.java
@@ -25,7 +25,7 @@ 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} also represents inline comments, but in ReviewDb. There are a few
+ * <p>{@link PatchLineComment} historically represented comments in ReviewDb. There are a few
* notable differences:
*
* <ul>
@@ -41,9 +41,9 @@ import java.util.Objects;
* </ul>
*
* <p>For all utility classes and middle layer functionality using Comment over PatchLineComment is
- * preferred, as PatchLineComment will go away together with ReviewDb. 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
+ * 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).
*/
@@ -283,15 +283,41 @@ public class Comment {
@Override
public boolean equals(Object o) {
- if (o instanceof Comment) {
- return Objects.equals(key, ((Comment) o).key);
+ if (!(o instanceof Comment)) {
+ return false;
}
- return false;
+ Comment c = (Comment) o;
+ return Objects.equals(key, c.key)
+ && lineNbr == c.lineNbr
+ && Objects.equals(author, c.author)
+ && Objects.equals(realAuthor, c.realAuthor)
+ && Objects.equals(writtenOn, c.writtenOn)
+ && side == c.side
+ && Objects.equals(message, c.message)
+ && Objects.equals(parentUuid, c.parentUuid)
+ && Objects.equals(range, c.range)
+ && Objects.equals(tag, c.tag)
+ && Objects.equals(revId, c.revId)
+ && Objects.equals(serverId, c.serverId)
+ && unresolved == c.unresolved;
}
@Override
public int hashCode() {
- return key.hashCode();
+ return Objects.hash(
+ key,
+ lineNbr,
+ author,
+ realAuthor,
+ writtenOn,
+ side,
+ message,
+ parentUuid,
+ range,
+ tag,
+ revId,
+ serverId,
+ unresolved);
}
@Override
diff --git a/java/com/google/gerrit/reviewdb/client/CommentRange.java b/java/com/google/gerrit/reviewdb/client/CommentRange.java
index 3c69538216..e6c5078218 100644
--- a/java/com/google/gerrit/reviewdb/client/CommentRange.java
+++ b/java/com/google/gerrit/reviewdb/client/CommentRange.java
@@ -14,20 +14,14 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
-
public class CommentRange {
- @Column(id = 1)
protected int startLine;
- @Column(id = 2)
protected int startCharacter;
- @Column(id = 3)
protected int endLine;
- @Column(id = 4)
protected int endCharacter;
protected CommentRange() {}
diff --git a/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java b/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java
deleted file mode 100644
index 6a3b69c50f..0000000000
--- a/java/com/google/gerrit/reviewdb/client/CurrentSchemaVersion.java
+++ /dev/null
@@ -1,58 +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.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-/** Current version of the database schema, to facilitate live upgrades. */
-public final class CurrentSchemaVersion {
- public static final class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- private static final String VALUE = "X";
-
- @Column(id = 1, length = 1)
- public String one = VALUE;
-
- public Key() {}
-
- @Override
- public String get() {
- return VALUE;
- }
-
- @Override
- protected void set(String newValue) {
- assert get().equals(newValue);
- }
- }
-
- /** Construct a new, unconfigured instance. */
- public static CurrentSchemaVersion create() {
- final CurrentSchemaVersion r = new CurrentSchemaVersion();
- r.singleton = new CurrentSchemaVersion.Key();
- return r;
- }
-
- @Column(id = 1)
- public Key singleton;
-
- /** Current version number of the schema. */
- @Column(id = 2)
- public transient int versionNbr;
-
- public CurrentSchemaVersion() {}
-}
diff --git a/java/com/google/gerrit/reviewdb/client/LabelId.java b/java/com/google/gerrit/reviewdb/client/LabelId.java
index e69cab2af2..abf131b09c 100644
--- a/java/com/google/gerrit/reviewdb/client/LabelId.java
+++ b/java/com/google/gerrit/reviewdb/client/LabelId.java
@@ -14,7 +14,6 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.StringKey;
public class LabelId extends StringKey<com.google.gwtorm.client.Key<?>> {
@@ -22,11 +21,14 @@ public class LabelId extends StringKey<com.google.gwtorm.client.Key<?>> {
static final String LEGACY_SUBMIT_NAME = "SUBM";
+ public static LabelId create(String n) {
+ return new LabelId(n);
+ }
+
public static LabelId legacySubmit() {
return new LabelId(LEGACY_SUBMIT_NAME);
}
- @Column(id = 1)
public String id;
public LabelId() {}
diff --git a/java/com/google/gerrit/reviewdb/client/Patch.java b/java/com/google/gerrit/reviewdb/client/Patch.java
index 0492c6ca39..d192df0d47 100644
--- a/java/com/google/gerrit/reviewdb/client/Patch.java
+++ b/java/com/google/gerrit/reviewdb/client/Patch.java
@@ -14,7 +14,6 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.StringKey;
/** A single modified file in a {@link PatchSet}. */
@@ -36,13 +35,15 @@ public final class Patch {
return COMMIT_MSG.equals(path) || MERGE_LIST.equals(path);
}
+ public static Key key(PatchSet.Id patchSetId, String fileName) {
+ return new Key(patchSetId, fileName);
+ }
+
public static class Key extends StringKey<PatchSet.Id> {
private static final long serialVersionUID = 1L;
- @Column(id = 1, name = Column.NONE)
protected PatchSet.Id patchSetId;
- @Column(id = 2)
protected String fileName;
protected Key() {
@@ -59,11 +60,19 @@ public final class Patch {
return patchSetId;
}
+ public PatchSet.Id patchSetId() {
+ return getParentKey();
+ }
+
@Override
public String get() {
return fileName;
}
+ public String fileName() {
+ return get();
+ }
+
@Override
protected void set(String newValue) {
fileName = newValue;
diff --git a/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
index de953dcb2b..ce218c0128 100644
--- a/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
+++ b/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
@@ -14,8 +14,8 @@
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.Column;
import com.google.gwtorm.client.StringKey;
import java.sql.Timestamp;
import java.util.Objects;
@@ -23,9 +23,7 @@ import java.util.Objects;
/**
* A comment left by a user on a specific line of a {@link Patch}.
*
- * <p>This class represents an inline comment in ReviewDb. It should only be used for
- * writing/reading inline comments to/from ReviewDb. For all other purposes inline comments should
- * be represented by {@link Comment}.
+ * <p>New APIs should not expose this class.
*
* @see Comment
*/
@@ -38,10 +36,8 @@ public final class PatchLineComment {
new Patch.Key(new PatchSet.Id(changeId, key.patchSetId), key.filename), key.uuid);
}
- @Column(id = 1, name = Column.NONE)
protected Patch.Key patchKey;
- @Column(id = 2, length = 40)
protected String uuid;
protected Key() {
@@ -124,57 +120,40 @@ public final class PatchLineComment {
return plc;
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
/** Line number this comment applies to; it should display after the line. */
- @Column(id = 2)
protected int lineNbr;
/** Who wrote this comment. */
- @Column(id = 3, name = "author_id")
protected Account.Id author;
/** When this comment was drafted. */
- @Column(id = 4)
protected Timestamp writtenOn;
/** Current publication state of the comment; see {@link Status}. */
- @Column(id = 5)
protected char status;
/** Which file is this comment; 0 is ancestor, 1 is new version. */
- @Column(id = 6)
protected short side;
/** The text left by the user. */
- @Column(id = 7, notNull = false, length = Integer.MAX_VALUE)
- protected String message;
+ @Nullable protected String message;
/** The parent of this comment, or null if this is the first comment on this line */
- @Column(id = 8, length = 40, notNull = false)
- protected String parentUuid;
+ @Nullable protected String parentUuid;
- @Column(id = 9, notNull = false)
- protected CommentRange range;
+ @Nullable protected CommentRange range;
- @Column(id = 10, notNull = false)
- protected String tag;
+ @Nullable protected String tag;
/** Real user that added this comment on behalf of the user recorded in {@link #author}. */
- @Column(id = 11, notNull = false)
- protected Account.Id realAuthor;
+ @Nullable protected Account.Id realAuthor;
/** True if this comment requires further action. */
- @Column(id = 12)
protected boolean unresolved;
- /**
- * The RevId for the commit to which this comment is referring.
- *
- * <p>Note that this field is not stored in the database. It is just provided for users of this
- * class to avoid a lookup when they don't have easy access to a ReviewDb.
- */
+ /** The RevId for the commit to which this comment is referring. */
protected RevId revId;
protected PatchLineComment() {}
diff --git a/java/com/google/gerrit/reviewdb/client/PatchSet.java b/java/com/google/gerrit/reviewdb/client/PatchSet.java
index 849fd75dee..684f09236b 100644
--- a/java/com/google/gerrit/reviewdb/client/PatchSet.java
+++ b/java/com/google/gerrit/reviewdb/client/PatchSet.java
@@ -14,7 +14,7 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
+import com.google.gerrit.common.Nullable;
import com.google.gwtorm.client.IntKey;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -39,7 +39,7 @@ public final class PatchSet {
return isChangeRef(name);
}
- static String joinGroups(List<String> groups) {
+ public static String joinGroups(List<String> groups) {
if (groups == null) {
throw new IllegalArgumentException("groups may not be null");
}
@@ -65,7 +65,7 @@ public final class PatchSet {
while (true) {
int idx = joinedGroups.indexOf(',', i);
if (idx < 0) {
- groups.add(joinedGroups.substring(i, joinedGroups.length()));
+ groups.add(joinedGroups.substring(i));
break;
}
groups.add(joinedGroups.substring(i, idx));
@@ -74,13 +74,15 @@ public final class PatchSet {
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;
- @Column(id = 1)
public Change.Id changeId;
- @Column(id = 2)
public int patchSetId;
public Id() {
@@ -97,6 +99,10 @@ public final class PatchSet {
return changeId;
}
+ public Change.Id changeId() {
+ return getParentKey();
+ }
+
@Override
public int get() {
return patchSetId;
@@ -156,21 +162,15 @@ public final class PatchSet {
}
}
- @Column(id = 1, name = Column.NONE)
protected Id id;
- @Column(id = 2, notNull = false)
- protected RevId revision;
+ @Nullable protected RevId revision;
- @Column(id = 3, name = "uploader_account_id")
protected Account.Id uploader;
/** When this patch set was first introduced onto the change. */
- @Column(id = 4)
protected Timestamp createdOn;
- // @Column(id = 5)
-
/**
* Opaque group identifier, usually assigned during creation.
*
@@ -180,14 +180,12 @@ public final class PatchSet {
* <p>Changes on the same branch having patch sets with intersecting groups are considered
* related, as in the "Related Changes" tab.
*/
- @Column(id = 6, notNull = false, length = Integer.MAX_VALUE)
- protected String groups;
+ @Nullable protected String groups;
// DELETED id = 7 (pushCertficate)
/** Certificate sent with a push that created this patch set. */
- @Column(id = 8, notNull = false, length = Integer.MAX_VALUE)
- protected String pushCertificate;
+ @Nullable protected String pushCertificate;
/**
* Optional user-supplied description for this patch set.
@@ -195,8 +193,7 @@ public final class PatchSet {
* <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.
*/
- @Column(id = 9, notNull = false, length = Integer.MAX_VALUE)
- protected String description;
+ @Nullable protected String description;
protected PatchSet() {}
diff --git a/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java b/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
index 5adf002871..e1c4ea9989 100644
--- a/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
+++ b/java/com/google/gerrit/reviewdb/client/PatchSetApproval.java
@@ -14,7 +14,7 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
+import com.google.gerrit.common.Nullable;
import com.google.gwtorm.client.CompoundKey;
import java.sql.Timestamp;
import java.util.Date;
@@ -22,16 +22,17 @@ 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;
- @Column(id = 1, name = Column.NONE)
protected PatchSet.Id patchSetId;
- @Column(id = 2)
protected Account.Id accountId;
- @Column(id = 3)
protected LabelId categoryId;
protected Key() {
@@ -51,21 +52,32 @@ public final class PatchSetApproval {
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};
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
/**
@@ -84,20 +96,15 @@ public final class PatchSetApproval {
* and in the negative and positive direction a magnitude can be assumed.The further from 0 the
* more assertive the approval.
*/
- @Column(id = 2)
protected short value;
- @Column(id = 3)
protected Timestamp granted;
- @Column(id = 6, notNull = false)
- protected String tag;
+ @Nullable protected String tag;
/** Real user that made this approval on behalf of the user recorded in {@link Key#accountId}. */
- @Column(id = 7, notNull = false)
- protected Account.Id realAccountId;
+ @Nullable protected Account.Id realAccountId;
- @Column(id = 8)
protected boolean postSubmit;
// DELETED: id = 4 (changeOpen)
diff --git a/java/com/google/gerrit/reviewdb/client/Project.java b/java/com/google/gerrit/reviewdb/client/Project.java
index 638742121c..e025c0f875 100644
--- a/java/com/google/gerrit/reviewdb/client/Project.java
+++ b/java/com/google/gerrit/reviewdb/client/Project.java
@@ -17,7 +17,6 @@ package com.google.gerrit.reviewdb.client;
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.Column;
import com.google.gwtorm.client.StringKey;
import java.util.Arrays;
import java.util.HashMap;
@@ -32,11 +31,14 @@ public final class Project {
/** Default submit type for root project (All-Projects). */
public static final SubmitType DEFAULT_ALL_PROJECTS_SUBMIT_TYPE = SubmitType.MERGE_IF_NECESSARY;
+ public static NameKey nameKey(String name) {
+ return new NameKey(name);
+ }
+
/** Project name key */
public static class NameKey extends StringKey<com.google.gwtorm.client.Key<?>> {
private static final long serialVersionUID = 1L;
- @Column(id = 1)
protected String name;
protected NameKey() {}
@@ -98,8 +100,6 @@ public final class Project {
protected String localDefaultDashboardId;
- protected String themeName;
-
protected String configRefState;
protected Project() {}
@@ -185,22 +185,6 @@ public final class Project {
this.localDefaultDashboardId = localDefaultDashboardId;
}
- public String getThemeName() {
- return themeName;
- }
-
- public void setThemeName(String themeName) {
- this.themeName = themeName;
- }
-
- public void copySettingsFrom(Project update) {
- description = update.description;
- booleanConfigs = new HashMap<>(update.booleanConfigs);
- submitType = update.submitType;
- state = update.state;
- maxObjectSizeLimit = update.maxObjectSizeLimit;
- }
-
/**
* Returns the name key of the parent project.
*
diff --git a/java/com/google/gerrit/reviewdb/client/RefNames.java b/java/com/google/gerrit/reviewdb/client/RefNames.java
index fd2fb56b0e..1f119218c8 100644
--- a/java/com/google/gerrit/reviewdb/client/RefNames.java
+++ b/java/com/google/gerrit/reviewdb/client/RefNames.java
@@ -14,6 +14,8 @@
package com.google.gerrit.reviewdb.client;
+import com.google.gerrit.common.UsedAt;
+
/** Constants and utilities for Gerrit-specific ref names. */
public class RefNames {
public static final String HEAD = "HEAD";
@@ -49,6 +51,9 @@ public class RefNames {
/** Sequence counters in NoteDb. */
public static final String REFS_SEQUENCES = "refs/sequences/";
+ /** NoteDb schema version number. */
+ public static final String REFS_VERSION = "refs/meta/version";
+
/**
* Prefix applied to merge commit base nodes.
*
@@ -134,6 +139,11 @@ public class RefNames {
return false;
}
+ /** True if the provided ref is in {@code refs/changes/*}. */
+ public static boolean isRefsChanges(String ref) {
+ return ref.startsWith(REFS_CHANGES);
+ }
+
public static String refsGroups(AccountGroup.UUID groupUuid) {
return REFS_GROUPS + shardUuid(groupUuid.get());
}
@@ -190,7 +200,8 @@ public class RefNames {
return sb;
}
- private static String shardUuid(String uuid) {
+ @UsedAt(UsedAt.Project.PLUGINS_ALL)
+ public static String shardUuid(String uuid) {
if (uuid == null || uuid.length() < 2) {
throw new IllegalArgumentException("UUIDs must consist of at least two characters");
}
@@ -263,6 +274,24 @@ public class RefNames {
return REFS_CONFIG.equals(ref);
}
+ /**
+ * Whether the ref is managed by Gerrit. Covers all Gerrit-internal refs like refs/cache-automerge
+ * and refs/meta as well as refs/changes. Does not cover user-created refs like branches or custom
+ * ref namespaces like refs/my-company.
+ */
+ public static boolean isGerritRef(String ref) {
+ return ref.startsWith(REFS_CHANGES)
+ || ref.startsWith(REFS_META)
+ || ref.startsWith(REFS_CACHE_AUTOMERGE)
+ || ref.startsWith(REFS_DRAFT_COMMENTS)
+ || ref.startsWith(REFS_DELETED_GROUPS)
+ || ref.startsWith(REFS_SEQUENCES)
+ || ref.startsWith(REFS_GROUPS)
+ || ref.startsWith(REFS_GROUPNAMES)
+ || ref.startsWith(REFS_USERS)
+ || ref.startsWith(REFS_STARRED_CHANGES);
+ }
+
static Integer parseShardedRefPart(String name) {
if (name == null) {
return null;
@@ -305,7 +334,8 @@ public class RefNames {
return id;
}
- static String parseShardedUuidFromRefPart(String name) {
+ @UsedAt(UsedAt.Project.PLUGINS_ALL)
+ public static String parseShardedUuidFromRefPart(String name) {
if (name == null) {
return null;
}
@@ -429,7 +459,7 @@ public class RefNames {
if (i == 0) {
return null;
}
- return Integer.valueOf(name.substring(i, name.length()));
+ return Integer.valueOf(name.substring(i));
}
private static StringBuilder newStringBuilder() {
diff --git a/java/com/google/gerrit/reviewdb/client/RevId.java b/java/com/google/gerrit/reviewdb/client/RevId.java
index 0b0f74a513..99b6c2ce56 100644
--- a/java/com/google/gerrit/reviewdb/client/RevId.java
+++ b/java/com/google/gerrit/reviewdb/client/RevId.java
@@ -14,14 +14,11 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
-
/** 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;
- @Column(id = 1, length = LEN)
protected String id;
protected RevId() {}
diff --git a/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java b/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java
index bd00d3766c..b297dfb2f5 100644
--- a/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java
+++ b/java/com/google/gerrit/reviewdb/client/SubmoduleSubscription.java
@@ -14,7 +14,6 @@
package com.google.gerrit.reviewdb.client;
-import com.google.gwtorm.client.Column;
import com.google.gwtorm.client.StringKey;
/**
@@ -34,10 +33,8 @@ public final class SubmoduleSubscription {
* Indicates the super project, aka subscriber: the project owner of the gitlinks to the
* submodules.
*/
- @Column(id = 1)
protected Branch.NameKey superProject;
- @Column(id = 2)
protected String submodulePath;
protected Key() {
@@ -65,10 +62,8 @@ public final class SubmoduleSubscription {
}
}
- @Column(id = 1, name = Column.NONE)
protected Key key;
- @Column(id = 2)
protected Branch.NameKey submodule;
protected SubmoduleSubscription() {}
diff --git a/java/com/google/gerrit/reviewdb/client/TrackingId.java b/java/com/google/gerrit/reviewdb/client/TrackingId.java
deleted file mode 100644
index 2f6008f178..0000000000
--- a/java/com/google/gerrit/reviewdb/client/TrackingId.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2010 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.Column;
-import com.google.gwtorm.client.CompoundKey;
-import com.google.gwtorm.client.StringKey;
-
-/** External tracking id associated with a {@link Change} */
-public final class TrackingId {
- public static final int TRACKING_ID_MAX_CHAR = 32;
- public static final int TRACKING_SYSTEM_MAX_CHAR = 10;
-
- /** External tracking id */
- public static class Id extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1, length = TrackingId.TRACKING_ID_MAX_CHAR)
- protected String id;
-
- protected Id() {}
-
- public Id(String id) {
- this.id = id;
- }
-
- @Override
- public String get() {
- return id;
- }
-
- @Override
- protected void set(String newValue) {
- id = newValue;
- }
- }
-
- /** Name of external tracking system */
- public static class System extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1, length = TrackingId.TRACKING_SYSTEM_MAX_CHAR)
- protected String system;
-
- protected System() {}
-
- public System(String s) {
- this.system = s;
- }
-
- @Override
- public String get() {
- return system;
- }
-
- @Override
- protected void set(String newValue) {
- system = newValue;
- }
- }
-
- public static class Key extends CompoundKey<Change.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1)
- protected Change.Id changeId;
-
- @Column(id = 2)
- protected Id trackingKey;
-
- @Column(id = 3)
- protected System trackingSystem;
-
- protected Key() {
- changeId = new Change.Id();
- trackingKey = new Id();
- trackingSystem = new System();
- }
-
- protected Key(Change.Id ch, Id id, System s) {
- changeId = ch;
- trackingKey = id;
- trackingSystem = s;
- }
-
- @Override
- public Change.Id getParentKey() {
- return changeId;
- }
-
- public TrackingId.Id getTrackingId() {
- return trackingKey;
- }
-
- public TrackingId.System getTrackingSystem() {
- return trackingSystem;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {trackingKey, trackingSystem};
- }
- }
-
- @Column(id = 1, name = Column.NONE)
- protected Key key;
-
- protected TrackingId() {}
-
- public TrackingId(Change.Id ch, TrackingId.Id id, TrackingId.System s) {
- key = new Key(ch, id, s);
- }
-
- public TrackingId(Change.Id ch, String id, String s) {
- key = new Key(ch, new TrackingId.Id(id), new TrackingId.System(s));
- }
-
- public TrackingId.Key getKey() {
- return key;
- }
-
- public Change.Id getChangeId() {
- return key.changeId;
- }
-
- public String getTrackingId() {
- return key.trackingKey.get();
- }
-
- public String getSystem() {
- return key.trackingSystem.get();
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof TrackingId) {
- final TrackingId tr = (TrackingId) obj;
- return key.equals(tr.key);
- }
- return false;
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java
new file mode 100644
index 0000000000..8dd179467b
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/AccountIdProtoConverter.java
@@ -0,0 +1,38 @@
+// 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.protobuf.Parser;
+
+public enum AccountIdProtoConverter implements ProtoConverter<Entities.Account_Id, Account.Id> {
+ INSTANCE;
+
+ @Override
+ public Entities.Account_Id toProto(Account.Id accountId) {
+ return Entities.Account_Id.newBuilder().setId(accountId.get()).build();
+ }
+
+ @Override
+ public Account.Id fromProto(Entities.Account_Id proto) {
+ return new Account.Id(proto.getId());
+ }
+
+ @Override
+ public Parser<Entities.Account_Id> getParser() {
+ return Entities.Account_Id.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java
new file mode 100644
index 0000000000..4558f9bae9
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverter.java
@@ -0,0 +1,47 @@
+// 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.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.protobuf.Parser;
+
+public enum BranchNameKeyProtoConverter
+ implements ProtoConverter<Entities.Branch_NameKey, Branch.NameKey> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.Project_NameKey, Project.NameKey> projectNameConverter =
+ ProjectNameKeyProtoConverter.INSTANCE;
+
+ @Override
+ public Entities.Branch_NameKey toProto(Branch.NameKey nameKey) {
+ return Entities.Branch_NameKey.newBuilder()
+ .setProjectName(projectNameConverter.toProto(nameKey.getParentKey()))
+ .setBranchName(nameKey.get())
+ .build();
+ }
+
+ @Override
+ public Branch.NameKey fromProto(Entities.Branch_NameKey proto) {
+ return new Branch.NameKey(
+ projectNameConverter.fromProto(proto.getProjectName()), proto.getBranchName());
+ }
+
+ @Override
+ public Parser<Entities.Branch_NameKey> getParser() {
+ return Entities.Branch_NameKey.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java
new file mode 100644
index 0000000000..14ed59c43b
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverter.java
@@ -0,0 +1,38 @@
+// 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.Change;
+import com.google.protobuf.Parser;
+
+public enum ChangeIdProtoConverter implements ProtoConverter<Entities.Change_Id, Change.Id> {
+ INSTANCE;
+
+ @Override
+ public Entities.Change_Id toProto(Change.Id changeId) {
+ return Entities.Change_Id.newBuilder().setId(changeId.get()).build();
+ }
+
+ @Override
+ public Change.Id fromProto(Entities.Change_Id proto) {
+ return new Change.Id(proto.getId());
+ }
+
+ @Override
+ public Parser<Entities.Change_Id> getParser() {
+ return Entities.Change_Id.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java
new file mode 100644
index 0000000000..dccd7d9d1a
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverter.java
@@ -0,0 +1,38 @@
+// 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.Change;
+import com.google.protobuf.Parser;
+
+public enum ChangeKeyProtoConverter implements ProtoConverter<Entities.Change_Key, Change.Key> {
+ INSTANCE;
+
+ @Override
+ public Entities.Change_Key toProto(Change.Key key) {
+ return Entities.Change_Key.newBuilder().setId(key.get()).build();
+ }
+
+ @Override
+ public Change.Key fromProto(Entities.Change_Key proto) {
+ return new Change.Key(proto.getId());
+ }
+
+ @Override
+ public Parser<Entities.Change_Key> getParser() {
+ return Entities.Change_Key.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java
new file mode 100644
index 0000000000..bb532dfaa4
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverter.java
@@ -0,0 +1,46 @@
+// 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.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.protobuf.Parser;
+
+public enum ChangeMessageKeyProtoConverter
+ implements ProtoConverter<Entities.ChangeMessage_Key, ChangeMessage.Key> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.Change_Id, Change.Id> changeIdConverter =
+ ChangeIdProtoConverter.INSTANCE;
+
+ @Override
+ public Entities.ChangeMessage_Key toProto(ChangeMessage.Key messageKey) {
+ return Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(changeIdConverter.toProto(messageKey.getParentKey()))
+ .setUuid(messageKey.get())
+ .build();
+ }
+
+ @Override
+ public ChangeMessage.Key fromProto(Entities.ChangeMessage_Key proto) {
+ return new ChangeMessage.Key(changeIdConverter.fromProto(proto.getChangeId()), proto.getUuid());
+ }
+
+ @Override
+ public Parser<Entities.ChangeMessage_Key> getParser() {
+ return Entities.ChangeMessage_Key.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java
new file mode 100644
index 0000000000..0895d8d89b
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverter.java
@@ -0,0 +1,97 @@
+// 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.ChangeMessage;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.protobuf.Parser;
+import java.sql.Timestamp;
+import java.util.Objects;
+
+public enum ChangeMessageProtoConverter
+ implements ProtoConverter<Entities.ChangeMessage, ChangeMessage> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.ChangeMessage_Key, ChangeMessage.Key>
+ changeMessageKeyConverter = ChangeMessageKeyProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
+ AccountIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.PatchSet_Id, PatchSet.Id> patchSetIdConverter =
+ PatchSetIdProtoConverter.INSTANCE;
+
+ @Override
+ public Entities.ChangeMessage toProto(ChangeMessage changeMessage) {
+ Entities.ChangeMessage.Builder builder =
+ Entities.ChangeMessage.newBuilder()
+ .setKey(changeMessageKeyConverter.toProto(changeMessage.getKey()));
+ Account.Id author = changeMessage.getAuthor();
+ if (author != null) {
+ builder.setAuthorId(accountIdConverter.toProto(author));
+ }
+ Timestamp writtenOn = changeMessage.getWrittenOn();
+ if (writtenOn != null) {
+ builder.setWrittenOn(writtenOn.getTime());
+ }
+ String message = changeMessage.getMessage();
+ if (message != null) {
+ builder.setMessage(message);
+ }
+ PatchSet.Id patchSetId = changeMessage.getPatchSetId();
+ if (patchSetId != null) {
+ builder.setPatchset(patchSetIdConverter.toProto(patchSetId));
+ }
+ String tag = changeMessage.getTag();
+ if (tag != null) {
+ builder.setTag(tag);
+ }
+ Account.Id realAuthor = changeMessage.getRealAuthor();
+ // ChangeMessage#getRealAuthor automatically delegates to ChangeMessage#getAuthor if the real
+ // author is not set. However, the previous protobuf representation kept 'realAuthor' empty if
+ // it wasn't set. To ensure binary compatibility, simulate the previous behavior.
+ if (realAuthor != null && !Objects.equals(realAuthor, author)) {
+ builder.setRealAuthor(accountIdConverter.toProto(realAuthor));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public ChangeMessage fromProto(Entities.ChangeMessage proto) {
+ ChangeMessage.Key key =
+ proto.hasKey() ? changeMessageKeyConverter.fromProto(proto.getKey()) : null;
+ Account.Id author =
+ proto.hasAuthorId() ? accountIdConverter.fromProto(proto.getAuthorId()) : null;
+ Timestamp writtenOn = proto.hasWrittenOn() ? new Timestamp(proto.getWrittenOn()) : null;
+ PatchSet.Id patchSetId =
+ proto.hasPatchset() ? patchSetIdConverter.fromProto(proto.getPatchset()) : null;
+ ChangeMessage changeMessage = new ChangeMessage(key, author, writtenOn, patchSetId);
+ if (proto.hasMessage()) {
+ changeMessage.setMessage(proto.getMessage());
+ }
+ if (proto.hasTag()) {
+ changeMessage.setTag(proto.getTag());
+ }
+ if (proto.hasRealAuthor()) {
+ changeMessage.setRealAuthor(accountIdConverter.fromProto(proto.getRealAuthor()));
+ }
+ return changeMessage;
+ }
+
+ @Override
+ public Parser<Entities.ChangeMessage> getParser() {
+ return Entities.ChangeMessage.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java
new file mode 100644
index 0000000000..30b6d27b43
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ChangeProtoConverter.java
@@ -0,0 +1,126 @@
+// 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.Branch;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.protobuf.Parser;
+import java.sql.Timestamp;
+
+public enum ChangeProtoConverter implements ProtoConverter<Entities.Change, Change> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.Change_Id, Change.Id> changeIdConverter =
+ ChangeIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Change_Key, Change.Key> changeKeyConverter =
+ ChangeKeyProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
+ AccountIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Branch_NameKey, Branch.NameKey> branchNameConverter =
+ BranchNameKeyProtoConverter.INSTANCE;
+
+ @Override
+ public Entities.Change toProto(Change change) {
+ Entities.Change.Builder builder =
+ Entities.Change.newBuilder()
+ .setChangeId(changeIdConverter.toProto(change.getId()))
+ .setRowVersion(change.getRowVersion())
+ .setChangeKey(changeKeyConverter.toProto(change.getKey()))
+ .setCreatedOn(change.getCreatedOn().getTime())
+ .setLastUpdatedOn(change.getLastUpdatedOn().getTime())
+ .setOwnerAccountId(accountIdConverter.toProto(change.getOwner()))
+ .setDest(branchNameConverter.toProto(change.getDest()))
+ .setStatus(change.getStatus().getCode())
+ .setIsPrivate(change.isPrivate())
+ .setWorkInProgress(change.isWorkInProgress())
+ .setReviewStarted(change.hasReviewStarted());
+ PatchSet.Id currentPatchSetId = change.currentPatchSetId();
+ // Special behavior necessary to ensure binary compatibility.
+ builder.setCurrentPatchSetId(currentPatchSetId == null ? 0 : currentPatchSetId.get());
+ String subject = change.getSubject();
+ if (subject != null) {
+ builder.setSubject(subject);
+ }
+ String topic = change.getTopic();
+ if (topic != null) {
+ builder.setTopic(topic);
+ }
+ String originalSubject = change.getOriginalSubjectOrNull();
+ if (originalSubject != null) {
+ builder.setOriginalSubject(originalSubject);
+ }
+ String submissionId = change.getSubmissionId();
+ if (submissionId != null) {
+ builder.setSubmissionId(submissionId);
+ }
+ Account.Id assignee = change.getAssignee();
+ if (assignee != null) {
+ builder.setAssignee(accountIdConverter.toProto(assignee));
+ }
+ Change.Id revertOf = change.getRevertOf();
+ if (revertOf != null) {
+ builder.setRevertOf(changeIdConverter.toProto(revertOf));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Change fromProto(Entities.Change proto) {
+ Change.Id changeId = changeIdConverter.fromProto(proto.getChangeId());
+ Change.Key key =
+ proto.hasChangeKey() ? changeKeyConverter.fromProto(proto.getChangeKey()) : null;
+ Account.Id owner =
+ proto.hasOwnerAccountId() ? accountIdConverter.fromProto(proto.getOwnerAccountId()) : null;
+ Branch.NameKey destination =
+ proto.hasDest() ? branchNameConverter.fromProto(proto.getDest()) : null;
+ Change change =
+ new Change(key, changeId, owner, destination, new Timestamp(proto.getCreatedOn()));
+ if (proto.hasLastUpdatedOn()) {
+ change.setLastUpdatedOn(new Timestamp(proto.getLastUpdatedOn()));
+ }
+ Change.Status status = Change.Status.forCode((char) proto.getStatus());
+ if (status != null) {
+ change.setStatus(status);
+ }
+ String subject = proto.hasSubject() ? proto.getSubject() : null;
+ String originalSubject = proto.hasOriginalSubject() ? proto.getOriginalSubject() : null;
+ change.setCurrentPatchSet(
+ new PatchSet.Id(changeId, proto.getCurrentPatchSetId()), subject, originalSubject);
+ if (proto.hasTopic()) {
+ change.setTopic(proto.getTopic());
+ }
+ if (proto.hasSubmissionId()) {
+ change.setSubmissionId(proto.getSubmissionId());
+ }
+ if (proto.hasAssignee()) {
+ change.setAssignee(accountIdConverter.fromProto(proto.getAssignee()));
+ }
+ change.setPrivate(proto.getIsPrivate());
+ change.setWorkInProgress(proto.getWorkInProgress());
+ change.setReviewStarted(proto.getReviewStarted());
+ if (proto.hasRevertOf()) {
+ change.setRevertOf(changeIdConverter.fromProto(proto.getRevertOf()));
+ }
+ return change;
+ }
+
+ @Override
+ public Parser<Entities.Change> getParser() {
+ return Entities.Change.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java
new file mode 100644
index 0000000000..274f23b26c
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/LabelIdProtoConverter.java
@@ -0,0 +1,38 @@
+// 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.LabelId;
+import com.google.protobuf.Parser;
+
+public enum LabelIdProtoConverter implements ProtoConverter<Entities.LabelId, LabelId> {
+ INSTANCE;
+
+ @Override
+ public Entities.LabelId toProto(LabelId labelId) {
+ return Entities.LabelId.newBuilder().setId(labelId.get()).build();
+ }
+
+ @Override
+ public LabelId fromProto(Entities.LabelId proto) {
+ return new LabelId(proto.getId());
+ }
+
+ @Override
+ public Parser<Entities.LabelId> getParser() {
+ return Entities.LabelId.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java
new file mode 100644
index 0000000000..353830195d
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverter.java
@@ -0,0 +1,56 @@
+// 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.LabelId;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.protobuf.Parser;
+
+public enum PatchSetApprovalKeyProtoConverter
+ implements ProtoConverter<Entities.PatchSetApproval_Key, PatchSetApproval.Key> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.PatchSet_Id, PatchSet.Id> patchSetIdConverter =
+ PatchSetIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
+ AccountIdProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.LabelId, LabelId> labelIdConverter =
+ LabelIdProtoConverter.INSTANCE;
+
+ @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()))
+ .build();
+ }
+
+ @Override
+ public PatchSetApproval.Key fromProto(Entities.PatchSetApproval_Key proto) {
+ return new PatchSetApproval.Key(
+ patchSetIdConverter.fromProto(proto.getPatchSetId()),
+ accountIdConverter.fromProto(proto.getAccountId()),
+ labelIdConverter.fromProto(proto.getCategoryId()));
+ }
+
+ @Override
+ public Parser<Entities.PatchSetApproval_Key> getParser() {
+ return Entities.PatchSetApproval_Key.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java
new file mode 100644
index 0000000000..418076f47f
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverter.java
@@ -0,0 +1,81 @@
+// 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.PatchSetApproval;
+import com.google.protobuf.Parser;
+import java.sql.Timestamp;
+import java.util.Objects;
+
+public enum PatchSetApprovalProtoConverter
+ implements ProtoConverter<Entities.PatchSetApproval, PatchSetApproval> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.PatchSetApproval_Key, PatchSetApproval.Key>
+ patchSetApprovalKeyProtoConverter = PatchSetApprovalKeyProtoConverter.INSTANCE;
+ private final ProtoConverter<Entities.Account_Id, Account.Id> accountIdConverter =
+ AccountIdProtoConverter.INSTANCE;
+
+ @Override
+ 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());
+
+ String tag = patchSetApproval.getTag();
+ if (tag != null) {
+ builder.setTag(tag);
+ }
+ Account.Id realAccountId = patchSetApproval.getRealAccountId();
+ // 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())) {
+ builder.setRealAccountId(accountIdConverter.toProto(realAccountId));
+ }
+
+ return builder.build();
+ }
+
+ @Override
+ public PatchSetApproval fromProto(Entities.PatchSetApproval proto) {
+ PatchSetApproval patchSetApproval =
+ new PatchSetApproval(
+ patchSetApprovalKeyProtoConverter.fromProto(proto.getKey()),
+ (short) proto.getValue(),
+ new Timestamp(proto.getGranted()));
+ if (proto.hasTag()) {
+ patchSetApproval.setTag(proto.getTag());
+ }
+ if (proto.hasRealAccountId()) {
+ patchSetApproval.setRealAccountId(accountIdConverter.fromProto(proto.getRealAccountId()));
+ }
+ if (proto.hasPostSubmit()) {
+ patchSetApproval.setPostSubmit(proto.getPostSubmit());
+ }
+ return patchSetApproval;
+ }
+
+ @Override
+ public Parser<Entities.PatchSetApproval> getParser() {
+ return Entities.PatchSetApproval.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java
new file mode 100644
index 0000000000..a2b95bd97d
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverter.java
@@ -0,0 +1,45 @@
+// 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.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.protobuf.Parser;
+
+public enum PatchSetIdProtoConverter implements ProtoConverter<Entities.PatchSet_Id, PatchSet.Id> {
+ INSTANCE;
+
+ private final ProtoConverter<Entities.Change_Id, Change.Id> changeIdConverter =
+ ChangeIdProtoConverter.INSTANCE;
+
+ @Override
+ public Entities.PatchSet_Id toProto(PatchSet.Id patchSetId) {
+ return Entities.PatchSet_Id.newBuilder()
+ .setChangeId(changeIdConverter.toProto(patchSetId.getParentKey()))
+ .setPatchSetId(patchSetId.get())
+ .build();
+ }
+
+ @Override
+ public PatchSet.Id fromProto(Entities.PatchSet_Id proto) {
+ return new PatchSet.Id(changeIdConverter.fromProto(proto.getChangeId()), proto.getPatchSetId());
+ }
+
+ @Override
+ public Parser<Entities.PatchSet_Id> getParser() {
+ return Entities.PatchSet_Id.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java
new file mode 100644
index 0000000000..75ee80099f
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/PatchSetProtoConverter.java
@@ -0,0 +1,93 @@
+// 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/ProjectNameKeyProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverter.java
new file mode 100644
index 0000000000..f7d809ec27
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverter.java
@@ -0,0 +1,39 @@
+// 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.Project;
+import com.google.protobuf.Parser;
+
+public enum ProjectNameKeyProtoConverter
+ implements ProtoConverter<Entities.Project_NameKey, Project.NameKey> {
+ INSTANCE;
+
+ @Override
+ public Entities.Project_NameKey toProto(Project.NameKey nameKey) {
+ return Entities.Project_NameKey.newBuilder().setName(nameKey.get()).build();
+ }
+
+ @Override
+ public Project.NameKey fromProto(Entities.Project_NameKey proto) {
+ return new Project.NameKey(proto.getName());
+ }
+
+ @Override
+ public Parser<Entities.Project_NameKey> getParser() {
+ return Entities.Project_NameKey.parser();
+ }
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/ProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/ProtoConverter.java
new file mode 100644
index 0000000000..568759c3c8
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/ProtoConverter.java
@@ -0,0 +1,27 @@
+// 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.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+
+public interface ProtoConverter<P extends MessageLite, C> {
+
+ P toProto(C valueClass);
+
+ C fromProto(P proto);
+
+ Parser<P> getParser();
+}
diff --git a/java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java b/java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java
new file mode 100644
index 0000000000..b3c998b2aa
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/converter/RevIdProtoConverter.java
@@ -0,0 +1,38 @@
+// 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/reviewdb/server/AccountGroupAccess.java b/java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java
deleted file mode 100644
index c5a1caffb5..0000000000
--- a/java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java
+++ /dev/null
@@ -1,34 +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.server;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountGroupAccess extends Access<AccountGroup, AccountGroup.Id> {
- @Override
- @PrimaryKey("groupId")
- AccountGroup get(AccountGroup.Id id) throws OrmException;
-
- @Query("WHERE groupUUID = ?")
- ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) throws OrmException;
-
- @Query
- ResultSet<AccountGroup> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/AccountGroupByIdAccess.java b/java/com/google/gerrit/reviewdb/server/AccountGroupByIdAccess.java
deleted file mode 100644
index 1634fda87b..0000000000
--- a/java/com/google/gerrit/reviewdb/server/AccountGroupByIdAccess.java
+++ /dev/null
@@ -1,38 +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.server;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountGroupByIdAccess extends Access<AccountGroupById, AccountGroupById.Key> {
- @Override
- @PrimaryKey("key")
- AccountGroupById get(AccountGroupById.Key key) throws OrmException;
-
- @Query("WHERE key.includeUUID = ?")
- ResultSet<AccountGroupById> byIncludeUUID(AccountGroup.UUID uuid) throws OrmException;
-
- @Query("WHERE key.groupId = ?")
- ResultSet<AccountGroupById> byGroup(AccountGroup.Id id) throws OrmException;
-
- @Query("")
- ResultSet<AccountGroupById> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/AccountGroupByIdAudAccess.java b/java/com/google/gerrit/reviewdb/server/AccountGroupByIdAudAccess.java
deleted file mode 100644
index 08f9a0a960..0000000000
--- a/java/com/google/gerrit/reviewdb/server/AccountGroupByIdAudAccess.java
+++ /dev/null
@@ -1,37 +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.server;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountGroupByIdAudAccess
- extends Access<AccountGroupByIdAud, AccountGroupByIdAud.Key> {
- @Override
- @PrimaryKey("key")
- AccountGroupByIdAud get(AccountGroupByIdAud.Key key) throws OrmException;
-
- @Query("WHERE key.groupId = ? AND key.includeUUID = ?")
- ResultSet<AccountGroupByIdAud> byGroupInclude(
- AccountGroup.Id groupId, AccountGroup.UUID incGroupUUID) throws OrmException;
-
- @Query("WHERE key.groupId = ?")
- ResultSet<AccountGroupByIdAud> byGroup(AccountGroup.Id groupId) throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/AccountGroupMemberAccess.java b/java/com/google/gerrit/reviewdb/server/AccountGroupMemberAccess.java
deleted file mode 100644
index 0213f25a29..0000000000
--- a/java/com/google/gerrit/reviewdb/server/AccountGroupMemberAccess.java
+++ /dev/null
@@ -1,37 +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.server;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountGroupMemberAccess
- extends Access<AccountGroupMember, AccountGroupMember.Key> {
- @Override
- @PrimaryKey("key")
- AccountGroupMember get(AccountGroupMember.Key key) throws OrmException;
-
- @Query("WHERE key.accountId = ?")
- ResultSet<AccountGroupMember> byAccount(Account.Id id) throws OrmException;
-
- @Query("WHERE key.groupId = ?")
- ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/AccountGroupMemberAuditAccess.java b/java/com/google/gerrit/reviewdb/server/AccountGroupMemberAuditAccess.java
deleted file mode 100644
index aa2f7c43d5..0000000000
--- a/java/com/google/gerrit/reviewdb/server/AccountGroupMemberAuditAccess.java
+++ /dev/null
@@ -1,38 +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.server;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountGroupMemberAuditAccess
- extends Access<AccountGroupMemberAudit, AccountGroupMemberAudit.Key> {
- @Override
- @PrimaryKey("key")
- AccountGroupMemberAudit get(AccountGroupMemberAudit.Key key) throws OrmException;
-
- @Query("WHERE key.groupId = ? AND key.accountId = ?")
- ResultSet<AccountGroupMemberAudit> byGroupAccount(AccountGroup.Id groupId, Account.Id accountId)
- throws OrmException;
-
- @Query("WHERE key.groupId = ?")
- ResultSet<AccountGroupMemberAudit> byGroup(AccountGroup.Id groupId) throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/AccountGroupNameAccess.java b/java/com/google/gerrit/reviewdb/server/AccountGroupNameAccess.java
deleted file mode 100644
index b8bc9f0b6c..0000000000
--- a/java/com/google/gerrit/reviewdb/server/AccountGroupNameAccess.java
+++ /dev/null
@@ -1,32 +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.server;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface AccountGroupNameAccess extends Access<AccountGroupName, AccountGroup.NameKey> {
- @Override
- @PrimaryKey("name")
- AccountGroupName get(AccountGroup.NameKey name) throws OrmException;
-
- @Query("ORDER BY name")
- ResultSet<AccountGroupName> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ChangeAccess.java b/java/com/google/gerrit/reviewdb/server/ChangeAccess.java
deleted file mode 100644
index 4e46efbc8b..0000000000
--- a/java/com/google/gerrit/reviewdb/server/ChangeAccess.java
+++ /dev/null
@@ -1,31 +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.server;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface ChangeAccess extends Access<Change, Change.Id> {
- @Override
- @PrimaryKey("changeId")
- Change get(Change.Id id) throws OrmException;
-
- @Query
- ResultSet<Change> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ChangeMessageAccess.java b/java/com/google/gerrit/reviewdb/server/ChangeMessageAccess.java
deleted file mode 100644
index fe87e59136..0000000000
--- a/java/com/google/gerrit/reviewdb/server/ChangeMessageAccess.java
+++ /dev/null
@@ -1,39 +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.server;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface ChangeMessageAccess extends Access<ChangeMessage, ChangeMessage.Key> {
- @Override
- @PrimaryKey("key")
- ChangeMessage get(ChangeMessage.Key id) throws OrmException;
-
- @Query("WHERE key.changeId = ? ORDER BY writtenOn")
- ResultSet<ChangeMessage> byChange(Change.Id id) throws OrmException;
-
- @Query("WHERE patchset = ?")
- ResultSet<ChangeMessage> byPatchSet(PatchSet.Id id) throws OrmException;
-
- @Query
- ResultSet<ChangeMessage> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/DisallowReadFromChangesReviewDbWrapper.java b/java/com/google/gerrit/reviewdb/server/DisallowReadFromChangesReviewDbWrapper.java
deleted file mode 100644
index fdf3d6c5a6..0000000000
--- a/java/com/google/gerrit/reviewdb/server/DisallowReadFromChangesReviewDbWrapper.java
+++ /dev/null
@@ -1,281 +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.server;
-
-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.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-
-public class DisallowReadFromChangesReviewDbWrapper extends ReviewDbWrapper {
- private static final String MSG = "This table has been migrated to NoteDb";
-
- private final Changes changes;
- private final PatchSetApprovals patchSetApprovals;
- private final ChangeMessages changeMessages;
- private final PatchSets patchSets;
- private final PatchLineComments patchComments;
-
- public DisallowReadFromChangesReviewDbWrapper(ReviewDb db) {
- super(db);
- changes = new Changes(delegate.changes());
- patchSetApprovals = new PatchSetApprovals(delegate.patchSetApprovals());
- changeMessages = new ChangeMessages(delegate.changeMessages());
- patchSets = new PatchSets(delegate.patchSets());
- patchComments = new PatchLineComments(delegate.patchComments());
- }
-
- @Override
- public ChangeAccess changes() {
- return changes;
- }
-
- @Override
- public PatchSetApprovalAccess patchSetApprovals() {
- return patchSetApprovals;
- }
-
- @Override
- public ChangeMessageAccess changeMessages() {
- return changeMessages;
- }
-
- @Override
- public PatchSetAccess patchSets() {
- return patchSets;
- }
-
- @Override
- public PatchLineCommentAccess patchComments() {
- return patchComments;
- }
-
- private static class Changes extends ChangeAccessWrapper {
-
- protected Changes(ChangeAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<Change> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<Change, OrmException> getAsync(
- Change.Id key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<Change> get(Iterable<Change.Id> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public Change get(Change.Id id) throws OrmException {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<Change> all() throws OrmException {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class PatchSetApprovals extends PatchSetApprovalAccessWrapper {
- PatchSetApprovals(PatchSetApprovalAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<PatchSetApproval> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<PatchSetApproval, OrmException> getAsync(
- PatchSetApproval.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchSetApproval> get(Iterable<PatchSetApproval.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public PatchSetApproval get(PatchSetApproval.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchSetApproval> byChange(Change.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchSetApproval> byPatchSet(PatchSet.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class ChangeMessages extends ChangeMessageAccessWrapper {
- ChangeMessages(ChangeMessageAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<ChangeMessage> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<ChangeMessage, OrmException> getAsync(
- ChangeMessage.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<ChangeMessage> get(Iterable<ChangeMessage.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ChangeMessage get(ChangeMessage.Key id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<ChangeMessage> byChange(Change.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<ChangeMessage> byPatchSet(PatchSet.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<ChangeMessage> all() {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class PatchSets extends PatchSetAccessWrapper {
- PatchSets(PatchSetAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<PatchSet> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<PatchSet, OrmException> getAsync(
- PatchSet.Id key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchSet> get(Iterable<PatchSet.Id> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public PatchSet get(PatchSet.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchSet> byChange(Change.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class PatchLineComments extends PatchLineCommentAccessWrapper {
- PatchLineComments(PatchLineCommentAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<PatchLineComment> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<PatchLineComment, OrmException> getAsync(
- PatchLineComment.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> get(Iterable<PatchLineComment.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public PatchLineComment get(PatchLineComment.Key id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> byChange(Change.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> byPatchSet(PatchSet.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> publishedByChangeFile(Change.Id id, String file) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> publishedByPatchSet(PatchSet.Id patchset) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByPatchSetAuthor(
- PatchSet.Id patchset, Account.Id author) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByChangeFileAuthor(
- Change.Id id, String file, Account.Id author) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByAuthor(Account.Id author) {
- throw new UnsupportedOperationException(MSG);
- }
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/server/PatchLineCommentAccess.java b/java/com/google/gerrit/reviewdb/server/PatchLineCommentAccess.java
deleted file mode 100644
index 08b8484d1b..0000000000
--- a/java/com/google/gerrit/reviewdb/server/PatchLineCommentAccess.java
+++ /dev/null
@@ -1,69 +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.server;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface PatchLineCommentAccess extends Access<PatchLineComment, PatchLineComment.Key> {
- @Override
- @PrimaryKey("key")
- PatchLineComment get(PatchLineComment.Key id) throws OrmException;
-
- @Query("WHERE key.patchKey.patchSetId.changeId = ?")
- ResultSet<PatchLineComment> byChange(Change.Id id) throws OrmException;
-
- @Query("WHERE key.patchKey.patchSetId = ?")
- ResultSet<PatchLineComment> byPatchSet(PatchSet.Id id) throws OrmException;
-
- @Query(
- "WHERE key.patchKey.patchSetId.changeId = ?"
- + " AND key.patchKey.fileName = ? AND status = '"
- + PatchLineComment.STATUS_PUBLISHED
- + "' ORDER BY lineNbr,writtenOn")
- ResultSet<PatchLineComment> publishedByChangeFile(Change.Id id, String file) throws OrmException;
-
- @Query(
- "WHERE key.patchKey.patchSetId = ? AND status = '" + PatchLineComment.STATUS_PUBLISHED + "'")
- ResultSet<PatchLineComment> publishedByPatchSet(PatchSet.Id patchset) throws OrmException;
-
- @Query(
- "WHERE key.patchKey.patchSetId = ? AND status = '"
- + PatchLineComment.STATUS_DRAFT
- + "' AND author = ? ORDER BY key.patchKey,lineNbr,writtenOn")
- ResultSet<PatchLineComment> draftByPatchSetAuthor(PatchSet.Id patchset, Account.Id author)
- throws OrmException;
-
- @Query(
- "WHERE key.patchKey.patchSetId.changeId = ?"
- + " AND key.patchKey.fileName = ? AND author = ? AND status = '"
- + PatchLineComment.STATUS_DRAFT
- + "' ORDER BY lineNbr,writtenOn")
- ResultSet<PatchLineComment> draftByChangeFileAuthor(Change.Id id, String file, Account.Id author)
- throws OrmException;
-
- @Query("WHERE status = '" + PatchLineComment.STATUS_DRAFT + "' AND author = ?")
- ResultSet<PatchLineComment> draftByAuthor(Account.Id author) throws OrmException;
-
- @Query
- ResultSet<PatchLineComment> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/PatchSetAccess.java b/java/com/google/gerrit/reviewdb/server/PatchSetAccess.java
deleted file mode 100644
index bf3c9e4a47..0000000000
--- a/java/com/google/gerrit/reviewdb/server/PatchSetAccess.java
+++ /dev/null
@@ -1,35 +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.server;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface PatchSetAccess extends Access<PatchSet, PatchSet.Id> {
- @Override
- @PrimaryKey("id")
- PatchSet get(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE id.changeId = ? ORDER BY id.patchSetId")
- ResultSet<PatchSet> byChange(Change.Id id) throws OrmException;
-
- @Query
- ResultSet<PatchSet> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/PatchSetApprovalAccess.java b/java/com/google/gerrit/reviewdb/server/PatchSetApprovalAccess.java
deleted file mode 100644
index 69357bcd07..0000000000
--- a/java/com/google/gerrit/reviewdb/server/PatchSetApprovalAccess.java
+++ /dev/null
@@ -1,44 +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.server;
-
-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.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-import com.google.gwtorm.server.Query;
-import com.google.gwtorm.server.ResultSet;
-
-public interface PatchSetApprovalAccess extends Access<PatchSetApproval, PatchSetApproval.Key> {
- @Override
- @PrimaryKey("key")
- PatchSetApproval get(PatchSetApproval.Key key) throws OrmException;
-
- @Query("WHERE key.patchSetId.changeId = ?")
- ResultSet<PatchSetApproval> byChange(Change.Id id) throws OrmException;
-
- @Query("WHERE key.patchSetId = ?")
- ResultSet<PatchSetApproval> byPatchSet(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE key.patchSetId = ? AND key.accountId = ?")
- ResultSet<PatchSetApproval> byPatchSetUser(PatchSet.Id patchSet, Account.Id account)
- throws OrmException;
-
- @Query
- ResultSet<PatchSetApproval> all() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
deleted file mode 100644
index f0661e9aa3..0000000000
--- a/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ /dev/null
@@ -1,120 +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.server;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.Relation;
-import com.google.gwtorm.server.Schema;
-import com.google.gwtorm.server.Sequence;
-
-/**
- * The review service database schema.
- *
- * <p>Root entities that are at the top level of some important data graph:
- *
- * <ul>
- * <li>{@link Account}: Per-user account registration, preferences, identity.
- * <li>{@link Change}: All review information about a single proposed change.
- * </ul>
- */
-public interface ReviewDb extends Schema {
- /* If you change anything, update SchemaVersion.C to use a new version. */
-
- @Relation(id = 1)
- SchemaVersionAccess schemaVersion();
-
- // Deleted @Relation(id = 2)
-
- // Deleted @Relation(id = 3)
-
- // Deleted @Relation(id = 4)
-
- // Deleted @Relation(id = 6)
-
- // Deleted @Relation(id = 7)
-
- // Deleted @Relation(id = 8)
-
- // Deleted @Relation(id = 10)
-
- // Deleted @Relation(id = 11)
-
- // Deleted @Relation(id = 12)
-
- // Deleted @Relation(id = 13)
-
- // Deleted @Relation(id = 17)
-
- // Deleted @Relation(id = 18)
-
- // Deleted @Relation(id = 19)
-
- // Deleted @Relation(id = 20)
-
- @Relation(id = 21)
- ChangeAccess changes();
-
- @Relation(id = 22)
- PatchSetApprovalAccess patchSetApprovals();
-
- @Relation(id = 23)
- ChangeMessageAccess changeMessages();
-
- @Relation(id = 24)
- PatchSetAccess patchSets();
-
- // Deleted @Relation(id = 25)
-
- @Relation(id = 26)
- PatchLineCommentAccess patchComments();
-
- // Deleted @Relation(id = 28)
-
- // Deleted @Relation(id = 29)
-
- // Deleted @Relation(id = 30)
-
- int FIRST_ACCOUNT_ID = 1000000;
-
- /**
- * Next unique id for a {@link Account}.
- *
- * @deprecated use {@link com.google.gerrit.server.Sequences#nextAccountId()}.
- */
- @Sequence(startWith = FIRST_ACCOUNT_ID)
- @Deprecated
- int nextAccountId() throws OrmException;
-
- int FIRST_GROUP_ID = 1;
-
- /** Next unique id for a {@link AccountGroup}. */
- @Sequence(startWith = FIRST_GROUP_ID)
- @Deprecated
- int nextAccountGroupId() throws OrmException;
-
- int FIRST_CHANGE_ID = 1;
-
- /**
- * Next unique id for a {@link Change}.
- *
- * @deprecated use {@link com.google.gerrit.server.Sequences#nextChangeId()}.
- */
- @Sequence(startWith = FIRST_CHANGE_ID)
- @Deprecated
- int nextChangeId() throws OrmException;
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbCodecs.java b/java/com/google/gerrit/reviewdb/server/ReviewDbCodecs.java
deleted file mode 100644
index 29584647f9..0000000000
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbCodecs.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.server;
-
-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.gwtorm.protobuf.CodecFactory;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-
-/** {@link ProtobufCodec} instances for ReviewDb types. */
-public class ReviewDbCodecs {
- public static final ProtobufCodec<PatchSetApproval> APPROVAL_CODEC =
- CodecFactory.encoder(PatchSetApproval.class);
-
- public static final ProtobufCodec<Change> CHANGE_CODEC = CodecFactory.encoder(Change.class);
-
- public static final ProtobufCodec<ChangeMessage> MESSAGE_CODEC =
- CodecFactory.encoder(ChangeMessage.class);
-
- public static final ProtobufCodec<PatchSet> PATCH_SET_CODEC =
- CodecFactory.encoder(PatchSet.class);
-
- private ReviewDbCodecs() {}
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java b/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
deleted file mode 100644
index aed97784fe..0000000000
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
+++ /dev/null
@@ -1,77 +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.server;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.TreeSet;
-
-/** Static utilities for ReviewDb types. */
-public class ReviewDbUtil {
- private static final Ordering<? extends IntKey<?>> INT_KEY_ORDERING =
- Ordering.natural().nullsFirst().<IntKey<?>>onResultOf(IntKey::get).nullsFirst();
-
- /**
- * Null-safe ordering over arbitrary subclass of {@code IntKey}.
- *
- * <p>In some cases, {@code Comparator.comparing(Change.Id::get)} may be shorter and cleaner.
- * However, this method may be preferable in some cases:
- *
- * <ul>
- * <li>This ordering is null-safe over both input and the result of {@link IntKey#get()}; {@code
- * comparing} is only a good idea if all inputs are obviously non-null.
- * <li>{@code intKeyOrdering().sortedCopy(iterable)} is shorter than the stream equivalent.
- * <li>Creating derived comparators may be more readable with {@link Ordering} method chaining
- * rather than static {@code Comparator} methods.
- * </ul>
- */
- @SuppressWarnings("unchecked")
- public static <K extends IntKey<?>> Ordering<K> intKeyOrdering() {
- return (Ordering<K>) INT_KEY_ORDERING;
- }
-
- public static ReviewDb unwrapDb(ReviewDb db) {
- if (db instanceof DisallowReadFromChangesReviewDbWrapper) {
- return unwrapDb(((DisallowReadFromChangesReviewDbWrapper) db).unsafeGetDelegate());
- }
- return db;
- }
-
- public static void checkColumns(Class<?> clazz, Integer... expected) {
- Set<Integer> ids = new TreeSet<>();
- for (Field f : clazz.getDeclaredFields()) {
- Column col = f.getAnnotation(Column.class);
- if (col != null) {
- ids.add(col.id());
- }
- }
- Set<Integer> expectedIds = Sets.newTreeSet(Arrays.asList(expected));
- checkState(
- ids.equals(expectedIds),
- "Unexpected column set for %s: %s != %s",
- clazz.getSimpleName(),
- ids,
- expectedIds);
- }
-
- private ReviewDbUtil() {}
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
deleted file mode 100644
index 202729e2aa..0000000000
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
+++ /dev/null
@@ -1,1267 +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.server;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-import com.google.gwtorm.server.StatementExecutor;
-import java.util.Map;
-
-public class ReviewDbWrapper implements ReviewDb {
- public static JdbcSchema unwrapJbdcSchema(ReviewDb db) {
- if (db instanceof ReviewDbWrapper) {
- return unwrapJbdcSchema(((ReviewDbWrapper) db).unsafeGetDelegate());
- }
- return (JdbcSchema) db;
- }
-
- protected final ReviewDb delegate;
-
- private boolean inTransaction;
-
- protected ReviewDbWrapper(ReviewDb delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- public ReviewDb unsafeGetDelegate() {
- return delegate;
- }
-
- public boolean inTransaction() {
- return inTransaction;
- }
-
- public void beginTransaction() {
- inTransaction = true;
- }
-
- @Override
- public void commit() throws OrmException {
- if (!inTransaction) {
- // This reads a little weird, we're not in a transaction, so why are we calling commit?
- // Because we want to let the underlying ReviewDb do its normal thing in this case (which may
- // be throwing an exception, or not, depending on implementation).
- delegate.commit();
- }
- }
-
- @Override
- public void rollback() throws OrmException {
- if (inTransaction) {
- inTransaction = false;
- } else {
- // See comment in commit(): we want to let the underlying ReviewDb do its thing.
- delegate.rollback();
- }
- }
-
- @Override
- public void updateSchema(StatementExecutor e) throws OrmException {
- delegate.updateSchema(e);
- }
-
- @Override
- public void pruneSchema(StatementExecutor e) throws OrmException {
- delegate.pruneSchema(e);
- }
-
- @Override
- public Access<?, ?>[] allRelations() {
- return delegate.allRelations();
- }
-
- @Override
- public void close() {
- delegate.close();
- }
-
- @Override
- public SchemaVersionAccess schemaVersion() {
- return delegate.schemaVersion();
- }
-
- @Override
- public ChangeAccess changes() {
- return delegate.changes();
- }
-
- @Override
- public PatchSetApprovalAccess patchSetApprovals() {
- return delegate.patchSetApprovals();
- }
-
- @Override
- public ChangeMessageAccess changeMessages() {
- return delegate.changeMessages();
- }
-
- @Override
- public PatchSetAccess patchSets() {
- return delegate.patchSets();
- }
-
- @Override
- public PatchLineCommentAccess patchComments() {
- return delegate.patchComments();
- }
-
- @Override
- @SuppressWarnings("deprecation")
- public int nextAccountId() throws OrmException {
- return delegate.nextAccountId();
- }
-
- @Override
- @SuppressWarnings("deprecation")
- public int nextAccountGroupId() throws OrmException {
- return delegate.nextAccountGroupId();
- }
-
- @Override
- @SuppressWarnings("deprecation")
- public int nextChangeId() throws OrmException {
- return delegate.nextChangeId();
- }
-
- public static class ChangeAccessWrapper implements ChangeAccess {
- protected final ChangeAccess delegate;
-
- protected ChangeAccessWrapper(ChangeAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<Change> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public Change.Id primaryKey(Change entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<Change.Id, Change> toMap(Iterable<Change> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<Change, OrmException> getAsync(
- Change.Id key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<Change> get(Iterable<Change.Id> keys) throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<Change> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<Change> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<Change> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<Change.Id> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<Change> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(Change.Id key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public Change atomicUpdate(Change.Id key, AtomicUpdate<Change> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public Change get(Change.Id id) throws OrmException {
- return delegate.get(id);
- }
-
- @Override
- public ResultSet<Change> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class PatchSetApprovalAccessWrapper implements PatchSetApprovalAccess {
- protected final PatchSetApprovalAccess delegate;
-
- protected PatchSetApprovalAccessWrapper(PatchSetApprovalAccess delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<PatchSetApproval> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public PatchSetApproval.Key primaryKey(PatchSetApproval entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<PatchSetApproval.Key, PatchSetApproval> toMap(Iterable<PatchSetApproval> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<PatchSetApproval, OrmException> getAsync(
- PatchSetApproval.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<PatchSetApproval> get(Iterable<PatchSetApproval.Key> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<PatchSetApproval> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<PatchSetApproval> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<PatchSetApproval> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<PatchSetApproval.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<PatchSetApproval> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(PatchSetApproval.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public PatchSetApproval atomicUpdate(
- PatchSetApproval.Key key, AtomicUpdate<PatchSetApproval> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public PatchSetApproval get(PatchSetApproval.Key key) throws OrmException {
- return delegate.get(key);
- }
-
- @Override
- public ResultSet<PatchSetApproval> byChange(Change.Id id) throws OrmException {
- return delegate.byChange(id);
- }
-
- @Override
- public ResultSet<PatchSetApproval> byPatchSet(PatchSet.Id id) throws OrmException {
- return delegate.byPatchSet(id);
- }
-
- @Override
- public ResultSet<PatchSetApproval> byPatchSetUser(PatchSet.Id patchSet, Account.Id account)
- throws OrmException {
- return delegate.byPatchSetUser(patchSet, account);
- }
-
- @Override
- public ResultSet<PatchSetApproval> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class ChangeMessageAccessWrapper implements ChangeMessageAccess {
- protected final ChangeMessageAccess delegate;
-
- protected ChangeMessageAccessWrapper(ChangeMessageAccess delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<ChangeMessage> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public ChangeMessage.Key primaryKey(ChangeMessage entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<ChangeMessage.Key, ChangeMessage> toMap(Iterable<ChangeMessage> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<ChangeMessage, OrmException> getAsync(
- ChangeMessage.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<ChangeMessage> get(Iterable<ChangeMessage.Key> keys) throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<ChangeMessage> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<ChangeMessage> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<ChangeMessage> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<ChangeMessage.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<ChangeMessage> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(ChangeMessage.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public ChangeMessage atomicUpdate(ChangeMessage.Key key, AtomicUpdate<ChangeMessage> update)
- throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public ChangeMessage get(ChangeMessage.Key id) throws OrmException {
- return delegate.get(id);
- }
-
- @Override
- public ResultSet<ChangeMessage> byChange(Change.Id id) throws OrmException {
- return delegate.byChange(id);
- }
-
- @Override
- public ResultSet<ChangeMessage> byPatchSet(PatchSet.Id id) throws OrmException {
- return delegate.byPatchSet(id);
- }
-
- @Override
- public ResultSet<ChangeMessage> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class PatchSetAccessWrapper implements PatchSetAccess {
- protected final PatchSetAccess delegate;
-
- protected PatchSetAccessWrapper(PatchSetAccess delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<PatchSet> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public PatchSet.Id primaryKey(PatchSet entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<PatchSet.Id, PatchSet> toMap(Iterable<PatchSet> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<PatchSet, OrmException> getAsync(
- PatchSet.Id key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<PatchSet> get(Iterable<PatchSet.Id> keys) throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<PatchSet> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<PatchSet> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<PatchSet> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<PatchSet.Id> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<PatchSet> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(PatchSet.Id key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public PatchSet atomicUpdate(PatchSet.Id key, AtomicUpdate<PatchSet> update)
- throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public PatchSet get(PatchSet.Id id) throws OrmException {
- return delegate.get(id);
- }
-
- @Override
- public ResultSet<PatchSet> byChange(Change.Id id) throws OrmException {
- return delegate.byChange(id);
- }
-
- @Override
- public ResultSet<PatchSet> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class PatchLineCommentAccessWrapper implements PatchLineCommentAccess {
- protected PatchLineCommentAccess delegate;
-
- protected PatchLineCommentAccessWrapper(PatchLineCommentAccess delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<PatchLineComment> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public PatchLineComment.Key primaryKey(PatchLineComment entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<PatchLineComment.Key, PatchLineComment> toMap(Iterable<PatchLineComment> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<PatchLineComment, OrmException> getAsync(
- PatchLineComment.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<PatchLineComment> get(Iterable<PatchLineComment.Key> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<PatchLineComment> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<PatchLineComment> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<PatchLineComment> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<PatchLineComment.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<PatchLineComment> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(PatchLineComment.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public PatchLineComment atomicUpdate(
- PatchLineComment.Key key, AtomicUpdate<PatchLineComment> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public PatchLineComment get(PatchLineComment.Key id) throws OrmException {
- return delegate.get(id);
- }
-
- @Override
- public ResultSet<PatchLineComment> byChange(Change.Id id) throws OrmException {
- return delegate.byChange(id);
- }
-
- @Override
- public ResultSet<PatchLineComment> byPatchSet(PatchSet.Id id) throws OrmException {
- return delegate.byPatchSet(id);
- }
-
- @Override
- public ResultSet<PatchLineComment> publishedByChangeFile(Change.Id id, String file)
- throws OrmException {
- return delegate.publishedByChangeFile(id, file);
- }
-
- @Override
- public ResultSet<PatchLineComment> publishedByPatchSet(PatchSet.Id patchset)
- throws OrmException {
- return delegate.publishedByPatchSet(patchset);
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByPatchSetAuthor(
- PatchSet.Id patchset, Account.Id author) throws OrmException {
- return delegate.draftByPatchSetAuthor(patchset, author);
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByChangeFileAuthor(
- Change.Id id, String file, Account.Id author) throws OrmException {
- return delegate.draftByChangeFileAuthor(id, file, author);
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByAuthor(Account.Id author) throws OrmException {
- return delegate.draftByAuthor(author);
- }
-
- @Override
- public ResultSet<PatchLineComment> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class AccountGroupAccessWrapper implements AccountGroupAccess {
- protected final AccountGroupAccess delegate;
-
- protected AccountGroupAccessWrapper(AccountGroupAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<AccountGroup> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public AccountGroup.Id primaryKey(AccountGroup entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<AccountGroup.Id, AccountGroup> toMap(Iterable<AccountGroup> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroup, OrmException> getAsync(
- AccountGroup.Id key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<AccountGroup> get(Iterable<AccountGroup.Id> keys) throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<AccountGroup> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<AccountGroup> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<AccountGroup> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<AccountGroup.Id> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<AccountGroup> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(AccountGroup.Id key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public AccountGroup atomicUpdate(AccountGroup.Id key, AtomicUpdate<AccountGroup> update)
- throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public AccountGroup get(AccountGroup.Id id) throws OrmException {
- return delegate.get(id);
- }
-
- @Override
- public ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) throws OrmException {
- return delegate.byUUID(uuid);
- }
-
- @Override
- public ResultSet<AccountGroup> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class AccountGroupNameAccessWrapper implements AccountGroupNameAccess {
- protected final AccountGroupNameAccess delegate;
-
- protected AccountGroupNameAccessWrapper(AccountGroupNameAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<AccountGroupName> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public AccountGroup.NameKey primaryKey(AccountGroupName entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<AccountGroup.NameKey, AccountGroupName> toMap(Iterable<AccountGroupName> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupName, OrmException> getAsync(
- AccountGroup.NameKey key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<AccountGroupName> get(Iterable<AccountGroup.NameKey> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<AccountGroupName> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<AccountGroupName> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<AccountGroupName> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<AccountGroup.NameKey> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<AccountGroupName> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(AccountGroup.NameKey key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public AccountGroupName atomicUpdate(
- AccountGroup.NameKey key, AtomicUpdate<AccountGroupName> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public AccountGroupName get(AccountGroup.NameKey name) throws OrmException {
- return delegate.get(name);
- }
-
- @Override
- public ResultSet<AccountGroupName> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class AccountGroupMemberAccessWrapper implements AccountGroupMemberAccess {
- protected final AccountGroupMemberAccess delegate;
-
- protected AccountGroupMemberAccessWrapper(AccountGroupMemberAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<AccountGroupMember> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public AccountGroupMember.Key primaryKey(AccountGroupMember entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<AccountGroupMember.Key, AccountGroupMember> toMap(Iterable<AccountGroupMember> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupMember, OrmException>
- getAsync(AccountGroupMember.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<AccountGroupMember> get(Iterable<AccountGroupMember.Key> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<AccountGroupMember> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<AccountGroupMember> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<AccountGroupMember> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<AccountGroupMember.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<AccountGroupMember> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(AccountGroupMember.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public AccountGroupMember atomicUpdate(
- AccountGroupMember.Key key, AtomicUpdate<AccountGroupMember> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public AccountGroupMember get(AccountGroupMember.Key key) throws OrmException {
- return delegate.get(key);
- }
-
- @Override
- public ResultSet<AccountGroupMember> byAccount(Account.Id id) throws OrmException {
- return delegate.byAccount(id);
- }
-
- @Override
- public ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) throws OrmException {
- return delegate.byGroup(id);
- }
- }
-
- public static class AccountGroupMemberAuditAccessWrapper
- implements AccountGroupMemberAuditAccess {
- protected final AccountGroupMemberAuditAccess delegate;
-
- protected AccountGroupMemberAuditAccessWrapper(AccountGroupMemberAuditAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public AccountGroupMemberAudit.Key primaryKey(AccountGroupMemberAudit entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<AccountGroupMemberAudit.Key, AccountGroupMemberAudit> toMap(
- Iterable<AccountGroupMemberAudit> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupMemberAudit, OrmException>
- getAsync(AccountGroupMemberAudit.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> get(Iterable<AccountGroupMemberAudit.Key> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<AccountGroupMemberAudit> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<AccountGroupMemberAudit> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<AccountGroupMemberAudit> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<AccountGroupMemberAudit.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<AccountGroupMemberAudit> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(AccountGroupMemberAudit.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public AccountGroupMemberAudit atomicUpdate(
- AccountGroupMemberAudit.Key key, AtomicUpdate<AccountGroupMemberAudit> update)
- throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public AccountGroupMemberAudit get(AccountGroupMemberAudit.Key key) throws OrmException {
- return delegate.get(key);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> byGroupAccount(
- AccountGroup.Id groupId, Account.Id accountId) throws OrmException {
- return delegate.byGroupAccount(groupId, accountId);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> byGroup(AccountGroup.Id groupId) throws OrmException {
- return delegate.byGroup(groupId);
- }
- }
-
- public static class AccountGroupByIdAccessWrapper implements AccountGroupByIdAccess {
- protected final AccountGroupByIdAccess delegate;
-
- protected AccountGroupByIdAccessWrapper(AccountGroupByIdAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<AccountGroupById> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public AccountGroupById.Key primaryKey(AccountGroupById entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<AccountGroupById.Key, AccountGroupById> toMap(Iterable<AccountGroupById> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupById, OrmException> getAsync(
- AccountGroupById.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<AccountGroupById> get(Iterable<AccountGroupById.Key> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<AccountGroupById> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<AccountGroupById> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<AccountGroupById> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<AccountGroupById.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<AccountGroupById> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(AccountGroupById.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public AccountGroupById atomicUpdate(
- AccountGroupById.Key key, AtomicUpdate<AccountGroupById> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public AccountGroupById get(AccountGroupById.Key key) throws OrmException {
- return delegate.get(key);
- }
-
- @Override
- public ResultSet<AccountGroupById> byIncludeUUID(AccountGroup.UUID uuid) throws OrmException {
- return delegate.byIncludeUUID(uuid);
- }
-
- @Override
- public ResultSet<AccountGroupById> byGroup(AccountGroup.Id id) throws OrmException {
- return delegate.byGroup(id);
- }
-
- @Override
- public ResultSet<AccountGroupById> all() throws OrmException {
- return delegate.all();
- }
- }
-
- public static class AccountGroupByIdAudAccessWrapper implements AccountGroupByIdAudAccess {
- protected final AccountGroupByIdAudAccess delegate;
-
- protected AccountGroupByIdAudAccessWrapper(AccountGroupByIdAudAccess delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public String getRelationName() {
- return delegate.getRelationName();
- }
-
- @Override
- public int getRelationID() {
- return delegate.getRelationID();
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> iterateAllEntities() throws OrmException {
- return delegate.iterateAllEntities();
- }
-
- @Override
- public AccountGroupByIdAud.Key primaryKey(AccountGroupByIdAud entity) {
- return delegate.primaryKey(entity);
- }
-
- @Override
- public Map<AccountGroupByIdAud.Key, AccountGroupByIdAud> toMap(
- Iterable<AccountGroupByIdAud> c) {
- return delegate.toMap(c);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupByIdAud, OrmException>
- getAsync(AccountGroupByIdAud.Key key) {
- return delegate.getAsync(key);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> get(Iterable<AccountGroupByIdAud.Key> keys)
- throws OrmException {
- return delegate.get(keys);
- }
-
- @Override
- public void insert(Iterable<AccountGroupByIdAud> instances) throws OrmException {
- delegate.insert(instances);
- }
-
- @Override
- public void update(Iterable<AccountGroupByIdAud> instances) throws OrmException {
- delegate.update(instances);
- }
-
- @Override
- public void upsert(Iterable<AccountGroupByIdAud> instances) throws OrmException {
- delegate.upsert(instances);
- }
-
- @Override
- public void deleteKeys(Iterable<AccountGroupByIdAud.Key> keys) throws OrmException {
- delegate.deleteKeys(keys);
- }
-
- @Override
- public void delete(Iterable<AccountGroupByIdAud> instances) throws OrmException {
- delegate.delete(instances);
- }
-
- @Override
- public void beginTransaction(AccountGroupByIdAud.Key key) throws OrmException {
- delegate.beginTransaction(key);
- }
-
- @Override
- public AccountGroupByIdAud atomicUpdate(
- AccountGroupByIdAud.Key key, AtomicUpdate<AccountGroupByIdAud> update) throws OrmException {
- return delegate.atomicUpdate(key, update);
- }
-
- @Override
- public AccountGroupByIdAud get(AccountGroupByIdAud.Key key) throws OrmException {
- return delegate.get(key);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> byGroupInclude(
- AccountGroup.Id groupId, AccountGroup.UUID incGroupUUID) throws OrmException {
- return delegate.byGroupInclude(groupId, incGroupUUID);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> byGroup(AccountGroup.Id groupId) throws OrmException {
- return delegate.byGroup(groupId);
- }
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/server/SchemaVersionAccess.java b/java/com/google/gerrit/reviewdb/server/SchemaVersionAccess.java
deleted file mode 100644
index 8819a6c355..0000000000
--- a/java/com/google/gerrit/reviewdb/server/SchemaVersionAccess.java
+++ /dev/null
@@ -1,28 +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.server;
-
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.PrimaryKey;
-
-/** Access interface for {@link CurrentSchemaVersion}. */
-public interface SchemaVersionAccess
- extends Access<CurrentSchemaVersion, CurrentSchemaVersion.Key> {
- @Override
- @PrimaryKey("singleton")
- CurrentSchemaVersion get(CurrentSchemaVersion.Key key) throws OrmException;
-}
diff --git a/java/com/google/gerrit/server/AccessPath.java b/java/com/google/gerrit/server/AccessPath.java
index cb720c857e..4d07d628b9 100644
--- a/java/com/google/gerrit/server/AccessPath.java
+++ b/java/com/google/gerrit/server/AccessPath.java
@@ -22,9 +22,6 @@ public enum AccessPath {
/** Access through the REST API. */
REST_API,
- /** Access through the old JSON-RPC interface. */
- JSON_RPC,
-
/** Access by a web cookie. This path is not protected like REST_API. */
WEB_BROWSER,
diff --git a/java/com/google/gerrit/server/ApprovalCopier.java b/java/com/google/gerrit/server/ApprovalCopier.java
index 903533e18f..09508bb9f2 100644
--- a/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/java/com/google/gerrit/server/ApprovalCopier.java
@@ -23,19 +23,17 @@ 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.change.LabelNormalizer;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -77,86 +75,32 @@ public class ApprovalCopier {
this.psUtil = psUtil;
}
- /**
- * Apply approval copy settings from prior PatchSets to a new PatchSet.
- *
- * @param db review database.
- * @param notes change notes for user uploading PatchSet
- * @param ps new PatchSet
- * @param rw open walk that can read the patch set commit; null to open the repo on demand.
- * @param repoConfig repo config used for change kind detection; null to read from repo on demand.
- * @throws OrmException
- */
- public void copyInReviewDb(
- ReviewDb db,
- ChangeNotes notes,
- PatchSet ps,
- @Nullable RevWalk rw,
- @Nullable Config repoConfig)
- throws OrmException {
- copyInReviewDb(db, notes, ps, rw, repoConfig, Collections.emptyList());
- }
-
- /**
- * Apply approval copy settings from prior PatchSets to a new PatchSet.
- *
- * @param db review database.
- * @param notes change notes for user uploading PatchSet
- * @param ps new PatchSet
- * @param rw open walk that can read the patch set commit; null to open the repo on demand.
- * @param repoConfig repo config used for change kind detection; null to read from repo on demand.
- * @param dontCopy PatchSetApprovals indicating which (account, label) pairs should not be copied
- * @throws OrmException
- */
- public void copyInReviewDb(
- ReviewDb db,
- ChangeNotes notes,
- PatchSet ps,
- @Nullable RevWalk rw,
- @Nullable Config repoConfig,
- Iterable<PatchSetApproval> dontCopy)
- throws OrmException {
- if (PrimaryStorage.of(notes.getChange()) == PrimaryStorage.REVIEW_DB) {
- db.patchSetApprovals().insert(getForPatchSet(db, notes, ps, rw, repoConfig, dontCopy));
- }
- }
-
Iterable<PatchSetApproval> getForPatchSet(
- ReviewDb db,
- ChangeNotes notes,
- PatchSet.Id psId,
- @Nullable RevWalk rw,
- @Nullable Config repoConfig)
- throws OrmException {
- return getForPatchSet(
- db, notes, psId, rw, repoConfig, Collections.<PatchSetApproval>emptyList());
+ ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
+ return getForPatchSet(notes, psId, rw, repoConfig, Collections.emptyList());
}
Iterable<PatchSetApproval> getForPatchSet(
- ReviewDb db,
ChangeNotes notes,
PatchSet.Id psId,
@Nullable RevWalk rw,
@Nullable Config repoConfig,
- Iterable<PatchSetApproval> dontCopy)
- throws OrmException {
- PatchSet ps = psUtil.get(db, notes, psId);
+ Iterable<PatchSetApproval> dontCopy) {
+ PatchSet ps = psUtil.get(notes, psId);
if (ps == null) {
return Collections.emptyList();
}
- return getForPatchSet(db, notes, ps, rw, repoConfig, dontCopy);
+ return getForPatchSet(notes, ps, rw, repoConfig, dontCopy);
}
private Iterable<PatchSetApproval> getForPatchSet(
- ReviewDb db,
ChangeNotes notes,
PatchSet ps,
@Nullable RevWalk rw,
@Nullable Config repoConfig,
- Iterable<PatchSetApproval> dontCopy)
- throws OrmException {
+ Iterable<PatchSetApproval> dontCopy) {
requireNonNull(ps, "ps should not be null");
- ChangeData cd = changeDataFactory.create(db, notes);
+ ChangeData cd = changeDataFactory.create(notes);
try {
ProjectState project = projectCache.checkedGet(cd.change().getDest().getParentKey());
ListMultimap<PatchSet.Id, PatchSetApproval> all = cd.approvals();
@@ -209,11 +153,11 @@ public class ApprovalCopier {
}
return labelNormalizer.normalize(notes, byUser.values()).getNormalized();
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
- private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) throws OrmException {
+ private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) {
Collection<PatchSet> patchSets = cd.patchSets();
TreeMap<Integer, PatchSet> result = new TreeMap<>();
for (PatchSet ps : patchSets) {
diff --git a/java/com/google/gerrit/server/ApprovalsUtil.java b/java/com/google/gerrit/server/ApprovalsUtil.java
index e2b7c6985e..135276e33f 100644
--- a/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -17,21 +17,19 @@ package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
-import static java.util.Comparator.comparing;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
-import com.google.common.collect.Ordering;
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.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -41,10 +39,8 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.LabelPermission;
@@ -52,7 +48,6 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.LabelVote;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -83,13 +78,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
public class ApprovalsUtil {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private static final Ordering<PatchSetApproval> SORT_APPROVALS =
- Ordering.from(comparing(PatchSetApproval::getGranted));
-
- public static List<PatchSetApproval> sortApprovals(Iterable<PatchSetApproval> approvals) {
- return SORT_APPROVALS.sortedCopy(approvals);
- }
-
public static PatchSetApproval newApproval(
PatchSet.Id psId, CurrentUser user, LabelId labelId, int value, Date when) {
PatchSetApproval psa =
@@ -106,7 +94,6 @@ public class ApprovalsUtil {
return Iterables.filter(psas, a -> Objects.equals(a.getAccountId(), accountId));
}
- private final NotesMigration migration;
private final ApprovalCopier copier;
private final PermissionBackend permissionBackend;
private final ProjectCache projectCache;
@@ -114,11 +101,7 @@ public class ApprovalsUtil {
@VisibleForTesting
@Inject
public ApprovalsUtil(
- NotesMigration migration,
- ApprovalCopier copier,
- PermissionBackend permissionBackend,
- ProjectCache projectCache) {
- this.migration = migration;
+ ApprovalCopier copier, PermissionBackend permissionBackend, ProjectCache projectCache) {
this.copier = copier;
this.permissionBackend = permissionBackend;
this.projectCache = projectCache;
@@ -127,15 +110,10 @@ public class ApprovalsUtil {
/**
* Get all reviewers for a change.
*
- * @param db review database.
* @param notes change notes.
* @return reviewers for the change.
- * @throws OrmException if reviewers for the change could not be read.
*/
- public ReviewerSet getReviewers(ReviewDb db, ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return ReviewerSet.fromApprovals(db.patchSetApprovals().byChange(notes.getChangeId()));
- }
+ public ReviewerSet getReviewers(ChangeNotes notes) {
return notes.load().getReviewers();
}
@@ -144,42 +122,30 @@ public class ApprovalsUtil {
*
* @param allApprovals all approvals to consider; must all belong to the same change.
* @return reviewers for the change.
- * @throws OrmException if reviewers for the change could not be read.
*/
- public ReviewerSet getReviewers(ChangeNotes notes, Iterable<PatchSetApproval> allApprovals)
- throws OrmException {
- if (!migration.readChanges()) {
- return ReviewerSet.fromApprovals(allApprovals);
- }
+ public ReviewerSet getReviewers(ChangeNotes notes, Iterable<PatchSetApproval> allApprovals) {
return notes.load().getReviewers();
}
/**
- * Get updates to reviewer set. Always returns empty list for ReviewDb.
+ * Get updates to reviewer set.
*
* @param notes change notes.
* @return reviewer updates for the change.
- * @throws OrmException if reviewer updates for the change could not be read.
*/
- public List<ReviewerStatusUpdate> getReviewerUpdates(ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return ImmutableList.of();
- }
+ public List<ReviewerStatusUpdate> getReviewerUpdates(ChangeNotes notes) {
return notes.load().getReviewerUpdates();
}
public List<PatchSetApproval> addReviewers(
- ReviewDb db,
ChangeUpdate update,
LabelTypes labelTypes,
Change change,
PatchSet ps,
PatchSetInfo info,
Iterable<Account.Id> wantReviewers,
- Collection<Account.Id> existingReviewers)
- throws OrmException {
+ Collection<Account.Id> existingReviewers) {
return addReviewers(
- db,
update,
labelTypes,
change,
@@ -191,22 +157,14 @@ public class ApprovalsUtil {
}
public List<PatchSetApproval> addReviewers(
- ReviewDb db,
ChangeNotes notes,
ChangeUpdate update,
LabelTypes labelTypes,
Change change,
- Iterable<Account.Id> wantReviewers)
- throws OrmException {
+ Iterable<Account.Id> wantReviewers) {
PatchSet.Id psId = change.currentPatchSetId();
Collection<Account.Id> existingReviewers;
- if (migration.readChanges()) {
- // If using NoteDB, we only want reviewers in the REVIEWER state.
- existingReviewers = notes.load().getReviewers().byState(REVIEWER);
- } else {
- // Prior to NoteDB, we gather all reviewers regardless of state.
- existingReviewers = getReviewers(db, notes).all();
- }
+ existingReviewers = notes.load().getReviewers().byState(REVIEWER);
// Existing reviewers should include pending additions in the REVIEWER
// state, taken from ChangeUpdate.
existingReviewers = Lists.newArrayList(existingReviewers);
@@ -216,11 +174,10 @@ public class ApprovalsUtil {
}
}
return addReviewers(
- db, update, labelTypes, change, psId, null, null, wantReviewers, existingReviewers);
+ update, labelTypes, change, psId, null, null, wantReviewers, existingReviewers);
}
private List<PatchSetApproval> addReviewers(
- ReviewDb db,
ChangeUpdate update,
LabelTypes labelTypes,
Change change,
@@ -228,19 +185,18 @@ public class ApprovalsUtil {
Account.Id authorId,
Account.Id committerId,
Iterable<Account.Id> wantReviewers,
- Collection<Account.Id> existingReviewers)
- throws OrmException {
+ Collection<Account.Id> existingReviewers) {
List<LabelType> allTypes = labelTypes.getLabelTypes();
if (allTypes.isEmpty()) {
return ImmutableList.of();
}
Set<Account.Id> need = Sets.newLinkedHashSet(wantReviewers);
- if (authorId != null && canSee(db, update.getNotes(), authorId)) {
+ if (authorId != null && canSee(update.getNotes(), authorId)) {
need.add(authorId);
}
- if (committerId != null && canSee(db, update.getNotes(), committerId)) {
+ if (committerId != null && canSee(update.getNotes(), committerId)) {
need.add(committerId);
}
need.remove(change.getOwner());
@@ -257,20 +213,15 @@ public class ApprovalsUtil {
new PatchSetApproval.Key(psId, account, labelId), (short) 0, update.getWhen()));
update.putReviewer(account, REVIEWER);
}
- db.patchSetApprovals().upsert(cells);
return Collections.unmodifiableList(cells);
}
- private boolean canSee(ReviewDb db, ChangeNotes notes, Account.Id accountId) {
+ private boolean canSee(ChangeNotes notes, Account.Id accountId) {
try {
if (!projectCache.checkedGet(notes.getProjectName()).statePermitsRead()) {
return false;
}
- permissionBackend
- .absentUser(accountId)
- .change(notes)
- .database(db)
- .check(ChangePermission.READ);
+ permissionBackend.absentUser(accountId).change(notes).check(ChangePermission.READ);
return true;
} catch (AuthException e) {
return false;
@@ -289,10 +240,9 @@ public class ApprovalsUtil {
* @param update change update.
* @param wantCCs accounts to CC.
* @return whether a change was made.
- * @throws OrmException
*/
public Collection<Account.Id> addCcs(
- ChangeNotes notes, ChangeUpdate update, Collection<Account.Id> wantCCs) throws OrmException {
+ ChangeNotes notes, ChangeUpdate update, Collection<Account.Id> wantCCs) {
return addCcs(update, wantCCs, notes.load().getReviewers());
}
@@ -308,25 +258,22 @@ public class ApprovalsUtil {
}
/**
- * Adds approvals to ChangeUpdate for a new patch set, and writes to ReviewDb.
+ * Adds approvals to ChangeUpdate for a new patch set, and writes to NoteDb.
*
- * @param db review database.
* @param update change update.
* @param labelTypes label types for the containing project.
* @param ps patch set being approved.
* @param user user adding approvals.
* @param approvals approvals to add.
* @throws RestApiException
- * @throws OrmException
*/
public Iterable<PatchSetApproval> addApprovalsForNewPatchSet(
- ReviewDb db,
ChangeUpdate update,
LabelTypes labelTypes,
PatchSet ps,
CurrentUser user,
Map<String, Short> approvals)
- throws RestApiException, OrmException, PermissionBackendException {
+ throws RestApiException, PermissionBackendException {
Account.Id accountId = user.getAccountId();
checkArgument(
accountId.equals(ps.getUploader()),
@@ -336,7 +283,7 @@ public class ApprovalsUtil {
if (approvals.isEmpty()) {
return ImmutableList.of();
}
- checkApprovals(approvals, permissionBackend.user(user).database(db).change(update.getNotes()));
+ checkApprovals(approvals, permissionBackend.user(user).change(update.getNotes()));
List<PatchSetApproval> cells = new ArrayList<>(approvals.size());
Date ts = update.getWhen();
for (Map.Entry<String, Short> vote : approvals.entrySet()) {
@@ -346,7 +293,6 @@ public class ApprovalsUtil {
for (PatchSetApproval psa : cells) {
update.putApproval(psa.getLabel(), psa.getValue());
}
- db.patchSetApprovals().insert(cells);
return cells;
}
@@ -377,54 +323,32 @@ public class ApprovalsUtil {
}
}
- public ListMultimap<PatchSet.Id, PatchSetApproval> byChange(ReviewDb db, ChangeNotes notes)
- throws OrmException {
- if (!migration.readChanges()) {
- ImmutableListMultimap.Builder<PatchSet.Id, PatchSetApproval> result =
- ImmutableListMultimap.builder();
- for (PatchSetApproval psa : db.patchSetApprovals().byChange(notes.getChangeId())) {
- result.put(psa.getPatchSetId(), psa);
- }
- return result.build();
- }
+ public ListMultimap<PatchSet.Id, PatchSetApproval> byChange(ChangeNotes notes) {
return notes.load().getApprovals();
}
public Iterable<PatchSetApproval> byPatchSet(
- ReviewDb db,
- ChangeNotes notes,
- PatchSet.Id psId,
- @Nullable RevWalk rw,
- @Nullable Config repoConfig)
- throws OrmException {
- if (!migration.readChanges()) {
- return sortApprovals(db.patchSetApprovals().byPatchSet(psId));
- }
- return copier.getForPatchSet(db, notes, psId, rw, repoConfig);
+ ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
+ return copier.getForPatchSet(notes, psId, rw, repoConfig);
}
public Iterable<PatchSetApproval> byPatchSetUser(
- ReviewDb db,
ChangeNotes notes,
PatchSet.Id psId,
Account.Id accountId,
@Nullable RevWalk rw,
- @Nullable Config repoConfig)
- throws OrmException {
- if (!migration.readChanges()) {
- return sortApprovals(db.patchSetApprovals().byPatchSetUser(psId, accountId));
- }
- return filterApprovals(byPatchSet(db, notes, psId, rw, repoConfig), accountId);
+ @Nullable Config repoConfig) {
+ return filterApprovals(byPatchSet(notes, psId, rw, repoConfig), accountId);
}
- public PatchSetApproval getSubmitter(ReviewDb db, ChangeNotes notes, PatchSet.Id c) {
+ public PatchSetApproval getSubmitter(ChangeNotes notes, PatchSet.Id c) {
if (c == null) {
return null;
}
try {
// Submit approval is never copied, so bypass expensive byPatchSet call.
- return getSubmitter(c, byChange(db, notes).get(c));
- } catch (OrmException e) {
+ return getSubmitter(c, byChange(notes).get(c));
+ } catch (StorageException e) {
return null;
}
}
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index cd7bd768da..9e7f3dd4fc 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -34,14 +34,19 @@ java_library(
":constants",
"//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/git",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//java/com/google/gerrit/index/project",
+ "//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/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",
@@ -50,8 +55,8 @@ 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",
- "//java/org/eclipse/jgit:server",
"//lib:args4j",
"//lib:autolink",
"//lib:automaton",
@@ -84,13 +89,11 @@ java_library(
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
- "//lib:gwtjsonrpc",
- "//lib:gwtorm",
"//lib:jsch",
"//lib:juniversalchardet",
"//lib:mime-util",
"//lib:protobuf",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib:soy",
"//lib:tukaani-xz",
"//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/server/ChangeMessagesUtil.java b/java/com/google/gerrit/server/ChangeMessagesUtil.java
index 969cf38542..97ba8f09f4 100644
--- a/java/com/google/gerrit/server/ChangeMessagesUtil.java
+++ b/java/com/google/gerrit/server/ChangeMessagesUtil.java
@@ -15,37 +15,23 @@
package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
import static java.util.Objects.requireNonNull;
-import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.common.Nullable;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
import com.google.gerrit.server.update.ChangeContext;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
-/**
- * Utility functions to manipulate ChangeMessages.
- *
- * <p>These methods either query for and update ChangeMessages in the NoteDb or ReviewDb, depending
- * on the state of the NotesMigration.
- */
+/** Utility functions to manipulate ChangeMessages. */
@Singleton
public class ChangeMessagesUtil {
public static final String AUTOGENERATED_TAG_PREFIX = "autogenerated:";
@@ -100,27 +86,11 @@ public class ChangeMessagesUtil {
return workInProgress ? TAG_UPLOADED_WIP_PATCH_SET : TAG_UPLOADED_PATCH_SET;
}
- private static List<ChangeMessage> sortChangeMessages(Iterable<ChangeMessage> changeMessage) {
- return ChangeNotes.MESSAGE_BY_TIME.sortedCopy(changeMessage);
- }
-
- private final NotesMigration migration;
-
- @VisibleForTesting
- @Inject
- public ChangeMessagesUtil(NotesMigration migration) {
- this.migration = migration;
- }
-
- public List<ChangeMessage> byChange(ReviewDb db, ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return sortChangeMessages(db.changeMessages().byChange(notes.getChangeId()));
- }
+ public List<ChangeMessage> byChange(ChangeNotes notes) {
return notes.load().getChangeMessages();
}
- public void addChangeMessage(ReviewDb db, ChangeUpdate update, ChangeMessage changeMessage)
- throws OrmException {
+ public void addChangeMessage(ChangeUpdate update, ChangeMessage changeMessage) {
checkState(
Objects.equals(changeMessage.getAuthor(), update.getNullableAccountId()),
"cannot store change message by %s in update by %s",
@@ -128,72 +98,21 @@ public class ChangeMessagesUtil {
update.getNullableAccountId());
update.setChangeMessage(changeMessage.getMessage());
update.setTag(changeMessage.getTag());
- db.changeMessages().insert(Collections.singleton(changeMessage));
}
/**
* Replace an existing change message with the provided new message.
*
* <p>The ID of a change message is different between NoteDb and ReviewDb. In NoteDb, it's the
- * commit SHA-1, but in ReviewDb it's generated randomly. To make sure the change message can be
- * deleted from both NoteDb and ReviewDb, the index of the change message must be used rather than
- * its ID.
+ * commit SHA-1, but in ReviewDb it was generated randomly. Taking the target message as an index
+ * rather than an ID allowed us to delete the message from both NoteDb and ReviewDb.
*
- * @param db the {@code ReviewDb} instance to update.
* @param update change update.
- * @param targetMessageIdx the index of the target change message.
+ * @param targetMessageId the id of the target change message.
* @param newMessage the new message which is going to replace the old.
- * @throws OrmException
*/
- public void replaceChangeMessage(
- ReviewDb db, ChangeUpdate update, int targetMessageIdx, String newMessage)
- throws OrmException {
- if (PrimaryStorage.of(update.getChange()).equals(PrimaryStorage.REVIEW_DB)) {
- if (db instanceof BatchUpdateReviewDb) {
- db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
- }
- db = unwrapDb(db);
-
- List<ChangeMessage> messagesInReviewDb =
- sortChangeMessages(db.changeMessages().byChange(update.getId()));
- if (migration.readChanges()) {
- sanityCheckForChangeMessages(messagesInReviewDb, update.getNotes().getChangeMessages());
- }
- ChangeMessage targetMessage = messagesInReviewDb.get(targetMessageIdx);
- targetMessage.setMessage(newMessage);
- db.changeMessages().upsert(Collections.singleton(targetMessage));
- }
-
- update.deleteChangeMessageByRewritingHistory(targetMessageIdx, newMessage);
- }
-
- private static void sanityCheckForChangeMessages(
- List<ChangeMessage> messagesInReviewDb, List<ChangeMessage> messagesInNoteDb) {
- String message =
- String.format(
- "Change messages in ReivewDb and NoteDb don't match: NoteDb %s; ReviewDb %s",
- messagesInNoteDb, messagesInReviewDb);
- if (messagesInReviewDb.size() != messagesInNoteDb.size()) {
- throw new IllegalStateException(message);
- }
-
- for (int i = 0; i < messagesInReviewDb.size(); i++) {
- ChangeMessage messageInReviewDb = messagesInReviewDb.get(i);
- ChangeMessage messageInNoteDb = messagesInNoteDb.get(i);
-
- // Don't compare the keys because they are different for the same change message in NoteDb and
- // ReviewDb.
- boolean isEqual =
- Objects.equals(messageInReviewDb.getAuthor(), messageInNoteDb.getAuthor())
- && Objects.equals(messageInReviewDb.getWrittenOn(), messageInNoteDb.getWrittenOn())
- && Objects.equals(messageInReviewDb.getMessage(), messageInNoteDb.getMessage())
- && Objects.equals(messageInReviewDb.getPatchSetId(), messageInNoteDb.getPatchSetId())
- && Objects.equals(messageInReviewDb.getTag(), messageInNoteDb.getTag())
- && Objects.equals(messageInReviewDb.getRealAuthor(), messageInNoteDb.getRealAuthor());
- if (!isEqual) {
- throw new IllegalStateException(message);
- }
- }
+ public void replaceChangeMessage(ChangeUpdate update, String targetMessageId, String newMessage) {
+ update.deleteChangeMessageByRewritingHistory(targetMessageId, newMessage);
}
/**
diff --git a/java/com/google/gerrit/server/ChangeUtil.java b/java/com/google/gerrit/server/ChangeUtil.java
index a13f105b0a..b8a00f4029 100644
--- a/java/com/google/gerrit/server/ChangeUtil.java
+++ b/java/com/google/gerrit/server/ChangeUtil.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server;
import static java.util.Comparator.comparingInt;
+import static java.util.stream.Collectors.toSet;
-import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.io.BaseEncoding;
import com.google.gerrit.reviewdb.client.Change;
@@ -24,9 +24,11 @@ import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.inject.Singleton;
import java.io.IOException;
import java.security.SecureRandom;
+import java.util.Collection;
import java.util.Map;
import java.util.Random;
-import org.eclipse.jgit.lib.ObjectId;
+import java.util.Set;
+import java.util.stream.Stream;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -66,18 +68,25 @@ public class ChangeUtil {
/**
* Get the next patch set ID from a previously-read map of refs below the change prefix.
*
- * @param changeRefs map of ref suffix to SHA-1, where the keys are ref names with the {@code
- * refs/changes/CD/ABCD/} prefix stripped. All refs should be under {@code id}'s change ref
- * prefix.
+ * @param changeRefNames existing full change ref names with the same change ID as {@code id}.
* @param id previous patch set ID.
* @return next unused patch set ID for the same change, skipping any IDs whose corresponding ref
* names appear in the {@code changeRefs} map.
*/
- public static PatchSet.Id nextPatchSetIdFromChangeRefsMap(
- Map<String, ObjectId> changeRefs, PatchSet.Id id) {
- int prefixLen = id.getParentKey().toRefPrefix().length();
+ public static PatchSet.Id nextPatchSetIdFromChangeRefs(
+ Collection<String> changeRefNames, PatchSet.Id id) {
+ return nextPatchSetIdFromChangeRefs(changeRefNames.stream(), id);
+ }
+
+ private static PatchSet.Id nextPatchSetIdFromChangeRefs(
+ Stream<String> changeRefNames, PatchSet.Id id) {
+ Set<PatchSet.Id> existing =
+ changeRefNames
+ .map(PatchSet.Id::fromRef)
+ .filter(psId -> psId != null && psId.getParentKey().equals(id.getParentKey()))
+ .collect(toSet());
PatchSet.Id next = nextPatchSetId(id);
- while (changeRefs.containsKey(next.toRefName().substring(prefixLen))) {
+ while (existing.contains(next)) {
next = nextPatchSetId(next);
}
return next;
@@ -88,7 +97,7 @@ public class ChangeUtil {
*
* <p>This patch set ID may or may not be available in the database; callers that want a
* previously-unused ID should use {@link #nextPatchSetIdFromAllRefsMap} or {@link
- * #nextPatchSetIdFromChangeRefsMap}.
+ * #nextPatchSetIdFromChangeRefs}.
*
* @param id previous patch set ID.
* @return next patch set ID for the same change, incrementing by 1.
@@ -106,9 +115,9 @@ public class ChangeUtil {
* names appear in the repository.
*/
public static PatchSet.Id nextPatchSetId(Repository git, PatchSet.Id id) throws IOException {
- return nextPatchSetIdFromChangeRefsMap(
- Maps.transformValues(
- git.getRefDatabase().getRefs(id.getParentKey().toRefPrefix()), Ref::getObjectId),
+ return nextPatchSetIdFromChangeRefs(
+ git.getRefDatabase().getRefsByPrefix(id.getParentKey().toRefPrefix()).stream()
+ .map(Ref::getName),
id);
}
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index d72d73afb6..a5332eb284 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -16,17 +16,14 @@ package com.google.gerrit.server;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.reviewdb.client.PatchLineComment.Status.PUBLISHED;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
-import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
+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;
@@ -35,47 +32,29 @@ 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.PatchLineComment.Status;
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.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
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.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
import com.google.gerrit.server.update.ChangeContext;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * Utility functions to manipulate Comments.
- *
- * <p>These methods either query for and update Comments in the NoteDb or ReviewDb, depending on the
- * state of the NotesMigration.
- */
+
+/** Utility functions to manipulate Comments. */
@Singleton
public class CommentsUtil {
public static final Ordering<Comment> COMMENT_ORDER =
@@ -127,18 +106,13 @@ public class CommentsUtil {
private final GitRepositoryManager repoManager;
private final AllUsersName allUsers;
- private final NotesMigration migration;
private final String serverId;
@Inject
CommentsUtil(
- GitRepositoryManager repoManager,
- AllUsersName allUsers,
- NotesMigration migration,
- @GerritServerId String serverId) {
+ GitRepositoryManager repoManager, AllUsersName allUsers, @GerritServerId String serverId) {
this.repoManager = repoManager;
this.allUsers = allUsers;
- this.migration = migration;
this.serverId = serverId;
}
@@ -150,7 +124,7 @@ public class CommentsUtil {
String message,
@Nullable Boolean unresolved,
@Nullable String parentUuid)
- throws OrmException, UnprocessableEntityException {
+ throws UnprocessableEntityException {
if (unresolved == null) {
if (parentUuid == null) {
// Default to false if comment is not descended from another.
@@ -158,7 +132,7 @@ public class CommentsUtil {
} else {
// Inherit unresolved value from inReplyTo comment if not specified.
Comment.Key key = new Comment.Key(parentUuid, path, psId.patchSetId);
- Optional<Comment> parent = getPublished(ctx.getDb(), ctx.getNotes(), key);
+ Optional<Comment> parent = getPublished(ctx.getNotes(), key);
if (!parent.isPresent()) {
throw new UnprocessableEntityException("Invalid parentUuid supplied for comment");
}
@@ -201,118 +175,60 @@ public class CommentsUtil {
return c;
}
- public Optional<Comment> getPublished(ReviewDb db, ChangeNotes notes, Comment.Key key)
- throws OrmException {
- if (!migration.readChanges()) {
- return getReviewDb(db, notes, key);
- }
- return publishedByChange(db, notes).stream().filter(c -> key.equals(c.key)).findFirst();
+ public Optional<Comment> getPublished(ChangeNotes notes, Comment.Key key) {
+ return publishedByChange(notes).stream().filter(c -> key.equals(c.key)).findFirst();
}
- public Optional<Comment> getDraft(
- ReviewDb db, ChangeNotes notes, IdentifiedUser user, Comment.Key key) throws OrmException {
- if (!migration.readChanges()) {
- Optional<Comment> c = getReviewDb(db, notes, key);
- if (c.isPresent() && !c.get().author.getId().equals(user.getAccountId())) {
- throw new OrmException(
- String.format(
- "Expected draft %s to belong to account %s, but it belongs to %s",
- key, user.getAccountId(), c.get().author.getId()));
- }
- return c;
- }
- return draftByChangeAuthor(db, notes, user.getAccountId()).stream()
+ public Optional<Comment> getDraft(ChangeNotes notes, IdentifiedUser user, Comment.Key key) {
+ return draftByChangeAuthor(notes, user.getAccountId()).stream()
.filter(c -> key.equals(c.key))
.findFirst();
}
- private Optional<Comment> getReviewDb(ReviewDb db, ChangeNotes notes, Comment.Key key)
- throws OrmException {
- return Optional.ofNullable(
- db.patchComments().get(PatchLineComment.Key.from(notes.getChangeId(), key)))
- .map(plc -> plc.asComment(serverId));
- }
-
- public List<Comment> publishedByChange(ReviewDb db, ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return sort(byCommentStatus(db.patchComments().byChange(notes.getChangeId()), PUBLISHED));
- }
-
+ public List<Comment> publishedByChange(ChangeNotes notes) {
notes.load();
return sort(Lists.newArrayList(notes.getComments().values()));
}
- public List<RobotComment> robotCommentsByChange(ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return ImmutableList.of();
- }
-
+ public List<RobotComment> robotCommentsByChange(ChangeNotes notes) {
notes.load();
return sort(Lists.newArrayList(notes.getRobotComments().values()));
}
- public List<Comment> draftByChange(ReviewDb db, ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return sort(byCommentStatus(db.patchComments().byChange(notes.getChangeId()), Status.DRAFT));
- }
-
+ public List<Comment> draftByChange(ChangeNotes notes) {
List<Comment> comments = new ArrayList<>();
for (Ref ref : getDraftRefs(notes.getChangeId())) {
Account.Id account = Account.Id.fromRefSuffix(ref.getName());
if (account != null) {
- comments.addAll(draftByChangeAuthor(db, notes, account));
+ comments.addAll(draftByChangeAuthor(notes, account));
}
}
return sort(comments);
}
- private List<Comment> byCommentStatus(
- ResultSet<PatchLineComment> comments, PatchLineComment.Status status) {
- return toComments(
- serverId, Lists.newArrayList(Iterables.filter(comments, c -> c.getStatus() == status)));
- }
-
- public List<Comment> byPatchSet(ReviewDb db, ChangeNotes notes, PatchSet.Id psId)
- throws OrmException {
- if (!migration.readChanges()) {
- return sort(toComments(serverId, db.patchComments().byPatchSet(psId).toList()));
- }
+ public List<Comment> byPatchSet(ChangeNotes notes, PatchSet.Id psId) {
List<Comment> comments = new ArrayList<>();
- comments.addAll(publishedByPatchSet(db, notes, psId));
+ comments.addAll(publishedByPatchSet(notes, psId));
for (Ref ref : getDraftRefs(notes.getChangeId())) {
Account.Id account = Account.Id.fromRefSuffix(ref.getName());
if (account != null) {
- comments.addAll(draftByPatchSetAuthor(db, psId, account, notes));
+ comments.addAll(draftByPatchSetAuthor(psId, account, notes));
}
}
return sort(comments);
}
- public List<Comment> publishedByChangeFile(
- ReviewDb db, ChangeNotes notes, Change.Id changeId, String file) throws OrmException {
- if (!migration.readChanges()) {
- return sort(
- toComments(serverId, db.patchComments().publishedByChangeFile(changeId, file).toList()));
- }
+ public List<Comment> publishedByChangeFile(ChangeNotes notes, String file) {
return commentsOnFile(notes.load().getComments().values(), file);
}
- public List<Comment> publishedByPatchSet(ReviewDb db, ChangeNotes notes, PatchSet.Id psId)
- throws OrmException {
- if (!migration.readChanges()) {
- return removeCommentsOnAncestorOfCommitMessage(
- sort(toComments(serverId, db.patchComments().publishedByPatchSet(psId).toList())));
- }
+ public List<Comment> publishedByPatchSet(ChangeNotes notes, PatchSet.Id psId) {
return removeCommentsOnAncestorOfCommitMessage(
commentsOnPatchSet(notes.load().getComments().values(), psId));
}
- public List<RobotComment> robotCommentsByPatchSet(ChangeNotes notes, PatchSet.Id psId)
- throws OrmException {
- if (!migration.readChanges()) {
- return ImmutableList.of();
- }
+ public List<RobotComment> robotCommentsByPatchSet(ChangeNotes notes, PatchSet.Id psId) {
return commentsOnPatchSet(notes.load().getRobotComments().values(), psId);
}
@@ -330,48 +246,25 @@ public class CommentsUtil {
}
public List<Comment> draftByPatchSetAuthor(
- ReviewDb db, PatchSet.Id psId, Account.Id author, ChangeNotes notes) throws OrmException {
- if (!migration.readChanges()) {
- return sort(
- toComments(serverId, db.patchComments().draftByPatchSetAuthor(psId, author).toList()));
- }
+ PatchSet.Id psId, Account.Id author, ChangeNotes notes) {
return commentsOnPatchSet(notes.load().getDraftComments(author).values(), psId);
}
- public List<Comment> draftByChangeFileAuthor(
- ReviewDb db, ChangeNotes notes, String file, Account.Id author) throws OrmException {
- if (!migration.readChanges()) {
- return sort(
- toComments(
- serverId,
- db.patchComments()
- .draftByChangeFileAuthor(notes.getChangeId(), file, author)
- .toList()));
- }
+ public List<Comment> draftByChangeFileAuthor(ChangeNotes notes, String file, Account.Id author) {
return commentsOnFile(notes.load().getDraftComments(author).values(), file);
}
- public List<Comment> draftByChangeAuthor(ReviewDb db, ChangeNotes notes, Account.Id author)
- throws OrmException {
- if (!migration.readChanges()) {
- return Streams.stream(db.patchComments().draftByAuthor(author))
- .filter(c -> c.getPatchSetId().getParentKey().equals(notes.getChangeId()))
- .map(plc -> plc.asComment(serverId))
- .sorted(COMMENT_ORDER)
- .collect(toList());
- }
+ public List<Comment> draftByChangeAuthor(ChangeNotes notes, Account.Id author) {
List<Comment> comments = new ArrayList<>();
comments.addAll(notes.getDraftComments(author).values());
return sort(comments);
}
public void putComments(
- ReviewDb db, ChangeUpdate update, PatchLineComment.Status status, Iterable<Comment> comments)
- throws OrmException {
+ ChangeUpdate update, PatchLineComment.Status status, Iterable<Comment> comments) {
for (Comment c : comments) {
update.putComment(status, c);
}
- db.patchComments().upsert(toPatchLineComments(update.getId(), status, comments));
}
public void putRobotComments(ChangeUpdate update, Iterable<RobotComment> comments) {
@@ -380,60 +273,17 @@ public class CommentsUtil {
}
}
- public void deleteComments(ReviewDb db, ChangeUpdate update, Iterable<Comment> comments)
- throws OrmException {
+ public void deleteComments(ChangeUpdate update, Iterable<Comment> comments) {
for (Comment c : comments) {
update.deleteComment(c);
}
- db.patchComments()
- .delete(toPatchLineComments(update.getId(), PatchLineComment.Status.DRAFT, comments));
}
public void deleteCommentByRewritingHistory(
- ReviewDb db, ChangeUpdate update, Comment.Key commentKey, PatchSet.Id psId, String newMessage)
- throws OrmException {
- if (PrimaryStorage.of(update.getChange()).equals(PrimaryStorage.REVIEW_DB)) {
- PatchLineComment.Key key =
- new PatchLineComment.Key(new Patch.Key(psId, commentKey.filename), commentKey.uuid);
-
- if (db instanceof BatchUpdateReviewDb) {
- db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
- }
- db = ReviewDbUtil.unwrapDb(db);
-
- PatchLineComment patchLineComment = db.patchComments().get(key);
-
- if (!patchLineComment.getStatus().equals(PUBLISHED)) {
- throw new OrmException(String.format("comment %s is not published", key));
- }
-
- patchLineComment.setMessage(newMessage);
- db.patchComments().upsert(Collections.singleton(patchLineComment));
- }
-
+ ChangeUpdate update, Comment.Key commentKey, String newMessage) {
update.deleteCommentByRewritingHistory(commentKey.uuid, newMessage);
}
- public void deleteAllDraftsFromAllUsers(Change.Id changeId) throws IOException {
- try (Repository repo = repoManager.openRepository(allUsers);
- RevWalk rw = new RevWalk(repo)) {
- BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
- for (Ref ref : getDraftRefs(repo, changeId)) {
- bru.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName()));
- }
- bru.setRefLogMessage("Delete drafts from NoteDb", false);
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- for (ReceiveCommand cmd : bru.getCommands()) {
- if (cmd.getResult() != ReceiveCommand.Result.OK) {
- throw new IOException(
- String.format(
- "Failed to delete draft comment ref %s at %s: %s (%s)",
- cmd.getRefName(), cmd.getOldId(), cmd.getResult(), cmd.getMessage()));
- }
- }
- }
- }
-
private static List<Comment> commentsOnFile(Collection<Comment> allComments, String file) {
List<Comment> result = new ArrayList<>(allComments.size());
for (Comment c : allComments) {
@@ -488,11 +338,11 @@ public class CommentsUtil {
* @param changeId change ID.
* @return raw refs from All-Users repo.
*/
- public Collection<Ref> getDraftRefs(Change.Id changeId) throws OrmException {
+ public Collection<Ref> getDraftRefs(Change.Id changeId) {
try (Repository repo = repoManager.openRepository(allUsers)) {
return getDraftRefs(repo, changeId);
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java b/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java
index 72eb468f73..27fb9d7a7d 100644
--- a/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java
+++ b/java/com/google/gerrit/server/CreateGroupPermissionSyncer.java
@@ -57,17 +57,20 @@ public class CreateGroupPermissionSyncer implements ChangeMergedListener {
private final AllUsersName allUsers;
private final ProjectCache projectCache;
private final Provider<MetaDataUpdate.Server> metaDataUpdateFactory;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
CreateGroupPermissionSyncer(
AllProjectsName allProjects,
AllUsersName allUsers,
ProjectCache projectCache,
- Provider<MetaDataUpdate.Server> metaDataUpdateFactory) {
+ Provider<MetaDataUpdate.Server> metaDataUpdateFactory,
+ ProjectConfig.Factory projectConfigFactory) {
this.allProjects = allProjects;
this.allUsers = allUsers;
this.projectCache = projectCache;
this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.projectConfigFactory = projectConfigFactory;
}
/**
@@ -102,7 +105,7 @@ public class CreateGroupPermissionSyncer implements ChangeMergedListener {
}
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsers)) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
AccessSection createGroupAccessSection =
config.getAccessSection(RefNames.REFS_GROUPS + "*", true);
if (createGroupsGlobal.isEmpty()) {
diff --git a/java/com/google/gerrit/server/DynamicOptions.java b/java/com/google/gerrit/server/DynamicOptions.java
index 3759f09241..41dc0828b9 100644
--- a/java/com/google/gerrit/server/DynamicOptions.java
+++ b/java/com/google/gerrit/server/DynamicOptions.java
@@ -26,7 +26,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.WeakHashMap;
/** Helper class to define and parse options from plugins on ssh and RestAPI commands. */
@@ -50,8 +49,18 @@ public class DynamicOptions {
* }
* </pre>
*
- * The option will be prefixed by the plugin name. In the example above, if the plugin name was
+ * <p>The option will be prefixed by the plugin name. In the example above, if the plugin name was
* my-plugin, then the --verbose option as used by the caller would be --my-plugin--verbose.
+ *
+ * <p>Additional options can be annotated with @RequiresOption which will cause them to be ignored
+ * unless the required option is present. For example:
+ *
+ * <pre>
+ * {@literal @}RequiresOptions("--help")
+ * {@literal @}Option(name = "--help-as-json",
+ * usage = "display help text in json format")
+ * public boolean displayHelpAsJson;
+ * </pre>
*/
public interface DynamicBean {}
@@ -71,7 +80,7 @@ public class DynamicOptions {
* .to(MyOptionsClassNameProvider.class);
*
* static class MyOptionsClassNameProvider implements DynamicOptions.ClassNameProvider {
- * @Override
+ * {@literal @}Override
* public String getClassName() {
* return "com.googlesource.gerrit.plugins.myplugin.CommandOptions";
* }
@@ -98,11 +107,11 @@ public class DynamicOptions {
* .to(MyOptionsModulesClassNamesProvider.class);
*
* static class MyOptionsModulesClassNamesProvider implements DynamicOptions.ClassNameProvider {
- * @Override
+ * {@literal @}Override
* public String getClassName() {
* return "com.googlesource.gerrit.plugins.myplugin.CommandOptions";
* }
- * @Override
+ * {@literal @}Override
* public Iterable<String> getModulesClassNames()() {
* return "com.googlesource.gerrit.plugins.myplugin.MyOptionsModule";
* }
@@ -144,6 +153,21 @@ public class DynamicOptions {
*/
public interface BeanReceiver {
void setDynamicBean(String plugin, DynamicBean dynamicBean);
+
+ /**
+ * Returns the class that should be used for looking up exported DynamicBean bindings from
+ * plugins. Override when a particular REST/SSH endpoint should respect DynamicBeans bound on a
+ * different endpoint. For example, {@code GetDetail} is just a synonym for a variant of {@code
+ * GetChange}, and it should respect any DynamicBeans on GetChange. GetChange}. So it should
+ * return {@code GetChange.class} from this method.
+ */
+ default Class<? extends BeanReceiver> getExportedBeanReceiver() {
+ return getClass();
+ }
+ }
+
+ public interface BeanProvider {
+ DynamicBean getDynamicBean(String plugin);
}
/**
@@ -161,8 +185,7 @@ public class DynamicOptions {
* classloaders.
*/
protected static Map<ClassLoader, Map<ClassLoader, WeakReference<ClassLoader>>> mergedClByCls =
- Collections.synchronizedMap(
- new WeakHashMap<ClassLoader, Map<ClassLoader, WeakReference<ClassLoader>>>());
+ Collections.synchronizedMap(new WeakHashMap<>());
protected Object bean;
protected Map<String, DynamicBean> beansByPlugin;
@@ -187,9 +210,13 @@ public class DynamicOptions {
this.bean = bean;
this.injector = injector;
beansByPlugin = new HashMap<>();
+ Class<?> beanClass =
+ (bean instanceof BeanReceiver)
+ ? ((BeanReceiver) bean).getExportedBeanReceiver()
+ : getClass();
for (String plugin : dynamicBeans.plugins()) {
Provider<DynamicBean> provider =
- dynamicBeans.byPlugin(plugin).get(bean.getClass().getCanonicalName());
+ dynamicBeans.byPlugin(plugin).get(beanClass.getCanonicalName());
if (provider != null) {
beansByPlugin.put(plugin, getDynamicBean(bean, provider.get()));
}
@@ -258,22 +285,23 @@ public class DynamicOptions {
}
public void parseDynamicBeans(CmdLineParser clp) {
- for (Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
+ for (Map.Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
clp.parseWithPrefix("--" + e.getKey(), e.getValue());
}
+ clp.drainOptionQueue();
}
public void setDynamicBeans() {
if (bean instanceof BeanReceiver) {
BeanReceiver receiver = (BeanReceiver) bean;
- for (Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
+ for (Map.Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
receiver.setDynamicBean(e.getKey(), e.getValue());
}
}
}
public void onBeanParseStart() {
- for (Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
+ for (Map.Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
DynamicBean instance = e.getValue();
if (instance instanceof BeanParseListener) {
BeanParseListener listener = (BeanParseListener) instance;
@@ -283,7 +311,7 @@ public class DynamicOptions {
}
public void onBeanParseEnd() {
- for (Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
+ for (Map.Entry<String, DynamicBean> e : beansByPlugin.entrySet()) {
DynamicBean instance = e.getValue();
if (instance instanceof BeanParseListener) {
BeanParseListener listener = (BeanParseListener) instance;
diff --git a/java/com/google/gerrit/server/IdentifiedUser.java b/java/com/google/gerrit/server/IdentifiedUser.java
index d9a4cae71a..e65f56257e 100644
--- a/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/java/com/google/gerrit/server/IdentifiedUser.java
@@ -32,7 +32,7 @@ import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.DisableReverseDnsLookup;
+import com.google.gerrit.server.config.EnableReverseDnsLookup;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.inject.Inject;
import com.google.inject.OutOfScopeException;
@@ -67,7 +67,7 @@ public class IdentifiedUser extends CurrentUser {
private final Provider<String> canonicalUrl;
private final AccountCache accountCache;
private final GroupBackend groupBackend;
- private final Boolean disableReverseDnsLookup;
+ private final Boolean enableReverseDnsLookup;
@Inject
public GenericFactory(
@@ -75,7 +75,7 @@ public class IdentifiedUser extends CurrentUser {
Realm realm,
@AnonymousCowardName String anonymousCowardName,
@CanonicalWebUrl Provider<String> canonicalUrl,
- @DisableReverseDnsLookup Boolean disableReverseDnsLookup,
+ @EnableReverseDnsLookup Boolean enableReverseDnsLookup,
AccountCache accountCache,
GroupBackend groupBackend) {
this.authConfig = authConfig;
@@ -84,7 +84,7 @@ public class IdentifiedUser extends CurrentUser {
this.canonicalUrl = canonicalUrl;
this.accountCache = accountCache;
this.groupBackend = groupBackend;
- this.disableReverseDnsLookup = disableReverseDnsLookup;
+ this.enableReverseDnsLookup = enableReverseDnsLookup;
}
public IdentifiedUser create(AccountState state) {
@@ -95,14 +95,14 @@ public class IdentifiedUser extends CurrentUser {
canonicalUrl,
accountCache,
groupBackend,
- disableReverseDnsLookup,
- Providers.of((SocketAddress) null),
+ enableReverseDnsLookup,
+ Providers.of(null),
state,
null);
}
public IdentifiedUser create(Account.Id id) {
- return create((SocketAddress) null, id);
+ return create(null, id);
}
public IdentifiedUser create(SocketAddress remotePeer, Account.Id id) {
@@ -118,7 +118,7 @@ public class IdentifiedUser extends CurrentUser {
canonicalUrl,
accountCache,
groupBackend,
- disableReverseDnsLookup,
+ enableReverseDnsLookup,
Providers.of(remotePeer),
id,
caller);
@@ -128,8 +128,8 @@ public class IdentifiedUser extends CurrentUser {
/**
* Create an IdentifiedUser, relying on current request state.
*
- * <p>Can only be used from within a module that has defined request scoped {@code @RemotePeer
- * SocketAddress} and {@code ReviewDb} providers.
+ * <p>Can only be used from within a module that has defined a request scoped {@code @RemotePeer
+ * SocketAddress} provider.
*/
@Singleton
public static class RequestFactory {
@@ -139,7 +139,7 @@ public class IdentifiedUser extends CurrentUser {
private final Provider<String> canonicalUrl;
private final AccountCache accountCache;
private final GroupBackend groupBackend;
- private final Boolean disableReverseDnsLookup;
+ private final Boolean enableReverseDnsLookup;
private final Provider<SocketAddress> remotePeerProvider;
@Inject
@@ -150,7 +150,7 @@ public class IdentifiedUser extends CurrentUser {
@CanonicalWebUrl Provider<String> canonicalUrl,
AccountCache accountCache,
GroupBackend groupBackend,
- @DisableReverseDnsLookup Boolean disableReverseDnsLookup,
+ @EnableReverseDnsLookup Boolean enableReverseDnsLookup,
@RemotePeer Provider<SocketAddress> remotePeerProvider) {
this.authConfig = authConfig;
this.realm = realm;
@@ -158,7 +158,7 @@ public class IdentifiedUser extends CurrentUser {
this.canonicalUrl = canonicalUrl;
this.accountCache = accountCache;
this.groupBackend = groupBackend;
- this.disableReverseDnsLookup = disableReverseDnsLookup;
+ this.enableReverseDnsLookup = enableReverseDnsLookup;
this.remotePeerProvider = remotePeerProvider;
}
@@ -170,7 +170,7 @@ public class IdentifiedUser extends CurrentUser {
canonicalUrl,
accountCache,
groupBackend,
- disableReverseDnsLookup,
+ enableReverseDnsLookup,
remotePeerProvider,
id,
null);
@@ -184,7 +184,7 @@ public class IdentifiedUser extends CurrentUser {
canonicalUrl,
accountCache,
groupBackend,
- disableReverseDnsLookup,
+ enableReverseDnsLookup,
remotePeerProvider,
id,
caller);
@@ -201,7 +201,7 @@ public class IdentifiedUser extends CurrentUser {
private final Realm realm;
private final GroupBackend groupBackend;
private final String anonymousCowardName;
- private final Boolean disableReverseDnsLookup;
+ private final Boolean enableReverseDnsLookup;
private final Set<String> validEmails = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
private final CurrentUser realUser; // Must be final since cached properties depend on it.
@@ -221,7 +221,7 @@ public class IdentifiedUser extends CurrentUser {
Provider<String> canonicalUrl,
AccountCache accountCache,
GroupBackend groupBackend,
- Boolean disableReverseDnsLookup,
+ Boolean enableReverseDnsLookup,
@Nullable Provider<SocketAddress> remotePeerProvider,
AccountState state,
@Nullable CurrentUser realUser) {
@@ -232,7 +232,7 @@ public class IdentifiedUser extends CurrentUser {
canonicalUrl,
accountCache,
groupBackend,
- disableReverseDnsLookup,
+ enableReverseDnsLookup,
remotePeerProvider,
state.getAccount().getId(),
realUser);
@@ -246,7 +246,7 @@ public class IdentifiedUser extends CurrentUser {
Provider<String> canonicalUrl,
AccountCache accountCache,
GroupBackend groupBackend,
- Boolean disableReverseDnsLookup,
+ Boolean enableReverseDnsLookup,
@Nullable Provider<SocketAddress> remotePeerProvider,
Account.Id id,
@Nullable CurrentUser realUser) {
@@ -256,7 +256,7 @@ public class IdentifiedUser extends CurrentUser {
this.authConfig = authConfig;
this.realm = realm;
this.anonymousCowardName = anonymousCowardName;
- this.disableReverseDnsLookup = disableReverseDnsLookup;
+ this.enableReverseDnsLookup = enableReverseDnsLookup;
this.remotePeerProvider = remotePeerProvider;
this.accountId = id;
this.realUser = realUser != null ? realUser : this;
@@ -512,11 +512,8 @@ public class IdentifiedUser extends CurrentUser {
remotePeer = Providers.of(remotePeerProvider.get());
} catch (OutOfScopeException | ProvisionException e) {
remotePeer =
- new Provider<SocketAddress>() {
- @Override
- public SocketAddress get() {
- throw e;
- }
+ () -> {
+ throw e;
};
}
return new IdentifiedUser(
@@ -526,7 +523,7 @@ public class IdentifiedUser extends CurrentUser {
Providers.of(canonicalUrl.get()),
accountCache,
groupBackend,
- disableReverseDnsLookup,
+ enableReverseDnsLookup,
remotePeer,
state,
realUser);
@@ -557,7 +554,7 @@ public class IdentifiedUser extends CurrentUser {
}
private String getHost(InetAddress in) {
- if (Boolean.FALSE.equals(disableReverseDnsLookup)) {
+ if (Boolean.TRUE.equals(enableReverseDnsLookup)) {
return in.getCanonicalHostName();
}
return in.getHostAddress();
diff --git a/java/com/google/gerrit/server/LibModuleLoader.java b/java/com/google/gerrit/server/LibModuleLoader.java
index d1067e1784..0a6fb9f868 100644
--- a/java/com/google/gerrit/server/LibModuleLoader.java
+++ b/java/com/google/gerrit/server/LibModuleLoader.java
@@ -30,9 +30,9 @@ import org.eclipse.jgit.lib.Config;
public class LibModuleLoader {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static List<Module> loadModules(Injector parent) {
+ public static List<Module> loadModules(Injector parent, LibModuleType moduleType) {
Config cfg = getConfig(parent);
- return Arrays.stream(cfg.getStringList("gerrit", null, "installModule"))
+ return Arrays.stream(cfg.getStringList("gerrit", null, "install" + moduleType.getConfigKey()))
.map(m -> createModule(parent, m))
.collect(toList());
}
diff --git a/java/com/google/gerrit/server/LibModuleType.java b/java/com/google/gerrit/server/LibModuleType.java
new file mode 100644
index 0000000000..557f8c044b
--- /dev/null
+++ b/java/com/google/gerrit/server/LibModuleType.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;
+
+/** Loadable module type for the different Gerrit injectors. */
+public enum LibModuleType {
+
+ /** Module for the sysInjector. */
+ SYS_MODULE("Module"),
+
+ /** Module for the dbInjector. */
+ DB_MODULE("DbModule");
+
+ private final String configKey;
+
+ LibModuleType(String configKey) {
+ this.configKey = configKey;
+ }
+
+ /**
+ * Returns the module type for libModule loaded from <gerrit_site/lib> directory.
+ *
+ * @return module type string
+ */
+ public String getConfigKey() {
+ return configKey;
+ }
+}
diff --git a/java/com/google/gerrit/server/OutputFormat.java b/java/com/google/gerrit/server/OutputFormat.java
deleted file mode 100644
index e555845a8b..0000000000
--- a/java/com/google/gerrit/server/OutputFormat.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2012 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.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gwtjsonrpc.server.SqlTimestampDeserializer;
-import java.sql.Timestamp;
-
-/** Standard output format used by an API call. */
-public enum OutputFormat {
- /**
- * The output is a human readable text format. It may also be regular enough to be machine
- * readable. Whether or not the text format is machine readable and will be committed to as a long
- * term format that tools can build upon is specific to each API call.
- */
- TEXT,
-
- /**
- * Pretty-printed JSON format. This format uses whitespace to make the output readable by a human,
- * but is also machine readable with a JSON library. The structure of the output is a long term
- * format that tools can rely upon.
- */
- JSON,
-
- /**
- * Same as {@link #JSON}, but with unnecessary whitespace removed to save generation time and copy
- * costs. Typically JSON_COMPACT format is used by a browser based HTML client running over the
- * network.
- */
- JSON_COMPACT;
-
- /** @return true when the format is either JSON or JSON_COMPACT. */
- public boolean isJson() {
- return this == JSON_COMPACT || this == JSON;
- }
-
- /** @return a new Gson instance configured according to the format. */
- public GsonBuilder newGsonBuilder() {
- if (!isJson()) {
- throw new IllegalStateException(String.format("%s is not JSON", this));
- }
- GsonBuilder gb =
- new GsonBuilder()
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .registerTypeAdapter(Timestamp.class, new SqlTimestampDeserializer());
- if (this == OutputFormat.JSON) {
- gb.setPrettyPrinting();
- }
- return gb;
- }
-
- /** @return a new Gson instance configured according to the format. */
- public Gson newGson() {
- return newGsonBuilder().create();
- }
-}
diff --git a/java/com/google/gerrit/server/PatchSetUtil.java b/java/com/google/gerrit/server/PatchSetUtil.java
index f6c7abc09a..2a78eb6c77 100644
--- a/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/java/com/google/gerrit/server/PatchSetUtil.java
@@ -15,17 +15,11 @@
package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
-import static com.google.gerrit.server.notedb.PatchSetState.PUBLISHED;
import static java.util.Objects.requireNonNull;
-import static java.util.function.Function.identity;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -34,20 +28,16 @@ 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.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
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.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
@@ -58,70 +48,42 @@ import org.eclipse.jgit.revwalk.RevWalk;
/** Utilities for manipulating patch sets. */
@Singleton
public class PatchSetUtil {
- private final NotesMigration migration;
private final Provider<ApprovalsUtil> approvalsUtilProvider;
private final ProjectCache projectCache;
- private final Provider<ReviewDb> dbProvider;
private final GitRepositoryManager repoManager;
@Inject
PatchSetUtil(
- NotesMigration migration,
Provider<ApprovalsUtil> approvalsUtilProvider,
ProjectCache projectCache,
- Provider<ReviewDb> dbProvider,
GitRepositoryManager repoManager) {
- this.migration = migration;
this.approvalsUtilProvider = approvalsUtilProvider;
this.projectCache = projectCache;
- this.dbProvider = dbProvider;
this.repoManager = repoManager;
}
- public PatchSet current(ReviewDb db, ChangeNotes notes) throws OrmException {
- return get(db, notes, notes.getChange().currentPatchSetId());
+ public PatchSet current(ChangeNotes notes) {
+ return get(notes, notes.getChange().currentPatchSetId());
}
- public PatchSet get(ReviewDb db, ChangeNotes notes, PatchSet.Id psId) throws OrmException {
- if (!migration.readChanges()) {
- return db.patchSets().get(psId);
- }
+ public PatchSet get(ChangeNotes notes, PatchSet.Id psId) {
return notes.load().getPatchSets().get(psId);
}
- public ImmutableCollection<PatchSet> byChange(ReviewDb db, ChangeNotes notes)
- throws OrmException {
- if (!migration.readChanges()) {
- return PS_ID_ORDER.immutableSortedCopy(db.patchSets().byChange(notes.getChangeId()));
- }
+ public ImmutableCollection<PatchSet> byChange(ChangeNotes notes) {
return notes.load().getPatchSets().values();
}
- public ImmutableMap<PatchSet.Id, PatchSet> byChangeAsMap(ReviewDb db, ChangeNotes notes)
- throws OrmException {
- if (!migration.readChanges()) {
- ImmutableMap.Builder<PatchSet.Id, PatchSet> result = ImmutableMap.builder();
- for (PatchSet ps : PS_ID_ORDER.sortedCopy(db.patchSets().byChange(notes.getChangeId()))) {
- result.put(ps.getId(), ps);
- }
- return result.build();
- }
+ public ImmutableMap<PatchSet.Id, PatchSet> byChangeAsMap(ChangeNotes notes) {
return notes.load().getPatchSets();
}
public ImmutableMap<PatchSet.Id, PatchSet> getAsMap(
- ReviewDb db, ChangeNotes notes, Set<PatchSet.Id> patchSetIds) throws OrmException {
- if (!migration.readChanges()) {
- patchSetIds = Sets.filter(patchSetIds, p -> p.getParentKey().equals(notes.getChangeId()));
- return Streams.stream(db.patchSets().get(patchSetIds))
- .sorted(PS_ID_ORDER)
- .collect(toImmutableMap(PatchSet::getId, identity()));
- }
+ ChangeNotes notes, Set<PatchSet.Id> patchSetIds) {
return ImmutableMap.copyOf(Maps.filterKeys(notes.load().getPatchSets(), patchSetIds::contains));
}
public PatchSet insert(
- ReviewDb db,
RevWalk rw,
ChangeUpdate update,
PatchSet.Id psId,
@@ -129,10 +91,14 @@ public class PatchSetUtil {
List<String> groups,
String pushCertificate,
String description)
- throws OrmException, IOException {
+ throws IOException {
requireNonNull(groups, "groups may not be null");
ensurePatchSetMatches(psId, update);
+ update.setCommit(rw, commit, pushCertificate);
+ update.setPsDescription(description);
+ update.setGroups(groups);
+
PatchSet ps = new PatchSet(psId);
ps.setRevision(new RevId(commit.name()));
ps.setUploader(update.getAccountId());
@@ -140,21 +106,9 @@ public class PatchSetUtil {
ps.setGroups(groups);
ps.setPushCertificate(pushCertificate);
ps.setDescription(description);
- db.patchSets().insert(Collections.singleton(ps));
-
- update.setCommit(rw, commit, pushCertificate);
- update.setPsDescription(description);
- update.setGroups(groups);
-
return ps;
}
- public void publish(ReviewDb db, ChangeUpdate update, PatchSet ps) throws OrmException {
- ensurePatchSetMatches(ps.getId(), update);
- update.setPatchSetState(PUBLISHED);
- db.patchSets().update(Collections.singleton(ps));
- }
-
private static void ensurePatchSetMatches(PatchSet.Id psId, ChangeUpdate update) {
Change.Id changeId = update.getChange().getId();
checkArgument(
@@ -173,16 +127,14 @@ public class PatchSetUtil {
}
}
- public void setGroups(ReviewDb db, ChangeUpdate update, PatchSet ps, List<String> groups)
- throws OrmException {
+ public void setGroups(ChangeUpdate update, PatchSet ps, List<String> groups) {
ps.setGroups(groups);
update.setGroups(groups);
- db.patchSets().update(Collections.singleton(ps));
}
/** Check if the current patch set of the change is locked. */
public void checkPatchSetNotLocked(ChangeNotes notes)
- throws OrmException, IOException, ResourceConflictException {
+ throws IOException, ResourceConflictException {
if (isPatchSetLocked(notes)) {
throw new ResourceConflictException(
String.format("The current patch set of change %s is locked", notes.getChangeId()));
@@ -190,9 +142,9 @@ public class PatchSetUtil {
}
/** Is the current patch set locked against state changes? */
- public boolean isPatchSetLocked(ChangeNotes notes) throws OrmException, IOException {
+ public boolean isPatchSetLocked(ChangeNotes notes) throws IOException {
Change change = notes.getChange();
- if (change.getStatus() == Change.Status.MERGED) {
+ if (change.isMerged()) {
return false;
}
@@ -202,7 +154,7 @@ public class PatchSetUtil {
ApprovalsUtil approvalsUtil = approvalsUtilProvider.get();
for (PatchSetApproval ap :
- approvalsUtil.byPatchSet(dbProvider.get(), notes, change.currentPatchSetId(), null, null)) {
+ approvalsUtil.byPatchSet(notes, change.currentPatchSetId(), null, null)) {
LabelType type = projectState.getLabelTypes(notes).byLabel(ap.getLabel());
if (type != null
&& ap.getValue() == 1
diff --git a/java/com/google/gerrit/server/ProjectUtil.java b/java/com/google/gerrit/server/ProjectUtil.java
index 1a327dbd3b..1db4aa3a6f 100644
--- a/java/com/google/gerrit/server/ProjectUtil.java
+++ b/java/com/google/gerrit/server/ProjectUtil.java
@@ -44,4 +44,28 @@ public class ProjectUtil {
return exists;
}
}
+
+ public static String sanitizeProjectName(String name) {
+ name = stripGitSuffix(name);
+ name = stripTrailingSlash(name);
+ return name;
+ }
+
+ public static String stripGitSuffix(String name) {
+ if (name.endsWith(".git")) {
+ // Be nice and drop the trailing ".git" suffix, which we never keep
+ // in our database, but clients might mistakenly provide anyway.
+ //
+ name = name.substring(0, name.length() - 4);
+ name = stripTrailingSlash(name);
+ }
+ return name;
+ }
+
+ private static String stripTrailingSlash(String name) {
+ while (name.endsWith("/")) {
+ name = name.substring(0, name.length() - 1);
+ }
+ return name;
+ }
}
diff --git a/java/com/google/gerrit/server/PublishCommentUtil.java b/java/com/google/gerrit/server/PublishCommentUtil.java
index a90f3e7365..ad93ef008d 100644
--- a/java/com/google/gerrit/server/PublishCommentUtil.java
+++ b/java/com/google/gerrit/server/PublishCommentUtil.java
@@ -19,14 +19,13 @@ import static com.google.gerrit.reviewdb.client.PatchLineComment.Status.PUBLISHE
import static java.util.stream.Collectors.toSet;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSet.Id;
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.update.ChangeContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Collection;
@@ -47,21 +46,19 @@ public class PublishCommentUtil {
}
public void publish(
- ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag)
- throws OrmException {
+ ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag) {
ChangeNotes notes = ctx.getNotes();
checkArgument(notes != null);
if (drafts.isEmpty()) {
return;
}
- Map<Id, PatchSet> patchSets =
- psUtil.getAsMap(
- ctx.getDb(), notes, drafts.stream().map(d -> psId(notes, d)).collect(toSet()));
+ 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));
if (ps == null) {
- throw new OrmException("patch set " + ps + " not found");
+ throw new StorageException("patch set " + ps + " not found");
}
d.writtenOn = ctx.getWhen();
d.tag = tag;
@@ -71,10 +68,10 @@ public class PublishCommentUtil {
try {
CommentsUtil.setCommentRevId(d, patchListCache, notes.getChange(), ps);
} catch (PatchListNotAvailableException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
- commentsUtil.putComments(ctx.getDb(), ctx.getUpdate(psId), PUBLISHED, drafts);
+ commentsUtil.putComments(ctx.getUpdate(psId), PUBLISHED, drafts);
}
private static PatchSet.Id psId(ChangeNotes notes, Comment c) {
diff --git a/java/com/google/gerrit/server/ReviewerSet.java b/java/com/google/gerrit/server/ReviewerSet.java
index 67b1d9dc34..f36e3abe48 100644
--- a/java/com/google/gerrit/server/ReviewerSet.java
+++ b/java/com/google/gerrit/server/ReviewerSet.java
@@ -34,8 +34,7 @@ import java.sql.Timestamp;
* state {@link ReviewerStateInternal#REMOVED} are ever exposed by this interface.
*/
public class ReviewerSet {
- private static final ReviewerSet EMPTY =
- new ReviewerSet(ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>of());
+ private static final ReviewerSet EMPTY = new ReviewerSet(ImmutableTable.of());
public static ReviewerSet fromApprovals(Iterable<PatchSetApproval> approvals) {
PatchSetApproval first = null;
diff --git a/java/com/google/gerrit/server/Sequences.java b/java/com/google/gerrit/server/Sequences.java
deleted file mode 100644
index fcf0759ee1..0000000000
--- a/java/com/google/gerrit/server/Sequences.java
+++ /dev/null
@@ -1,166 +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.server;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-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.Timer2;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-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.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.util.ArrayList;
-import java.util.List;
-import org.eclipse.jgit.lib.Config;
-
-@Singleton
-public class Sequences {
- public static final String NAME_ACCOUNTS = "accounts";
- public static final String NAME_GROUPS = "groups";
- public static final String NAME_CHANGES = "changes";
-
- public static int getChangeSequenceGap(Config cfg) {
- return cfg.getInt("noteDb", "changes", "initialSequenceGap", 1000);
- }
-
- private enum SequenceType {
- ACCOUNTS,
- CHANGES,
- GROUPS;
- }
-
- private final Provider<ReviewDb> db;
- private final NotesMigration migration;
- private final RepoSequence accountSeq;
- private final RepoSequence changeSeq;
- private final RepoSequence groupSeq;
- private final Timer2<SequenceType, Boolean> nextIdLatency;
-
- @Inject
- public Sequences(
- @GerritServerConfig Config cfg,
- Provider<ReviewDb> db,
- NotesMigration migration,
- GitRepositoryManager repoManager,
- GitReferenceUpdated gitRefUpdated,
- AllProjectsName allProjects,
- AllUsersName allUsers,
- MetricMaker metrics) {
- this.db = db;
- this.migration = migration;
-
- int accountBatchSize = cfg.getInt("noteDb", "accounts", "sequenceBatchSize", 1);
- accountSeq =
- new RepoSequence(
- repoManager,
- gitRefUpdated,
- allUsers,
- NAME_ACCOUNTS,
- () -> ReviewDb.FIRST_ACCOUNT_ID,
- accountBatchSize);
-
- int gap = getChangeSequenceGap(cfg);
- @SuppressWarnings("deprecation")
- RepoSequence.Seed changeSeed = () -> db.get().nextChangeId() + gap;
- int changeBatchSize = cfg.getInt("noteDb", "changes", "sequenceBatchSize", 20);
- changeSeq =
- new RepoSequence(
- repoManager, gitRefUpdated, allProjects, NAME_CHANGES, changeSeed, changeBatchSize);
-
- RepoSequence.Seed groupSeed = () -> nextGroupId(db.get());
- int groupBatchSize = 1;
- groupSeq =
- new RepoSequence(
- repoManager, gitRefUpdated, allUsers, NAME_GROUPS, groupSeed, groupBatchSize);
-
- nextIdLatency =
- metrics.newTimer(
- "sequence/next_id_latency",
- new Description("Latency of requesting IDs from repo sequences")
- .setCumulative()
- .setUnit(Units.MILLISECONDS),
- Field.ofEnum(SequenceType.class, "sequence"),
- Field.ofBoolean("multiple"));
- }
-
- public int nextAccountId() throws OrmException {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.ACCOUNTS, false)) {
- return accountSeq.next();
- }
- }
-
- public int nextChangeId() throws OrmException {
- if (!migration.readChangeSequence()) {
- return nextChangeId(db.get());
- }
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.CHANGES, false)) {
- return changeSeq.next();
- }
- }
-
- public ImmutableList<Integer> nextChangeIds(int count) throws OrmException {
- if (migration.readChangeSequence()) {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.CHANGES, count > 1)) {
- return changeSeq.next(count);
- }
- }
-
- if (count == 0) {
- return ImmutableList.of();
- }
- checkArgument(count > 0, "count is negative: %s", count);
- List<Integer> ids = new ArrayList<>(count);
- ReviewDb db = this.db.get();
- for (int i = 0; i < count; i++) {
- ids.add(nextChangeId(db));
- }
- return ImmutableList.copyOf(ids);
- }
-
- public int nextGroupId() throws OrmException {
- try (Timer2.Context timer = nextIdLatency.start(SequenceType.GROUPS, false)) {
- return groupSeq.next();
- }
- }
-
- @VisibleForTesting
- public RepoSequence getChangeIdRepoSequence() {
- return changeSeq;
- }
-
- @SuppressWarnings("deprecation")
- private static int nextChangeId(ReviewDb db) throws OrmException {
- return db.nextChangeId();
- }
-
- @SuppressWarnings("deprecation")
- static int nextGroupId(ReviewDb db) throws OrmException {
- return db.nextAccountGroupId();
- }
-}
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index 9962c39153..deca550483 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -31,16 +31,16 @@ 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.exceptions.StorageException;
+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.reviewdb.server.ReviewDb;
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.git.LockFailureException;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.logging.TraceContext;
@@ -48,7 +48,6 @@ import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -168,7 +167,6 @@ public class StarredChangesUtil {
private final GitRepositoryManager repoManager;
private final GitReferenceUpdated gitRefUpdated;
private final AllUsersName allUsers;
- private final Provider<ReviewDb> dbProvider;
private final Provider<PersonIdent> serverIdent;
private final ChangeIndexer indexer;
private final Provider<InternalChangeQuery> queryProvider;
@@ -178,25 +176,22 @@ public class StarredChangesUtil {
GitRepositoryManager repoManager,
GitReferenceUpdated gitRefUpdated,
AllUsersName allUsers,
- Provider<ReviewDb> dbProvider,
@GerritPersonIdent Provider<PersonIdent> serverIdent,
ChangeIndexer indexer,
Provider<InternalChangeQuery> queryProvider) {
this.repoManager = repoManager;
this.gitRefUpdated = gitRefUpdated;
this.allUsers = allUsers;
- this.dbProvider = dbProvider;
this.serverIdent = serverIdent;
this.indexer = indexer;
this.queryProvider = queryProvider;
}
- public ImmutableSortedSet<String> getLabels(Account.Id accountId, Change.Id changeId)
- throws OrmException {
+ public ImmutableSortedSet<String> getLabels(Account.Id accountId, Change.Id changeId) {
try (Repository repo = repoManager.openRepository(allUsers)) {
return readLabels(repo, RefNames.refsStarredChanges(changeId, accountId)).labels();
} catch (IOException e) {
- throw new OrmException(
+ throw new StorageException(
String.format(
"Reading stars from change %d for account %d failed",
changeId.get(), accountId.get()),
@@ -210,7 +205,7 @@ public class StarredChangesUtil {
Change.Id changeId,
Set<String> labelsToAdd,
Set<String> labelsToRemove)
- throws OrmException, IllegalLabelException {
+ throws IllegalLabelException {
try (Repository repo = repoManager.openRepository(allUsers)) {
String refName = RefNames.refsStarredChanges(changeId, accountId);
StarRef old = readLabels(repo, refName);
@@ -230,16 +225,16 @@ public class StarredChangesUtil {
updateLabels(repo, refName, old.objectId(), labels);
}
- indexer.index(dbProvider.get(), project, changeId);
+ indexer.index(project, changeId);
return ImmutableSortedSet.copyOf(labels);
} catch (IOException e) {
- throw new OrmException(
+ throw new StorageException(
String.format("Star change %d for account %d failed", changeId.get(), accountId.get()),
e);
}
}
- public void unstarAll(Project.NameKey project, Change.Id changeId) throws OrmException {
+ public void unstarAll(Project.NameKey project, Change.Id changeId) {
try (Repository repo = repoManager.openRepository(allUsers);
RevWalk rw = new RevWalk(repo)) {
BatchRefUpdate batchUpdate = repo.getRefDatabase().newBatchUpdate();
@@ -248,10 +243,8 @@ public class StarredChangesUtil {
batchUpdate.setRefLogMessage("Unstar change " + changeId.get(), true);
for (Account.Id accountId : byChangeFromIndex(changeId).keySet()) {
String refName = RefNames.refsStarredChanges(changeId, accountId);
- Ref ref = repo.getRefDatabase().getRef(refName);
- if (ref != null) {
- batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), refName));
- }
+ Ref ref = repo.getRefDatabase().exactRef(refName);
+ batchUpdate.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), refName));
}
batchUpdate.execute(rw, NullProgressMonitor.INSTANCE);
for (ReceiveCommand command : batchUpdate.getCommands()) {
@@ -266,13 +259,13 @@ public class StarredChangesUtil {
throw new IOException(message);
}
}
- indexer.index(dbProvider.get(), project, changeId);
+ indexer.index(project, changeId);
} catch (IOException e) {
- throw new OrmException(String.format("Unstar change %d failed", changeId.get()), e);
+ throw new StorageException(String.format("Unstar change %d failed", changeId.get()), e);
}
}
- public ImmutableMap<Account.Id, StarRef> byChange(Change.Id changeId) throws OrmException {
+ public ImmutableMap<Account.Id, StarRef> byChange(Change.Id changeId) {
try (Repository repo = repoManager.openRepository(allUsers)) {
ImmutableMap.Builder<Account.Id, StarRef> builder = ImmutableMap.builder();
for (String refPart : getRefNames(repo, RefNames.refsStarredChangesPrefix(changeId))) {
@@ -285,13 +278,12 @@ public class StarredChangesUtil {
}
return builder.build();
} catch (IOException e) {
- throw new OrmException(
+ throw new StorageException(
String.format("Get accounts that starred change %d failed", changeId.get()), e);
}
}
- public ImmutableListMultimap<Account.Id, String> byChangeFromIndex(Change.Id changeId)
- throws OrmException {
+ public ImmutableListMultimap<Account.Id, String> byChangeFromIndex(Change.Id changeId) {
List<ChangeData> changeData =
queryProvider
.get()
@@ -322,7 +314,7 @@ public class StarredChangesUtil {
}
}
- public void ignore(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+ public void ignore(ChangeResource rsrc) throws IllegalLabelException {
star(
rsrc.getUser().asIdentifiedUser().getAccountId(),
rsrc.getProject(),
@@ -331,7 +323,7 @@ public class StarredChangesUtil {
ImmutableSet.of());
}
- public void unignore(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+ public void unignore(ChangeResource rsrc) throws IllegalLabelException {
star(
rsrc.getUser().asIdentifiedUser().getAccountId(),
rsrc.getProject(),
@@ -340,11 +332,11 @@ public class StarredChangesUtil {
ImmutableSet.of(IGNORE_LABEL));
}
- public boolean isIgnoredBy(Change.Id changeId, Account.Id accountId) throws OrmException {
+ public boolean isIgnoredBy(Change.Id changeId, Account.Id accountId) {
return getLabels(accountId, changeId).contains(IGNORE_LABEL);
}
- public boolean isIgnored(ChangeResource rsrc) throws OrmException {
+ public boolean isIgnored(ChangeResource rsrc) {
return isIgnoredBy(rsrc.getChange().getId(), rsrc.getUser().asIdentifiedUser().getAccountId());
}
@@ -364,7 +356,7 @@ public class StarredChangesUtil {
return UNREVIEWED_LABEL + "/" + ps;
}
- public void markAsReviewed(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+ public void markAsReviewed(ChangeResource rsrc) throws IllegalLabelException {
star(
rsrc.getUser().asIdentifiedUser().getAccountId(),
rsrc.getProject(),
@@ -373,7 +365,7 @@ public class StarredChangesUtil {
ImmutableSet.of(getUnreviewedLabel(rsrc.getChange())));
}
- public void markAsUnreviewed(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+ public void markAsUnreviewed(ChangeResource rsrc) throws IllegalLabelException {
star(
rsrc.getUser().asIdentifiedUser().getAccountId(),
rsrc.getProject(),
@@ -455,7 +447,7 @@ public class StarredChangesUtil {
private void updateLabels(
Repository repo, String refName, ObjectId oldObjectId, Collection<String> labels)
- throws IOException, OrmException, InvalidLabelsException {
+ throws IOException, InvalidLabelsException {
try (TraceTimer traceTimer =
TraceContext.newTimer("Update star labels in %s (labels=%s)", refName, labels);
RevWalk rw = new RevWalk(repo)) {
@@ -482,14 +474,13 @@ public class StarredChangesUtil {
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- throw new OrmException(
+ throw new StorageException(
String.format("Update star labels on ref %s failed: %s", refName, result.name()));
}
}
}
- private void deleteRef(Repository repo, String refName, ObjectId oldObjectId)
- throws IOException, OrmException {
+ private void deleteRef(Repository repo, String refName, ObjectId oldObjectId) throws IOException {
if (ObjectId.zeroId().equals(oldObjectId)) {
// ref doesn't exist
return;
@@ -518,7 +509,7 @@ public class StarredChangesUtil {
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- throw new OrmException(
+ throw new StorageException(
String.format("Delete star ref %s failed: %s", refName, result.name()));
}
}
diff --git a/java/com/google/gerrit/server/StartupChecks.java b/java/com/google/gerrit/server/StartupChecks.java
index 5ece91d7cb..9bf94ae8b1 100644
--- a/java/com/google/gerrit/server/StartupChecks.java
+++ b/java/com/google/gerrit/server/StartupChecks.java
@@ -44,7 +44,7 @@ public class StartupChecks implements LifecycleListener {
@Override
public void start() throws StartupException {
- startupChecks.runEach(c -> c.check(), StartupException.class);
+ startupChecks.runEach(StartupCheck::check, StartupException.class);
}
@Override
diff --git a/java/com/google/gerrit/server/UsedAt.java b/java/com/google/gerrit/server/UsedAt.java
deleted file mode 100644
index a5459d1c16..0000000000
--- a/java/com/google/gerrit/server/UsedAt.java
+++ /dev/null
@@ -1,46 +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;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.common.annotations.GwtCompatible;
-import com.google.inject.BindingAnnotation;
-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.
- */
-@BindingAnnotation
-@Target({METHOD, TYPE})
-@Retention(RUNTIME)
-@GwtCompatible
-public @interface UsedAt {
- /** Enumeration of projects that call a method that would otherwise be private. */
- enum Project {
- GOOGLE,
- COLLABNET,
- PLUGIN_DELETE_PROJECT,
- PLUGIN_SERVICEUSER,
- PLUGINS_ALL, // Use this project if a method/type is generally made available to all plugins.
- }
-
- /** Reference to the project that uses the method annotated with this annotation. */
- Project value();
-}
diff --git a/java/com/google/gerrit/server/WebLinks.java b/java/com/google/gerrit/server/WebLinks.java
index 39a2328ed1..589344c314 100644
--- a/java/com/google/gerrit/server/WebLinks.java
+++ b/java/com/google/gerrit/server/WebLinks.java
@@ -19,7 +19,6 @@ import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.data.WebLinkInfoCommon;
import com.google.gerrit.extensions.common.DiffWebLinkInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -54,17 +53,6 @@ public class WebLinks {
return true;
};
- private static final Predicate<WebLinkInfoCommon> INVALID_WEBLINK_COMMON =
- link -> {
- if (link == null) {
- return false;
- } else if (Strings.isNullOrEmpty(link.name) || Strings.isNullOrEmpty(link.url)) {
- logger.atWarning().log("%s is missing name and/or url", link.getClass().getName());
- return false;
- }
- return true;
- };
-
private final DynamicSet<PatchSetWebLink> patchSetLinks;
private final DynamicSet<ParentWebLink> parentLinks;
private final DynamicSet<FileWebLink> fileLinks;
@@ -130,25 +118,13 @@ public class WebLinks {
* @param file File name.
* @return Links for file history
*/
- public List<WebLinkInfoCommon> getFileHistoryLinks(String project, String revision, String file) {
+ public List<WebLinkInfo> getFileHistoryLinks(String project, String revision, String file) {
if (Patch.isMagic(file)) {
return Collections.emptyList();
}
return FluentIterable.from(fileHistoryLinks)
- .transform(
- webLink -> {
- WebLinkInfo info = webLink.getFileHistoryWebLink(project, revision, file);
- if (info == null) {
- return null;
- }
- WebLinkInfoCommon commonInfo = new WebLinkInfoCommon();
- commonInfo.name = info.name;
- commonInfo.imageUrl = info.imageUrl;
- commonInfo.url = info.url;
- commonInfo.target = info.target;
- return commonInfo;
- })
- .filter(INVALID_WEBLINK_COMMON)
+ .transform(webLink -> webLink.getFileHistoryWebLink(project, revision, file))
+ .filter(INVALID_WEBLINK)
.toList();
}
diff --git a/java/com/google/gerrit/server/account/AccountConfig.java b/java/com/google/gerrit/server/account/AccountConfig.java
index d58036d97d..06f7a08002 100644
--- a/java/com/google/gerrit/server/account/AccountConfig.java
+++ b/java/com/google/gerrit/server/account/AccountConfig.java
@@ -21,6 +21,7 @@ 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.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
@@ -34,7 +35,6 @@ import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -200,9 +200,9 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
* Creates a new account.
*
* @return the new account
- * @throws OrmDuplicateKeyException if the user branch already exists
+ * @throws DuplicateKeyException if the user branch already exists
*/
- public Account getNewAccount() throws OrmDuplicateKeyException {
+ public Account getNewAccount() throws DuplicateKeyException {
return getNewAccount(TimeUtil.nowTs());
}
@@ -210,12 +210,12 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
* Creates a new account.
*
* @return the new account
- * @throws OrmDuplicateKeyException if the user branch already exists
+ * @throws DuplicateKeyException if the user branch already exists
*/
- Account getNewAccount(Timestamp registeredOn) throws OrmDuplicateKeyException {
+ Account getNewAccount(Timestamp registeredOn) throws DuplicateKeyException {
checkLoaded();
if (revision != null) {
- throw new OrmDuplicateKeyException(String.format("account %s already exists", accountId));
+ throw new DuplicateKeyException(String.format("account %s already exists", accountId));
}
this.loadedAccountProperties =
Optional.of(new AccountProperties(accountId, registeredOn, new Config(), null));
diff --git a/java/com/google/gerrit/server/account/AccountControl.java b/java/com/google/gerrit/server/account/AccountControl.java
index b43e786e6e..4b8be810f0 100644
--- a/java/com/google/gerrit/server/account/AccountControl.java
+++ b/java/com/google/gerrit/server/account/AccountControl.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.account;
import static java.util.stream.Collectors.toSet;
import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+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;
@@ -80,7 +80,7 @@ public class AccountControl {
private Boolean viewAll;
- AccountControl(
+ private AccountControl(
PermissionBackend permissionBackend,
ProjectCache projectCache,
GroupControl.Factory groupControlFactory,
@@ -106,17 +106,6 @@ public class AccountControl {
* because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective
* groups.
*/
- public boolean canSee(Account otherUser) {
- return canSee(otherUser.getId());
- }
-
- /**
- * Returns true if the current user is allowed to see the otherUser, based on the account
- * visibility policy. Depending on the group membership realms supported, this may not be able to
- * determine SAME_GROUP or VISIBLE_GROUP correctly (defaulting to not being visible). This is
- * because {@link GroupMembership#getKnownGroups()} may only return a subset of the effective
- * groups.
- */
public boolean canSee(Account.Id otherUser) {
return canSee(
new OtherUser() {
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index fcf9c6ba32..c52b8bc43f 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -26,12 +26,12 @@ 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.common.errors.NoSuchGroupException;
+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.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate.AccountUpdater;
import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
@@ -41,9 +41,9 @@ import com.google.gerrit.server.auth.NoSuchUserException;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -167,7 +167,7 @@ public class AccountManager {
// return the identity to the caller.
update(who, extId);
return new AuthResult(extId.accountId(), who.getExternalIdKey(), false);
- } catch (OrmException | ConfigInvalidException e) {
+ } catch (StorageException | ConfigInvalidException e) {
throw new AccountException("Authentication error", e);
}
}
@@ -218,7 +218,7 @@ public class AccountManager {
}
private void update(AuthRequest who, ExternalId extId)
- throws OrmException, IOException, ConfigInvalidException, AccountException {
+ throws IOException, ConfigInvalidException, AccountException {
IdentifiedUser user = userFactory.create(extId.accountId());
List<Consumer<InternalAccountUpdate.Builder>> accountUpdates = new ArrayList<>();
@@ -263,12 +263,12 @@ public class AccountManager {
user.getAccountId(),
AccountUpdater.joinConsumers(accountUpdates))
.orElseThrow(
- () -> new OrmException("Account " + user.getAccountId() + " has been deleted"));
+ () -> new StorageException("Account " + user.getAccountId() + " has been deleted"));
}
}
private AuthResult create(AuthRequest who)
- throws OrmException, AccountException, IOException, ConfigInvalidException {
+ throws AccountException, IOException, ConfigInvalidException {
Account.Id newId = new Account.Id(sequences.nextAccountId());
logger.atFine().log("Assigning new Id %s to account", newId);
@@ -377,7 +377,7 @@ public class AccountManager {
}
private void addGroupMember(AccountGroup.UUID groupUuid, IdentifiedUser user)
- throws OrmException, IOException, ConfigInvalidException, AccountException {
+ throws IOException, ConfigInvalidException, AccountException {
// The user initiated this request by logging in. -> Attribute all modifications to that user.
GroupsUpdate groupsUpdate = groupsUpdateFactory.create(user);
InternalGroupUpdate groupUpdate =
@@ -402,7 +402,7 @@ public class AccountManager {
* this time.
*/
public AuthResult link(Account.Id to, AuthRequest who)
- throws AccountException, OrmException, IOException, ConfigInvalidException {
+ throws AccountException, IOException, ConfigInvalidException {
Optional<ExternalId> optionalExtId = externalIds.get(who.getExternalIdKey());
if (optionalExtId.isPresent()) {
ExternalId extId = optionalExtId.get();
@@ -439,12 +439,11 @@ public class AccountManager {
* @param to account to link the identity onto.
* @param who the additional identity.
* @return the result of linking the identity to the user.
- * @throws OrmException
* @throws AccountException the identity belongs to a different account, or it cannot be linked at
* this time.
*/
public AuthResult updateLink(Account.Id to, AuthRequest who)
- throws OrmException, AccountException, IOException, ConfigInvalidException {
+ throws AccountException, IOException, ConfigInvalidException {
accountsUpdateProvider
.get()
.update(
@@ -476,7 +475,7 @@ public class AccountManager {
* found
*/
public void unlink(Account.Id from, ExternalId.Key extIdKey)
- throws AccountException, OrmException, IOException, ConfigInvalidException {
+ throws AccountException, IOException, ConfigInvalidException {
unlink(from, ImmutableList.of(extIdKey));
}
@@ -489,7 +488,7 @@ public class AccountManager {
* identity was not found
*/
public void unlink(Account.Id from, Collection<ExternalId.Key> extIdKeys)
- throws AccountException, OrmException, IOException, ConfigInvalidException {
+ throws AccountException, IOException, ConfigInvalidException {
if (extIdKeys.isEmpty()) {
return;
}
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index 8c7cdb4dea..f9945b5046 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 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.
@@ -14,269 +14,605 @@
package com.google.gerrit.server.account;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static java.util.stream.Collectors.toSet;
+import static java.util.Comparator.comparing;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
+import com.google.common.annotations.VisibleForTesting;
+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.common.Nullable;
-import com.google.gerrit.extensions.restapi.AuthException;
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.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
+/**
+ * Helper for resolving accounts given arbitrary user-provided input.
+ *
+ * <p>The {@code resolve*} methods each define a list of accepted formats for account resolution.
+ * The algorithm for resolving accounts from a list of formats is as follows:
+ *
+ * <ol>
+ * <li>For each recognized format in the order listed in the method Javadoc, check whether the
+ * input matches that format.
+ * <li>If so, resolve accounts according to that format.
+ * <li>Filter out invisible and inactive accounts.
+ * <li>If the result list is non-empty, return.
+ * <li>If the format is listed above as being short-circuiting, return.
+ * <li>Otherwise, return to step 1 with the next format.
+ * </ol>
+ *
+ * <p>The result never includes accounts that are not visible to the calling user. It also never
+ * includes inactive accounts, with a small number of specific exceptions noted in method Javadoc.
+ */
@Singleton
public class AccountResolver {
- private final Provider<CurrentUser> self;
- private final Realm realm;
- private final Accounts accounts;
- private final AccountCache byId;
- private final IdentifiedUser.GenericFactory userFactory;
- private final AccountControl.Factory accountControlFactory;
- private final Provider<InternalAccountQuery> accountQueryProvider;
- private final Emails emails;
+ public static class UnresolvableAccountException extends UnprocessableEntityException {
+ private static final long serialVersionUID = 1L;
+ private final Result result;
- @Inject
- AccountResolver(
- Provider<CurrentUser> self,
- Realm realm,
- Accounts accounts,
- AccountCache byId,
- IdentifiedUser.GenericFactory userFactory,
- AccountControl.Factory accountControlFactory,
- Provider<InternalAccountQuery> accountQueryProvider,
- Emails emails) {
- this.self = self;
- this.realm = realm;
- this.accounts = accounts;
- this.byId = byId;
- this.userFactory = userFactory;
- this.accountControlFactory = accountControlFactory;
- this.accountQueryProvider = accountQueryProvider;
- this.emails = emails;
- }
+ @VisibleForTesting
+ UnresolvableAccountException(Result result) {
+ super(exceptionMessage(result));
+ this.result = result;
+ }
- /**
- * Locate exactly one account matching the input string.
- *
- * @param input a string of the format "Full Name &lt;email@example&gt;", just the email address
- * ("email@example"), a full name ("Full Name"), an account ID ("18419") or a user name
- * ("username").
- * @return the single account that matches; null if no account matches or there are multiple
- * candidates. If {@code input} is a numeric string, returns an account if and only if that
- * number corresponds to an actual account ID.
- */
- public Account find(String input) throws OrmException, IOException, ConfigInvalidException {
- Set<Account.Id> r = findAll(input);
- if (r.size() == 1) {
- return byId.get(r.iterator().next()).map(AccountState::getAccount).orElse(null);
+ public boolean isSelf() {
+ return result.isSelf();
}
+ }
- Account match = null;
- for (Account.Id id : r) {
- Optional<Account> account = byId.get(id).map(AccountState::getAccount);
- if (!account.map(Account::isActive).orElse(false)) {
- continue;
+ public static String exceptionMessage(Result result) {
+ checkArgument(result.asList().size() != 1);
+ if (result.asList().isEmpty()) {
+ if (result.isSelf()) {
+ return "Resolving account '" + result.input() + "' requires login";
}
- if (match != null) {
- return null;
+ if (result.filteredInactive().isEmpty()) {
+ return "Account '" + result.input() + "' not found";
}
- match = account.get();
+ return result.filteredInactive().stream()
+ .map(a -> formatForException(result, a))
+ .collect(
+ joining(
+ "\n",
+ "Account '"
+ + result.input()
+ + "' only matches inactive accounts. To use an inactive account, retry with"
+ + " one of the following exact account IDs:\n",
+ ""));
}
- return match;
+
+ return result.asList().stream()
+ .map(a -> formatForException(result, a))
+ .collect(joining("\n", "Account '" + result.input() + "' is ambiguous:\n", ""));
}
- /**
- * Find all accounts matching the input string.
- *
- * @param input a string of the format "Full Name &lt;email@example&gt;", just the email address
- * ("email@example"), a full name ("Full Name"), an account ID ("18419") or a user name
- * ("username").
- * @return the accounts that match, empty set if none. Never null. If {@code input} is a numeric
- * string, returns a singleton set if that number corresponds to a real account ID, and an
- * empty set otherwise if it does not.
- */
- public Set<Account.Id> findAll(String input)
- throws OrmException, IOException, ConfigInvalidException {
- Matcher m = Pattern.compile("^.* \\(([1-9][0-9]*)\\)$").matcher(input);
- if (m.matches()) {
- Optional<Account.Id> id = Account.Id.tryParse(m.group(1));
- if (id.isPresent()) {
- return Streams.stream(accounts.get(id.get()))
- .map(a -> a.getAccount().getId())
- .collect(toImmutableSet());
+ private static String formatForException(Result result, AccountState state) {
+ return state.getAccount().getId()
+ + ": "
+ + state.getAccount().getNameEmail(result.accountResolver().anonymousCowardName);
+ }
+
+ public static boolean isSelf(String input) {
+ return "self".equals(input) || "me".equals(input);
+ }
+
+ public class Result {
+ private final String input;
+ private final ImmutableList<AccountState> list;
+ private final ImmutableList<AccountState> filteredInactive;
+
+ @VisibleForTesting
+ Result(String input, List<AccountState> list, List<AccountState> filteredInactive) {
+ this.input = requireNonNull(input);
+ this.list = canonicalize(list);
+ this.filteredInactive = canonicalize(filteredInactive);
+ }
+
+ private ImmutableList<AccountState> canonicalize(List<AccountState> list) {
+ TreeSet<AccountState> set = new TreeSet<>(comparing(a -> a.getAccount().getId().get()));
+ set.addAll(requireNonNull(list));
+ return ImmutableList.copyOf(set);
+ }
+
+ public String input() {
+ return input;
+ }
+
+ public boolean isSelf() {
+ return AccountResolver.isSelf(input);
+ }
+
+ public ImmutableList<AccountState> asList() {
+ return list;
+ }
+
+ public ImmutableSet<Account.Id> asNonEmptyIdSet() throws UnresolvableAccountException {
+ if (list.isEmpty()) {
+ throw new UnresolvableAccountException(this);
+ }
+ return asIdSet();
+ }
+
+ public ImmutableSet<Account.Id> asIdSet() {
+ return list.stream().map(a -> a.getAccount().getId()).collect(toImmutableSet());
+ }
+
+ public AccountState asUnique() throws UnresolvableAccountException {
+ ensureUnique();
+ return list.get(0);
+ }
+
+ private void ensureUnique() throws UnresolvableAccountException {
+ if (list.size() != 1) {
+ throw new UnresolvableAccountException(this);
}
}
- if (input.matches("^[1-9][0-9]*$")) {
- Optional<Account.Id> id = Account.Id.tryParse(input);
- if (id.isPresent()) {
- return Streams.stream(accounts.get(id.get()))
- .map(a -> a.getAccount().getId())
- .collect(toImmutableSet());
+ public IdentifiedUser asUniqueUser() throws UnresolvableAccountException {
+ ensureUnique();
+ if (isSelf()) {
+ // In the special case of "self", use the exact IdentifiedUser from the request context, to
+ // preserve the peer address and any other per-request state.
+ return self.get().asIdentifiedUser();
}
+ return userFactory.create(asUnique());
}
- if (ExternalId.isValidUsername(input)) {
- Optional<AccountState> who = byId.getByUsername(input);
- if (who.isPresent()) {
- return ImmutableSet.of(who.map(a -> a.getAccount().getId()).get());
+ public IdentifiedUser asUniqueUserOnBehalfOf(CurrentUser caller)
+ throws UnresolvableAccountException {
+ ensureUnique();
+ if (isSelf()) {
+ // TODO(dborowitz): This preserves old behavior, but it seems wrong to discard the caller.
+ return self.get().asIdentifiedUser();
}
+ return userFactory.runAs(
+ null, list.get(0).getAccount().getId(), requireNonNull(caller).getRealUser());
+ }
+
+ @VisibleForTesting
+ ImmutableList<AccountState> filteredInactive() {
+ return filteredInactive;
}
- return findAllByNameOrEmail(input);
+ private AccountResolver accountResolver() {
+ return AccountResolver.this;
+ }
}
- /**
- * Locate exactly one account matching the name or name/email string.
- *
- * @param nameOrEmail a string of the format "Full Name &lt;email@example&gt;", just the email
- * address ("email@example"), a full name ("Full Name").
- * @return the single account that matches; null if no account matches or there are multiple
- * candidates.
- */
- public Account findByNameOrEmail(String nameOrEmail) throws OrmException, IOException {
- Set<Account.Id> r = findAllByNameOrEmail(nameOrEmail);
- return r.size() == 1
- ? byId.get(r.iterator().next()).map(AccountState::getAccount).orElse(null)
- : null;
+ @VisibleForTesting
+ interface Searcher<I> {
+ default boolean callerShouldFilterOutInactiveCandidates() {
+ return true;
+ }
+
+ default boolean callerMayAssumeCandidatesAreVisible() {
+ return false;
+ }
+
+ Optional<I> tryParse(String input) throws IOException;
+
+ Stream<AccountState> search(I input) throws IOException, ConfigInvalidException;
+
+ boolean shortCircuitIfNoResults();
+
+ default Optional<Stream<AccountState>> trySearch(String input)
+ throws IOException, ConfigInvalidException {
+ Optional<I> parsed = tryParse(input);
+ return parsed.isPresent() ? Optional.of(search(parsed.get())) : Optional.empty();
+ }
}
- /**
- * Locate exactly one account matching the name or name/email string.
- *
- * @param nameOrEmail a string of the format "Full Name &lt;email@example&gt;", just the email
- * address ("email@example"), a full name ("Full Name").
- * @return the accounts that match, empty collection if none. Never null.
- */
- public Set<Account.Id> findAllByNameOrEmail(String nameOrEmail) throws OrmException, IOException {
- int lt = nameOrEmail.indexOf('<');
- int gt = nameOrEmail.indexOf('>');
- if (lt >= 0 && gt > lt && nameOrEmail.contains("@")) {
+ @VisibleForTesting
+ abstract static class StringSearcher implements Searcher<String> {
+ @Override
+ public final Optional<String> tryParse(String input) {
+ return matches(input) ? Optional.of(input) : Optional.empty();
+ }
+
+ protected abstract boolean matches(String input);
+ }
+
+ private abstract class AccountIdSearcher implements Searcher<Account.Id> {
+ @Override
+ public final Stream<AccountState> search(Account.Id input) {
+ return Streams.stream(accountCache.get(input));
+ }
+ }
+
+ private class BySelf extends StringSearcher {
+ @Override
+ public boolean callerShouldFilterOutInactiveCandidates() {
+ return false;
+ }
+
+ @Override
+ public boolean callerMayAssumeCandidatesAreVisible() {
+ return true;
+ }
+
+ @Override
+ protected boolean matches(String input) {
+ return "self".equals(input) || "me".equals(input);
+ }
+
+ @Override
+ public Stream<AccountState> search(String input) {
+ CurrentUser user = self.get();
+ if (!user.isIdentifiedUser()) {
+ return Stream.empty();
+ }
+ return Stream.of(user.asIdentifiedUser().state());
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return true;
+ }
+ }
+
+ private class ByExactAccountId extends AccountIdSearcher {
+ @Override
+ public boolean callerShouldFilterOutInactiveCandidates() {
+ return false;
+ }
+
+ @Override
+ public Optional<Account.Id> tryParse(String input) {
+ return Account.Id.tryParse(input);
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return true;
+ }
+ }
+
+ private class ByParenthesizedAccountId extends AccountIdSearcher {
+ private final Pattern pattern = Pattern.compile("^.* \\(([1-9][0-9]*)\\)$");
+
+ @Override
+ public Optional<Account.Id> tryParse(String input) {
+ Matcher m = pattern.matcher(input);
+ return m.matches() ? Account.Id.tryParse(m.group(1)) : Optional.empty();
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return true;
+ }
+ }
+
+ private class ByUsername extends StringSearcher {
+ @Override
+ public boolean matches(String input) {
+ return ExternalId.isValidUsername(input);
+ }
+
+ @Override
+ public Stream<AccountState> search(String input) {
+ return Streams.stream(accountCache.getByUsername(input));
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return false;
+ }
+ }
+
+ private class ByNameAndEmail extends StringSearcher {
+ @Override
+ protected boolean matches(String input) {
+ int lt = input.indexOf('<');
+ int gt = input.indexOf('>');
+ return lt >= 0 && gt > lt && input.contains("@");
+ }
+
+ @Override
+ public Stream<AccountState> search(String nameOrEmail) throws IOException {
+ // TODO(dborowitz): This would probably work as a Searcher<Address>
+ int lt = nameOrEmail.indexOf('<');
+ int gt = nameOrEmail.indexOf('>');
Set<Account.Id> ids = emails.getAccountFor(nameOrEmail.substring(lt + 1, gt));
- if (ids.isEmpty() || ids.size() == 1) {
- return ids;
+ ImmutableList<AccountState> allMatches = toAccountStates(ids).collect(toImmutableList());
+ if (allMatches.isEmpty() || allMatches.size() == 1) {
+ return allMatches.stream();
}
- // more than one match, try to return the best one
+ // More than one match. If there are any that match the full name as well, return only that
+ // subset. Otherwise, all are equally non-matching, so return the full set.
String name = nameOrEmail.substring(0, lt - 1);
- Set<Account.Id> nameMatches = new HashSet<>();
- for (Account.Id id : ids) {
- Optional<Account> a = byId.get(id).map(AccountState::getAccount);
- if (a.isPresent() && name.equals(a.get().getFullName())) {
- nameMatches.add(id);
- }
- }
- return nameMatches.isEmpty() ? ids : nameMatches;
+ ImmutableList<AccountState> nameMatches =
+ allMatches.stream()
+ .filter(a -> name.equals(a.getAccount().getFullName()))
+ .collect(toImmutableList());
+ return !nameMatches.isEmpty() ? nameMatches.stream() : allMatches.stream();
}
- if (nameOrEmail.contains("@")) {
- return emails.getAccountFor(nameOrEmail);
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return true;
}
+ }
- Account.Id id = realm.lookup(nameOrEmail);
- if (id != null) {
- return Collections.singleton(id);
+ private class ByEmail extends StringSearcher {
+ @Override
+ protected boolean matches(String input) {
+ return input.contains("@");
}
- List<AccountState> m = accountQueryProvider.get().byFullName(nameOrEmail);
- if (m.size() == 1) {
- return Collections.singleton(m.get(0).getAccount().getId());
+ @Override
+ public Stream<AccountState> search(String input) throws IOException {
+ return toAccountStates(emails.getAccountFor(input));
}
- // At this point we have no clue. Just perform a whole bunch of suggestions
- // and pray we come up with a reasonable result list.
- // TODO(dborowitz): This doesn't match the documentation; consider whether it's possible to be
- // more strict here.
- return accountQueryProvider.get().byDefault(nameOrEmail).stream()
- .map(a -> a.getAccount().getId())
- .collect(toSet());
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return true;
+ }
+ }
+
+ private class FromRealm extends AccountIdSearcher {
+ @Override
+ public Optional<Account.Id> tryParse(String input) throws IOException {
+ return Optional.ofNullable(realm.lookup(input));
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return false;
+ }
+ }
+
+ private class ByFullName implements Searcher<AccountState> {
+ @Override
+ public boolean callerMayAssumeCandidatesAreVisible() {
+ return true; // Rely on enforceVisibility from the index.
+ }
+
+ @Override
+ public Optional<AccountState> tryParse(String input) {
+ List<AccountState> results =
+ accountQueryProvider.get().enforceVisibility(true).byFullName(input);
+ return results.size() == 1 ? Optional.of(results.get(0)) : Optional.empty();
+ }
+
+ @Override
+ public Stream<AccountState> search(AccountState input) {
+ return Stream.of(input);
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return false;
+ }
+ }
+
+ private class ByDefaultSearch extends StringSearcher {
+ @Override
+ public boolean callerMayAssumeCandidatesAreVisible() {
+ return true; // Rely on enforceVisibility from the index.
+ }
+
+ @Override
+ protected boolean matches(String input) {
+ return true;
+ }
+
+ @Override
+ public Stream<AccountState> search(String input) {
+ // At this point we have no clue. Just perform a whole bunch of suggestions and pray we come
+ // up with a reasonable result list.
+ // TODO(dborowitz): This doesn't match the documentation; consider whether it's possible to be
+ // more strict here.
+ return accountQueryProvider.get().enforceVisibility(true).byDefault(input).stream();
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ // In practice this doesn't matter since this is the last searcher in the list, but considered
+ // on its own, it doesn't necessarily need to be terminal.
+ return false;
+ }
+ }
+
+ private final ImmutableList<Searcher<?>> nameOrEmailSearchers =
+ ImmutableList.of(
+ new ByNameAndEmail(),
+ new ByEmail(),
+ new FromRealm(),
+ new ByFullName(),
+ new ByDefaultSearch());
+
+ private final ImmutableList<Searcher<?>> searchers =
+ ImmutableList.<Searcher<?>>builder()
+ .add(new BySelf())
+ .add(new ByExactAccountId())
+ .add(new ByParenthesizedAccountId())
+ .add(new ByUsername())
+ .addAll(nameOrEmailSearchers)
+ .build();
+
+ private final AccountCache accountCache;
+ private final AccountControl.Factory accountControlFactory;
+ private final Emails emails;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final Provider<CurrentUser> self;
+ private final Provider<InternalAccountQuery> accountQueryProvider;
+ private final Realm realm;
+ private final String anonymousCowardName;
+
+ @Inject
+ AccountResolver(
+ AccountCache accountCache,
+ Emails emails,
+ AccountControl.Factory accountControlFactory,
+ IdentifiedUser.GenericFactory userFactory,
+ Provider<CurrentUser> self,
+ Provider<InternalAccountQuery> accountQueryProvider,
+ Realm realm,
+ @AnonymousCowardName String anonymousCowardName) {
+ this.realm = realm;
+ this.accountCache = accountCache;
+ this.accountControlFactory = accountControlFactory;
+ this.userFactory = userFactory;
+ this.self = self;
+ this.accountQueryProvider = accountQueryProvider;
+ this.emails = emails;
+ this.anonymousCowardName = anonymousCowardName;
}
/**
- * Parses a account ID from a request body and returns the user.
+ * Resolves all accounts matching the input string.
+ *
+ * <p>The following input formats are recognized:
*
- * @param id ID of the account, can be a string of the format "{@code Full Name
- * <email@example.com>}", just the email address, a full name if it is unique, an account ID,
- * a user name or "{@code self}" for the calling user
- * @return the user, never null.
- * @throws UnprocessableEntityException thrown if the account ID cannot be resolved or if the
- * account is not visible to the calling user
+ * <ul>
+ * <li>The strings {@code "self"} and {@code "me"}, if the current user is an {@link
+ * IdentifiedUser}. In this case, may return exactly one inactive account.
+ * <li>A bare account ID ({@code "18419"}). In this case, may return exactly one inactive
+ * account. This case short-circuits if the input matches.
+ * <li>An account ID in parentheses following a full name ({@code "Full Name (18419)"}). This
+ * case short-circuits if the input matches.
+ * <li>A username ({@code "username"}).
+ * <li>A full name and email address ({@code "Full Name <email@example>"}). This case
+ * short-circuits if the input matches.
+ * <li>An email address ({@code "email@example"}. This case short-circuits if the input matches.
+ * <li>An account name recognized by the configured {@link Realm#lookup(String)} Realm}.
+ * <li>A full name ({@code "Full Name"}).
+ * <li>As a fallback, a {@link
+ * com.google.gerrit.server.query.account.AccountPredicates#defaultPredicate(Schema,
+ * boolean, String) default search} against the account index.
+ * </ul>
+ *
+ * @param input input string.
+ * @return a result describing matching accounts. Never null even if the result set is empty.
+ * @throws ConfigInvalidException if an error occurs.
+ * @throws IOException if an error occurs.
*/
- public IdentifiedUser parse(String id)
- throws AuthException, UnprocessableEntityException, OrmException, IOException,
- ConfigInvalidException {
- return parseOnBehalfOf(null, id);
+ public Result resolve(String input) throws ConfigInvalidException, IOException {
+ return searchImpl(input, searchers, visibilitySupplier(), accountActivityPredicate());
+ }
+
+ public Result resolve(String input, Predicate<AccountState> accountActivityPredicate)
+ throws ConfigInvalidException, IOException {
+ return searchImpl(input, searchers, visibilitySupplier(), accountActivityPredicate);
}
/**
- * Parses an account ID and returns the user without making any permission check whether the
- * current user can see the account.
+ * Resolves all accounts matching the input string by name or email.
+ *
+ * <p>The following input formats are recognized:
+ *
+ * <ul>
+ * <li>A full name and email address ({@code "Full Name <email@example>"}). This case
+ * short-circuits if the input matches.
+ * <li>An email address ({@code "email@example"}. This case short-circuits if the input matches.
+ * <li>An account name recognized by the configured {@link Realm#lookup(String)} Realm}.
+ * <li>A full name ({@code "Full Name"}).
+ * <li>As a fallback, a {@link
+ * com.google.gerrit.server.query.account.AccountPredicates#defaultPredicate(Schema,
+ * boolean, String) default search} against the account index.
+ * </ul>
*
- * @param id ID of the account, can be a string of the format "{@code Full Name
- * <email@example.com>}", just the email address, a full name if it is unique, an account ID,
- * a user name or "{@code self}" for the calling user
- * @return the user, null if no user is found for the given account ID
- * @throws AuthException thrown if 'self' is used as account ID and the current user is not
- * authenticated
- * @throws OrmException
- * @throws ConfigInvalidException
- * @throws IOException
+ * @param input input string.
+ * @return a result describing matching accounts. Never null even if the result set is empty.
+ * @throws ConfigInvalidException if an error occurs.
+ * @throws IOException if an error occurs.
+ * @deprecated for use only by MailUtil for parsing commit footers; that class needs to be
+ * reevaluated.
*/
- public IdentifiedUser parseId(String id)
- throws AuthException, OrmException, IOException, ConfigInvalidException {
- return parseIdOnBehalfOf(null, id);
+ @Deprecated
+ public Result resolveByNameOrEmail(String input) throws ConfigInvalidException, IOException {
+ return searchImpl(
+ input, nameOrEmailSearchers, visibilitySupplier(), accountActivityPredicate());
}
- /**
- * Like {@link #parse(String)}, but also sets the {@link CurrentUser#getRealUser()} on the result.
- */
- public IdentifiedUser parseOnBehalfOf(@Nullable CurrentUser caller, String id)
- throws AuthException, UnprocessableEntityException, OrmException, IOException,
- ConfigInvalidException {
- IdentifiedUser user = parseIdOnBehalfOf(caller, id);
- if (user == null || !accountControlFactory.get().canSee(user.getAccount())) {
- throw new UnprocessableEntityException(
- String.format("Account '%s' is not found or ambiguous", id));
- }
- return user;
+ private Supplier<Predicate<AccountState>> visibilitySupplier() {
+ return () -> accountControlFactory.get()::canSee;
}
- private IdentifiedUser parseIdOnBehalfOf(@Nullable CurrentUser caller, String id)
- throws AuthException, OrmException, IOException, ConfigInvalidException {
- if (id.equals("self")) {
- CurrentUser user = self.get();
- if (user.isIdentifiedUser()) {
- return user.asIdentifiedUser();
- } else if (user instanceof AnonymousUser) {
- throw new AuthException("Authentication required");
+ private Predicate<AccountState> accountActivityPredicate() {
+ return (AccountState accountState) -> accountState.getAccount().isActive();
+ }
+
+ @VisibleForTesting
+ Result searchImpl(
+ String input,
+ ImmutableList<Searcher<?>> searchers,
+ Supplier<Predicate<AccountState>> visibilitySupplier,
+ Predicate<AccountState> accountActivityPredicate)
+ throws ConfigInvalidException, IOException {
+ visibilitySupplier = Suppliers.memoize(visibilitySupplier::get);
+ List<AccountState> inactive = new ArrayList<>();
+
+ for (Searcher<?> searcher : searchers) {
+ Optional<Stream<AccountState>> maybeResults = searcher.trySearch(input);
+ if (!maybeResults.isPresent()) {
+ continue;
+ }
+ Stream<AccountState> results = maybeResults.get();
+
+ if (!searcher.callerMayAssumeCandidatesAreVisible()) {
+ results = results.filter(visibilitySupplier.get());
+ }
+
+ List<AccountState> list;
+ if (searcher.callerShouldFilterOutInactiveCandidates()) {
+ // Keep track of all inactive candidates discovered by any searchers. If we end up short-
+ // circuiting, the inactive list will be discarded.
+ List<AccountState> active = new ArrayList<>();
+ results.forEach(a -> (accountActivityPredicate.test(a) ? active : inactive).add(a));
+ list = active;
} else {
- return null;
+ list = results.collect(toImmutableList());
}
- }
- Account match = find(id);
- if (match == null) {
- return null;
+ if (!list.isEmpty()) {
+ return createResult(input, list);
+ }
+ if (searcher.shortCircuitIfNoResults()) {
+ // For a short-circuiting searcher, return results even if empty.
+ return !inactive.isEmpty() ? emptyResult(input, inactive) : createResult(input, list);
+ }
}
- CurrentUser realUser = caller != null ? caller.getRealUser() : null;
- return userFactory.runAs(null, match.getId(), realUser);
+ return emptyResult(input, inactive);
+ }
+
+ private Result createResult(String input, List<AccountState> list) {
+ return new Result(input, list, ImmutableList.of());
+ }
+
+ private Result emptyResult(String input, List<AccountState> inactive) {
+ return new Result(input, ImmutableList.of(), inactive);
+ }
+
+ private Stream<AccountState> toAccountStates(Set<Account.Id> ids) {
+ return accountCache.get(ids).values().stream();
}
}
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index 0e3f379814..20a1c978f1 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -24,29 +24,28 @@ 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.common.Nullable;
+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.InternalAccountUpdate.Builder;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIdNotes.ExternalIdNotesLoader;
import com.google.gerrit.server.account.externalids.ExternalIds;
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.git.LockFailureException;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
-import com.google.gerrit.server.update.RefUpdateUtil;
+import com.google.gerrit.server.notedb.Sequences;
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.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;
@@ -81,7 +80,7 @@ import org.eclipse.jgit.lib.Repository;
* commit in the user branch, and thus help debugging.
*
* <p>For creating a new account a new account ID can be retrieved from {@link
- * com.google.gerrit.server.Sequences#nextAccountId()}.
+ * Sequences#nextAccountId()}.
*
* <p>The account updates are written to NoteDb. In NoteDb accounts are represented as user branches
* in the {@code All-Users} repository. Optionally a user branch can contain a 'account.config' file
@@ -123,16 +122,28 @@ public class AccountsUpdate {
public interface Factory {
/**
* Creates an {@code AccountsUpdate} which uses the identity of the specified user as author for
- * all commits related to accounts. The Gerrit server identity will be used as committer.
+ * all commits related to accounts. The server identity will be used as committer.
*
- * <p><strong>Note</strong>: Please use this method with care and rather consider to use the
- * correct annotation on the provider of an {@code AccountsUpdate} instead.
+ * <p><strong>Note</strong>: Please use this method with care and consider using the {@link
+ * com.google.gerrit.server.UserInitiated} annotation on the provider of an {@code
+ * AccountsUpdate} instead.
*
- * @param currentUser the user to which modifications should be attributed, or {@code null} if
- * the Gerrit server identity should also be used as author
+ * @param currentUser the user to which modifications should be attributed
+ * @param externalIdNotesLoader the loader that should be used to load external ID notes
*/
- AccountsUpdate create(
- @Nullable IdentifiedUser currentUser, ExternalIdNotesLoader externalIdNotesLoader);
+ AccountsUpdate create(IdentifiedUser currentUser, ExternalIdNotesLoader externalIdNotesLoader);
+
+ /**
+ * Creates an {@code AccountsUpdate} which uses the server identity as author and committer for
+ * all commits related to accounts.
+ *
+ * <p><strong>Note</strong>: Please use this method with care and consider using the {@link
+ * com.google.gerrit.server.ServerInitiated} annotation on the provider of an {@code
+ * AccountsUpdate} instead.
+ *
+ * @param externalIdNotesLoader the loader that should be used to load external ID notes
+ */
+ AccountsUpdate createWithServerIdent(ExternalIdNotesLoader externalIdNotesLoader);
}
/**
@@ -154,12 +165,9 @@ public class AccountsUpdate {
void update(AccountState accountState, InternalAccountUpdate.Builder update) throws IOException;
static AccountUpdater join(List<AccountUpdater> updaters) {
- return new AccountUpdater() {
- @Override
- public void update(AccountState accountState, Builder update) throws IOException {
- for (AccountUpdater updater : updaters) {
- updater.update(accountState, update);
- }
+ return (accountState, update) -> {
+ for (AccountUpdater updater : updaters) {
+ updater.update(accountState, update);
}
};
}
@@ -175,7 +183,7 @@ public class AccountsUpdate {
private final GitRepositoryManager repoManager;
private final GitReferenceUpdated gitRefUpdated;
- @Nullable private final IdentifiedUser currentUser;
+ private final Optional<IdentifiedUser> currentUser;
private final AllUsersName allUsersName;
private final ExternalIds externalIds;
private final Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
@@ -190,7 +198,32 @@ public class AccountsUpdate {
// Invoked after updating the account but before committing the changes.
private final Runnable beforeCommit;
- @Inject
+ @AssistedInject
+ AccountsUpdate(
+ GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
+ AllUsersName allUsersName,
+ ExternalIds externalIds,
+ Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
+ RetryHelper retryHelper,
+ @GerritPersonIdent PersonIdent serverIdent,
+ @Assisted ExternalIdNotesLoader extIdNotesLoader) {
+ this(
+ repoManager,
+ gitRefUpdated,
+ Optional.empty(),
+ allUsersName,
+ externalIds,
+ metaDataUpdateInternalFactory,
+ retryHelper,
+ extIdNotesLoader,
+ serverIdent,
+ createPersonIdent(serverIdent, Optional.empty()),
+ Runnables.doNothing(),
+ Runnables.doNothing());
+ }
+
+ @AssistedInject
AccountsUpdate(
GitRepositoryManager repoManager,
GitReferenceUpdated gitRefUpdated,
@@ -199,19 +232,19 @@ public class AccountsUpdate {
Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
RetryHelper retryHelper,
@GerritPersonIdent PersonIdent serverIdent,
- @Assisted @Nullable IdentifiedUser currentUser,
+ @Assisted IdentifiedUser currentUser,
@Assisted ExternalIdNotesLoader extIdNotesLoader) {
this(
repoManager,
gitRefUpdated,
- currentUser,
+ Optional.of(currentUser),
allUsersName,
externalIds,
metaDataUpdateInternalFactory,
retryHelper,
extIdNotesLoader,
serverIdent,
- createPersonIdent(serverIdent, currentUser),
+ createPersonIdent(serverIdent, Optional.of(currentUser)),
Runnables.doNothing(),
Runnables.doNothing());
}
@@ -220,7 +253,7 @@ public class AccountsUpdate {
public AccountsUpdate(
GitRepositoryManager repoManager,
GitReferenceUpdated gitRefUpdated,
- @Nullable IdentifiedUser currentUser,
+ Optional<IdentifiedUser> currentUser,
AllUsersName allUsersName,
ExternalIds externalIds,
Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory,
@@ -246,11 +279,11 @@ public class AccountsUpdate {
}
private static PersonIdent createPersonIdent(
- PersonIdent serverIdent, @Nullable IdentifiedUser user) {
- if (user == null) {
+ PersonIdent serverIdent, Optional<IdentifiedUser> user) {
+ if (!user.isPresent()) {
return serverIdent;
}
- return user.newCommitterIdent(serverIdent.getWhen(), serverIdent.getTimeZone());
+ return user.get().newCommitterIdent(serverIdent.getWhen(), serverIdent.getTimeZone());
}
/**
@@ -260,14 +293,13 @@ public class AccountsUpdate {
* @param accountId ID of the new account
* @param init consumer to populate the new account
* @return the newly created account
- * @throws OrmDuplicateKeyException if the account already exists
+ * @throws DuplicateKeyException if the account already exists
* @throws IOException if creating the user branch fails due to an IO error
- * @throws OrmException if creating the user branch fails
* @throws ConfigInvalidException if any of the account fields has an invalid value
*/
public AccountState insert(
String message, Account.Id accountId, Consumer<InternalAccountUpdate.Builder> init)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
return insert(message, accountId, AccountUpdater.fromConsumer(init));
}
@@ -278,13 +310,12 @@ public class AccountsUpdate {
* @param accountId ID of the new account
* @param updater updater to populate the new account
* @return the newly created account
- * @throws OrmDuplicateKeyException if the account already exists
+ * @throws DuplicateKeyException if the account already exists
* @throws IOException if creating the user branch fails due to an IO error
- * @throws OrmException if creating the user branch fails
* @throws ConfigInvalidException if any of the account fields has an invalid value
*/
public AccountState insert(String message, Account.Id accountId, AccountUpdater updater)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
return updateAccount(
r -> {
AccountConfig accountConfig = read(r, accountId);
@@ -318,12 +349,11 @@ public class AccountsUpdate {
* @throws IOException if updating the user branch fails due to an IO error
* @throws LockFailureException if updating the user branch still fails due to concurrent updates
* after the retry timeout exceeded
- * @throws OrmException if updating the user branch fails
* @throws ConfigInvalidException if any of the account fields has an invalid value
*/
public Optional<AccountState> update(
String message, Account.Id accountId, Consumer<InternalAccountUpdate.Builder> update)
- throws OrmException, LockFailureException, IOException, ConfigInvalidException {
+ throws LockFailureException, IOException, ConfigInvalidException {
return update(message, accountId, AccountUpdater.fromConsumer(update));
}
@@ -339,11 +369,10 @@ public class AccountsUpdate {
* @throws IOException if updating the user branch fails due to an IO error
* @throws LockFailureException if updating the user branch still fails due to concurrent updates
* after the retry timeout exceeded
- * @throws OrmException if updating the user branch fails
* @throws ConfigInvalidException if any of the account fields has an invalid value
*/
public Optional<AccountState> update(String message, Account.Id accountId, AccountUpdater updater)
- throws OrmException, LockFailureException, IOException, ConfigInvalidException {
+ throws LockFailureException, IOException, ConfigInvalidException {
return updateAccount(
r -> {
AccountConfig accountConfig = read(r, accountId);
@@ -374,7 +403,7 @@ public class AccountsUpdate {
}
private Optional<AccountState> updateAccount(AccountUpdate accountUpdate)
- throws IOException, ConfigInvalidException, OrmException {
+ throws IOException, ConfigInvalidException {
return executeAccountUpdate(
() -> {
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
@@ -390,7 +419,7 @@ public class AccountsUpdate {
}
private Optional<AccountState> executeAccountUpdate(Action<Optional<AccountState>> action)
- throws IOException, ConfigInvalidException, OrmException {
+ throws IOException, ConfigInvalidException {
try {
return retryHelper.execute(
ActionType.ACCOUNT_UPDATE, action, LockFailureException.class::isInstance);
@@ -398,8 +427,7 @@ public class AccountsUpdate {
Throwables.throwIfUnchecked(e);
Throwables.throwIfInstanceOf(e, IOException.class);
Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
- Throwables.throwIfInstanceOf(e, OrmException.class);
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
@@ -408,7 +436,7 @@ public class AccountsUpdate {
Optional<ObjectId> rev,
Account.Id accountId,
InternalAccountUpdate update)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException {
ExternalIdNotes.checkSameAccount(
Iterables.concat(
update.getCreatedExternalIds(),
@@ -458,7 +486,7 @@ public class AccountsUpdate {
.updateCaches(accountsThatWillBeReindexByReindexAfterRefUpdate);
gitRefUpdated.fire(
- allUsersName, batchRefUpdate, currentUser != null ? currentUser.state() : null);
+ allUsersName, batchRefUpdate, currentUser.map(user -> user.state()).orElse(null));
}
private static Set<Account.Id> getUpdatedAccounts(BatchRefUpdate batchRefUpdate) {
@@ -529,8 +557,7 @@ public class AccountsUpdate {
@FunctionalInterface
private static interface AccountUpdate {
- UpdatedAccount update(Repository allUsersRepo)
- throws IOException, ConfigInvalidException, OrmException;
+ UpdatedAccount update(Repository allUsersRepo) throws IOException, ConfigInvalidException;
}
private static class UpdatedAccount {
diff --git a/java/com/google/gerrit/server/account/CapabilityCollection.java b/java/com/google/gerrit/server/account/CapabilityCollection.java
index 1abc33f726..b52d616eb3 100644
--- a/java/com/google/gerrit/server/account/CapabilityCollection.java
+++ b/java/com/google/gerrit/server/account/CapabilityCollection.java
@@ -81,7 +81,7 @@ public class CapabilityCollection {
}
configureDefaults(tmp, section);
if (!tmp.containsKey(GlobalCapability.ADMINISTRATE_SERVER) && !admins.isEmpty()) {
- tmp.put(GlobalCapability.ADMINISTRATE_SERVER, ImmutableList.<PermissionRule>of());
+ tmp.put(GlobalCapability.ADMINISTRATE_SERVER, ImmutableList.of());
}
ImmutableMap.Builder<String, ImmutableList<PermissionRule>> m = ImmutableMap.builder();
@@ -123,7 +123,7 @@ public class CapabilityCollection {
public ImmutableList<PermissionRule> getPermission(String permissionName) {
ImmutableList<PermissionRule> r = permissions.get(permissionName);
- return r != null ? r : ImmutableList.<PermissionRule>of();
+ return r != null ? r : ImmutableList.of();
}
private void configureDefaults(Map<String, List<PermissionRule>> out, AccessSection section) {
diff --git a/java/com/google/gerrit/server/account/DefaultRealm.java b/java/com/google/gerrit/server/account/DefaultRealm.java
index dde6e81f0c..33de2d2d0d 100644
--- a/java/com/google/gerrit/server/account/DefaultRealm.java
+++ b/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -16,11 +16,11 @@ package com.google.gerrit.server.account;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -87,7 +87,7 @@ public class DefaultRealm extends AbstractRealm {
if (1 == c.size()) {
return c.iterator().next();
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new IOException("Failed to query accounts by email", e);
}
}
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java
index 7c798ca7e4..426d6ea7d9 100644
--- a/java/com/google/gerrit/server/account/Emails.java
+++ b/java/com/google/gerrit/server/account/Emails.java
@@ -20,6 +20,7 @@ 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.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;
@@ -27,7 +28,6 @@ import com.google.gerrit.server.query.account.InternalAccountQuery;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -69,7 +69,7 @@ public class Emails {
*
* @see #getAccountsFor(String...)
*/
- public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException {
+ 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())
@@ -83,7 +83,7 @@ public class Emails {
* @see #getAccountFor(String)
*/
public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails)
- throws IOException, OrmException {
+ throws IOException {
ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder();
externalIds.byEmails(emails).entries().stream()
.forEach(e -> builder.put(e.getKey(), e.getValue().accountId()));
@@ -102,13 +102,13 @@ public class Emails {
return externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet());
}
- private <T> T executeIndexQuery(Action<T> action) throws OrmException {
+ private <T> T executeIndexQuery(Action<T> action) {
try {
- return retryHelper.execute(ActionType.INDEX_QUERY, action, OrmException.class::isInstance);
+ return retryHelper.execute(
+ ActionType.INDEX_QUERY, action, StorageException.class::isInstance);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
- Throwables.throwIfInstanceOf(e, OrmException.class);
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
}
diff --git a/java/com/google/gerrit/server/account/GroupControl.java b/java/com/google/gerrit/server/account/GroupControl.java
index 5649629ad3..b3e6739c2d 100644
--- a/java/com/google/gerrit/server/account/GroupControl.java
+++ b/java/com/google/gerrit/server/account/GroupControl.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.account;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+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;
diff --git a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index a089d9e216..c27d6c30c5 100644
--- a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -30,7 +30,6 @@ import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.query.group.InternalGroupQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
@@ -152,7 +151,7 @@ public class GroupIncludeCacheImpl implements GroupIncludeCache {
}
@Override
- public ImmutableSet<AccountGroup.UUID> load(Account.Id memberId) throws OrmException {
+ public ImmutableSet<AccountGroup.UUID> load(Account.Id memberId) {
try (TraceTimer timer = TraceContext.newTimer("Loading groups with member %s", memberId)) {
return groupQueryProvider.get().byMember(memberId).stream()
.map(InternalGroup::getGroupUUID)
@@ -171,7 +170,7 @@ public class GroupIncludeCacheImpl implements GroupIncludeCache {
}
@Override
- public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key) throws OrmException {
+ public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key) {
try (TraceTimer timer = TraceContext.newTimer("Loading parent groups of %s", key)) {
return groupQueryProvider.get().bySubgroup(key).stream()
.map(InternalGroup::getGroupUUID)
diff --git a/java/com/google/gerrit/server/account/GroupMembers.java b/java/com/google/gerrit/server/account/GroupMembers.java
index 710f553e0f..d7e97ba551 100644
--- a/java/com/google/gerrit/server/account/GroupMembers.java
+++ b/java/com/google/gerrit/server/account/GroupMembers.java
@@ -67,7 +67,7 @@ public class GroupMembers {
throw new IllegalStateException("listAccounts called with PROJECT_OWNERS argument");
}
try {
- return listAccounts(groupUUID, null, new HashSet<AccountGroup.UUID>());
+ return listAccounts(groupUUID, null, new HashSet<>());
} catch (NoSuchProjectException e) {
throw new IllegalStateException(e);
}
@@ -81,7 +81,7 @@ public class GroupMembers {
*/
public Set<Account> listAccounts(AccountGroup.UUID groupUUID, Project.NameKey project)
throws NoSuchProjectException, IOException {
- return listAccounts(groupUUID, project, new HashSet<AccountGroup.UUID>());
+ return listAccounts(groupUUID, project, new HashSet<>());
}
private Set<Account> listAccounts(
diff --git a/java/com/google/gerrit/server/account/GroupMembership.java b/java/com/google/gerrit/server/account/GroupMembership.java
index cc73222f4b..b59b9897fc 100644
--- a/java/com/google/gerrit/server/account/GroupMembership.java
+++ b/java/com/google/gerrit/server/account/GroupMembership.java
@@ -24,7 +24,7 @@ import java.util.Set;
* <p>Different accounts systems (eg. LDAP, gerrit groups) provide concrete implementations.
*/
public interface GroupMembership {
- GroupMembership EMPTY = new ListGroupMembership(Collections.<AccountGroup.UUID>emptySet());
+ GroupMembership EMPTY = new ListGroupMembership(Collections.emptySet());
/**
* Returns {@code true} when the user this object was created for is a member of the specified
diff --git a/java/com/google/gerrit/server/account/Preferences.java b/java/com/google/gerrit/server/account/Preferences.java
index 6767b85dec..cd849b8273 100644
--- a/java/com/google/gerrit/server/account/Preferences.java
+++ b/java/com/google/gerrit/server/account/Preferences.java
@@ -21,11 +21,8 @@ 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_MATCH;
import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_TOKEN;
import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static com.google.gerrit.server.git.UserConfigSections.URL_ALIAS;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
@@ -47,10 +44,7 @@ import com.google.gerrit.server.git.meta.VersionedMetaData;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
import java.util.Optional;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -154,7 +148,6 @@ public class Preferences {
parseDefaultGeneralPreferences(defaultCfg, null));
setChangeTable(cfg, mergedGeneralPreferencesInput.changeTable);
setMy(cfg, mergedGeneralPreferencesInput.my);
- setUrlAliases(cfg, mergedGeneralPreferencesInput.urlAliases);
// evict the cached general preferences
this.generalPreferences = null;
@@ -249,11 +242,9 @@ public class Preferences {
if (input != null) {
r.changeTable = input.changeTable;
r.my = input.my;
- r.urlAliases = input.urlAliases;
} else {
r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
r.my = parseMyMenus(cfg, defaultCfg);
- r.urlAliases = parseUrlAliases(cfg, defaultCfg);
}
return r;
}
@@ -407,14 +398,6 @@ public class Preferences {
return my;
}
- private static Map<String, String> parseUrlAliases(Config cfg, @Nullable Config defaultCfg) {
- Map<String, String> urlAliases = urlAliases(cfg);
- if (urlAliases == null && defaultCfg != null) {
- urlAliases = urlAliases(defaultCfg);
- }
- return urlAliases;
- }
-
public static GeneralPreferencesInfo readDefaultGeneralPreferences(
AllUsersName allUsersName, Repository allUsersRepo)
throws IOException, ConfigInvalidException {
@@ -452,7 +435,6 @@ public class Preferences {
GeneralPreferencesInfo.defaults());
setMy(defaultPrefs.getConfig(), input.my);
setChangeTable(defaultPrefs.getConfig(), input.changeTable);
- setUrlAliases(defaultPrefs.getConfig(), input.urlAliases);
defaultPrefs.commit(md);
return parseGeneralPreferences(defaultPrefs.getConfig(), null, null);
@@ -557,31 +539,6 @@ public class Preferences {
}
}
- private static Map<String, String> urlAliases(Config cfg) {
- HashMap<String, String> urlAliases = new HashMap<>();
- for (String subsection : cfg.getSubsections(URL_ALIAS)) {
- urlAliases.put(
- cfg.getString(URL_ALIAS, subsection, KEY_MATCH),
- cfg.getString(URL_ALIAS, subsection, KEY_TOKEN));
- }
- return !urlAliases.isEmpty() ? urlAliases : null;
- }
-
- private static void setUrlAliases(Config cfg, Map<String, String> urlAliases) {
- if (urlAliases != null) {
- for (String subsection : cfg.getSubsections(URL_ALIAS)) {
- cfg.unsetSection(URL_ALIAS, subsection);
- }
-
- int i = 1;
- for (Entry<String, String> e : urlAliases.entrySet()) {
- cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_MATCH, e.getKey());
- cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_TOKEN, e.getValue());
- i++;
- }
- }
- }
-
private static void unsetSection(Config cfg, String section) {
cfg.unsetSection(section, null);
for (String subsection : cfg.getSubsections(section)) {
diff --git a/java/com/google/gerrit/server/account/SetInactiveFlag.java b/java/com/google/gerrit/server/account/SetInactiveFlag.java
index a683849a81..da2d640b01 100644
--- a/java/com/google/gerrit/server/account/SetInactiveFlag.java
+++ b/java/com/google/gerrit/server/account/SetInactiveFlag.java
@@ -23,7 +23,6 @@ import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.AccountActivationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -48,7 +47,7 @@ public class SetInactiveFlag {
}
public Response<?> deactivate(Account.Id accountId)
- throws RestApiException, IOException, ConfigInvalidException, OrmException {
+ throws RestApiException, IOException, ConfigInvalidException {
AtomicBoolean alreadyInactive = new AtomicBoolean(false);
AtomicReference<Optional<RestApiException>> exception = new AtomicReference<>(Optional.empty());
accountsUpdateProvider
@@ -81,7 +80,7 @@ public class SetInactiveFlag {
}
public Response<String> activate(Account.Id accountId)
- throws RestApiException, IOException, ConfigInvalidException, OrmException {
+ throws RestApiException, IOException, ConfigInvalidException {
AtomicBoolean alreadyActive = new AtomicBoolean(false);
AtomicReference<Optional<RestApiException>> exception = new AtomicReference<>(Optional.empty());
accountsUpdateProvider
diff --git a/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java b/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
index 965f1ba068..c7808de9b5 100644
--- a/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
+++ b/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
@@ -20,7 +20,7 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
import com.google.common.collect.Ordering;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+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;
diff --git a/java/com/google/gerrit/server/account/externalids/AllExternalIds.java b/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
index 7492906632..5d12ae101b 100644
--- a/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
+++ b/java/com/google/gerrit/server/account/externalids/AllExternalIds.java
@@ -21,12 +21,12 @@ 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.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;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import java.util.Collection;
/** Cache value containing all external IDs. */
@@ -64,7 +64,7 @@ public abstract class AllExternalIds {
object.byAccount().values().stream()
.map(extId -> toProto(idConverter, extId))
.forEach(allBuilder::addExternalId);
- return ProtoCacheSerializers.toByteArray(allBuilder.build());
+ return Protos.toByteArray(allBuilder.build());
}
private static ExternalIdProto toProto(ObjectIdConverter idConverter, ExternalId externalId) {
@@ -88,8 +88,7 @@ public abstract class AllExternalIds {
public AllExternalIds deserialize(byte[] in) {
ObjectIdConverter idConverter = ObjectIdConverter.create();
return create(
- ProtoCacheSerializers.parseUnchecked(AllExternalIdsProto.parser(), in).getExternalIdList()
- .stream()
+ Protos.parseUnchecked(AllExternalIdsProto.parser(), in).getExternalIdList().stream()
.map(proto -> toExternalId(idConverter, proto))
.collect(toList()));
}
diff --git a/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java b/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java
index b4c82d0b94..aa092786f9 100644
--- a/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java
+++ b/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.account.externalids;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gerrit.exceptions.DuplicateKeyException;
/**
* Exception that is thrown if an external ID cannot be inserted because an external ID with the
* same key already exists.
*/
-public class DuplicateExternalIdKeyException extends OrmDuplicateKeyException {
+public class DuplicateExternalIdKeyException extends DuplicateKeyException {
private static final long serialVersionUID = 1L;
private final ExternalId.Key duplicateKey;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java
index 767bfd587a..5aa19d88f5 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheImpl.java
@@ -122,6 +122,11 @@ class ExternalIdCacheImpl implements ExternalIdCache {
ObjectId oldNotesRev,
ObjectId newNotesRev,
Consumer<SetMultimap<Account.Id, ExternalId>> update) {
+ if (oldNotesRev.equals(newNotesRev)) {
+ // No need to update external id cache since there is no update to those external ids.
+ return;
+ }
+
lock.lock();
try {
SetMultimap<Account.Id, ExternalId> m;
diff --git a/java/com/google/gerrit/server/api/BUILD b/java/com/google/gerrit/server/api/BUILD
index 61bc119672..37e04bb588 100644
--- a/java/com/google/gerrit/server/api/BUILD
+++ b/java/com/google/gerrit/server/api/BUILD
@@ -9,13 +9,15 @@ java_library(
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/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/restapi",
+ "//java/com/google/gerrit/util/cli",
+ "//lib:args4j",
"//lib:guava",
- "//lib:gwtorm",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
"//lib/jgit/org.eclipse.jgit:jgit",
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 1b8222c666..f027c92b3a 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -16,7 +16,10 @@ package com.google.gerrit.server.api.changes;
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ListMultimap;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.AbandonInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
@@ -49,12 +52,14 @@ import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.BadRequestException;
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.server.DynamicOptions;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
-import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeMessageResource;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.SetPrivateOp;
@@ -68,6 +73,7 @@ import com.google.gerrit.server.restapi.change.DeleteAssignee;
import com.google.gerrit.server.restapi.change.DeleteChange;
import com.google.gerrit.server.restapi.change.DeletePrivate;
import com.google.gerrit.server.restapi.change.GetAssignee;
+import com.google.gerrit.server.restapi.change.GetChange;
import com.google.gerrit.server.restapi.change.GetHashtags;
import com.google.gerrit.server.restapi.change.GetPastAssignees;
import com.google.gerrit.server.restapi.change.GetPureRevert;
@@ -97,14 +103,17 @@ import com.google.gerrit.server.restapi.change.SetWorkInProgress;
import com.google.gerrit.server.restapi.change.SubmittedTogether;
import com.google.gerrit.server.restapi.change.SuggestChangeReviewers;
import com.google.gerrit.server.restapi.change.Unignore;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.util.cli.CmdLineParser;
import com.google.inject.Inject;
+import com.google.inject.Injector;
import com.google.inject.Provider;
+import com.google.inject.Singleton;
import com.google.inject.assistedinject.Assisted;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.kohsuke.args4j.CmdLineException;
class ChangeApiImpl implements ChangeApi {
interface Factory {
@@ -132,7 +141,7 @@ class ChangeApiImpl implements ChangeApi {
private final PutTopic putTopic;
private final ChangeIncludedIn includedIn;
private final PostReviewers postReviewers;
- private final ChangeJson.Factory changeJson;
+ private final Provider<GetChange> getChangeProvider;
private final PostHashtags postHashtags;
private final GetHashtags getHashtags;
private final PutAssignee putAssignee;
@@ -157,6 +166,7 @@ class ChangeApiImpl implements ChangeApi {
private final PutMessage putMessage;
private final Provider<GetPureRevert> getPureRevertProvider;
private final StarredChangesUtil stars;
+ private final DynamicOptionParser dynamicOptionParser;
@Inject
ChangeApiImpl(
@@ -180,7 +190,7 @@ class ChangeApiImpl implements ChangeApi {
PutTopic putTopic,
ChangeIncludedIn includedIn,
PostReviewers postReviewers,
- ChangeJson.Factory changeJson,
+ Provider<GetChange> getChangeProvider,
PostHashtags postHashtags,
GetHashtags getHashtags,
PutAssignee putAssignee,
@@ -205,6 +215,7 @@ class ChangeApiImpl implements ChangeApi {
PutMessage putMessage,
Provider<GetPureRevert> getPureRevertProvider,
StarredChangesUtil stars,
+ DynamicOptionParser dynamicOptionParser,
@Assisted ChangeResource change) {
this.changeApi = changeApi;
this.revert = revert;
@@ -226,7 +237,7 @@ class ChangeApiImpl implements ChangeApi {
this.putTopic = putTopic;
this.includedIn = includedIn;
this.postReviewers = postReviewers;
- this.changeJson = changeJson;
+ this.getChangeProvider = getChangeProvider;
this.postHashtags = postHashtags;
this.getHashtags = getHashtags;
this.putAssignee = putAssignee;
@@ -251,6 +262,7 @@ class ChangeApiImpl implements ChangeApi {
this.putMessage = putMessage;
this.getPureRevertProvider = getPureRevertProvider;
this.stars = stars;
+ this.dynamicOptionParser = dynamicOptionParser;
this.change = change;
}
@@ -260,16 +272,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public RevisionApi current() throws RestApiException {
- return revision("current");
- }
-
- @Override
- public RevisionApi revision(int id) throws RestApiException {
- return revision(String.valueOf(id));
- }
-
- @Override
public RevisionApi revision(String id) throws RestApiException {
try {
return revisionApi.create(revisions.parse(change, IdString.fromDecoded(id)));
@@ -288,11 +290,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public void abandon() throws RestApiException {
- abandon(new AbandonInput());
- }
-
- @Override
public void abandon(AbandonInput in) throws RestApiException {
try {
abandon.apply(change, in);
@@ -302,11 +299,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public void restore() throws RestApiException {
- restore(new RestoreInput());
- }
-
- @Override
public void restore(RestoreInput in) throws RestApiException {
try {
restore.apply(change, in);
@@ -316,13 +308,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public void move(String destination) throws RestApiException {
- MoveInput in = new MoveInput();
- in.destinationBranch = destination;
- move(in);
- }
-
- @Override
public void move(MoveInput in) throws RestApiException {
try {
move.apply(change, in);
@@ -364,11 +349,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public ChangeApi revert() throws RestApiException {
- return revert(new RevertInput());
- }
-
- @Override
public ChangeApi revert(RevertInput in) throws RestApiException {
try {
return changeApi.id(revert.apply(change, in)._number);
@@ -387,20 +367,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public List<ChangeInfo> submittedTogether() throws RestApiException {
- SubmittedTogetherInfo info =
- submittedTogether(
- EnumSet.noneOf(ListChangesOption.class), EnumSet.noneOf(SubmittedTogetherOption.class));
- return info.changes;
- }
-
- @Override
- public SubmittedTogetherInfo submittedTogether(EnumSet<SubmittedTogetherOption> options)
- throws RestApiException {
- return submittedTogether(EnumSet.noneOf(ListChangesOption.class), options);
- }
-
- @Override
public SubmittedTogetherInfo submittedTogether(
EnumSet<ListChangesOption> listOptions, EnumSet<SubmittedTogetherOption> submitOptions)
throws RestApiException {
@@ -416,11 +382,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public void rebase() throws RestApiException {
- rebase(new RebaseInput());
- }
-
- @Override
public void rebase(RebaseInput in) throws RestApiException {
try {
rebase.apply(change, in);
@@ -464,13 +425,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public AddReviewerResult addReviewer(String reviewer) throws RestApiException {
- AddReviewerInput in = new AddReviewerInput();
- in.reviewer = reviewer;
- return addReviewer(in);
- }
-
- @Override
public AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException {
try {
return postReviewers.apply(change, in);
@@ -489,11 +443,6 @@ class ChangeApiImpl implements ChangeApi {
};
}
- @Override
- public SuggestedReviewersRequest suggestReviewers(String query) throws RestApiException {
- return suggestReviewers().withQuery(query);
- }
-
private List<SuggestedReviewerInfo> suggestReviewers(SuggestedReviewersRequest r)
throws RestApiException {
try {
@@ -515,34 +464,25 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public ChangeInfo get(EnumSet<ListChangesOption> s) throws RestApiException {
+ public ChangeInfo get(
+ EnumSet<ListChangesOption> options, ImmutableListMultimap<String, String> pluginOptions)
+ throws RestApiException {
try {
- return changeJson.create(s).format(change);
+ GetChange getChange = getChangeProvider.get();
+ options.forEach(getChange::addOption);
+ dynamicOptionParser.parseDynamicOptions(getChange, pluginOptions);
+ return getChange.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve change", e);
}
}
@Override
- public ChangeInfo get() throws RestApiException {
- return get(
- EnumSet.complementOf(
- EnumSet.of(ListChangesOption.CHECK, ListChangesOption.SKIP_MERGEABLE)));
- }
-
- @Override
public ChangeEditApi edit() throws RestApiException {
return changeEditApi.create(change);
}
@Override
- public void setMessage(String msg) throws RestApiException {
- CommitMessageInput in = new CommitMessageInput();
- in.message = msg;
- setMessage(in);
- }
-
- @Override
public void setMessage(CommitMessageInput in) throws RestApiException {
try {
putMessage.apply(change, in);
@@ -552,11 +492,6 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
- public ChangeInfo info() throws RestApiException {
- return get(EnumSet.noneOf(ListChangesOption.class));
- }
-
- @Override
public void setHashtags(HashtagsInput input) throws RestApiException {
try {
postHashtags.apply(change, input);
@@ -696,7 +631,7 @@ class ChangeApiImpl implements ChangeApi {
} else {
unignore.apply(change, new Input());
}
- } catch (OrmException | IllegalLabelException e) {
+ } catch (StorageException | IllegalLabelException e) {
throw asRestApiException("Cannot ignore change", e);
}
}
@@ -705,7 +640,7 @@ class ChangeApiImpl implements ChangeApi {
public boolean ignored() throws RestApiException {
try {
return stars.isIgnored(change);
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw asRestApiException("Cannot check if ignored", e);
}
}
@@ -720,7 +655,7 @@ class ChangeApiImpl implements ChangeApi {
} else {
markAsUnreviewed.apply(change, new Input());
}
- } catch (OrmException | IllegalLabelException e) {
+ } catch (StorageException | IllegalLabelException e) {
throw asRestApiException(
"Cannot mark change as " + (reviewed ? "reviewed" : "unreviewed"), e);
}
@@ -760,4 +695,36 @@ class ChangeApiImpl implements ChangeApi {
throw asRestApiException("Cannot parse change message " + id, e);
}
}
+
+ @Singleton
+ static class DynamicOptionParser {
+ private final CmdLineParser.Factory cmdLineParserFactory;
+ private final Injector injector;
+ private final DynamicMap<DynamicOptions.DynamicBean> dynamicBeans;
+
+ @Inject
+ DynamicOptionParser(
+ CmdLineParser.Factory cmdLineParserFactory,
+ Injector injector,
+ DynamicMap<DynamicOptions.DynamicBean> dynamicBeans) {
+ this.cmdLineParserFactory = cmdLineParserFactory;
+ this.injector = injector;
+ this.dynamicBeans = dynamicBeans;
+ }
+
+ void parseDynamicOptions(Object bean, ListMultimap<String, String> pluginOptions)
+ throws BadRequestException {
+ CmdLineParser clp = cmdLineParserFactory.create(bean);
+ DynamicOptions dynamicOptions = new DynamicOptions(bean, injector, dynamicBeans);
+ dynamicOptions.parseDynamicBeans(clp);
+ dynamicOptions.setDynamicBeans();
+ dynamicOptions.onBeanParseStart();
+ try {
+ clp.parseOptionMap(pluginOptions);
+ } catch (CmdLineException | NumberFormatException e) {
+ throw new BadRequestException(e.getMessage(), e);
+ }
+ dynamicOptions.onBeanParseEnd();
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
index 5aa4cf13ad..ffc65245d6 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
@@ -34,7 +34,6 @@ import com.google.gerrit.server.restapi.change.ChangeEdits;
import com.google.gerrit.server.restapi.change.DeleteChangeEdit;
import com.google.gerrit.server.restapi.change.PublishChangeEdit;
import com.google.gerrit.server.restapi.change.RebaseChangeEdit;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -242,7 +241,7 @@ public class ChangeEditApiImpl implements ChangeEditApi {
}
private ChangeEditResource getChangeEditResource(String filePath)
- throws ResourceNotFoundException, AuthException, IOException, OrmException {
+ throws ResourceNotFoundException, AuthException, IOException {
return changeEdits.parse(changeResource, IdString.fromDecoded(filePath));
}
}
diff --git a/java/com/google/gerrit/server/api/changes/ChangesImpl.java b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
index 2f19040935..acff137628 100644
--- a/java/com/google/gerrit/server/api/changes/ChangesImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangesImpl.java
@@ -30,6 +30,7 @@ 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;
import com.google.gerrit.server.restapi.change.QueryChanges;
@@ -43,6 +44,7 @@ class ChangesImpl implements Changes {
private final ChangesCollection changes;
private final ChangeApiImpl.Factory api;
private final CreateChange createChange;
+ private final DynamicOptionParser dynamicOptionParser;
private final Provider<QueryChanges> queryProvider;
@Inject
@@ -50,10 +52,12 @@ class ChangesImpl implements Changes {
ChangesCollection changes,
ChangeApiImpl.Factory api,
CreateChange createChange,
+ DynamicOptionParser dynamicOptionParser,
Provider<QueryChanges> queryProvider) {
this.changes = changes;
this.api = api;
this.createChange = createChange;
+ this.dynamicOptionParser = dynamicOptionParser;
this.queryProvider = queryProvider;
}
@@ -116,9 +120,11 @@ class ChangesImpl implements Changes {
}
qc.setLimit(q.getLimit());
qc.setStart(q.getStart());
+ qc.setNoLimit(q.getNoLimit());
for (ListChangesOption option : q.getOptions()) {
qc.addOption(option);
}
+ dynamicOptionParser.parseDynamicOptions(qc, q.getPluginOptions());
try {
List<?> result = qc.apply(TopLevelResource.INSTANCE);
diff --git a/java/com/google/gerrit/server/api/changes/FileApiImpl.java b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
index f2d0ef834a..883ab99184 100644
--- a/java/com/google/gerrit/server/api/changes/FileApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/FileApiImpl.java
@@ -17,16 +17,20 @@ package com.google.gerrit.server.api.changes;
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
import com.google.gerrit.extensions.api.changes.FileApi;
+import com.google.gerrit.extensions.common.BlameInfo;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.change.FileResource;
+import com.google.gerrit.server.restapi.change.GetBlame;
import com.google.gerrit.server.restapi.change.GetContent;
import com.google.gerrit.server.restapi.change.GetDiff;
import com.google.gerrit.server.restapi.change.Reviewed;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
+import java.util.List;
class FileApiImpl implements FileApi {
interface Factory {
@@ -34,6 +38,7 @@ class FileApiImpl implements FileApi {
}
private final GetContent getContent;
+ private final Provider<GetBlame> getBlame;
private final GetDiff getDiff;
private final Reviewed.PutReviewed putReviewed;
private final Reviewed.DeleteReviewed deleteReviewed;
@@ -42,11 +47,13 @@ class FileApiImpl implements FileApi {
@Inject
FileApiImpl(
GetContent getContent,
+ Provider<GetBlame> getBlame,
GetDiff getDiff,
Reviewed.PutReviewed putReviewed,
Reviewed.DeleteReviewed deleteReviewed,
@Assisted FileResource file) {
this.getContent = getContent;
+ this.getBlame = getBlame;
this.getDiff = getDiff;
this.putReviewed = putReviewed;
this.deleteReviewed = deleteReviewed;
@@ -132,4 +139,18 @@ class FileApiImpl implements FileApi {
throw asRestApiException("Cannot retrieve diff", e);
}
}
+
+ @Override
+ public BlameRequest blameRequest() throws RestApiException {
+ return new BlameRequest() {
+ @Override
+ public List<BlameInfo> get() throws RestApiException {
+ try {
+ return getBlame.get().setBase(isForBase()).apply(file).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot retrieve blame", e);
+ }
+ }
+ };
+ }
}
diff --git a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
index 23558dc6fa..2df7ae6c5f 100644
--- a/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/RevisionApiImpl.java
@@ -20,6 +20,7 @@ import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
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.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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.account.AccountLoader;
@@ -144,7 +144,6 @@ class RevisionApiImpl implements RevisionApi {
private final PutDescription putDescription;
private final GetDescription getDescription;
private final ApprovalsUtil approvalsUtil;
- private final Provider<ReviewDb> db;
private final AccountLoader.Factory accountLoaderFactory;
@Inject
@@ -188,7 +187,6 @@ class RevisionApiImpl implements RevisionApi {
PutDescription putDescription,
GetDescription getDescription,
ApprovalsUtil approvalsUtil,
- Provider<ReviewDb> db,
AccountLoader.Factory accountLoaderFactory,
@Assisted RevisionResource r) {
this.repoManager = repoManager;
@@ -230,7 +228,6 @@ class RevisionApiImpl implements RevisionApi {
this.putDescription = putDescription;
this.getDescription = getDescription;
this.approvalsUtil = approvalsUtil;
- this.db = db;
this.accountLoaderFactory = accountLoaderFactory;
this.revision = r;
}
@@ -245,12 +242,6 @@ class RevisionApiImpl implements RevisionApi {
}
@Override
- public void submit() throws RestApiException {
- SubmitInput in = new SubmitInput();
- submit(in);
- }
-
- @Override
public void submit(SubmitInput in) throws RestApiException {
try {
submit.apply(revision, in);
@@ -260,11 +251,6 @@ class RevisionApiImpl implements RevisionApi {
}
@Override
- public BinaryResult submitPreview() throws RestApiException {
- return submitPreview("zip");
- }
-
- @Override
public BinaryResult submitPreview(String format) throws RestApiException {
try {
submitPreview.setFormat(format);
@@ -275,12 +261,6 @@ class RevisionApiImpl implements RevisionApi {
}
@Override
- public ChangeApi rebase() throws RestApiException {
- RebaseInput in = new RebaseInput();
- return rebase(in);
- }
-
- @Override
public ChangeApi rebase(RebaseInput in) throws RestApiException {
try {
return changes.id(rebase.apply(revision, in)._number);
@@ -374,17 +354,7 @@ class RevisionApiImpl implements RevisionApi {
@SuppressWarnings("unchecked")
@Override
- public Map<String, FileInfo> files() throws RestApiException {
- try {
- return (Map<String, FileInfo>) listFiles.apply(revision).value();
- } catch (Exception e) {
- throw asRestApiException("Cannot retrieve files", e);
- }
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public Map<String, FileInfo> files(String base) throws RestApiException {
+ public Map<String, FileInfo> files(@Nullable String base) throws RestApiException {
try {
return (Map<String, FileInfo>) listFiles.setBase(base).apply(revision).value();
} catch (Exception e) {
@@ -617,8 +587,7 @@ class RevisionApiImpl implements RevisionApi {
ListMultimapBuilder.treeKeys().arrayListValues().build();
try {
Iterable<PatchSetApproval> approvals =
- approvalsUtil.byPatchSet(
- db.get(), revision.getNotes(), revision.getPatchSet().getId(), null, null);
+ approvalsUtil.byPatchSet(revision.getNotes(), revision.getPatchSet().getId(), null, null);
AccountLoader accountLoader =
accountLoaderFactory.create(
EnumSet.of(
diff --git a/java/com/google/gerrit/server/api/config/ServerImpl.java b/java/com/google/gerrit/server/api/config/ServerImpl.java
index ec08507239..6e78be241c 100644
--- a/java/com/google/gerrit/server/api/config/ServerImpl.java
+++ b/java/com/google/gerrit/server/api/config/ServerImpl.java
@@ -25,18 +25,21 @@ import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.ServerInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.webui.TopMenu;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.restapi.config.CheckConsistency;
import com.google.gerrit.server.restapi.config.GetDiffPreferences;
import com.google.gerrit.server.restapi.config.GetEditPreferences;
import com.google.gerrit.server.restapi.config.GetPreferences;
import com.google.gerrit.server.restapi.config.GetServerInfo;
+import com.google.gerrit.server.restapi.config.ListTopMenus;
import com.google.gerrit.server.restapi.config.SetDiffPreferences;
import com.google.gerrit.server.restapi.config.SetEditPreferences;
import com.google.gerrit.server.restapi.config.SetPreferences;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.util.List;
@Singleton
public class ServerImpl implements Server {
@@ -48,6 +51,7 @@ public class ServerImpl implements Server {
private final SetEditPreferences setEditPreferences;
private final GetServerInfo getServerInfo;
private final Provider<CheckConsistency> checkConsistency;
+ private final ListTopMenus listTopMenus;
@Inject
ServerImpl(
@@ -58,7 +62,8 @@ public class ServerImpl implements Server {
GetEditPreferences getEditPreferences,
SetEditPreferences setEditPreferences,
GetServerInfo getServerInfo,
- Provider<CheckConsistency> checkConsistency) {
+ Provider<CheckConsistency> checkConsistency,
+ ListTopMenus listTopMenus) {
this.getPreferences = getPreferences;
this.setPreferences = setPreferences;
this.getDiffPreferences = getDiffPreferences;
@@ -67,6 +72,7 @@ public class ServerImpl implements Server {
this.setEditPreferences = setEditPreferences;
this.getServerInfo = getServerInfo;
this.checkConsistency = checkConsistency;
+ this.listTopMenus = listTopMenus;
}
@Override
@@ -148,4 +154,9 @@ public class ServerImpl implements Server {
throw asRestApiException("Cannot check consistency", e);
}
}
+
+ @Override
+ public List<TopMenu.MenuEntry> topMenus() {
+ return listTopMenus.apply(new ConfigResource()).value();
+ }
}
diff --git a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
index 9909ed77cd..b70a0294a3 100644
--- a/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupApiImpl.java
@@ -47,7 +47,6 @@ import com.google.gerrit.server.restapi.group.PutOptions;
import com.google.gerrit.server.restapi.group.PutOwner;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.util.Arrays;
import java.util.List;
class GroupApiImpl implements GroupApi {
@@ -217,18 +216,18 @@ class GroupApiImpl implements GroupApi {
}
@Override
- public void addMembers(String... members) throws RestApiException {
+ public void addMembers(List<String> members) throws RestApiException {
try {
- addMembers.apply(rsrc, AddMembers.Input.fromMembers(Arrays.asList(members)));
+ addMembers.apply(rsrc, AddMembers.Input.fromMembers(members));
} catch (Exception e) {
throw asRestApiException("Cannot add group members", e);
}
}
@Override
- public void removeMembers(String... members) throws RestApiException {
+ public void removeMembers(List<String> members) throws RestApiException {
try {
- deleteMembers.apply(rsrc, AddMembers.Input.fromMembers(Arrays.asList(members)));
+ deleteMembers.apply(rsrc, AddMembers.Input.fromMembers(members));
} catch (Exception e) {
throw asRestApiException("Cannot remove group members", e);
}
@@ -244,18 +243,18 @@ class GroupApiImpl implements GroupApi {
}
@Override
- public void addGroups(String... groups) throws RestApiException {
+ public void addGroups(List<String> groups) throws RestApiException {
try {
- addSubgroups.apply(rsrc, AddSubgroups.Input.fromGroups(Arrays.asList(groups)));
+ addSubgroups.apply(rsrc, AddSubgroups.Input.fromGroups(groups));
} catch (Exception e) {
throw asRestApiException("Cannot add subgroups", e);
}
}
@Override
- public void removeGroups(String... groups) throws RestApiException {
+ public void removeGroups(List<String> groups) throws RestApiException {
try {
- deleteSubgroups.apply(rsrc, AddSubgroups.Input.fromGroups(Arrays.asList(groups)));
+ deleteSubgroups.apply(rsrc, AddSubgroups.Input.fromGroups(groups));
} catch (Exception e) {
throw asRestApiException("Cannot remove subgroups", e);
}
diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index e8d6cf4748..bae75dbea3 100644
--- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -141,7 +141,7 @@ class GroupsImpl implements Groups {
if (req.getUser() != null) {
try {
- list.setUser(accountResolver.parse(req.getUser()).getAccountId());
+ list.setUser(accountResolver.resolve(req.getUser()).asUnique().getAccount().getId());
} catch (Exception e) {
throw asRestApiException("Error looking up user " + req.getUser(), e);
}
diff --git a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
index debb97c8be..5d25d1aba0 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.api.projects;
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.projects.ProjectApi;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.api.projects.Projects;
@@ -28,7 +29,6 @@ import com.google.gerrit.server.restapi.project.ListProjects;
import com.google.gerrit.server.restapi.project.ListProjects.FilterType;
import com.google.gerrit.server.restapi.project.ProjectsCollection;
import com.google.gerrit.server.restapi.project.QueryProjects;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -117,9 +117,6 @@ class ProjectsImpl implements Projects {
case CODE:
type = FilterType.CODE;
break;
- case PARENT_CANDIDATES:
- type = FilterType.PARENT_CANDIDATES;
- break;
case PERMISSIONS:
type = FilterType.PERMISSIONS;
break;
@@ -158,7 +155,7 @@ class ProjectsImpl implements Projects {
.withLimit(r.getLimit())
.withStart(r.getStart())
.apply();
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new RestApiException("Cannot query projects", e);
}
}
diff --git a/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index 512937ba22..37e52f4c82 100644
--- a/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -17,7 +17,9 @@ 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.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;
@@ -25,7 +27,6 @@ import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -63,10 +64,9 @@ public class AccountIdHandler extends OptionHandler<Account.Id> {
String token = params.getParameter(0);
Account.Id accountId;
try {
- Account a = accountResolver.find(token);
- if (a != null) {
- accountId = a.getId();
- } else {
+ try {
+ accountId = accountResolver.resolve(token).asUnique().getAccount().getId();
+ } catch (UnprocessableEntityException e) {
switch (authType) {
case HTTP_LDAP:
case CLIENT_SSL_CERT_LDAP:
@@ -81,10 +81,12 @@ public class AccountIdHandler extends OptionHandler<Account.Id> {
case OPENID:
case OPENID_SSO:
default:
- throw new CmdLineException(owner, localizable("user \"%s\" not found"), token);
+ String msg = "user \"%s\" not found";
+ logger.atSevere().withCause(e).log(msg, token);
+ throw new CmdLineException(owner, localizable(msg), token);
}
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
String msg = "database is down";
logger.atSevere().withCause(e).log(msg);
throw new CmdLineException(owner, localizable(msg));
diff --git a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
index 9f72b2fbdc..ceba3e620d 100644
--- a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
@@ -18,12 +18,12 @@ 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.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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -76,7 +76,7 @@ public class ChangeIdHandler extends OptionHandler<Change.Id> {
}
} catch (IllegalArgumentException e) {
throw new CmdLineException(owner, localizable("Change-Id is not valid: %s"), e.getMessage());
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new CmdLineException(owner, localizable("Database error: %s"), e.getMessage());
}
diff --git a/java/com/google/gerrit/server/args4j/ProjectHandler.java b/java/com/google/gerrit/server/args4j/ProjectHandler.java
index 52fadd242d..e3af82b9ce 100644
--- a/java/com/google/gerrit/server/args4j/ProjectHandler.java
+++ b/java/com/google/gerrit/server/args4j/ProjectHandler.java
@@ -17,9 +17,9 @@ 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.common.ProjectUtil;
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;
import com.google.gerrit.server.permissions.ProjectPermission;
diff --git a/java/com/google/gerrit/server/audit/BUILD b/java/com/google/gerrit/server/audit/BUILD
index a995b57208..c8b71a9be2 100644
--- a/java/com/google/gerrit/server/audit/BUILD
+++ b/java/com/google/gerrit/server/audit/BUILD
@@ -12,6 +12,12 @@ java_library(
"//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",
@@ -44,13 +50,11 @@ java_library(
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
- "//lib:gwtjsonrpc",
- "//lib:gwtorm",
"//lib:jsch",
"//lib:juniversalchardet",
"//lib:mime-util",
"//lib:protobuf",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib:soy",
"//lib:tukaani-xz",
"//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/server/audit/RpcAuditEvent.java b/java/com/google/gerrit/server/audit/RpcAuditEvent.java
deleted file mode 100644
index 6c53bb29ae..0000000000
--- a/java/com/google/gerrit/server/audit/RpcAuditEvent.java
+++ /dev/null
@@ -1,47 +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.audit;
-
-import com.google.common.collect.ListMultimap;
-import com.google.gerrit.server.CurrentUser;
-
-public class RpcAuditEvent extends HttpAuditEvent {
-
- /**
- * Creates a new audit event with results
- *
- * @param sessionId session id the event belongs to
- * @param who principal that has generated the event
- * @param what object of the event
- * @param when time-stamp of when the event started
- * @param params parameters of the event
- * @param httpMethod HTTP method
- * @param input input
- * @param status HTTP status
- * @param result result of the event
- */
- public RpcAuditEvent(
- String sessionId,
- CurrentUser who,
- String what,
- long when,
- ListMultimap<String, ?> params,
- String httpMethod,
- Object input,
- int status,
- Object result) {
- super(sessionId, who, what, when, params, httpMethod, input, status, result);
- }
-}
diff --git a/java/com/google/gerrit/server/auth/UniversalAuthBackend.java b/java/com/google/gerrit/server/auth/UniversalAuthBackend.java
index 94faeef130..4e93ff27b6 100644
--- a/java/com/google/gerrit/server/auth/UniversalAuthBackend.java
+++ b/java/com/google/gerrit/server/auth/UniversalAuthBackend.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.auth;
import static java.util.Objects.requireNonNull;
-import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
@@ -25,10 +25,10 @@ import java.util.List;
/** Universal implementation of the AuthBackend that works with the injected set of AuthBackends. */
@Singleton
public final class UniversalAuthBackend implements AuthBackend {
- private final DynamicSet<AuthBackend> authBackends;
+ private final PluginSetContext<AuthBackend> authBackends;
@Inject
- UniversalAuthBackend(DynamicSet<AuthBackend> authBackends) {
+ UniversalAuthBackend(PluginSetContext<AuthBackend> authBackends) {
this.authBackends = authBackends;
}
@@ -36,15 +36,16 @@ public final class UniversalAuthBackend implements AuthBackend {
public AuthUser authenticate(AuthRequest request) throws AuthException {
List<AuthUser> authUsers = new ArrayList<>();
List<AuthException> authExs = new ArrayList<>();
- for (AuthBackend backend : authBackends) {
- try {
- authUsers.add(requireNonNull(backend.authenticate(request)));
- } catch (MissingCredentialsException ex) {
- // Not handled by this backend.
- } catch (AuthException ex) {
- authExs.add(ex);
- }
- }
+ authBackends.runEach(
+ backend -> {
+ try {
+ authUsers.add(requireNonNull(backend.authenticate(request)));
+ } catch (MissingCredentialsException ex) {
+ // Not handled by this backend.
+ } catch (AuthException ex) {
+ authExs.add(ex);
+ }
+ });
// Handle the valid responses
if (authUsers.size() == 1) {
diff --git a/java/com/google/gerrit/server/auth/ldap/Helper.java b/java/com/google/gerrit/server/auth/ldap/Helper.java
index 8e294b7cda..4b14c385dc 100644
--- a/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -195,13 +195,7 @@ class Helper {
Subject subject = ctx.getSubject();
try {
return Subject.doAs(
- subject,
- new PrivilegedExceptionAction<DirContext>() {
- @Override
- public DirContext run() throws IOException, NamingException {
- return createContext(env);
- }
- });
+ subject, (PrivilegedExceptionAction<DirContext>) () -> createContext(env));
} catch (PrivilegedActionException e) {
Throwables.throwIfInstanceOf(e.getException(), IOException.class);
Throwables.throwIfInstanceOf(e.getException(), NamingException.class);
@@ -422,7 +416,7 @@ class Helper {
groupBase,
groupScope,
new ParameterizedString(groupMemberPattern),
- Collections.<String>emptySet());
+ Collections.emptySet());
if (groupMemberQuery.getParameters().isEmpty()) {
throw new IllegalArgumentException("No variables in ldap.groupMemberPattern");
}
diff --git a/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java b/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java
index 3a6be0c436..0980116c7a 100644
--- a/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java
+++ b/java/com/google/gerrit/server/auth/oauth/OAuthTokenCache.java
@@ -22,12 +22,12 @@ import com.google.common.cache.Cache;
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.ProtoCacheSerializers;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
@@ -57,7 +57,7 @@ public class OAuthTokenCache {
static class Serializer implements CacheSerializer<OAuthToken> {
@Override
public byte[] serialize(OAuthToken object) {
- return ProtoCacheSerializers.toByteArray(
+ return Protos.toByteArray(
OAuthTokenProto.newBuilder()
.setToken(object.getToken())
.setSecret(object.getSecret())
@@ -69,7 +69,7 @@ public class OAuthTokenCache {
@Override
public OAuthToken deserialize(byte[] in) {
- OAuthTokenProto proto = ProtoCacheSerializers.parseUnchecked(OAuthTokenProto.parser(), in);
+ OAuthTokenProto proto = Protos.parseUnchecked(OAuthTokenProto.parser(), in);
return new OAuthToken(
proto.getToken(),
proto.getSecret(),
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 60dd62f47b..063ddbc660 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -70,7 +70,7 @@ public class CacheMetrics {
F_NAME);
Set<CallbackMetric<?>> cacheMetrics =
- ImmutableSet.<CallbackMetric<?>>of(memEnt, memHit, memEvict, perDiskEnt, perDiskHit);
+ ImmutableSet.of(memEnt, memHit, memEvict, perDiskEnt, perDiskHit);
metrics.newTrigger(
cacheMetrics,
diff --git a/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java b/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java
index a7fdbbd670..ee672cd0c2 100644
--- a/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java
+++ b/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.cache;
import com.google.common.base.Strings;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.PluginName;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -35,13 +35,13 @@ public class ForwardingRemovalListener<K, V> implements RemovalListener<K, V> {
ForwardingRemovalListener create(String cacheName);
}
- private final DynamicSet<CacheRemovalListener> listeners;
+ private final PluginSetContext<CacheRemovalListener> listeners;
private final String cacheName;
private String pluginName = PluginName.GERRIT;
@Inject
ForwardingRemovalListener(
- DynamicSet<CacheRemovalListener> listeners, @Assisted String cacheName) {
+ PluginSetContext<CacheRemovalListener> listeners, @Assisted String cacheName) {
this.listeners = listeners;
this.cacheName = cacheName;
}
@@ -56,8 +56,6 @@ public class ForwardingRemovalListener<K, V> implements RemovalListener<K, V> {
@Override
@SuppressWarnings("unchecked")
public void onRemoval(RemovalNotification<K, V> notification) {
- for (CacheRemovalListener<K, V> l : listeners) {
- l.onRemoval(pluginName, cacheName, notification);
- }
+ listeners.runEach(l -> l.onRemoval(pluginName, cacheName, notification));
}
}
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java b/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java
index 48c0a5bd81..5d9ce60b22 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheDefProxy.java
@@ -42,7 +42,6 @@ class H2CacheDefProxy<K, V> implements PersistentCacheDef<K, V> {
return source.expireFromMemoryAfterAccess();
}
- @SuppressWarnings("unchecked")
@Override
public Weigher<K, V> weigher() {
Weigher<K, V> weigher = source.weigher();
@@ -52,13 +51,10 @@ class H2CacheDefProxy<K, V> implements PersistentCacheDef<K, V> {
// introduce weigher that performs calculations
// on value that is being stored not on ValueHolder
- return (Weigher<K, V>)
- new Weigher<K, ValueHolder<V>>() {
- @Override
- public int weigh(K key, ValueHolder<V> value) {
- return weigher.weigh(key, value.value);
- }
- };
+ Weigher<K, ValueHolder<V>> holderWeigher = (k, v) -> weigher.weigh(k, v.value);
+ @SuppressWarnings("unchecked")
+ Weigher<K, V> ret = (Weigher<K, V>) holderWeigher;
+ return ret;
}
@Override
diff --git a/java/com/google/gerrit/server/cache/serialize/BUILD b/java/com/google/gerrit/server/cache/serialize/BUILD
index 786df8bb35..76dcbb1c28 100644
--- a/java/com/google/gerrit/server/cache/serialize/BUILD
+++ b/java/com/google/gerrit/server/cache/serialize/BUILD
@@ -6,8 +6,9 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/proto",
+ "//java/com/google/gwtorm",
"//lib:guava",
- "//lib:gwtorm",
"//lib:protobuf",
"//lib/jgit/org.eclipse.jgit:jgit",
],
diff --git a/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java b/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java
new file mode 100644
index 0000000000..eb946a9a4a
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/ObjectIdConverter.java
@@ -0,0 +1,56 @@
+// 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.base.Preconditions.checkArgument;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+
+import com.google.protobuf.ByteString;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Helper for serializing {@link ObjectId} instances to/from protobuf fields.
+ *
+ * <p>Reuse a single instance's {@link #toByteString(ObjectId)} and {@link
+ * #fromByteString(ByteString)} within a single {@link CacheSerializer#serialize} or {@link
+ * CacheSerializer#deserialize} method body to minimize allocation of temporary buffers.
+ *
+ * <p><strong>Note:</strong> This class is not threadsafe. Instances must not be stored in {@link
+ * CacheSerializer} fields if the serializer instances will be used from multiple threads.
+ */
+public class ObjectIdConverter {
+ public static ObjectIdConverter create() {
+ return new ObjectIdConverter();
+ }
+
+ private final byte[] buf = new byte[OBJECT_ID_LENGTH];
+
+ private ObjectIdConverter() {}
+
+ public ByteString toByteString(ObjectId id) {
+ id.copyRawTo(buf, 0);
+ return ByteString.copyFrom(buf);
+ }
+
+ public ObjectId fromByteString(ByteString in) {
+ checkArgument(
+ in.size() == OBJECT_ID_LENGTH,
+ "expected ByteString of length %s: %s",
+ OBJECT_ID_LENGTH,
+ in);
+ in.copyTo(buf, 0);
+ return ObjectId.fromRaw(buf);
+ }
+}
diff --git a/java/com/google/gerrit/server/cache/serialize/ProtoCacheSerializers.java b/java/com/google/gerrit/server/cache/serialize/ProtoCacheSerializers.java
deleted file mode 100644
index 4e0b1066c0..0000000000
--- a/java/com/google/gerrit/server/cache/serialize/ProtoCacheSerializers.java
+++ /dev/null
@@ -1,127 +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.base.Preconditions.checkArgument;
-import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
-
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import com.google.protobuf.ByteString;
-import com.google.protobuf.CodedOutputStream;
-import com.google.protobuf.MessageLite;
-import com.google.protobuf.Parser;
-import java.io.IOException;
-import org.eclipse.jgit.lib.ObjectId;
-
-/** Static utilities for writing protobuf-based {@link CacheSerializer} implementations. */
-public class ProtoCacheSerializers {
- /**
- * Serializes a proto to a byte array.
- *
- * <p>Guarantees deterministic serialization and thus is suitable for use in persistent caches.
- * Should be used in preference to {@link MessageLite#toByteArray()}, which is not guaranteed
- * deterministic.
- *
- * @param message the proto message to serialize.
- * @return a byte array with the message contents.
- */
- public static byte[] toByteArray(MessageLite message) {
- byte[] bytes = new byte[message.getSerializedSize()];
- CodedOutputStream cout = CodedOutputStream.newInstance(bytes);
- cout.useDeterministicSerialization();
- try {
- message.writeTo(cout);
- cout.checkNoSpaceLeft();
- return bytes;
- } catch (IOException e) {
- throw new IllegalStateException("exception writing to byte array", e);
- }
- }
-
- /**
- * Serializes an object to a {@link ByteString} using a protobuf codec.
- *
- * <p>Guarantees deterministic serialization and thus is suitable for use in persistent caches.
- * Should be used in preference to {@link ProtobufCodec#encodeToByteString(Object)}, which is not
- * guaranteed deterministic.
- *
- * @param object the object to serialize.
- * @param codec codec for serializing.
- * @return a {@code ByteString} with the message contents.
- */
- public static <T> ByteString toByteString(T object, ProtobufCodec<T> codec) {
- try (ByteString.Output bout = ByteString.newOutput()) {
- CodedOutputStream cout = CodedOutputStream.newInstance(bout);
- cout.useDeterministicSerialization();
- codec.encode(object, cout);
- cout.flush();
- return bout.toByteString();
- } catch (IOException e) {
- throw new IllegalStateException("exception writing to ByteString", e);
- }
- }
-
- /**
- * Parses a byte array to a protobuf message.
- *
- * @param parser parser for the proto type.
- * @param in byte array with the message contents.
- * @return parsed proto.
- */
- public static <M extends MessageLite> M parseUnchecked(Parser<M> parser, byte[] in) {
- try {
- return parser.parseFrom(in);
- } catch (IOException e) {
- throw new IllegalArgumentException("exception parsing byte array to proto", e);
- }
- }
-
- /**
- * Helper for serializing {@link ObjectId} instances to/from protobuf fields.
- *
- * <p>Reuse a single instance's {@link #toByteString(ObjectId)} and {@link
- * #fromByteString(ByteString)} within a single {@link CacheSerializer#serialize} or {@link
- * CacheSerializer#deserialize} method body to minimize allocation of temporary buffers.
- *
- * <p><strong>Note:</strong> This class is not threadsafe. Instances must not be stored in {@link
- * CacheSerializer} fields if the serializer instances will be used from multiple threads.
- */
- public static class ObjectIdConverter {
- public static ObjectIdConverter create() {
- return new ObjectIdConverter();
- }
-
- private final byte[] buf = new byte[OBJECT_ID_LENGTH];
-
- private ObjectIdConverter() {}
-
- public ByteString toByteString(ObjectId id) {
- id.copyRawTo(buf, 0);
- return ByteString.copyFrom(buf);
- }
-
- public ObjectId fromByteString(ByteString in) {
- checkArgument(
- in.size() == OBJECT_ID_LENGTH,
- "expected ByteString of length %s: %s",
- OBJECT_ID_LENGTH,
- in);
- in.copyTo(buf, 0);
- return ObjectId.fromRaw(buf);
- }
- }
-
- private ProtoCacheSerializers() {}
-}
diff --git a/java/com/google/gerrit/server/cache/serialize/ProtobufSerializer.java b/java/com/google/gerrit/server/cache/serialize/ProtobufSerializer.java
new file mode 100644
index 0000000000..180646b392
--- /dev/null
+++ b/java/com/google/gerrit/server/cache/serialize/ProtobufSerializer.java
@@ -0,0 +1,38 @@
+// 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.cache.serialize;
+
+import com.google.gerrit.proto.Protos;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+
+/** A CacheSerializer for Protobuf messages. */
+public class ProtobufSerializer<T extends MessageLite> implements CacheSerializer<T> {
+ private final Parser<T> parser;
+
+ public ProtobufSerializer(Parser<T> parser) {
+ this.parser = parser;
+ }
+
+ @Override
+ public byte[] serialize(T object) {
+ return Protos.toByteArray(object);
+ }
+
+ @Override
+ public T deserialize(byte[] in) {
+ return Protos.parseUnchecked(parser, in);
+ }
+}
diff --git a/java/com/google/gerrit/server/cache/testing/BUILD b/java/com/google/gerrit/server/cache/testing/BUILD
index 47f7324a3f..09f698c125 100644
--- a/java/com/google/gerrit/server/cache/testing/BUILD
+++ b/java/com/google/gerrit/server/cache/testing/BUILD
@@ -7,9 +7,6 @@ java_library(
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//lib:guava",
"//lib:protobuf",
- "//lib/commons:lang3",
- "//lib/truth",
],
)
diff --git a/java/com/google/gerrit/server/cache/testing/SerializedClassSubject.java b/java/com/google/gerrit/server/cache/testing/SerializedClassSubject.java
deleted file mode 100644
index 2351e2c05a..0000000000
--- a/java/com/google/gerrit/server/cache/testing/SerializedClassSubject.java
+++ /dev/null
@@ -1,109 +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.testing;
-
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import com.google.common.truth.FailureMetadata;
-import com.google.common.truth.Subject;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Map;
-import org.apache.commons.lang3.reflect.FieldUtils;
-
-/**
- * Subject about classes that are serialized into persistent caches.
- *
- * <p>Hand-written {@link com.google.gerrit.server.cache.serialize.CacheSerializer CacheSerializer}
- * 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 implementation.
- *
- * <p>Changing fields of a serialized class (or abstract methods, in the case of {@code @AutoValue}
- * classes) will likely require changes to the serializer implementation, and may require bumping
- * the {@link com.google.gerrit.server.cache.PersistentCacheBinding#version(int) version} in the
- * cache binding, in case the representation has changed in such a way that old serialized data
- * becomes unreadable.
- *
- * <p>Changes to a serialized class such as adding or removing fields generally requires a change to
- * 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 static SerializedClassSubject assertThatSerializedClass(Class<?> actual) {
- // This formulation fails in Eclipse 4.7.3a with "The type
- // SerializedClassSubject does not define SerializedClassSubject() that is
- // applicable here", due to
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=534694 or a similar bug:
- // return assertAbout(SerializedClassSubject::new).that(actual);
- Subject.Factory<SerializedClassSubject, Class<?>> factory =
- (m, a) -> new SerializedClassSubject(m, a);
- return assertAbout(factory).that(actual);
- }
-
- private SerializedClassSubject(FailureMetadata metadata, Class<?> actual) {
- super(metadata, actual);
- }
-
- public void isAbstract() {
- isNotNull();
- assertWithMessage("expected class %s to be abstract", actual().getName())
- .that(Modifier.isAbstract(actual().getModifiers()))
- .isTrue();
- }
-
- public void isConcrete() {
- isNotNull();
- assertWithMessage("expected class %s to be concrete", actual().getName())
- .that(!Modifier.isAbstract(actual().getModifiers()))
- .isTrue();
- }
-
- public void hasFields(Map<String, Type> expectedFields) {
- isConcrete();
- assertThat(
- FieldUtils.getAllFieldsList(actual()).stream()
- .filter(f -> !Modifier.isStatic(f.getModifiers()))
- .collect(toImmutableMap(Field::getName, Field::getGenericType)))
- .containsExactlyEntriesIn(expectedFields);
- }
-
- 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();
- assertThat(
- Arrays.stream(actual().getDeclaredMethods())
- .filter(m -> !Modifier.isStatic(m.getModifiers()))
- .filter(m -> Modifier.isAbstract(m.getModifiers()))
- .filter(m -> m.getParameters().length == 0)
- .collect(toImmutableMap(Method::getName, Method::getGenericReturnType)))
- .named("no-argument abstract methods on %s", actual().getName())
- .isEqualTo(expectedMethods);
- }
-
- public void extendsClass(Type superclassType) {
- isNotNull();
- assertThat(actual().getGenericSuperclass())
- .named("superclass of %s", actual().getName())
- .isEqualTo(superclassType);
- }
-}
diff --git a/java/com/google/gerrit/server/change/AbandonOp.java b/java/com/google/gerrit/server/change/AbandonOp.java
index 5affd5c75a..5ee5bc7fa1 100644
--- a/java/com/google/gerrit/server/change/AbandonOp.java
+++ b/java/com/google/gerrit/server/change/AbandonOp.java
@@ -15,13 +15,9 @@
package com.google.gerrit.server.change;
import com.google.common.base.Strings;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.api.changes.RecipientType;
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.PatchSet;
@@ -36,7 +32,6 @@ import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -49,8 +44,6 @@ public class AbandonOp implements BatchUpdateOp {
private final ChangeAbandoned changeAbandoned;
private final String msgTxt;
- private final NotifyHandling notifyHandling;
- private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
private final AccountState accountState;
private Change change;
@@ -59,10 +52,7 @@ public class AbandonOp implements BatchUpdateOp {
public interface Factory {
AbandonOp create(
- @Assisted @Nullable AccountState accountState,
- @Assisted @Nullable String msgTxt,
- @Assisted NotifyHandling notifyHandling,
- @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify);
+ @Assisted @Nullable AccountState accountState, @Assisted @Nullable String msgTxt);
}
@Inject
@@ -72,9 +62,7 @@ public class AbandonOp implements BatchUpdateOp {
PatchSetUtil psUtil,
ChangeAbandoned changeAbandoned,
@Assisted @Nullable AccountState accountState,
- @Assisted @Nullable String msgTxt,
- @Assisted NotifyHandling notifyHandling,
- @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify) {
+ @Assisted @Nullable String msgTxt) {
this.abandonedSenderFactory = abandonedSenderFactory;
this.cmUtil = cmUtil;
this.psUtil = psUtil;
@@ -82,8 +70,6 @@ public class AbandonOp implements BatchUpdateOp {
this.accountState = accountState;
this.msgTxt = Strings.nullToEmpty(msgTxt);
- this.notifyHandling = notifyHandling;
- this.accountsToNotify = accountsToNotify;
}
@Nullable
@@ -92,20 +78,20 @@ public class AbandonOp implements BatchUpdateOp {
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException, ResourceConflictException {
+ public boolean updateChange(ChangeContext ctx) throws ResourceConflictException {
change = ctx.getChange();
PatchSet.Id psId = change.currentPatchSetId();
ChangeUpdate update = ctx.getUpdate(psId);
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
- patchSet = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ patchSet = psUtil.get(ctx.getNotes(), psId);
change.setStatus(Change.Status.ABANDONED);
change.setLastUpdatedOn(ctx.getWhen());
update.setStatus(change.getStatus());
message = newMessage(ctx);
- cmUtil.addChangeMessage(ctx.getDb(), update, message);
+ cmUtil.addChangeMessage(update, message);
return true;
}
@@ -121,19 +107,19 @@ public class AbandonOp implements BatchUpdateOp {
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
+ NotifyResolver.Result notify = ctx.getNotify(change.getId());
try {
ReplyToChangeSender cm = abandonedSenderFactory.create(ctx.getProject(), change.getId());
if (accountState != null) {
cm.setFrom(accountState.getAccount().getId());
}
cm.setChangeMessage(message.getMessage(), ctx.getWhen());
- cm.setNotify(notifyHandling);
- cm.setAccountsToNotify(accountsToNotify);
+ cm.setNotify(notify);
cm.send();
} catch (Exception e) {
logger.atSevere().withCause(e).log("Cannot email update for change %s", change.getId());
}
- changeAbandoned.fire(change, patchSet, accountState, msgTxt, ctx.getWhen(), notifyHandling);
+ changeAbandoned.fire(change, patchSet, accountState, msgTxt, ctx.getWhen(), notify.handling());
}
}
diff --git a/java/com/google/gerrit/server/change/AbandonUtil.java b/java/com/google/gerrit/server/change/AbandonUtil.java
index f505f6d1d2..6f464984a4 100644
--- a/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -17,6 +17,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.InternalUser;
@@ -25,7 +26,6 @@ import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -96,14 +96,14 @@ public class AbandonUtil {
}
}
logger.atInfo().log("Auto-Abandoned %d of %d changes.", count, changesToAbandon.size());
- } catch (QueryParseException | OrmException e) {
+ } catch (QueryParseException | StorageException e) {
logger.atSevere().withCause(e).log(
"Failed to query inactive open changes for auto-abandoning.");
}
}
private Collection<ChangeData> getValidChanges(Collection<ChangeData> changes, String query)
- throws OrmException, QueryParseException {
+ throws QueryParseException {
Collection<ChangeData> validChanges = new ArrayList<>();
for (ChangeData cd : changes) {
String newQuery = query + " change:" + cd.getId();
diff --git a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
index 69825eaf42..fff3274447 100644
--- a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
@@ -17,8 +17,8 @@ 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.gwtorm.server.OrmException;
import java.util.Collection;
import java.util.Optional;
@@ -54,9 +54,8 @@ public interface AccountPatchReviewStore {
* @param path file path
* @return {@code true} if the reviewed flag was updated, {@code false} if the reviewed flag was
* already set
- * @throws OrmException thrown if updating the reviewed flag failed
*/
- boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) throws OrmException;
+ boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path);
/**
* Marks the given files in the given patch set as reviewed by the given user.
@@ -64,10 +63,8 @@ public interface AccountPatchReviewStore {
* @param psId patch set ID
* @param accountId account ID of the user
* @param paths file paths
- * @throws OrmException thrown if updating the reviewed flag failed
*/
- void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths)
- throws OrmException;
+ void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths);
/**
* Clears the reviewed flag for the given file in the given patch set for the given user.
@@ -75,17 +72,22 @@ public interface AccountPatchReviewStore {
* @param psId patch set ID
* @param accountId account ID of the user
* @param path file path
- * @throws OrmException thrown if clearing the reviewed flag failed
*/
- void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) throws OrmException;
+ void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path);
/**
* Clears the reviewed flags for all files in the given patch set for all users.
*
* @param psId patch set ID
- * @throws OrmException thrown if clearing the reviewed flags failed
*/
- void clearReviewed(PatchSet.Id psId) throws OrmException;
+ void clearReviewed(PatchSet.Id psId);
+
+ /**
+ * Clears the reviewed flags for all files in all patch sets in the given change for all users.
+ *
+ * @param changeId change ID
+ */
+ void clearReviewed(Change.Id changeId);
/**
* Find the latest patch set, that is smaller or equals to the given patch set, where at least,
@@ -95,8 +97,6 @@ public interface AccountPatchReviewStore {
* @param accountId account ID of the user
* @return optionally, all files the have been reviewed by the given user that belong to the patch
* set that is smaller or equals to the given patch set
- * @throws OrmException thrown if accessing the reviewed flags failed
*/
- Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId)
- throws OrmException;
+ Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId);
}
diff --git a/java/com/google/gerrit/server/change/ActionJson.java b/java/com/google/gerrit/server/change/ActionJson.java
index 0a9fe81107..d493b318f0 100644
--- a/java/com/google/gerrit/server/change/ActionJson.java
+++ b/java/com/google/gerrit/server/change/ActionJson.java
@@ -29,11 +29,9 @@ import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.extensions.webui.UiActions;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -71,7 +69,7 @@ public class ActionJson {
this.userProvider = userProvider;
}
- public Map<String, ActionInfo> format(RevisionResource rsrc) throws OrmException {
+ public Map<String, ActionInfo> format(RevisionResource rsrc) {
ChangeInfo changeInfo = null;
RevisionInfo revisionInfo = null;
List<ActionVisitor> visitors = visitors();
@@ -98,7 +96,7 @@ public class ActionJson {
}
public RevisionInfo addRevisionActions(
- @Nullable ChangeInfo changeInfo, RevisionInfo to, RevisionResource rsrc) throws OrmException {
+ @Nullable ChangeInfo changeInfo, RevisionInfo to, RevisionResource rsrc) {
List<ActionVisitor> visitors = visitors();
if (!visitors.isEmpty()) {
if (changeInfo != null) {
@@ -180,8 +178,7 @@ public class ActionJson {
// The followup action is a client-side only operation that does not
// have a server side handler. It must be manually registered into the
// resulting action map.
- Status status = notes.getChange().getStatus();
- if (status.isOpen() || status.equals(Status.MERGED)) {
+ if (!notes.getChange().isAbandoned()) {
UiAction.Description descr = new UiAction.Description();
PrivateInternals_UiActionDescription.setId(descr, "followup");
PrivateInternals_UiActionDescription.setMethod(descr, "POST");
diff --git a/java/com/google/gerrit/server/change/AddReviewersEmail.java b/java/com/google/gerrit/server/change/AddReviewersEmail.java
index 4173950a58..d9c5dade4f 100644
--- a/java/com/google/gerrit/server/change/AddReviewersEmail.java
+++ b/java/com/google/gerrit/server/change/AddReviewersEmail.java
@@ -16,12 +16,8 @@ package com.google.gerrit.server.change;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
-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.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
@@ -49,9 +45,7 @@ public class AddReviewersEmail {
Collection<Account.Id> copied,
Collection<Address> addedByEmail,
Collection<Address> copiedByEmail,
- NotifyHandling notify,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
- boolean readyForReview) {
+ NotifyResolver.Result notify) {
// The user knows they added themselves, don't bother emailing them.
Account.Id userId = user.getAccountId();
ImmutableList<Account.Id> toMail =
@@ -64,11 +58,7 @@ public class AddReviewersEmail {
try {
AddReviewerSender cm = addReviewerSenderFactory.create(change.getProject(), change.getId());
- // Default to silent operation on WIP changes.
- NotifyHandling defaultNotifyHandling =
- readyForReview ? NotifyHandling.ALL : NotifyHandling.NONE;
- cm.setNotify(MoreObjects.firstNonNull(notify, defaultNotifyHandling));
- cm.setAccountsToNotify(accountsToNotify);
+ cm.setNotify(notify);
cm.setFrom(userId);
cm.addReviewers(toMail);
cm.addReviewersByEmail(addedByEmail);
diff --git a/java/com/google/gerrit/server/change/AddReviewersOp.java b/java/com/google/gerrit/server/change/AddReviewersOp.java
index 6c0aa942db..610290dfeb 100644
--- a/java/com/google/gerrit/server/change/AddReviewersOp.java
+++ b/java/com/google/gerrit/server/change/AddReviewersOp.java
@@ -25,12 +25,8 @@ import static java.util.stream.Collectors.toList;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.mail.Address;
@@ -43,13 +39,11 @@ import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.extensions.events.ReviewerAdded;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -70,16 +64,10 @@ public class AddReviewersOp implements BatchUpdateOp {
* @param accountIds account IDs to add.
* @param addresses email addresses to add.
* @param state resulting reviewer state.
- * @param notify notification handling.
- * @param accountsToNotify additional accounts to notify.
* @return batch update operation.
*/
AddReviewersOp create(
- Set<Account.Id> accountIds,
- Collection<Address> addresses,
- ReviewerState state,
- @Nullable NotifyHandling notify,
- ListMultimap<RecipientType, Account.Id> accountsToNotify);
+ Set<Account.Id> accountIds, Collection<Address> addresses, ReviewerState state);
}
@AutoValue
@@ -116,12 +104,9 @@ public class AddReviewersOp implements BatchUpdateOp {
private final AccountCache accountCache;
private final ProjectCache projectCache;
private final AddReviewersEmail addReviewersEmail;
- private final NotesMigration migration;
private final Set<Account.Id> accountIds;
private final Collection<Address> addresses;
private final ReviewerState state;
- private final NotifyHandling notify;
- private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
// Unlike addedCCs, addedReviewers is a PatchSetApproval because the AddReviewerResult returned
// via the REST API is supposed to include vote information.
@@ -130,6 +115,7 @@ public class AddReviewersOp implements BatchUpdateOp {
private Collection<Account.Id> addedCCs = ImmutableList.of();
private Collection<Address> addedCCsByEmail = ImmutableList.of();
+ private boolean sendEmail = true;
private Change change;
private PatchSet patchSet;
private Result opResult;
@@ -142,12 +128,9 @@ public class AddReviewersOp implements BatchUpdateOp {
AccountCache accountCache,
ProjectCache projectCache,
AddReviewersEmail addReviewersEmail,
- NotesMigration migration,
@Assisted Set<Account.Id> accountIds,
@Assisted Collection<Address> addresses,
- @Assisted ReviewerState state,
- @Assisted @Nullable NotifyHandling notify,
- @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify) {
+ @Assisted ReviewerState state) {
checkArgument(state == REVIEWER || state == CC, "must be %s or %s: %s", REVIEWER, CC, state);
this.approvalsUtil = approvalsUtil;
this.psUtil = psUtil;
@@ -155,13 +138,17 @@ public class AddReviewersOp implements BatchUpdateOp {
this.accountCache = accountCache;
this.projectCache = projectCache;
this.addReviewersEmail = addReviewersEmail;
- this.migration = migration;
this.accountIds = accountIds;
this.addresses = addresses;
this.state = state;
- this.notify = notify;
- this.accountsToNotify = accountsToNotify;
+ }
+
+ // TODO(dborowitz): This mutable setter is ugly, but a) it's less ugly than adding boolean args
+ // all the way through the constructor stack, and b) this class is slated to be completely
+ // rewritten.
+ public void suppressEmail() {
+ this.sendEmail = false;
}
void setPatchSet(PatchSet patchSet) {
@@ -169,18 +156,16 @@ public class AddReviewersOp implements BatchUpdateOp {
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws RestApiException, OrmException, IOException {
+ public boolean updateChange(ChangeContext ctx) throws RestApiException, IOException {
change = ctx.getChange();
if (!accountIds.isEmpty()) {
- if (migration.readChanges() && state == CC) {
+ if (state == CC) {
addedCCs =
approvalsUtil.addCcs(
ctx.getNotes(), ctx.getUpdate(change.currentPatchSetId()), accountIds);
} else {
addedReviewers =
approvalsUtil.addReviewers(
- ctx.getDb(),
ctx.getNotes(),
ctx.getUpdate(change.currentPatchSetId()),
projectCache.checkedGet(change.getProject()).getLabelTypes(change.getDest()),
@@ -191,21 +176,21 @@ public class AddReviewersOp implements BatchUpdateOp {
ImmutableList<Address> addressesToAdd = ImmutableList.of();
ReviewerStateInternal internalState = ReviewerStateInternal.fromReviewerState(state);
- if (migration.readChanges()) {
- // TODO(dborowitz): This behavior should live in ApprovalsUtil or something, like addCcs does.
- ImmutableSet<Address> existing = ctx.getNotes().getReviewersByEmail().byState(internalState);
- addressesToAdd =
- addresses.stream().filter(a -> !existing.contains(a)).collect(toImmutableList());
- if (state == CC) {
- addedCCsByEmail = addressesToAdd;
- } else {
- addedReviewersByEmail = addressesToAdd;
- }
- for (Address a : addressesToAdd) {
- ctx.getUpdate(change.currentPatchSetId()).putReviewerByEmail(a, internalState);
- }
+ // TODO(dborowitz): This behavior should live in ApprovalsUtil or something, like addCcs does.
+ ImmutableSet<Address> existing = ctx.getNotes().getReviewersByEmail().byState(internalState);
+ addressesToAdd =
+ addresses.stream().filter(a -> !existing.contains(a)).collect(toImmutableList());
+
+ if (state == CC) {
+ addedCCsByEmail = addressesToAdd;
+ } else {
+ addedReviewersByEmail = addressesToAdd;
+ }
+ for (Address a : addressesToAdd) {
+ ctx.getUpdate(change.currentPatchSetId()).putReviewerByEmail(a, internalState);
}
+
if (addedCCs.isEmpty() && addedReviewers.isEmpty() && addressesToAdd.isEmpty()) {
return false;
}
@@ -213,7 +198,7 @@ public class AddReviewersOp implements BatchUpdateOp {
checkAdded();
if (patchSet == null) {
- patchSet = requireNonNull(psUtil.current(ctx.getDb(), ctx.getNotes()));
+ patchSet = requireNonNull(psUtil.current(ctx.getNotes()));
}
return true;
}
@@ -251,16 +236,16 @@ public class AddReviewersOp implements BatchUpdateOp {
.setAddedCCs(addedCCs)
.setAddedCCsByEmail(addedCCsByEmail)
.build();
- addReviewersEmail.emailReviewers(
- ctx.getUser().asIdentifiedUser(),
- change,
- Lists.transform(addedReviewers, PatchSetApproval::getAccountId),
- addedCCs,
- addedReviewersByEmail,
- addedCCsByEmail,
- notify,
- accountsToNotify,
- !change.isWorkInProgress());
+ if (sendEmail) {
+ addReviewersEmail.emailReviewers(
+ ctx.getUser().asIdentifiedUser(),
+ change,
+ Lists.transform(addedReviewers, PatchSetApproval::getAccountId),
+ addedCCs,
+ addedReviewersByEmail,
+ addedCCsByEmail,
+ ctx.getNotify(change.getId()));
+ }
if (!addedReviewers.isEmpty()) {
List<AccountState> reviewers =
addedReviewers.stream()
diff --git a/java/com/google/gerrit/server/change/BatchAbandon.java b/java/com/google/gerrit/server/change/BatchAbandon.java
index a8e2407e84..bc29c5dc54 100644
--- a/java/com/google/gerrit/server/change/BatchAbandon.java
+++ b/java/com/google/gerrit/server/change/BatchAbandon.java
@@ -14,35 +14,35 @@
package com.google.gerrit.server.change;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.api.changes.RecipientType;
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.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.ChangeCleanupConfig;
+import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
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.util.Collection;
@Singleton
public class BatchAbandon {
- private final Provider<ReviewDb> dbProvider;
private final AbandonOp.Factory abandonOpFactory;
+ private final ChangeCleanupConfig cfg;
+ private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
@Inject
- BatchAbandon(Provider<ReviewDb> dbProvider, AbandonOp.Factory abandonOpFactory) {
- this.dbProvider = dbProvider;
+ BatchAbandon(
+ AbandonOp.Factory abandonOpFactory,
+ ChangeCleanupConfig cfg,
+ PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
this.abandonOpFactory = abandonOpFactory;
+ this.cfg = cfg;
+ this.accountPatchReviewStore = accountPatchReviewStore;
}
/**
@@ -58,14 +58,14 @@ public class BatchAbandon {
CurrentUser user,
Collection<ChangeData> changes,
String msgTxt,
- NotifyHandling notifyHandling,
- ListMultimap<RecipientType, Account.Id> accountsToNotify)
+ NotifyResolver.Result notify)
throws RestApiException, UpdateException {
if (changes.isEmpty()) {
return;
}
AccountState accountState = user.isIdentifiedUser() ? user.asIdentifiedUser().state() : null;
- try (BatchUpdate u = updateFactory.create(dbProvider.get(), project, user, TimeUtil.nowTs())) {
+ try (BatchUpdate u = updateFactory.create(project, user, TimeUtil.nowTs())) {
+ u.setNotify(notify);
for (ChangeData change : changes) {
if (!project.equals(change.project())) {
throw new ResourceConflictException(
@@ -73,11 +73,13 @@ public class BatchAbandon {
"Project name \"%s\" doesn't match \"%s\"",
change.project().get(), project.get()));
}
- u.addOp(
- change.getId(),
- abandonOpFactory.create(accountState, msgTxt, notifyHandling, accountsToNotify));
+ u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
}
u.execute();
+
+ if (cfg.getCleanupAccountPatchReview()) {
+ cleanupAccountPatchReview(changes);
+ }
}
}
@@ -88,14 +90,7 @@ public class BatchAbandon {
Collection<ChangeData> changes,
String msgTxt)
throws RestApiException, UpdateException {
- batchAbandon(
- updateFactory,
- project,
- user,
- changes,
- msgTxt,
- NotifyHandling.ALL,
- ImmutableListMultimap.of());
+ batchAbandon(updateFactory, project, user, changes, msgTxt, NotifyResolver.Result.all());
}
public void batchAbandon(
@@ -104,7 +99,12 @@ public class BatchAbandon {
CurrentUser user,
Collection<ChangeData> changes)
throws RestApiException, UpdateException {
- batchAbandon(
- updateFactory, project, user, changes, "", NotifyHandling.ALL, ImmutableListMultimap.of());
+ batchAbandon(updateFactory, project, user, changes, "", NotifyResolver.Result.all());
+ }
+
+ private void cleanupAccountPatchReview(Collection<ChangeData> changes) {
+ for (ChangeData change : changes) {
+ accountPatchReviewStore.run(s -> s.clearReviewed(change.getId()));
+ }
}
}
diff --git a/java/com/google/gerrit/server/change/ChangeAttributeFactory.java b/java/com/google/gerrit/server/change/ChangeAttributeFactory.java
new file mode 100644
index 0000000000..95355cf230
--- /dev/null
+++ b/java/com/google/gerrit/server/change/ChangeAttributeFactory.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.server.change;
+
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
+import com.google.gerrit.server.DynamicOptions.BeanProvider;
+import com.google.gerrit.server.query.change.ChangeData;
+
+/**
+ * Interface for plugins to provide additional fields in {@link
+ * com.google.gerrit.extensions.common.ChangeInfo ChangeInfo}.
+ *
+ * <p>Register a {@code ChangeAttributeFactory} in a plugin {@code Module} like this:
+ *
+ * <pre>
+ * DynamicSet.bind(binder(), ChangeAttributeFactory.class).to(YourClass.class);
+ * </pre>
+ *
+ * <p>See the <a
+ * href="https://gerrit-review.googlesource.com/Documentation/dev-plugins.html#query_attributes">plugin
+ * developer documentation for more details and examples.
+ */
+public interface ChangeAttributeFactory {
+
+ /**
+ * Create a plugin-provided info field.
+ *
+ * <p>Typically, implementations will subclass {@code PluginDefinedInfo} to add additional fields.
+ *
+ * @param cd change.
+ * @param beanProvider provider of {@code DynamicBean}s, which may be used for reading options.
+ * @param plugin plugin name.
+ * @return the plugin's special change info.
+ */
+ PluginDefinedInfo create(ChangeData cd, BeanProvider beanProvider, String plugin);
+}
diff --git a/java/com/google/gerrit/server/change/ChangeCleanupRunner.java b/java/com/google/gerrit/server/change/ChangeCleanupRunner.java
index b24d3cee45..0299f1096e 100644
--- a/java/com/google/gerrit/server/change/ChangeCleanupRunner.java
+++ b/java/com/google/gerrit/server/change/ChangeCleanupRunner.java
@@ -24,7 +24,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
/** Runnable to enable scheduling change cleanups to run periodically */
@@ -85,7 +84,7 @@ public class ChangeCleanupRunner implements Runnable {
abandonUtil.abandonInactiveOpenChanges(updateFactory);
return null;
});
- } catch (RestApiException | UpdateException | OrmException e) {
+ } catch (RestApiException | UpdateException e) {
logger.atSevere().withCause(e).log("Failed to cleanup changes.");
}
}
diff --git a/java/com/google/gerrit/server/change/ChangeFinder.java b/java/com/google/gerrit/server/change/ChangeFinder.java
index 4e30b031a8..751d4b89b8 100644
--- a/java/com/google/gerrit/server/change/ChangeFinder.java
+++ b/java/com/google/gerrit/server/change/ChangeFinder.java
@@ -19,6 +19,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.metrics.Counter1;
@@ -28,13 +29,11 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
@@ -72,7 +71,6 @@ public class ChangeFinder {
private final IndexConfig indexConfig;
private final Cache<Change.Id, String> changeIdProjectCache;
private final Provider<InternalChangeQuery> queryProvider;
- private final Provider<ReviewDb> reviewDb;
private final ChangeNotes.Factory changeNotesFactory;
private final Counter1<ChangeIdType> changeIdCounter;
@@ -81,13 +79,11 @@ public class ChangeFinder {
IndexConfig indexConfig,
@Named(CACHE_NAME) Cache<Change.Id, String> changeIdProjectCache,
Provider<InternalChangeQuery> queryProvider,
- Provider<ReviewDb> reviewDb,
ChangeNotes.Factory changeNotesFactory,
MetricMaker metricMaker) {
this.indexConfig = indexConfig;
this.changeIdProjectCache = changeIdProjectCache;
this.queryProvider = queryProvider;
- this.reviewDb = reviewDb;
this.changeNotesFactory = changeNotesFactory;
this.changeIdCounter =
metricMaker.newCounter(
@@ -98,7 +94,7 @@ public class ChangeFinder {
Field.ofEnum(ChangeIdType.class, "change_id_type"));
}
- public ChangeNotes findOne(String id) throws OrmException {
+ public ChangeNotes findOne(String id) {
List<ChangeNotes> ctls = find(id);
if (ctls.size() != 1) {
return null;
@@ -111,9 +107,8 @@ public class ChangeFinder {
*
* @param id change identifier.
* @return possibly-empty list of notes for all matching changes; may or may not be visible.
- * @throws OrmException if an error occurred querying the database
*/
- public List<ChangeNotes> find(String id) throws OrmException {
+ public List<ChangeNotes> find(String id) {
if (id.isEmpty()) {
return Collections.emptyList();
}
@@ -166,17 +161,16 @@ public class ChangeFinder {
return notes;
}
- private List<ChangeNotes> fromProjectNumber(String project, int changeNumber)
- throws OrmException {
+ private List<ChangeNotes> fromProjectNumber(String project, int changeNumber) {
Change.Id cId = new Change.Id(changeNumber);
try {
return ImmutableList.of(
- changeNotesFactory.createChecked(reviewDb.get(), Project.NameKey.parse(project), cId));
+ changeNotesFactory.createChecked(Project.NameKey.parse(project), cId));
} catch (NoSuchChangeException e) {
return Collections.emptyList();
- } catch (OrmException e) {
+ } catch (StorageException e) {
// Distinguish between a RepositoryNotFoundException (project argument invalid) and
- // other OrmExceptions (failure in the persistence layer).
+ // other StorageExceptions (failure in the persistence layer).
if (Throwables.getRootCause(e) instanceof RepositoryNotFoundException) {
return Collections.emptyList();
}
@@ -184,7 +178,7 @@ public class ChangeFinder {
}
}
- public ChangeNotes findOne(Change.Id id) throws OrmException {
+ public ChangeNotes findOne(Change.Id id) {
List<ChangeNotes> notes = find(id);
if (notes.size() != 1) {
throw new NoSuchChangeException(id);
@@ -192,7 +186,7 @@ public class ChangeFinder {
return notes.get(0);
}
- public List<ChangeNotes> find(Change.Id id) throws OrmException {
+ public List<ChangeNotes> find(Change.Id id) {
String project = changeIdProjectCache.getIfPresent(id);
if (project != null) {
return fromProjectNumber(project, id.get());
@@ -208,7 +202,7 @@ public class ChangeFinder {
return asChangeNotes(r);
}
- private List<ChangeNotes> asChangeNotes(List<ChangeData> cds) throws OrmException {
+ private List<ChangeNotes> asChangeNotes(List<ChangeData> cds) {
List<ChangeNotes> notes = new ArrayList<>(cds.size());
if (!indexConfig.separateChangeSubIndexes()) {
for (ChangeData cd : cds) {
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index 4e69fbeaa5..a3b5db0e4e 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -24,16 +24,13 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.Streams;
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.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -45,7 +42,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil;
@@ -73,7 +69,6 @@ import com.google.gerrit.server.update.InsertChangeOp;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -124,8 +119,6 @@ public class ChangeInserter implements InsertChangeOp {
private boolean workInProgress;
private List<String> groups = Collections.emptyList();
private boolean validate = true;
- private NotifyHandling notify = NotifyHandling.ALL;
- private ListMultimap<RecipientType, Account.Id> accountsToNotify = ImmutableListMultimap.of();
private Map<String, Short> approvals;
private RequestScopePropagator requestScopePropagator;
private boolean fireRevisionCreated;
@@ -249,17 +242,6 @@ public class ChangeInserter implements InsertChangeOp {
return this;
}
- public ChangeInserter setNotify(NotifyHandling notify) {
- this.notify = notify;
- return this;
- }
-
- public ChangeInserter setAccountsToNotify(
- ListMultimap<RecipientType, Account.Id> accountsToNotify) {
- this.accountsToNotify = requireNonNull(accountsToNotify);
- return this;
- }
-
public ChangeInserter setReviewersAndCcs(
Iterable<Account.Id> reviewers, Iterable<Account.Id> ccs) {
return setReviewersAndCcsAsStrings(
@@ -379,10 +361,8 @@ public class ChangeInserter implements InsertChangeOp {
@Override
public boolean updateChange(ChangeContext ctx)
- throws RestApiException, OrmException, IOException, PermissionBackendException,
- ConfigInvalidException {
+ throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
change = ctx.getChange(); // Use defensive copy created by ChangeControl.
- ReviewDb db = ctx.getDb();
patchSetInfo =
patchSetInfoFactory.get(ctx.getRevWalk(), ctx.getRevWalk().parseCommit(commitId), psId);
ctx.getChange().setCurrentPatchSet(patchSetInfo);
@@ -405,14 +385,7 @@ public class ChangeInserter implements InsertChangeOp {
}
patchSet =
psUtil.insert(
- ctx.getDb(),
- ctx.getRevWalk(),
- update,
- psId,
- commitId,
- newGroups,
- pushCert,
- patchSetDescription);
+ ctx.getRevWalk(), update, psId, commitId, newGroups, pushCert, patchSetDescription);
/* TODO: fixStatus is used here because the tests
* (byStatusClosed() in AbstractQueryChangesTest)
@@ -425,8 +398,7 @@ public class ChangeInserter implements InsertChangeOp {
update.fixStatus(change.getStatus());
reviewerAdditions =
- reviewerAdder.prepare(
- ctx.getDb(), ctx.getNotes(), ctx.getUser(), getReviewerInputs(), true);
+ reviewerAdder.prepare(ctx.getNotes(), ctx.getUser(), getReviewerInputs(), true);
Optional<ReviewerAddition> reviewerError = reviewerAdditions.getFailures().stream().findFirst();
if (reviewerError.isPresent()) {
throw new UnprocessableEntityException(reviewerError.get().result.error);
@@ -435,7 +407,7 @@ public class ChangeInserter implements InsertChangeOp {
LabelTypes labelTypes = projectState.getLabelTypes();
approvalsUtil.addApprovalsForNewPatchSet(
- db, update, labelTypes, patchSet, ctx.getUser(), approvals);
+ update, labelTypes, patchSet, ctx.getUser(), approvals);
// Check if approvals are changing in with this update. If so, add current user to reviewers.
// Note that this is done separately as addReviewers is filtering out the change owner as
@@ -452,7 +424,7 @@ public class ChangeInserter implements InsertChangeOp {
patchSet.getCreatedOn(),
message,
ChangeMessagesUtil.uploadedPatchSetTag(workInProgress));
- cmUtil.addChangeMessage(db, update, changeMessage);
+ cmUtil.addChangeMessage(update, changeMessage);
}
return true;
}
@@ -460,7 +432,8 @@ public class ChangeInserter implements InsertChangeOp {
@Override
public void postUpdate(Context ctx) throws Exception {
reviewerAdditions.postUpdate(ctx);
- if (sendMail && (notify != NotifyHandling.NONE || !accountsToNotify.isEmpty())) {
+ NotifyResolver.Result notify = ctx.getNotify(change.getId());
+ if (sendMail && notify.shouldNotify()) {
Runnable sender =
new Runnable() {
@Override
@@ -471,7 +444,6 @@ public class ChangeInserter implements InsertChangeOp {
cm.setFrom(change.getOwner());
cm.setPatchSet(patchSet, patchSetInfo);
cm.setNotify(notify);
- cm.setAccountsToNotify(accountsToNotify);
cm.addReviewers(
reviewerAdditions.flattenResults(AddReviewersOp.Result::addedReviewers).stream()
.map(PatchSetApproval::getAccountId)
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 2aaf4c513c..1008a307d3 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -48,6 +48,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.ReviewerState;
@@ -74,7 +75,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GpgException;
@@ -84,6 +84,7 @@ import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.account.AccountInfoComparator;
import com.google.gerrit.server.account.AccountLoader;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -96,8 +97,6 @@ import com.google.gerrit.server.project.RemoveReviewerControl;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
-import com.google.gerrit.server.query.change.PluginDefinedAttributesFactory;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -112,6 +111,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
+import org.eclipse.jgit.lib.Config;
/**
* Produces {@link ChangeInfo} (which is serialized to JSON afterwards) from {@link ChangeData}.
@@ -202,7 +202,6 @@ public class ChangeJson {
}
}
- private final Provider<ReviewDb> db;
private final Provider<CurrentUser> userProvider;
private final PermissionBackend permissionBackend;
private final ChangeData.Factory changeDataFactory;
@@ -218,6 +217,7 @@ public class ChangeJson {
private final Metrics metrics;
private final RevisionJson revisionJson;
private final Optional<PluginDefinedAttributesFactory> pluginDefinedAttributesFactory;
+ private final boolean excludeMergeableInChangeInfo;
private final boolean lazyLoad;
private AccountLoader accountLoader;
@@ -225,7 +225,6 @@ public class ChangeJson {
@Inject
ChangeJson(
- Provider<ReviewDb> db,
Provider<CurrentUser> user,
PermissionBackend permissionBackend,
ChangeData.Factory cdf,
@@ -239,9 +238,9 @@ public class ChangeJson {
TrackingFooters trackingFooters,
Metrics metrics,
RevisionJson.Factory revisionJsonFactory,
+ @GerritServerConfig Config cfg,
@Assisted Iterable<ListChangesOption> options,
@Assisted Optional<PluginDefinedAttributesFactory> pluginDefinedAttributesFactory) {
- this.db = db;
this.userProvider = user;
this.changeDataFactory = cdf;
this.permissionBackend = permissionBackend;
@@ -256,6 +255,8 @@ public class ChangeJson {
this.metrics = metrics;
this.revisionJson = revisionJsonFactory.create(options);
this.options = Sets.immutableEnumSet(options);
+ this.excludeMergeableInChangeInfo =
+ cfg.getBoolean("change", "api", "excludeMergeableInChangeInfo", false);
this.lazyLoad = containsAnyOf(this.options, REQUIRE_LAZY_LOAD);
this.pluginDefinedAttributesFactory = pluginDefinedAttributesFactory;
@@ -267,24 +268,24 @@ public class ChangeJson {
return this;
}
- public ChangeInfo format(ChangeResource rsrc) throws OrmException {
- return format(changeDataFactory.create(db.get(), rsrc.getNotes()));
+ public ChangeInfo format(ChangeResource rsrc) {
+ return format(changeDataFactory.create(rsrc.getNotes()));
}
- public ChangeInfo format(Change change) throws OrmException {
- return format(changeDataFactory.create(db.get(), change));
+ public ChangeInfo format(Change change) {
+ return format(changeDataFactory.create(change));
}
- public ChangeInfo format(Project.NameKey project, Change.Id id) throws OrmException {
+ public ChangeInfo format(Project.NameKey project, Change.Id id) {
return format(project, id, ChangeInfo::new);
}
- public ChangeInfo format(ChangeData cd) throws OrmException {
+ public ChangeInfo format(ChangeData cd) {
return format(cd, Optional.empty(), true, ChangeInfo::new);
}
- public ChangeInfo format(RevisionResource rsrc) throws OrmException {
- ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
+ public ChangeInfo format(RevisionResource rsrc) {
+ ChangeData cd = changeDataFactory.create(rsrc.getNotes());
return format(cd, Optional.of(rsrc.getPatchSet().getId()), true, ChangeInfo::new);
}
@@ -306,8 +307,7 @@ public class ChangeJson {
}
}
- public List<ChangeInfo> format(Collection<ChangeData> in)
- throws OrmException, PermissionBackendException {
+ public List<ChangeInfo> format(Collection<ChangeData> in) throws PermissionBackendException {
accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
ensureLoaded(in);
List<ChangeInfo> out = new ArrayList<>(in.size());
@@ -319,18 +319,17 @@ public class ChangeJson {
}
public <I extends ChangeInfo> I format(
- Project.NameKey project, Change.Id id, Supplier<I> changeInfoSupplier) throws OrmException {
+ Project.NameKey project, Change.Id id, Supplier<I> changeInfoSupplier) {
ChangeNotes notes;
try {
- notes = notesFactory.createChecked(db.get(), project, id);
- } catch (OrmException e) {
+ notes = notesFactory.createChecked(project, id);
+ } catch (StorageException e) {
if (!has(CHECK)) {
throw e;
}
- return checkOnly(changeDataFactory.create(db.get(), project, id), changeInfoSupplier);
+ return checkOnly(changeDataFactory.create(project, id), changeInfoSupplier);
}
- return format(
- changeDataFactory.create(db.get(), notes), Optional.empty(), true, changeInfoSupplier);
+ return format(changeDataFactory.create(notes), Optional.empty(), true, changeInfoSupplier);
}
private static Collection<SubmitRequirementInfo> requirementsFor(ChangeData cd) {
@@ -365,8 +364,7 @@ public class ChangeJson {
ChangeData cd,
Optional<PatchSet.Id> limitToPsId,
boolean fillAccountLoader,
- Supplier<I> changeInfoSupplier)
- throws OrmException {
+ Supplier<I> changeInfoSupplier) {
try {
if (fillAccountLoader) {
accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
@@ -377,19 +375,18 @@ public class ChangeJson {
return toChangeInfo(cd, limitToPsId, changeInfoSupplier);
} catch (PatchListNotAvailableException
| GpgException
- | OrmException
| IOException
| PermissionBackendException
| RuntimeException e) {
if (!has(CHECK)) {
- Throwables.throwIfInstanceOf(e, OrmException.class);
- throw new OrmException(e);
+ Throwables.throwIfInstanceOf(e, StorageException.class);
+ throw new StorageException(e);
}
return checkOnly(cd, changeInfoSupplier);
}
}
- private void ensureLoaded(Iterable<ChangeData> all) throws OrmException {
+ private void ensureLoaded(Iterable<ChangeData> all) {
if (lazyLoad) {
ChangeData.ensureChangeLoaded(all);
if (has(ALL_REVISIONS)) {
@@ -439,7 +436,7 @@ public class ChangeJson {
if (isCacheable) {
cache.put(new Change.Id(info._number), info);
}
- } catch (OrmException | RuntimeException e) {
+ } catch (RuntimeException e) {
logger.atWarning().withCause(e).log(
"Omitting corrupt change %s from results", cd.getId());
}
@@ -452,7 +449,7 @@ public class ChangeJson {
ChangeNotes notes;
try {
notes = cd.notes();
- } catch (OrmException e) {
+ } catch (StorageException e) {
String msg = "Error loading change";
logger.atWarning().withCause(e).log(msg + " %s", cd.getId());
I info = changeInfoSupplier.get();
@@ -491,8 +488,7 @@ public class ChangeJson {
private <I extends ChangeInfo> I toChangeInfo(
ChangeData cd, Optional<PatchSet.Id> limitToPsId, Supplier<I> changeInfoSupplier)
- throws PatchListNotAvailableException, GpgException, OrmException, PermissionBackendException,
- IOException {
+ throws PatchListNotAvailableException, GpgException, PermissionBackendException, IOException {
try (Timer0.Context ignored = metrics.toChangeInfoLatency.start()) {
return toChangeInfoImpl(cd, limitToPsId, changeInfoSupplier);
}
@@ -500,8 +496,7 @@ public class ChangeJson {
private <I extends ChangeInfo> I toChangeInfoImpl(
ChangeData cd, Optional<PatchSet.Id> limitToPsId, Supplier<I> changeInfoSupplier)
- throws PatchListNotAvailableException, GpgException, OrmException, PermissionBackendException,
- IOException {
+ throws PatchListNotAvailableException, GpgException, PermissionBackendException, IOException {
I out = changeInfoSupplier.get();
CurrentUser user = userProvider.get();
@@ -510,7 +505,7 @@ public class ChangeJson {
// If any problems were fixed, the ChangeData needs to be reloaded.
for (ProblemInfo p : out.problems) {
if (p.status == ProblemInfo.Status.FIXED) {
- cd = changeDataFactory.create(cd.db(), cd.project(), cd.getId());
+ cd = changeDataFactory.create(cd.project(), cd.getId());
break;
}
}
@@ -523,12 +518,12 @@ public class ChangeJson {
out.assignee = in.getAssignee() != null ? accountLoader.get(in.getAssignee()) : null;
out.hashtags = cd.hashtags();
out.changeId = in.getKey().get();
- if (in.getStatus().isOpen()) {
+ if (in.isNew()) {
SubmitTypeRecord str = cd.submitTypeRecord();
if (str.isOk()) {
out.submitType = str.type;
}
- if (!has(SKIP_MERGEABLE)) {
+ if (!excludeMergeableInChangeInfo && !has(SKIP_MERGEABLE)) {
out.mergeable = cd.isMergeable();
}
if (has(SUBMITTABLE)) {
@@ -549,6 +544,7 @@ public class ChangeJson {
out.created = in.getCreatedOn();
out.updated = in.getLastUpdatedOn();
out._number = in.getId().get();
+ out.totalCommentCount = cd.totalCommentCount();
out.unresolvedCommentCount = cd.unresolvedCommentCount();
if (user.isIdentifiedUser()) {
@@ -559,7 +555,7 @@ public class ChangeJson {
}
}
- if (in.getStatus().isOpen() && has(REVIEWED) && user.isIdentifiedUser()) {
+ if (in.isNew() && has(REVIEWED) && user.isIdentifiedUser()) {
out.reviewed = cd.isReviewedBy(user.getAccountId()) ? true : null;
}
@@ -572,7 +568,7 @@ public class ChangeJson {
if (user.isIdentifiedUser()
&& (!limitToPsId.isPresent() || limitToPsId.get().equals(in.currentPatchSetId()))) {
out.permittedLabels =
- cd.change().getStatus() != Change.Status.ABANDONED
+ !cd.change().isAbandoned()
? labelsJson.permittedLabels(user.getAccountId(), cd)
: ImmutableMap.of();
}
@@ -651,7 +647,7 @@ public class ChangeJson {
return reviewerMap;
}
- private Collection<ReviewerUpdateInfo> reviewerUpdates(ChangeData cd) throws OrmException {
+ private Collection<ReviewerUpdateInfo> reviewerUpdates(ChangeData cd) {
List<ReviewerStatusUpdate> reviewerUpdates = cd.reviewerUpdates();
List<ReviewerUpdateInfo> result = new ArrayList<>(reviewerUpdates.size());
for (ReviewerStatusUpdate c : reviewerUpdates) {
@@ -669,7 +665,7 @@ public class ChangeJson {
return SubmitRecord.allRecordsOK(cd.submitRecords(SUBMIT_RULE_OPTIONS_STRICT));
}
- private void setSubmitter(ChangeData cd, ChangeInfo out) throws OrmException {
+ private void setSubmitter(ChangeData cd, ChangeInfo out) {
Optional<PatchSetApproval> s = cd.getSubmitApproval();
if (!s.isPresent()) {
return;
@@ -678,8 +674,8 @@ public class ChangeJson {
out.submitter = accountLoader.get(s.get().getAccountId());
}
- private Collection<ChangeMessageInfo> messages(ChangeData cd) throws OrmException {
- List<ChangeMessage> messages = cmUtil.byChange(db.get(), cd.notes());
+ private Collection<ChangeMessageInfo> messages(ChangeData cd) {
+ List<ChangeMessage> messages = cmUtil.byChange(cd.notes());
if (messages.isEmpty()) {
return Collections.emptyList();
}
@@ -692,7 +688,7 @@ public class ChangeJson {
}
private Collection<AccountInfo> removableReviewers(ChangeData cd, ChangeInfo out)
- throws PermissionBackendException, OrmException {
+ throws PermissionBackendException {
// Although this is called removableReviewers, this method also determines
// which CCs are removable.
//
@@ -780,8 +776,8 @@ public class ChangeJson {
.collect(toList());
}
- private Map<PatchSet.Id, PatchSet> loadPatchSets(ChangeData cd, Optional<PatchSet.Id> limitToPsId)
- throws OrmException {
+ private Map<PatchSet.Id, PatchSet> loadPatchSets(
+ ChangeData cd, Optional<PatchSet.Id> limitToPsId) {
Collection<PatchSet> src;
if (has(ALL_REVISIONS) || has(MESSAGES)) {
src = cd.patchSets();
@@ -790,12 +786,12 @@ public class ChangeJson {
if (limitToPsId.isPresent()) {
ps = cd.patchSet(limitToPsId.get());
if (ps == null) {
- throw new OrmException("missing patch set " + limitToPsId.get());
+ throw new StorageException("missing patch set " + limitToPsId.get());
}
} else {
ps = cd.currentPatchSet();
if (ps == null) {
- throw new OrmException("missing current patch set for change " + cd.getId());
+ throw new StorageException("missing current patch set for change " + cd.getId());
}
}
src = Collections.singletonList(ps);
@@ -812,9 +808,8 @@ public class ChangeJson {
* from either an index-backed or a database-backed {@link ChangeData} depending on {@code
* lazyload}.
*/
- private PermissionBackend.ForChange permissionBackendForChange(CurrentUser user, ChangeData cd)
- throws OrmException {
- PermissionBackend.WithUser withUser = permissionBackend.user(user).database(db);
+ private PermissionBackend.ForChange permissionBackendForChange(CurrentUser user, ChangeData cd) {
+ PermissionBackend.WithUser withUser = permissionBackend.user(user);
return lazyLoad
? withUser.change(cd)
: withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
diff --git a/java/com/google/gerrit/server/change/ChangeKindCache.java b/java/com/google/gerrit/server/change/ChangeKindCache.java
index 6baeefc145..44da4d6094 100644
--- a/java/com/google/gerrit/server/change/ChangeKindCache.java
+++ b/java/com/google/gerrit/server/change/ChangeKindCache.java
@@ -19,7 +19,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.query.change.ChangeData;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
@@ -39,7 +38,7 @@ public interface ChangeKindCache {
ObjectId prior,
ObjectId next);
- ChangeKind getChangeKind(ReviewDb db, Change change, PatchSet patch);
+ ChangeKind getChangeKind(Change change, PatchSet patch);
ChangeKind getChangeKind(
@Nullable RevWalk rw, @Nullable Config repoConfig, ChangeData cd, PatchSet patch);
diff --git a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index 49a16ec06a..c20c0a27d0 100644
--- a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -23,23 +23,22 @@ 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache.ChangeKindKeyProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
import com.google.gerrit.server.cache.serialize.EnumCacheSerializer;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
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.MergeUtil;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.name.Named;
@@ -80,7 +79,6 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
};
}
- @VisibleForTesting
public static class NoCache implements ChangeKindCache {
private final boolean useRecursiveMerge;
private final ChangeData.Factory changeDataFactory;
@@ -114,8 +112,8 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
}
@Override
- public ChangeKind getChangeKind(ReviewDb db, Change change, PatchSet patch) {
- return getChangeKindInternal(this, db, change, patch, changeDataFactory, repoManager);
+ public ChangeKind getChangeKind(Change change, PatchSet patch) {
+ return getChangeKindInternal(this, change, patch, changeDataFactory, repoManager);
}
@Override
@@ -146,7 +144,7 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
@Override
public byte[] serialize(Key object) {
ObjectIdConverter idConverter = ObjectIdConverter.create();
- return ProtoCacheSerializers.toByteArray(
+ return Protos.toByteArray(
ChangeKindKeyProto.newBuilder()
.setPrior(idConverter.toByteString(object.prior()))
.setNext(idConverter.toByteString(object.next()))
@@ -156,8 +154,7 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
@Override
public Key deserialize(byte[] in) {
- ChangeKindKeyProto proto =
- ProtoCacheSerializers.parseUnchecked(ChangeKindKeyProto.parser(), in);
+ ChangeKindKeyProto proto = Protos.parseUnchecked(ChangeKindKeyProto.parser(), in);
ObjectIdConverter idConverter = ObjectIdConverter.create();
return create(
idConverter.fromByteString(proto.getPrior()),
@@ -358,8 +355,8 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
}
@Override
- public ChangeKind getChangeKind(ReviewDb db, Change change, PatchSet patch) {
- return getChangeKindInternal(this, db, change, patch, changeDataFactory, repoManager);
+ public ChangeKind getChangeKind(Change change, PatchSet patch) {
+ return getChangeKindInternal(this, change, patch, changeDataFactory, repoManager);
}
@Override
@@ -402,7 +399,7 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
ObjectId.fromString(priorPs.getRevision().get()),
ObjectId.fromString(patch.getRevision().get()));
}
- } catch (OrmException e) {
+ } 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",
@@ -417,7 +414,6 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
private static ChangeKind getChangeKindInternal(
ChangeKindCache cache,
- ReviewDb db,
Change change,
PatchSet patch,
ChangeData.Factory changeDataFactory,
@@ -431,7 +427,7 @@ public class ChangeKindCacheImpl implements ChangeKindCache {
RevWalk rw = new RevWalk(repo)) {
kind =
getChangeKindInternal(
- cache, rw, repo.getConfig(), changeDataFactory.create(db, change), patch);
+ cache, rw, repo.getConfig(), changeDataFactory.create(change), patch);
} catch (IOException e) {
// Do nothing; assume we have a complex change
logger.atWarning().withCause(e).log(
diff --git a/java/com/google/gerrit/server/change/ChangeMessages.java b/java/com/google/gerrit/server/change/ChangeMessages.java
index 41b685550d..6cd3726ae5 100644
--- a/java/com/google/gerrit/server/change/ChangeMessages.java
+++ b/java/com/google/gerrit/server/change/ChangeMessages.java
@@ -25,9 +25,7 @@ public class ChangeMessages extends TranslationBundle {
public String revertChangeDefaultMessage;
public String reviewerCantSeeChange;
- public String reviewerInactive;
public String reviewerInvalid;
- public String reviewerNotFoundUser;
public String reviewerNotFoundUserOrGroup;
public String groupIsNotAllowed;
diff --git a/java/com/google/gerrit/server/change/ChangeResource.java b/java/com/google/gerrit/server/change/ChangeResource.java
index d61d56f538..98b728f167 100644
--- a/java/com/google/gerrit/server/change/ChangeResource.java
+++ b/java/com/google/gerrit/server/change/ChangeResource.java
@@ -21,6 +21,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestResource.HasETag;
import com.google.gerrit.extensions.restapi.RestView;
@@ -29,7 +30,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -40,9 +40,7 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -71,7 +69,6 @@ public class ChangeResource implements RestResource, HasETag {
private static final String ZERO_ID_STRING = ObjectId.zeroId().name();
- private final Provider<ReviewDb> db;
private final AccountCache accountCache;
private final ApprovalsUtil approvalUtil;
private final PatchSetUtil patchSetUtil;
@@ -83,7 +80,6 @@ public class ChangeResource implements RestResource, HasETag {
@Inject
ChangeResource(
- Provider<ReviewDb> db,
AccountCache accountCache,
ApprovalsUtil approvalUtil,
PatchSetUtil patchSetUtil,
@@ -92,7 +88,6 @@ public class ChangeResource implements RestResource, HasETag {
ProjectCache projectCache,
@Assisted ChangeNotes notes,
@Assisted CurrentUser user) {
- this.db = db;
this.accountCache = accountCache;
this.approvalUtil = approvalUtil;
this.patchSetUtil = patchSetUtil;
@@ -104,7 +99,7 @@ public class ChangeResource implements RestResource, HasETag {
}
public PermissionBackend.ForChange permissions() {
- return permissionBackend.user(user).database(db).change(notes);
+ return permissionBackend.user(user).change(notes);
}
public CurrentUser getUser() {
@@ -154,9 +149,7 @@ public class ChangeResource implements RestResource, HasETag {
accounts.add(getChange().getAssignee());
}
try {
- patchSetUtil.byChange(db.get(), notes).stream()
- .map(PatchSet::getUploader)
- .forEach(accounts::add);
+ patchSetUtil.byChange(notes).stream().map(PatchSet::getUploader).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.
@@ -165,8 +158,8 @@ public class ChangeResource implements RestResource, HasETag {
// set of accounts that posted a message is too expensive. However everyone who posts a
// message is automatically added as reviewer. Hence if we include removed reviewers we can
// be sure that we have all accounts that posted messages on the change.
- accounts.addAll(approvalUtil.getReviewers(db.get(), notes).all());
- } catch (OrmException e) {
+ accounts.addAll(approvalUtil.getReviewers(notes).all());
+ } catch (StorageException e) {
// This ETag will be invalidated if it loads next time.
}
@@ -182,7 +175,7 @@ public class ChangeResource implements RestResource, HasETag {
ObjectId noteId;
try {
noteId = notes.loadRevision();
- } catch (OrmException e) {
+ } catch (StorageException e) {
noteId = null; // This ETag will be invalidated if it loads next time.
}
hashObjectId(h, noteId, buf);
diff --git a/java/com/google/gerrit/server/change/ConsistencyChecker.java b/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 128297f16c..80b7190df7 100644
--- a/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -15,9 +15,10 @@
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.reviewdb.server.ReviewDbUtil.intKeyOrdering;
import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
+import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
@@ -29,16 +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.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
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.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -52,13 +51,11 @@ import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -67,8 +64,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -114,7 +113,6 @@ public class ConsistencyChecker {
private final PatchSetUtil psUtil;
private final Provider<CurrentUser> user;
private final Provider<PersonIdent> serverIdent;
- private final Provider<ReviewDb> db;
private final RetryHelper retryHelper;
private BatchUpdate.Factory updateFactory;
@@ -142,11 +140,9 @@ public class ConsistencyChecker {
PatchSetInserter.Factory patchSetInserterFactory,
PatchSetUtil psUtil,
Provider<CurrentUser> user,
- Provider<ReviewDb> db,
RetryHelper retryHelper) {
this.accounts = accounts;
this.accountPatchReviewStore = accountPatchReviewStore;
- this.db = db;
this.notesFactory = notesFactory;
this.patchSetInfoFactory = patchSetInfoFactory;
this.patchSetInserterFactory = patchSetInserterFactory;
@@ -231,12 +227,12 @@ public class ConsistencyChecker {
private void checkCurrentPatchSetEntity() {
try {
- currPs = psUtil.current(db.get(), notes);
+ currPs = psUtil.current(notes);
if (currPs == null) {
problem(
String.format("Current patch set %d not found", change().currentPatchSetId().get()));
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
error("Failed to look up current patch set", e);
}
}
@@ -259,8 +255,8 @@ public class ConsistencyChecker {
List<PatchSet> all;
try {
// Iterate in descending order.
- all = PS_ID_ORDER.sortedCopy(psUtil.byChange(db.get(), notes));
- } catch (OrmException e) {
+ all = PS_ID_ORDER.sortedCopy(psUtil.byChange(notes));
+ } catch (StorageException e) {
return error("Failed to look up patch sets", e);
}
patchSetsBySha = MultimapBuilder.hashKeys(all.size()).treeSetValues(PS_ID_ORDER).build();
@@ -365,28 +361,39 @@ public class ConsistencyChecker {
private ProblemInfo wrongChangeStatus(PatchSet.Id psId, RevCommit commit) {
String refName = change().getDest().get();
return problem(
- String.format(
+ formatProblemMessage(
"Patch set %d (%s) is merged into destination ref %s (%s), but change"
+ " status is %s",
- psId.get(), commit.name(), refName, tip.name(), change().getStatus()));
+ psId.get(), commit.name(), refName, tip.name()));
}
private void checkMergedBitMatchesStatus(PatchSet.Id psId, RevCommit commit, boolean merged) {
String refName = change().getDest().get();
- if (merged && change().getStatus() != Change.Status.MERGED) {
+ if (merged && !change().isMerged()) {
ProblemInfo p = wrongChangeStatus(psId, commit);
if (fix != null) {
fixMerged(p);
}
- } else if (!merged && change().getStatus() == Change.Status.MERGED) {
+ } else if (!merged && change().isMerged()) {
problem(
- String.format(
+ 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(), change().getStatus()));
+ currPs.getId().get(), commit.name(), refName, tip.name()));
}
}
+ private String formatProblemMessage(
+ String message, int psId, String commitName, String refName, String tipName) {
+ return String.format(
+ message,
+ psId,
+ commitName,
+ refName,
+ tipName,
+ ChangeUtil.status(change()).toUpperCase(Locale.US));
+ }
+
private void checkExpectMergedAs() {
ObjectId objId = parseObjectId(fix.expectMergedAs, "expected merged commit");
RevCommit commit = parseCommit(objId, "expected merged commit");
@@ -414,13 +421,11 @@ public class ConsistencyChecker {
}
try {
Change c =
- notesFactory
- .createChecked(db.get(), change().getProject(), psId.getParentKey())
- .getChange();
+ notesFactory.createChecked(change().getProject(), psId.getParentKey()).getChange();
if (!c.getDest().equals(change().getDest())) {
continue;
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
warn(e);
// Include this patch set; should cause an error below, which is good.
}
@@ -464,7 +469,10 @@ public class ConsistencyChecker {
problem(
String.format(
"Multiple patch sets for expected merged commit %s: %s",
- commit.name(), intKeyOrdering().sortedCopy(thisCommitPsIds)));
+ commit.name(),
+ thisCommitPsIds.stream()
+ .sorted(comparing(PatchSet.Id::get))
+ .collect(toImmutableList())));
break;
}
} catch (IOException e) {
@@ -534,21 +542,21 @@ public class ConsistencyChecker {
}
}
+ bu.setNotify(NotifyResolver.Result.none());
bu.addOp(
notes.getChangeId(),
inserter
.setValidate(false)
.setFireRevisionCreated(false)
- .setNotify(NotifyHandling.NONE)
.setAllowClosed(true)
.setMessage("Patch set for merged commit inserted by consistency checker"));
bu.addOp(notes.getChangeId(), new FixMergedOp(notFound));
bu.execute();
}
- notes = notesFactory.createChecked(db.get(), inserter.getChange());
+ notes = notesFactory.createChecked(inserter.getChange());
insertPatchSetProblem.status = Status.FIXED;
insertPatchSetProblem.outcome = "Inserted as patch set " + psId.get();
- } catch (OrmException | IOException | UpdateException | RestApiException e) {
+ } catch (StorageException | IOException | UpdateException | RestApiException e) {
warn(e);
for (ProblemInfo pi : currProblems) {
pi.status = Status.FIX_FAILED;
@@ -566,7 +574,7 @@ public class ConsistencyChecker {
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
ctx.getChange().setStatus(Change.Status.MERGED);
ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
p.status = Status.FIXED;
@@ -588,7 +596,7 @@ public class ConsistencyChecker {
}
private BatchUpdate newBatchUpdate() {
- return updateFactory.create(db.get(), change().getProject(), user.get(), TimeUtil.nowTs());
+ return updateFactory.create(change().getProject(), user.get(), TimeUtil.nowTs());
}
private void fixPatchSetRef(ProblemInfo p, PatchSet ps) {
@@ -664,18 +672,11 @@ public class ConsistencyChecker {
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws OrmException, PatchSetInfoNotAvailableException {
+ public boolean updateChange(ChangeContext ctx) throws PatchSetInfoNotAvailableException {
// Delete dangling key references.
- ReviewDb db = BatchUpdateReviewDb.unwrap(ctx.getDb());
- accountPatchReviewStore.run(s -> s.clearReviewed(psId), OrmException.class);
- db.changeMessages().delete(db.changeMessages().byChange(psId.getParentKey()));
- db.patchSetApprovals().delete(db.patchSetApprovals().byPatchSet(psId));
- db.patchComments().delete(db.patchComments().byPatchSet(psId));
- db.patchSets().deleteKeys(Collections.singleton(psId));
-
- // NoteDb requires no additional fiddling; setting the state to deleted is
- // sufficient to filter everything else out.
+ accountPatchReviewStore.run(s -> s.clearReviewed(psId));
+
+ // For NoteDb setting the state to deleted is sufficient to filter everything out.
ctx.getUpdate(psId).setPatchSetState(PatchSetState.DELETED);
p.status = Status.FIXED;
@@ -704,15 +705,15 @@ public class ConsistencyChecker {
@Override
public boolean updateChange(ChangeContext ctx)
- throws OrmException, PatchSetInfoNotAvailableException, NoPatchSetsWouldRemainException {
+ throws PatchSetInfoNotAvailableException, NoPatchSetsWouldRemainException {
if (!toDelete.contains(ctx.getChange().currentPatchSetId())) {
return false;
}
- Set<PatchSet.Id> all = new HashSet<>();
+ TreeSet<PatchSet.Id> all = new TreeSet<>(comparing(PatchSet.Id::get));
// Doesn't make any assumptions about the order in which deletes happen
// 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.getDb(), ctx.getNotes())) {
+ for (PatchSet ps : psUtil.byChange(ctx.getNotes())) {
if (!toDelete.contains(ps.getId())) {
all.add(ps.getId());
}
@@ -720,9 +721,7 @@ public class ConsistencyChecker {
if (all.isEmpty()) {
throw new NoPatchSetsWouldRemainException();
}
- PatchSet.Id latest = ReviewDbUtil.intKeyOrdering().max(all);
- ctx.getChange()
- .setCurrentPatchSet(patchSetInfoFactory.get(ctx.getDb(), ctx.getNotes(), latest));
+ ctx.getChange().setCurrentPatchSet(patchSetInfoFactory.get(ctx.getNotes(), all.last()));
return true;
}
}
diff --git a/java/com/google/gerrit/server/change/DeleteChangeOp.java b/java/com/google/gerrit/server/change/DeleteChangeOp.java
index 461274b363..ec20e87093 100644
--- a/java/com/google/gerrit/server/change/DeleteChangeOp.java
+++ b/java/com/google/gerrit/server/change/DeleteChangeOp.java
@@ -14,26 +14,21 @@
package com.google.gerrit.server.change;
-import static com.google.common.base.Preconditions.checkState;
-
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.reviewdb.server.ReviewDb;
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.BatchUpdateReviewDb;
import com.google.gerrit.server.update.ChangeContext;
-import com.google.gerrit.server.update.Order;
import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
@@ -42,40 +37,41 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
public class DeleteChangeOp implements BatchUpdateOp {
+ public interface Factory {
+ DeleteChangeOp create(Change.Id id);
+ }
+
private final PatchSetUtil psUtil;
private final StarredChangesUtil starredChangesUtil;
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
private final ChangeDeleted changeDeleted;
-
- private Change.Id id;
+ private final Change.Id id;
@Inject
DeleteChangeOp(
PatchSetUtil psUtil,
StarredChangesUtil starredChangesUtil,
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
- ChangeDeleted changeDeleted) {
+ ChangeDeleted changeDeleted,
+ @Assisted Change.Id id) {
this.psUtil = psUtil;
this.starredChangesUtil = starredChangesUtil;
this.accountPatchReviewStore = accountPatchReviewStore;
this.changeDeleted = changeDeleted;
+ this.id = id;
}
+ // The relative order of updateChange and updateRepo doesn't matter as long as all operations are
+ // executed in a single atomic BatchRefUpdate. Actually deleting the change refs first would not
+ // fail gracefully if the second delete fails, but fortunately that's not what happens.
@Override
- public boolean updateChange(ChangeContext ctx)
- throws RestApiException, OrmException, IOException, NoSuchChangeException {
- checkState(
- ctx.getOrder() == Order.DB_BEFORE_REPO, "must use DeleteChangeOp with DB_BEFORE_REPO");
- checkState(id == null, "cannot reuse DeleteChangeOp");
-
- id = ctx.getChange().getId();
- Collection<PatchSet> patchSets = psUtil.byChange(ctx.getDb(), ctx.getNotes());
+ public boolean updateChange(ChangeContext ctx) throws RestApiException, IOException {
+ Collection<PatchSet> patchSets = psUtil.byChange(ctx.getNotes());
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, patchSets);
- deleteChangeElementsFromDb(ctx, id);
+ cleanUpReferences(ctx, id);
ctx.deleteChange();
changeDeleted.fire(ctx.getChange(), ctx.getAccount(), ctx.getWhen());
@@ -84,8 +80,7 @@ public class DeleteChangeOp implements BatchUpdateOp {
private void ensureDeletable(ChangeContext ctx, Change.Id id, Collection<PatchSet> patchSets)
throws ResourceConflictException, MethodNotAllowedException, IOException {
- Change.Status status = ctx.getChange().getStatus();
- if (status == Change.Status.MERGED) {
+ if (ctx.getChange().isMerged()) {
throw new MethodNotAllowedException("Deleting merged change " + id + " is not allowed");
}
for (PatchSet patchSet : patchSets) {
@@ -109,25 +104,8 @@ public class DeleteChangeOp implements BatchUpdateOp {
return revWalk.isMergedInto(revWalk.parseCommit(objectId), revWalk.parseCommit(destId.get()));
}
- private void deleteChangeElementsFromDb(ChangeContext ctx, Change.Id id) throws OrmException {
- // Only delete from ReviewDb here; deletion from NoteDb is handled in
- // BatchUpdate.
- //
- // This is special. We want to delete exactly the rows that are present in
- // the database, even when reading everything else from NoteDb, so we need
- // to bypass the write-only wrapper.
- ReviewDb db = BatchUpdateReviewDb.unwrap(ctx.getDb());
- db.patchComments().delete(db.patchComments().byChange(id));
- db.patchSetApprovals().delete(db.patchSetApprovals().byChange(id));
- db.patchSets().delete(db.patchSets().byChange(id));
- db.changeMessages().delete(db.changeMessages().byChange(id));
- }
-
- private void cleanUpReferences(ChangeContext ctx, Change.Id id, Collection<PatchSet> patchSets)
- throws OrmException, NoSuchChangeException {
- for (PatchSet ps : patchSets) {
- accountPatchReviewStore.run(s -> s.clearReviewed(ps.getId()), OrmException.class);
- }
+ private void cleanUpReferences(ChangeContext ctx, Change.Id id) throws NoSuchChangeException {
+ accountPatchReviewStore.run(s -> s.clearReviewed(id));
// Non-atomic operation on Accounts table; not much we can do to make it
// atomic.
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
index ad4f4af8d6..4b5572b6bb 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
@@ -15,8 +15,6 @@
package com.google.gerrit.server.change;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.mail.Address;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
@@ -26,7 +24,6 @@ import com.google.gerrit.server.mail.send.DeleteReviewerSender;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Collections;
@@ -35,31 +32,24 @@ public class DeleteReviewerByEmailOp implements BatchUpdateOp {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public interface Factory {
- DeleteReviewerByEmailOp create(Address reviewer, DeleteReviewerInput input);
+ DeleteReviewerByEmailOp create(Address reviewer);
}
private final DeleteReviewerSender.Factory deleteReviewerSenderFactory;
- private final NotifyUtil notifyUtil;
private final Address reviewer;
- private final DeleteReviewerInput input;
private ChangeMessage changeMessage;
private Change change;
@Inject
DeleteReviewerByEmailOp(
- DeleteReviewerSender.Factory deleteReviewerSenderFactory,
- NotifyUtil notifyUtil,
- @Assisted Address reviewer,
- @Assisted DeleteReviewerInput input) {
+ DeleteReviewerSender.Factory deleteReviewerSenderFactory, @Assisted Address reviewer) {
this.deleteReviewerSenderFactory = deleteReviewerSenderFactory;
- this.notifyUtil = notifyUtil;
this.reviewer = reviewer;
- this.input = input;
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
change = ctx.getChange();
PatchSet.Id psId = ctx.getChange().currentPatchSetId();
String msg = "Removed reviewer " + reviewer;
@@ -78,24 +68,17 @@ public class DeleteReviewerByEmailOp implements BatchUpdateOp {
@Override
public void postUpdate(Context ctx) {
- if (input.notify == null) {
- if (change.isWorkInProgress()) {
- input.notify = NotifyHandling.NONE;
- } else {
- input.notify = NotifyHandling.ALL;
- }
- }
- if (!NotifyUtil.shouldNotify(input.notify, input.notifyDetails)) {
- return;
- }
try {
+ NotifyResolver.Result notify = ctx.getNotify(change.getId());
+ if (!notify.shouldNotify()) {
+ return;
+ }
DeleteReviewerSender cm =
deleteReviewerSenderFactory.create(ctx.getProject(), change.getId());
cm.setFrom(ctx.getAccountId());
cm.addReviewersByEmail(Collections.singleton(reviewer));
cm.setChangeMessage(changeMessage.getMessage(), changeMessage.getWrittenOn());
- cm.setNotify(input.notify);
- cm.setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
+ cm.setNotify(notify);
cm.send();
} catch (Exception err) {
logger.atSevere().withCause(err).log("Cannot email update for change %s", change.getId());
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerOp.java b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
index 7915cb7ba9..29458a864b 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
@@ -18,6 +18,7 @@ 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.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;
@@ -27,9 +28,7 @@ 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.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
+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;
@@ -38,16 +37,12 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.extensions.events.ReviewerDeleted;
import com.google.gerrit.server.mail.send.DeleteReviewerSender;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.RemoveReviewerControl;
import com.google.gerrit.server.update.BatchUpdateOp;
-import com.google.gerrit.server.update.BatchUpdateReviewDb;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -72,8 +67,6 @@ public class DeleteReviewerOp implements BatchUpdateOp {
private final ReviewerDeleted reviewerDeleted;
private final Provider<IdentifiedUser> user;
private final DeleteReviewerSender.Factory deleteReviewerSenderFactory;
- private final NotesMigration migration;
- private final NotifyUtil notifyUtil;
private final RemoveReviewerControl removeReviewerControl;
private final ProjectCache projectCache;
@@ -95,8 +88,6 @@ public class DeleteReviewerOp implements BatchUpdateOp {
ReviewerDeleted reviewerDeleted,
Provider<IdentifiedUser> user,
DeleteReviewerSender.Factory deleteReviewerSenderFactory,
- NotesMigration migration,
- NotifyUtil notifyUtil,
RemoveReviewerControl removeReviewerControl,
ProjectCache projectCache,
@Assisted AccountState reviewerAccount,
@@ -108,8 +99,6 @@ public class DeleteReviewerOp implements BatchUpdateOp {
this.reviewerDeleted = reviewerDeleted;
this.user = user;
this.deleteReviewerSenderFactory = deleteReviewerSenderFactory;
- this.migration = migration;
- this.notifyUtil = notifyUtil;
this.removeReviewerControl = removeReviewerControl;
this.projectCache = projectCache;
this.reviewer = reviewerAccount;
@@ -118,17 +107,16 @@ public class DeleteReviewerOp implements BatchUpdateOp {
@Override
public boolean updateChange(ChangeContext ctx)
- throws AuthException, ResourceNotFoundException, OrmException, PermissionBackendException,
- IOException {
+ throws AuthException, ResourceNotFoundException, PermissionBackendException, IOException {
Account.Id reviewerId = reviewer.getAccount().getId();
// 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);
- if (!approvalsUtil.getReviewers(ctx.getDb(), ctx.getNotes()).all().contains(reviewerId)) {
+ if (!approvalsUtil.getReviewers(ctx.getNotes()).all().contains(reviewerId)) {
throw new ResourceNotFoundException();
}
currChange = ctx.getChange();
- currPs = psUtil.current(ctx.getDb(), ctx.getNotes());
+ currPs = psUtil.current(ctx.getNotes());
LabelTypes labelTypes = projectCache.checkedGet(ctx.getProject()).getLabelTypes(ctx.getNotes());
// removing a reviewer will remove all her votes
@@ -164,28 +152,33 @@ public class DeleteReviewerOp implements BatchUpdateOp {
} else {
msg.append(".");
}
- ctx.getDb().patchSetApprovals().delete(del);
ChangeUpdate update = ctx.getUpdate(currPs.getId());
update.removeReviewer(reviewerId);
changeMessage =
ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_DELETE_REVIEWER);
- cmUtil.addChangeMessage(ctx.getDb(), update, changeMessage);
+ cmUtil.addChangeMessage(update, changeMessage);
return true;
}
@Override
public void postUpdate(Context ctx) {
- if (input.notify == null) {
- if (currChange.isWorkInProgress()) {
- input.notify = oldApprovals.isEmpty() ? NotifyHandling.NONE : NotifyHandling.OWNER;
- } else {
- input.notify = NotifyHandling.ALL;
- }
+ NotifyResolver.Result notify = ctx.getNotify(currChange.getId());
+ if (input.notify == null
+ && currChange.isWorkInProgress()
+ && !oldApprovals.isEmpty()
+ && notify.handling().compareTo(NotifyHandling.OWNER) < 0) {
+ // Override NotifyHandling from the context to notify owner if votes were removed on a WIP
+ // change.
+ notify = notify.withHandling(NotifyHandling.OWNER);
}
- if (NotifyUtil.shouldNotify(input.notify, input.notifyDetails)) {
- emailReviewers(ctx.getProject(), currChange, changeMessage);
+ try {
+ if (notify.shouldNotify()) {
+ emailReviewers(ctx.getProject(), currChange, changeMessage, notify);
+ }
+ } catch (Exception err) {
+ logger.atSevere().withCause(err).log("Cannot email update for change %s", currChange.getId());
}
reviewerDeleted.fire(
currChange,
@@ -195,30 +188,13 @@ public class DeleteReviewerOp implements BatchUpdateOp {
changeMessage.getMessage(),
newApprovals,
oldApprovals,
- input.notify,
+ notify.handling(),
ctx.getWhen());
}
- private Iterable<PatchSetApproval> approvals(ChangeContext ctx, Account.Id accountId)
- throws OrmException {
- Change.Id changeId = ctx.getNotes().getChangeId();
+ private Iterable<PatchSetApproval> approvals(ChangeContext ctx, Account.Id accountId) {
Iterable<PatchSetApproval> approvals;
- PrimaryStorage r = PrimaryStorage.of(ctx.getChange());
-
- if (migration.readChanges() && r == PrimaryStorage.REVIEW_DB) {
- // Because NoteDb and ReviewDb have different semantics for zero-value
- // approvals, we must fall back to ReviewDb as the source of truth here.
- ReviewDb db = ctx.getDb();
-
- if (db instanceof BatchUpdateReviewDb) {
- db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
- }
- db = ReviewDbUtil.unwrapDb(db);
- approvals = db.patchSetApprovals().byChange(changeId);
- } else {
- approvals = approvalsUtil.byChange(ctx.getDb(), ctx.getNotes()).values();
- }
-
+ approvals = approvalsUtil.byChange(ctx.getNotes()).values();
return Iterables.filter(approvals, psa -> accountId.equals(psa.getAccountId()));
}
@@ -230,22 +206,18 @@ public class DeleteReviewerOp implements BatchUpdateOp {
}
private void emailReviewers(
- Project.NameKey projectName, Change change, ChangeMessage changeMessage) {
+ NameKey projectName, Change change, ChangeMessage changeMessage, NotifyResolver.Result notify)
+ throws EmailException {
Account.Id userId = user.get().getAccountId();
if (userId.equals(reviewer.getAccount().getId())) {
// The user knows they removed themselves, don't bother emailing them.
return;
}
- try {
- DeleteReviewerSender cm = deleteReviewerSenderFactory.create(projectName, change.getId());
- cm.setFrom(userId);
- cm.addReviewers(Collections.singleton(reviewer.getAccount().getId()));
- cm.setChangeMessage(changeMessage.getMessage(), changeMessage.getWrittenOn());
- cm.setNotify(input.notify);
- cm.setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
- cm.send();
- } catch (Exception err) {
- logger.atSevere().withCause(err).log("Cannot email update for change %s", change.getId());
- }
+ DeleteReviewerSender cm = deleteReviewerSenderFactory.create(projectName, change.getId());
+ cm.setFrom(userId);
+ cm.addReviewers(Collections.singleton(reviewer.getAccount().getId()));
+ cm.setChangeMessage(changeMessage.getMessage(), changeMessage.getWrittenOn());
+ cm.setNotify(notify);
+ cm.send();
}
}
diff --git a/java/com/google/gerrit/server/change/EmailReviewComments.java b/java/com/google/gerrit/server/change/EmailReviewComments.java
index 65bef70da4..8353501f93 100644
--- a/java/com/google/gerrit/server/change/EmailReviewComments.java
+++ b/java/com/google/gerrit/server/change/EmailReviewComments.java
@@ -16,16 +16,11 @@ package com.google.gerrit.server.change;
import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-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.ChangeMessage;
import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.SendEmailExecutor;
@@ -35,11 +30,7 @@ import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted;
import java.util.List;
import java.util.concurrent.ExecutorService;
@@ -53,7 +44,6 @@ public class EmailReviewComments implements Runnable, RequestContext {
// on the same set of inputs.
/**
* @param notify setting for handling notification.
- * @param accountsToNotify detailed map of accounts to notify.
* @param notes change notes.
* @param patchSet patch set corresponding to the top-level op
* @param user user the email should come from.
@@ -68,8 +58,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
* @return handle for sending email.
*/
EmailReviewComments create(
- NotifyHandling notify,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
+ NotifyResolver.Result notify,
ChangeNotes notes,
PatchSet patchSet,
IdentifiedUser user,
@@ -82,11 +71,9 @@ public class EmailReviewComments implements Runnable, RequestContext {
private final ExecutorService sendEmailsExecutor;
private final PatchSetInfoFactory patchSetInfoFactory;
private final CommentSender.Factory commentSenderFactory;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ThreadLocalRequestContext requestContext;
- private final NotifyHandling notify;
- private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
+ private final NotifyResolver.Result notify;
private final ChangeNotes notes;
private final PatchSet patchSet;
private final IdentifiedUser user;
@@ -94,17 +81,14 @@ public class EmailReviewComments implements Runnable, RequestContext {
private final List<Comment> comments;
private final String patchSetComment;
private final List<LabelVote> labels;
- private ReviewDb db;
@Inject
EmailReviewComments(
@SendEmailExecutor ExecutorService executor,
PatchSetInfoFactory patchSetInfoFactory,
CommentSender.Factory commentSenderFactory,
- SchemaFactory<ReviewDb> schemaFactory,
ThreadLocalRequestContext requestContext,
- @Assisted NotifyHandling notify,
- @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify,
+ @Assisted NotifyResolver.Result notify,
@Assisted ChangeNotes notes,
@Assisted PatchSet patchSet,
@Assisted IdentifiedUser user,
@@ -115,10 +99,8 @@ public class EmailReviewComments implements Runnable, RequestContext {
this.sendEmailsExecutor = executor;
this.patchSetInfoFactory = patchSetInfoFactory;
this.commentSenderFactory = commentSenderFactory;
- this.schemaFactory = schemaFactory;
this.requestContext = requestContext;
this.notify = notify;
- this.accountsToNotify = accountsToNotify;
this.notes = notes;
this.patchSet = patchSet;
this.user = user;
@@ -137,7 +119,6 @@ public class EmailReviewComments implements Runnable, RequestContext {
public void run() {
RequestContext old = requestContext.setContext(this);
try {
-
CommentSender cm = commentSenderFactory.create(notes.getProjectName(), notes.getChangeId());
cm.setFrom(user.getAccountId());
cm.setPatchSet(patchSet, patchSetInfoFactory.get(notes.getProjectName(), patchSet));
@@ -146,16 +127,11 @@ public class EmailReviewComments implements Runnable, RequestContext {
cm.setPatchSetComment(patchSetComment);
cm.setLabels(labels);
cm.setNotify(notify);
- cm.setAccountsToNotify(accountsToNotify);
cm.send();
} catch (Exception e) {
logger.atSevere().withCause(e).log("Cannot email comments for %s", patchSet.getId());
} finally {
requestContext.setContext(old);
- if (db != null) {
- db.close();
- db = null;
- }
}
}
@@ -168,21 +144,4 @@ public class EmailReviewComments implements Runnable, RequestContext {
public CurrentUser getUser() {
return user.getRealUser();
}
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- if (db == null) {
- try {
- db = schemaFactory.open();
- } catch (OrmException e) {
- throw new ProvisionException("Cannot open ReviewDb", e);
- }
- }
- return db;
- }
- };
- }
}
diff --git a/java/com/google/gerrit/server/change/LabelsJson.java b/java/com/google/gerrit/server/change/LabelsJson.java
index 787e8ef0f7..6fde5a5892 100644
--- a/java/com/google/gerrit/server/change/LabelsJson.java
+++ b/java/com/google/gerrit/server/change/LabelsJson.java
@@ -41,11 +41,9 @@ 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.Account.Id;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
+import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
@@ -53,9 +51,7 @@ 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.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -79,7 +75,6 @@ public class LabelsJson {
LabelsJson create(Iterable<ListChangesOption> options);
}
- private final Provider<ReviewDb> db;
private final ApprovalsUtil approvalsUtil;
private final ChangeNotes.Factory notesFactory;
private final PermissionBackend permissionBackend;
@@ -87,12 +82,10 @@ public class LabelsJson {
@Inject
LabelsJson(
- Provider<ReviewDb> db,
ApprovalsUtil approvalsUtil,
ChangeNotes.Factory notesFactory,
PermissionBackend permissionBackend,
@Assisted Iterable<ListChangesOption> options) {
- this.db = db;
this.approvalsUtil = approvalsUtil;
this.notesFactory = notesFactory;
this.permissionBackend = permissionBackend;
@@ -106,14 +99,14 @@ public class LabelsJson {
*/
Map<String, LabelInfo> labelsFor(
AccountLoader accountLoader, ChangeData cd, boolean standard, boolean detailed)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
if (!standard && !detailed) {
return null;
}
LabelTypes labelTypes = cd.getLabelTypes();
Map<String, LabelWithStatus> withStatus =
- cd.change().getStatus() == Change.Status.MERGED
+ cd.change().isMerged()
? labelsForSubmittedChange(accountLoader, cd, labelTypes, standard, detailed)
: labelsForUnsubmittedChange(accountLoader, cd, labelTypes, standard, detailed);
return ImmutableMap.copyOf(Maps.transformValues(withStatus, LabelWithStatus::label));
@@ -121,8 +114,8 @@ public class LabelsJson {
/** Returns all labels that the provided user has permission to vote on. */
Map<String, Collection<String>> permittedLabels(Account.Id filterApprovalsBy, ChangeData cd)
- throws OrmException, PermissionBackendException {
- boolean isMerged = cd.change().getStatus() == Change.Status.MERGED;
+ throws PermissionBackendException {
+ boolean isMerged = cd.change().isMerged();
LabelTypes labelTypes = cd.getLabelTypes();
Map<String, LabelType> toCheck = new HashMap<>();
for (SubmitRecord rec : submitRecords(cd)) {
@@ -200,7 +193,7 @@ public class LabelsJson {
LabelTypes labelTypes,
boolean standard,
boolean detailed)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
Map<String, LabelWithStatus> labels = initLabels(accountLoader, cd, labelTypes, standard);
if (detailed) {
setAllApprovals(accountLoader, cd, labels);
@@ -258,12 +251,10 @@ public class LabelsJson {
}
}
- private Map<String, Short> currentLabels(Account.Id accountId, ChangeData cd)
- throws OrmException {
+ private Map<String, Short> currentLabels(Account.Id accountId, ChangeData cd) {
Map<String, Short> result = new HashMap<>();
for (PatchSetApproval psa :
approvalsUtil.byPatchSetUser(
- db.get(),
lazyLoad ? cd.notes() : notesFactory.createFromIndexedChange(cd.change()),
cd.change().currentPatchSetId(),
accountId,
@@ -280,7 +271,7 @@ public class LabelsJson {
LabelTypes labelTypes,
boolean standard,
boolean detailed)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
Set<Account.Id> allUsers = new HashSet<>();
if (detailed) {
// Users expect to see all reviewers on closed changes, even if they
@@ -293,7 +284,8 @@ public class LabelsJson {
}
Set<String> labelNames = new HashSet<>();
- SetMultimap<Id, PatchSetApproval> current = MultimapBuilder.hashKeys().hashSetValues().build();
+ SetMultimap<Account.Id, PatchSetApproval> current =
+ MultimapBuilder.hashKeys().hashSetValues().build();
for (PatchSetApproval a : cd.currentApprovals()) {
allUsers.add(a.getAccountId());
LabelType type = labelTypes.byLabel(a.getLabelId());
@@ -437,21 +429,22 @@ public class LabelsJson {
private void setAllApprovals(
AccountLoader accountLoader, ChangeData cd, Map<String, LabelWithStatus> labels)
- throws OrmException, PermissionBackendException {
- Change.Status status = cd.change().getStatus();
+ throws PermissionBackendException {
checkState(
- status != Change.Status.MERGED, "should not call setAllApprovals on %s change", status);
+ !cd.change().isMerged(),
+ "should not call setAllApprovals on %s change",
+ ChangeUtil.status(cd.change()));
// Include a user in the output for this label if either:
// - They are an explicit reviewer.
// - They ever voted on this change.
- Set<Id> allUsers = new HashSet<>();
+ Set<Account.Id> allUsers = new HashSet<>();
allUsers.addAll(cd.reviewers().byState(ReviewerStateInternal.REVIEWER));
for (PatchSetApproval psa : cd.approvals().values()) {
allUsers.add(psa.getAccountId());
}
- Table<Id, String, PatchSetApproval> current =
+ 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);
@@ -504,9 +497,8 @@ public class LabelsJson {
* from either an index-backed or a database-backed {@link ChangeData} depending on {@code
* lazyload}.
*/
- private PermissionBackend.ForChange permissionBackendForChange(Account.Id user, ChangeData cd)
- throws OrmException {
- PermissionBackend.WithUser withUser = permissionBackend.absentUser(user).database(db);
+ private PermissionBackend.ForChange permissionBackendForChange(Account.Id user, ChangeData cd) {
+ PermissionBackend.WithUser withUser = permissionBackend.absentUser(user);
return lazyLoad
? withUser.change(cd)
: withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
diff --git a/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java b/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
index 131f3a19b0..d4085191dc 100644
--- a/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
+++ b/java/com/google/gerrit/server/change/MergeabilityCacheImpl.java
@@ -25,13 +25,13 @@ import com.google.common.cache.Weigher;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.UncheckedExecutionException;
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;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.submit.SubmitDryRun;
@@ -143,7 +143,7 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
@Override
public byte[] serialize(EntryKey object) {
ObjectIdConverter idConverter = ObjectIdConverter.create();
- return ProtoCacheSerializers.toByteArray(
+ return Protos.toByteArray(
MergeabilityKeyProto.newBuilder()
.setCommit(idConverter.toByteString(object.getCommit()))
.setInto(idConverter.toByteString(object.getInto()))
@@ -154,8 +154,7 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
@Override
public EntryKey deserialize(byte[] in) {
- MergeabilityKeyProto proto =
- ProtoCacheSerializers.parseUnchecked(MergeabilityKeyProto.parser(), in);
+ MergeabilityKeyProto proto = Protos.parseUnchecked(MergeabilityKeyProto.parser(), in);
ObjectIdConverter idConverter = ObjectIdConverter.create();
return new EntryKey(
idConverter.fromByteString(proto.getCommit()),
@@ -208,7 +207,7 @@ public class MergeabilityCacheImpl implements MergeabilityCache {
accepted.add(rw.parseCommit(key.into));
accepted.addAll(Arrays.asList(rw.parseCommit(key.commit).getParents()));
return submitDryRun.run(
- key.submitType, repo, rw, dest, key.into, key.commit, accepted);
+ null, key.submitType, repo, rw, dest, key.into, key.commit, accepted);
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
diff --git a/java/com/google/gerrit/server/change/NotifyResolver.java b/java/com/google/gerrit/server/change/NotifyResolver.java
new file mode 100644
index 0000000000..5b156846a5
--- /dev/null
+++ b/java/com/google/gerrit/server/change/NotifyResolver.java
@@ -0,0 +1,116 @@
+// 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.server.change;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
+
+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.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;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+
+@Singleton
+public class NotifyResolver {
+ @AutoValue
+ public abstract static class Result {
+ public static Result none() {
+ return create(NotifyHandling.NONE);
+ }
+
+ public static Result all() {
+ return create(NotifyHandling.ALL);
+ }
+
+ public static Result create(NotifyHandling notifyHandling) {
+ return create(notifyHandling, ImmutableSetMultimap.of());
+ }
+
+ public static Result create(
+ NotifyHandling handling, ImmutableSetMultimap<RecipientType, Account.Id> recipients) {
+ return new AutoValue_NotifyResolver_Result(handling, recipients);
+ }
+
+ public abstract NotifyHandling handling();
+
+ public abstract ImmutableSetMultimap<RecipientType, Account.Id> accounts();
+
+ public Result withHandling(NotifyHandling notifyHandling) {
+ return create(notifyHandling, accounts());
+ }
+
+ public boolean shouldNotify() {
+ return !accounts().isEmpty() || handling().compareTo(NotifyHandling.NONE) > 0;
+ }
+ }
+
+ private final AccountResolver accountResolver;
+
+ @Inject
+ NotifyResolver(AccountResolver accountResolver) {
+ this.accountResolver = accountResolver;
+ }
+
+ public Result resolve(
+ NotifyHandling handling, @Nullable Map<RecipientType, NotifyInfo> notifyDetails)
+ throws BadRequestException, IOException, ConfigInvalidException {
+ requireNonNull(handling);
+ ImmutableSetMultimap.Builder<RecipientType, Account.Id> b = ImmutableSetMultimap.builder();
+ if (notifyDetails != null) {
+ for (Map.Entry<RecipientType, NotifyInfo> e : notifyDetails.entrySet()) {
+ b.putAll(e.getKey(), find(e.getValue().accounts));
+ }
+ }
+ return Result.create(handling, b.build());
+ }
+
+ private ImmutableList<Account.Id> find(@Nullable List<String> inputs)
+ throws BadRequestException, IOException, ConfigInvalidException {
+ if (inputs == null || inputs.isEmpty()) {
+ return ImmutableList.of();
+ }
+ ImmutableList.Builder<Account.Id> r = ImmutableList.builder();
+ List<String> problems = new ArrayList<>(inputs.size());
+ for (String nameOrEmail : inputs) {
+ try {
+ r.add(accountResolver.resolve(nameOrEmail).asUnique().getAccount().getId());
+ } catch (UnprocessableEntityException e) {
+ problems.add(e.getMessage());
+ }
+ }
+
+ if (!problems.isEmpty()) {
+ throw new BadRequestException(
+ "Some accounts that should be notified could not be resolved: "
+ + problems.stream().collect(joining("\n")));
+ }
+
+ return r.build();
+ }
+}
diff --git a/java/com/google/gerrit/server/change/NotifyUtil.java b/java/com/google/gerrit/server/change/NotifyUtil.java
deleted file mode 100644
index c29faee1d8..0000000000
--- a/java/com/google/gerrit/server/change/NotifyUtil.java
+++ /dev/null
@@ -1,117 +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.server.change;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.common.Nullable;
-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.reviewdb.client.Account;
-import com.google.gerrit.server.account.AccountResolver;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-@Singleton
-public class NotifyUtil {
- private final AccountResolver accountResolver;
-
- @Inject
- NotifyUtil(AccountResolver accountResolver) {
- this.accountResolver = accountResolver;
- }
-
- public static boolean shouldNotify(
- NotifyHandling notify, @Nullable Map<RecipientType, NotifyInfo> notifyDetails) {
- if (!isNullOrEmpty(notifyDetails)) {
- return true;
- }
-
- return notify.compareTo(NotifyHandling.NONE) > 0;
- }
-
- private static boolean isNullOrEmpty(@Nullable Map<RecipientType, NotifyInfo> notifyDetails) {
- if (notifyDetails == null || notifyDetails.isEmpty()) {
- return true;
- }
-
- for (NotifyInfo notifyInfo : notifyDetails.values()) {
- if (!isEmpty(notifyInfo)) {
- return false;
- }
- }
-
- return true;
- }
-
- private static boolean isEmpty(NotifyInfo notifyInfo) {
- return notifyInfo.accounts == null || notifyInfo.accounts.isEmpty();
- }
-
- public ListMultimap<RecipientType, Account.Id> resolveAccounts(
- @Nullable Map<RecipientType, NotifyInfo> notifyDetails)
- throws OrmException, BadRequestException, IOException, ConfigInvalidException {
- if (isNullOrEmpty(notifyDetails)) {
- return ImmutableListMultimap.of();
- }
-
- ListMultimap<RecipientType, Account.Id> m = null;
- for (Entry<RecipientType, NotifyInfo> e : notifyDetails.entrySet()) {
- List<String> accounts = e.getValue().accounts;
- if (accounts != null) {
- if (m == null) {
- m = MultimapBuilder.hashKeys().arrayListValues().build();
- }
- m.putAll(e.getKey(), find(accounts));
- }
- }
-
- return m != null ? m : ImmutableListMultimap.of();
- }
-
- private List<Account.Id> find(List<String> nameOrEmails)
- throws OrmException, BadRequestException, IOException, ConfigInvalidException {
- List<String> missing = new ArrayList<>(nameOrEmails.size());
- List<Account.Id> r = new ArrayList<>(nameOrEmails.size());
- for (String nameOrEmail : nameOrEmails) {
- Account a = accountResolver.find(nameOrEmail);
- if (a != null) {
- r.add(a.getId());
- } else {
- missing.add(nameOrEmail);
- }
- }
-
- if (!missing.isEmpty()) {
- throw new BadRequestException(
- "The following accounts that should be notified could not be resolved: "
- + missing.stream().distinct().sorted().collect(joining(", ")));
- }
-
- return r;
- }
-}
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index fc2167f08a..d3649f660d 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -19,20 +19,14 @@ import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
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.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.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ApprovalCopier;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -55,7 +49,6 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -79,7 +72,6 @@ public class PatchSetInserter implements BatchUpdateOp {
private final ProjectCache projectCache;
private final RevisionCreated revisionCreated;
private final ApprovalsUtil approvalsUtil;
- private final ApprovalCopier approvalCopier;
private final ChangeMessagesUtil cmUtil;
private final PatchSetUtil psUtil;
@@ -98,10 +90,7 @@ public class PatchSetInserter implements BatchUpdateOp {
private boolean checkAddPatchSetPermission = true;
private List<String> groups = Collections.emptyList();
private boolean fireRevisionCreated = true;
- private NotifyHandling notify = NotifyHandling.ALL;
- private ListMultimap<RecipientType, Account.Id> accountsToNotify = ImmutableListMultimap.of();
private boolean allowClosed;
- private boolean copyApprovals = true;
private boolean sendEmail = true;
// Fields set during some phase of BatchUpdate.Op.
@@ -115,7 +104,6 @@ public class PatchSetInserter implements BatchUpdateOp {
public PatchSetInserter(
PermissionBackend permissionBackend,
ApprovalsUtil approvalsUtil,
- ApprovalCopier approvalCopier,
ChangeMessagesUtil cmUtil,
PatchSetInfoFactory patchSetInfoFactory,
CommitValidators.Factory commitValidatorsFactory,
@@ -128,7 +116,6 @@ public class PatchSetInserter implements BatchUpdateOp {
@Assisted ObjectId commitId) {
this.permissionBackend = permissionBackend;
this.approvalsUtil = approvalsUtil;
- this.approvalCopier = approvalCopier;
this.cmUtil = cmUtil;
this.patchSetInfoFactory = patchSetInfoFactory;
this.commitValidatorsFactory = commitValidatorsFactory;
@@ -177,27 +164,11 @@ public class PatchSetInserter implements BatchUpdateOp {
return this;
}
- public PatchSetInserter setNotify(NotifyHandling notify) {
- this.notify = requireNonNull(notify);
- return this;
- }
-
- public PatchSetInserter setAccountsToNotify(
- ListMultimap<RecipientType, Account.Id> accountsToNotify) {
- this.accountsToNotify = requireNonNull(accountsToNotify);
- return this;
- }
-
public PatchSetInserter setAllowClosed(boolean allowClosed) {
this.allowClosed = allowClosed;
return this;
}
- public PatchSetInserter setCopyApprovals(boolean copyApprovals) {
- this.copyApprovals = copyApprovals;
- return this;
- }
-
public PatchSetInserter setSendEmail(boolean sendEmail) {
this.sendEmail = sendEmail;
return this;
@@ -215,22 +186,18 @@ public class PatchSetInserter implements BatchUpdateOp {
@Override
public void updateRepo(RepoContext ctx)
- throws AuthException, ResourceConflictException, IOException, OrmException,
- PermissionBackendException {
+ throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
validate(ctx);
ctx.addRefUpdate(ObjectId.zeroId(), commitId, getPatchSetId().toRefName());
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws ResourceConflictException, OrmException, IOException {
- ReviewDb db = ctx.getDb();
-
+ public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, IOException {
change = ctx.getChange();
ChangeUpdate update = ctx.getUpdate(psId);
update.setSubjectForCommit("Create patch set " + psId.get());
- if (!change.getStatus().isOpen() && !allowClosed) {
+ if (!change.isNew() && !allowClosed) {
throw new ResourceConflictException(
String.format(
"Cannot create new patch set of change %s because it is %s",
@@ -239,24 +206,17 @@ public class PatchSetInserter implements BatchUpdateOp {
List<String> newGroups = groups;
if (newGroups.isEmpty()) {
- PatchSet prevPs = psUtil.current(db, ctx.getNotes());
+ PatchSet prevPs = psUtil.current(ctx.getNotes());
if (prevPs != null) {
newGroups = prevPs.getGroups();
}
}
patchSet =
psUtil.insert(
- db,
- ctx.getRevWalk(),
- ctx.getUpdate(psId),
- psId,
- commitId,
- newGroups,
- null,
- description);
+ ctx.getRevWalk(), ctx.getUpdate(psId), psId, commitId, newGroups, null, description);
- if (notify != NotifyHandling.NONE) {
- oldReviewers = approvalsUtil.getReviewers(db, ctx.getNotes());
+ if (ctx.getNotify(change.getId()).handling() != NotifyHandling.NONE) {
+ oldReviewers = approvalsUtil.getReviewers(ctx.getNotes());
}
if (message != null) {
@@ -276,19 +236,17 @@ public class PatchSetInserter implements BatchUpdateOp {
change.setStatus(Change.Status.NEW);
}
change.setCurrentPatchSet(patchSetInfo);
- if (copyApprovals) {
- approvalCopier.copyInReviewDb(
- db, ctx.getNotes(), patchSet, ctx.getRevWalk(), ctx.getRepoView().getConfig());
- }
if (changeMessage != null) {
- cmUtil.addChangeMessage(db, update, changeMessage);
+ cmUtil.addChangeMessage(update, changeMessage);
}
return true;
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
- if (sendEmail && (notify != NotifyHandling.NONE || !accountsToNotify.isEmpty())) {
+ public void postUpdate(Context ctx) {
+ NotifyResolver.Result notify = ctx.getNotify(change.getId());
+ if (notify.shouldNotify() && sendEmail) {
+ requireNonNull(changeMessage);
try {
ReplacePatchSetSender cm = replacePatchSetFactory.create(ctx.getProject(), change.getId());
cm.setFrom(ctx.getAccountId());
@@ -297,7 +255,6 @@ public class PatchSetInserter implements BatchUpdateOp {
cm.addReviewers(oldReviewers.byState(REVIEWER));
cm.addExtraCC(oldReviewers.byState(CC));
cm.setNotify(notify);
- cm.setAccountsToNotify(accountsToNotify);
cm.send();
} catch (Exception err) {
logger.atSevere().withCause(err).log(
@@ -311,17 +268,12 @@ public class PatchSetInserter implements BatchUpdateOp {
}
private void validate(RepoContext ctx)
- throws AuthException, ResourceConflictException, IOException, PermissionBackendException,
- OrmException {
+ throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
// Not allowed to create a new patch set if the current patch set is locked.
psUtil.checkPatchSetNotLocked(origNotes);
if (checkAddPatchSetPermission) {
- permissionBackend
- .user(ctx.getUser())
- .database(ctx.getDb())
- .change(origNotes)
- .check(ChangePermission.ADD_PATCH_SET);
+ permissionBackend.user(ctx.getUser()).change(origNotes).check(ChangePermission.ADD_PATCH_SET);
}
projectCache.checkedGet(ctx.getProject()).checkStatePermitsWrite();
if (!validate) {
diff --git a/java/com/google/gerrit/server/change/PluginDefinedAttributesFactories.java b/java/com/google/gerrit/server/change/PluginDefinedAttributesFactories.java
new file mode 100644
index 0000000000..99281258eb
--- /dev/null
+++ b/java/com/google/gerrit/server/change/PluginDefinedAttributesFactories.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.change;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
+import com.google.gerrit.extensions.registration.Extension;
+import com.google.gerrit.server.DynamicOptions.BeanProvider;
+import com.google.gerrit.server.query.change.ChangeData;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/** Static helpers for use by {@link PluginDefinedAttributesFactory} implementations. */
+public class PluginDefinedAttributesFactories {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Nullable
+ public static ImmutableList<PluginDefinedInfo> createAll(
+ ChangeData cd,
+ BeanProvider beanProvider,
+ Stream<Extension<ChangeAttributeFactory>> attrFactories) {
+ ImmutableList<PluginDefinedInfo> result =
+ attrFactories
+ .map(e -> tryCreate(cd, beanProvider, e.getPluginName(), e.get()))
+ .filter(Objects::nonNull)
+ .collect(toImmutableList());
+ return !result.isEmpty() ? result : null;
+ }
+
+ @Nullable
+ private static PluginDefinedInfo tryCreate(
+ ChangeData cd, BeanProvider beanProvider, String plugin, ChangeAttributeFactory attrFactory) {
+ PluginDefinedInfo pdi = null;
+ try {
+ pdi = attrFactory.create(cd, beanProvider, plugin);
+ } catch (RuntimeException ex) {
+ logger.atWarning().atMostEvery(1, MINUTES).withCause(ex).log(
+ "error populating attribute on change %s from plugin %s", cd.getId(), plugin);
+ }
+ if (pdi != null) {
+ pdi.name = plugin;
+ }
+ return pdi;
+ }
+
+ private PluginDefinedAttributesFactories() {}
+}
diff --git a/java/com/google/gerrit/server/change/PluginDefinedAttributesFactory.java b/java/com/google/gerrit/server/change/PluginDefinedAttributesFactory.java
new file mode 100644
index 0000000000..08d6ce7cf5
--- /dev/null
+++ b/java/com/google/gerrit/server/change/PluginDefinedAttributesFactory.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.server.change;
+
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
+import com.google.gerrit.server.query.change.ChangeData;
+import java.util.List;
+
+public interface PluginDefinedAttributesFactory {
+ List<PluginDefinedInfo> create(ChangeData cd);
+}
diff --git a/java/com/google/gerrit/server/change/PureRevert.java b/java/com/google/gerrit/server/change/PureRevert.java
index 766b82f13a..acb3dd7879 100644
--- a/java/com/google/gerrit/server/change/PureRevert.java
+++ b/java/com/google/gerrit/server/change/PureRevert.java
@@ -14,116 +14,48 @@
package com.google.gerrit.server.change;
-import com.google.gerrit.common.Nullable;
-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.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MergeUtil;
+import com.google.gerrit.server.git.PureRevertCache;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
+import java.util.Optional;
import org.eclipse.jgit.errors.InvalidObjectIdException;
-import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.merge.ThreeWayMerger;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
+/** Can check if a change is a pure revert (= a revert with no further modifications). */
@Singleton
public class PureRevert {
- private final MergeUtil.Factory mergeUtilFactory;
- private final GitRepositoryManager repoManager;
- private final ProjectCache projectCache;
- private final ChangeNotes.Factory notesFactory;
- private final Provider<ReviewDb> dbProvider;
- private final PatchSetUtil psUtil;
+ private final PureRevertCache pureRevertCache;
@Inject
- PureRevert(
- MergeUtil.Factory mergeUtilFactory,
- GitRepositoryManager repoManager,
- ProjectCache projectCache,
- ChangeNotes.Factory notesFactory,
- Provider<ReviewDb> dbProvider,
- PatchSetUtil psUtil) {
- this.mergeUtilFactory = mergeUtilFactory;
- this.repoManager = repoManager;
- this.projectCache = projectCache;
- this.notesFactory = notesFactory;
- this.dbProvider = dbProvider;
- this.psUtil = psUtil;
+ PureRevert(PureRevertCache pureRevertCache) {
+ this.pureRevertCache = pureRevertCache;
}
- public PureRevertInfo get(ChangeNotes notes, @Nullable String claimedOriginal)
- throws OrmException, IOException, BadRequestException, ResourceConflictException {
- PatchSet currentPatchSet = psUtil.current(dbProvider.get(), notes);
+ public boolean get(ChangeNotes notes, Optional<String> claimedOriginal)
+ throws IOException, BadRequestException, ResourceConflictException {
+ PatchSet currentPatchSet = notes.getCurrentPatchSet();
if (currentPatchSet == null) {
throw new ResourceConflictException("current revision is missing");
}
-
- if (claimedOriginal == null) {
- if (notes.getChange().getRevertOf() == null) {
- throw new BadRequestException("no ID was provided and change isn't a revert");
- }
- PatchSet ps =
- psUtil.current(
- dbProvider.get(),
- notesFactory.createChecked(
- dbProvider.get(), notes.getProjectName(), notes.getChange().getRevertOf()));
- claimedOriginal = ps.getRevision().get();
+ if (!claimedOriginal.isPresent()) {
+ return pureRevertCache.isPureRevert(notes);
}
- try (Repository repo = repoManager.openRepository(notes.getProjectName());
- ObjectInserter oi = repo.newObjectInserter();
- RevWalk rw = new RevWalk(repo)) {
- RevCommit claimedOriginalCommit;
- try {
- claimedOriginalCommit = rw.parseCommit(ObjectId.fromString(claimedOriginal));
- } catch (InvalidObjectIdException | MissingObjectException e) {
- throw new BadRequestException("invalid object ID", e);
- }
- if (claimedOriginalCommit.getParentCount() == 0) {
- throw new BadRequestException("can't check against initial commit");
- }
- RevCommit claimedRevertCommit =
- rw.parseCommit(ObjectId.fromString(currentPatchSet.getRevision().get()));
- if (claimedRevertCommit.getParentCount() == 0) {
- throw new BadRequestException("claimed revert has no parents");
- }
- // Rebase claimed revert onto claimed original
- ThreeWayMerger merger =
- mergeUtilFactory
- .create(projectCache.checkedGet(notes.getProjectName()))
- .newThreeWayMerger(oi, repo.getConfig());
- merger.setBase(claimedRevertCommit.getParent(0));
- boolean success = merger.merge(claimedRevertCommit, claimedOriginalCommit);
- if (!success || merger.getResultTreeId() == null) {
- // Merge conflict during rebase
- return new PureRevertInfo(false);
- }
-
- // 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())) {
- df.setReader(oi.newReader(), repo.getConfig());
- List<DiffEntry> entries =
- df.scan(claimedOriginalCommit.getParent(0), merger.getResultTreeId());
- return new PureRevertInfo(entries.isEmpty());
- }
+ ObjectId claimedOriginalObjectId;
+ try {
+ claimedOriginalObjectId = ObjectId.fromString(claimedOriginal.get());
+ } catch (InvalidObjectIdException e) {
+ throw new BadRequestException("invalid object ID", e);
}
+
+ return pureRevertCache.isPureRevert(
+ notes.getProjectName(),
+ ObjectId.fromString(notes.getCurrentPatchSet().getRevision().get()),
+ claimedOriginalObjectId);
}
}
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index f48ea57d5d..fccda7c138 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.change;
import static com.google.common.base.Preconditions.checkState;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -26,6 +25,7 @@ import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.RebaseUtil.Base;
+import com.google.gerrit.server.git.GroupCollector;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -37,7 +37,6 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -69,7 +68,6 @@ public class RebaseChangeOp implements BatchUpdateOp {
private boolean validate = true;
private boolean checkAddPatchSetPermission = true;
private boolean forceContentMerge;
- private boolean copyApprovals = true;
private boolean detailedCommitMessage;
private boolean postMessage = true;
private boolean sendEmail = true;
@@ -127,11 +125,6 @@ public class RebaseChangeOp implements BatchUpdateOp {
return this;
}
- public RebaseChangeOp setCopyApprovals(boolean copyApprovals) {
- this.copyApprovals = copyApprovals;
- return this;
- }
-
public RebaseChangeOp setDetailedCommitMessage(boolean detailedCommitMessage) {
this.detailedCommitMessage = detailedCommitMessage;
return this;
@@ -155,7 +148,7 @@ public class RebaseChangeOp implements BatchUpdateOp {
@Override
public void updateRepo(RepoContext ctx)
throws MergeConflictException, InvalidChangeOperationException, RestApiException, IOException,
- OrmException, NoSuchChangeException, PermissionBackendException {
+ NoSuchChangeException, PermissionBackendException {
// Ok that originalPatchSet was not read in a transaction, since we just
// need its revision.
RevId oldRev = originalPatchSet.getRevision();
@@ -184,16 +177,16 @@ public class RebaseChangeOp implements BatchUpdateOp {
baseCommitId.name());
rebasedPatchSetId =
- ChangeUtil.nextPatchSetIdFromChangeRefsMap(
- ctx.getRepoView().getRefs(originalPatchSet.getId().getParentKey().toRefPrefix()),
+ ChangeUtil.nextPatchSetIdFromChangeRefs(
+ ctx.getRepoView()
+ .getRefs(originalPatchSet.getId().getParentKey().toRefPrefix())
+ .keySet(),
notes.getChange().currentPatchSetId());
patchSetInserter =
patchSetInserterFactory
.create(notes, rebasedPatchSetId, rebasedCommit)
.setDescription("Rebase")
- .setNotify(NotifyHandling.NONE)
.setFireRevisionCreated(fireRevisionCreated)
- .setCopyApprovals(copyApprovals)
.setCheckAddPatchSetPermission(checkAddPatchSetPermission)
.setValidate(validate)
.setSendEmail(sendEmail);
@@ -206,22 +199,27 @@ public class RebaseChangeOp implements BatchUpdateOp {
+ " was rebased");
}
- if (base != null) {
- patchSetInserter.setGroups(base.patchSet().getGroups());
+ 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());
+ } else {
+ // If the base is merged, start a new relation chain.
+ patchSetInserter.setGroups(GroupCollector.getDefaultGroups(rebasedCommit));
+ }
}
patchSetInserter.updateRepo(ctx);
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws ResourceConflictException, OrmException, IOException {
+ public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, IOException {
boolean ret = patchSetInserter.updateChange(ctx);
rebasedPatchSet = patchSetInserter.getPatchSet();
return ret;
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
patchSetInserter.postUpdate(ctx);
}
diff --git a/java/com/google/gerrit/server/change/RebaseUtil.java b/java/com/google/gerrit/server/change/RebaseUtil.java
index 22f98b87df..a4cf5ba5d5 100644
--- a/java/com/google/gerrit/server/change/RebaseUtil.java
+++ b/java/com/google/gerrit/server/change/RebaseUtil.java
@@ -17,20 +17,18 @@ 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.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.Change.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -46,18 +44,15 @@ public class RebaseUtil {
private final Provider<InternalChangeQuery> queryProvider;
private final ChangeNotes.Factory notesFactory;
- private final Provider<ReviewDb> dbProvider;
private final PatchSetUtil psUtil;
@Inject
RebaseUtil(
Provider<InternalChangeQuery> queryProvider,
ChangeNotes.Factory notesFactory,
- Provider<ReviewDb> dbProvider,
PatchSetUtil psUtil) {
this.queryProvider = queryProvider;
this.notesFactory = notesFactory;
- this.dbProvider = dbProvider;
this.psUtil = psUtil;
}
@@ -67,7 +62,7 @@ public class RebaseUtil {
return true;
} catch (RestApiException e) {
return false;
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
logger.atWarning().withCause(e).log(
"Error checking if patch set %s on %s can be rebased", patchSet.getId(), dest);
return false;
@@ -88,9 +83,7 @@ public class RebaseUtil {
public abstract PatchSet patchSet();
}
- public Base parseBase(RevisionResource rsrc, String base) throws OrmException {
- ReviewDb db = dbProvider.get();
-
+ public Base parseBase(RevisionResource rsrc, String base) {
// Try parsing the base as a ref string.
PatchSet.Id basePatchSetId = PatchSet.Id.fromRef(base);
if (basePatchSetId != null) {
@@ -98,8 +91,7 @@ public class RebaseUtil {
ChangeNotes baseNotes = notesFor(rsrc, baseChangeId);
if (baseNotes != null) {
return Base.create(
- notesFor(rsrc, basePatchSetId.getParentKey()),
- psUtil.get(db, baseNotes, basePatchSetId));
+ notesFor(rsrc, basePatchSetId.getParentKey()), psUtil.get(baseNotes, basePatchSetId));
}
}
@@ -108,7 +100,7 @@ public class RebaseUtil {
if (baseChangeId != null) {
ChangeNotes baseNotes = notesFor(rsrc, new Change.Id(baseChangeId));
if (baseNotes != null) {
- return Base.create(baseNotes, psUtil.current(db, baseNotes));
+ return Base.create(baseNotes, psUtil.current(baseNotes));
}
}
@@ -127,11 +119,11 @@ public class RebaseUtil {
return ret;
}
- private ChangeNotes notesFor(RevisionResource rsrc, Change.Id id) throws OrmException {
+ private ChangeNotes notesFor(RevisionResource rsrc, Change.Id id) {
if (rsrc.getChange().getId().equals(id)) {
return rsrc.getNotes();
}
- return notesFactory.createChecked(dbProvider.get(), rsrc.getProject(), id);
+ return notesFactory.createChecked(rsrc.getProject(), id);
}
/**
@@ -147,11 +139,10 @@ public class RebaseUtil {
* @return the commit onto which the patch set should be rebased.
* @throws RestApiException if rebase is not possible.
* @throws IOException if accessing the repository fails.
- * @throws OrmException if accessing the database fails.
*/
public ObjectId findBaseRevision(
PatchSet patchSet, Branch.NameKey destBranch, Repository git, RevWalk rw)
- throws RestApiException, IOException, OrmException {
+ throws RestApiException, IOException {
String baseRev = null;
RevCommit commit = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
@@ -171,12 +162,12 @@ public class RebaseUtil {
continue;
}
Change depChange = cd.change();
- if (depChange.getStatus() == Status.ABANDONED) {
+ if (depChange.isAbandoned()) {
throw new ResourceConflictException(
"Cannot rebase a change with an abandoned parent: " + depChange.getKey());
}
- if (depChange.getStatus().isOpen()) {
+ if (depChange.isNew()) {
if (depPatchSet.getId().equals(depChange.currentPatchSetId())) {
throw new ResourceConflictException(
"Change is already based on the latest patch set of the dependent change.");
diff --git a/java/com/google/gerrit/server/change/ReviewerAdder.java b/java/com/google/gerrit/server/change/ReviewerAdder.java
index fe6090f385..a6ad559fac 100644
--- a/java/com/google/gerrit/server/change/ReviewerAdder.java
+++ b/java/com/google/gerrit/server/change/ReviewerAdder.java
@@ -24,23 +24,20 @@ import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
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.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.api.changes.RecipientType;
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
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.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.mail.Address;
@@ -51,7 +48,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -63,7 +59,6 @@ import com.google.gerrit.server.group.GroupResolver;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -73,7 +68,6 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -89,6 +83,8 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
public class ReviewerAdder {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
public static final int DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK = 10;
public static final int DEFAULT_MAX_REVIEWERS = 20;
@@ -150,8 +146,6 @@ public class ReviewerAdder {
private final AccountLoader.Factory accountLoaderFactory;
private final Config cfg;
private final ReviewerJson json;
- private final NotesMigration migration;
- private final NotifyUtil notifyUtil;
private final ProjectCache projectCache;
private final Provider<AnonymousUser> anonymousProvider;
private final AddReviewersOp.Factory addReviewersOpFactory;
@@ -166,8 +160,6 @@ public class ReviewerAdder {
AccountLoader.Factory accountLoaderFactory,
@GerritServerConfig Config cfg,
ReviewerJson json,
- NotesMigration migration,
- NotifyUtil notifyUtil,
ProjectCache projectCache,
Provider<AnonymousUser> anonymousProvider,
AddReviewersOp.Factory addReviewersOpFactory,
@@ -179,8 +171,6 @@ public class ReviewerAdder {
this.accountLoaderFactory = accountLoaderFactory;
this.cfg = cfg;
this.json = json;
- this.migration = migration;
- this.notifyUtil = notifyUtil;
this.projectCache = projectCache;
this.anonymousProvider = anonymousProvider;
this.addReviewersOpFactory = addReviewersOpFactory;
@@ -190,7 +180,6 @@ public class ReviewerAdder {
/**
* Prepare application of a single {@link AddReviewerInput}.
*
- * @param db database.
* @param notes change notes.
* @param user user performing the reviewer addition.
* @param input input describing user or group to add as a reviewer.
@@ -198,48 +187,47 @@ public class ReviewerAdder {
* @return handle describing the addition operation. If the {@code op} field is present, this
* operation may be added to a {@code BatchUpdate}. Otherwise, the {@code error} field
* contains information about an error that occurred
- * @throws OrmException
* @throws IOException
* @throws PermissionBackendException
* @throws ConfigInvalidException
*/
public ReviewerAddition prepare(
- ReviewDb db, ChangeNotes notes, CurrentUser user, AddReviewerInput input, boolean allowGroup)
- throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
+ ChangeNotes notes, CurrentUser user, AddReviewerInput input, boolean allowGroup)
+ throws IOException, PermissionBackendException, ConfigInvalidException {
requireNonNull(input.reviewer);
- ListMultimap<RecipientType, Account.Id> accountsToNotify;
- try {
- accountsToNotify = notifyUtil.resolveAccounts(input.notifyDetails);
- } catch (BadRequestException e) {
- return fail(input, FailureType.OTHER, e.getMessage());
- }
boolean confirmed = input.confirmed();
boolean allowByEmail =
projectCache
.checkedGet(notes.getProjectName())
.is(BooleanProjectConfig.ENABLE_REVIEWER_BY_EMAIL);
- ReviewerAddition byAccountId =
- addByAccountId(db, input, notes, user, accountsToNotify, allowGroup, allowByEmail);
+ ReviewerAddition byAccountId = addByAccountId(input, notes, user);
ReviewerAddition wholeGroup = null;
- if (byAccountId == null || !byAccountId.exactMatchFound) {
- wholeGroup =
- addWholeGroup(
- db, input, notes, user, accountsToNotify, confirmed, allowGroup, allowByEmail);
+ if (!byAccountId.exactMatchFound) {
+ wholeGroup = addWholeGroup(input, notes, user, confirmed, allowGroup, allowByEmail);
if (wholeGroup != null && wholeGroup.exactMatchFound) {
return wholeGroup;
}
}
- if (byAccountId != null) {
+ if (wholeGroup != null
+ && byAccountId.failureType == FailureType.NOT_FOUND
+ && wholeGroup.failureType == FailureType.NOT_FOUND) {
+ return fail(
+ byAccountId.input,
+ FailureType.NOT_FOUND,
+ byAccountId.result.error + "\n" + wholeGroup.result.error);
+ }
+
+ if (byAccountId.failureType != FailureType.NOT_FOUND) {
return byAccountId;
}
if (wholeGroup != null) {
return wholeGroup;
}
- return addByEmail(db, input, notes, user, accountsToNotify);
+ return addByEmail(input, notes, user);
}
public ReviewerAddition ccCurrentUser(CurrentUser user, RevisionResource revision) {
@@ -249,58 +237,30 @@ public class ReviewerAdder {
revision.getUser(),
ImmutableSet.of(user.getAccountId()),
null,
- ImmutableListMultimap.of(),
true);
}
@Nullable
private ReviewerAddition addByAccountId(
- ReviewDb db,
- AddReviewerInput input,
- ChangeNotes notes,
- CurrentUser user,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
- boolean allowGroup,
- boolean allowByEmail)
- throws OrmException, PermissionBackendException, IOException, ConfigInvalidException {
+ AddReviewerInput input, ChangeNotes notes, CurrentUser user)
+ throws PermissionBackendException, IOException, ConfigInvalidException {
IdentifiedUser reviewerUser;
boolean exactMatchFound = false;
try {
- reviewerUser = accountResolver.parse(input.reviewer);
+ reviewerUser = accountResolver.resolve(input.reviewer).asUniqueUser();
if (input.reviewer.equalsIgnoreCase(reviewerUser.getName())
|| input.reviewer.equals(String.valueOf(reviewerUser.getAccountId()))) {
exactMatchFound = true;
}
- } catch (UnprocessableEntityException | AuthException e) {
- // AuthException won't occur since the user is authenticated at this point.
- if (!allowGroup && !allowByEmail) {
- // Only return failure if we aren't going to try other interpretations.
- return fail(
- input,
- FailureType.NOT_FOUND,
- MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, input.reviewer));
- }
- return null;
+ } catch (UnprocessableEntityException e) {
+ // Caller might choose to ignore this NOT_FOUND result if they find another result e.g. by
+ // group, but if not, the error message will be useful.
+ return fail(input, FailureType.NOT_FOUND, e.getMessage());
}
- if (isValidReviewer(db, notes.getChange().getDest(), reviewerUser.getAccount())) {
+ if (isValidReviewer(notes.getChange().getDest(), reviewerUser.getAccount())) {
return new ReviewerAddition(
- input,
- notes,
- user,
- ImmutableSet.of(reviewerUser.getAccountId()),
- null,
- accountsToNotify,
- exactMatchFound);
- }
- if (!reviewerUser.getAccount().isActive()) {
- if (allowByEmail && input.state() == CC) {
- return null;
- }
- return fail(
- input,
- FailureType.OTHER,
- MessageFormat.format(ChangeMessages.get().reviewerInactive, input.reviewer));
+ input, notes, user, ImmutableSet.of(reviewerUser.getAccountId()), null, exactMatchFound);
}
return fail(
input,
@@ -310,11 +270,9 @@ public class ReviewerAdder {
@Nullable
private ReviewerAddition addWholeGroup(
- ReviewDb db,
AddReviewerInput input,
ChangeNotes notes,
CurrentUser user,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
boolean confirmed,
boolean allowGroup,
boolean allowByEmail)
@@ -357,6 +315,8 @@ public class ReviewerAdder {
// reviewers
int maxAllowed = cfg.getInt("addreviewer", "maxAllowed", DEFAULT_MAX_REVIEWERS);
if (maxAllowed > 0 && members.size() > maxAllowed) {
+ logger.atFine().log(
+ "Adding %d group members is not allowed (maxAllowed = %d)", members.size(), maxAllowed);
return fail(
input,
FailureType.OTHER,
@@ -367,6 +327,9 @@ public class ReviewerAdder {
int maxWithoutConfirmation =
cfg.getInt("addreviewer", "maxWithoutConfirmation", DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK);
if (!confirmed && maxWithoutConfirmation > 0 && members.size() > maxWithoutConfirmation) {
+ logger.atFine().log(
+ "Adding %d group members as reviewer requires confirmation (maxWithoutConfirmation = %d)",
+ members.size(), maxWithoutConfirmation);
return fail(
input,
FailureType.OTHER,
@@ -376,28 +339,19 @@ public class ReviewerAdder {
}
for (Account member : members) {
- if (isValidReviewer(db, notes.getChange().getDest(), member)) {
+ if (isValidReviewer(notes.getChange().getDest(), member)) {
reviewers.add(member.getId());
}
}
- return new ReviewerAddition(input, notes, user, reviewers, null, accountsToNotify, true);
+ return new ReviewerAddition(input, notes, user, reviewers, null, true);
}
@Nullable
- private ReviewerAddition addByEmail(
- ReviewDb db,
- AddReviewerInput input,
- ChangeNotes notes,
- CurrentUser user,
- ListMultimap<RecipientType, Account.Id> accountsToNotify)
+ private ReviewerAddition addByEmail(AddReviewerInput input, ChangeNotes notes, CurrentUser user)
throws PermissionBackendException {
try {
- permissionBackend
- .user(anonymousProvider.get())
- .database(db)
- .change(notes)
- .check(ChangePermission.READ);
+ permissionBackend.user(anonymousProvider.get()).change(notes).check(ChangePermission.READ);
} catch (AuthException e) {
return fail(
input,
@@ -405,13 +359,6 @@ public class ReviewerAdder {
MessageFormat.format(ChangeMessages.get().reviewerCantSeeChange, input.reviewer));
}
- if (!migration.readChanges()) {
- // addByEmail depends on NoteDb.
- return fail(
- input,
- FailureType.NOT_FOUND,
- MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, input.reviewer));
- }
Address adr = Address.tryParse(input.reviewer);
if (adr == null || !validator.isValid(adr.getEmail())) {
return fail(
@@ -419,25 +366,16 @@ public class ReviewerAdder {
FailureType.NOT_FOUND,
MessageFormat.format(ChangeMessages.get().reviewerInvalid, input.reviewer));
}
- return new ReviewerAddition(
- input, notes, user, null, ImmutableList.of(adr), accountsToNotify, true);
+ return new ReviewerAddition(input, notes, user, null, ImmutableList.of(adr), true);
}
- private boolean isValidReviewer(ReviewDb db, Branch.NameKey branch, Account member)
+ private boolean isValidReviewer(Branch.NameKey branch, Account member)
throws PermissionBackendException {
- if (!member.isActive()) {
- return false;
- }
-
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())
- .database(db)
- .ref(branch)
- .check(RefPermission.READ);
+ permissionBackend.absentUser(member.getId()).ref(branch).check(RefPermission.READ);
return true;
} catch (AuthException e) {
return false;
@@ -483,7 +421,6 @@ public class ReviewerAdder {
CurrentUser caller,
@Nullable Iterable<Account.Id> reviewers,
@Nullable Iterable<Address> reviewersByEmail,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
boolean exactMatchFound) {
checkArgument(
reviewers != null || reviewersByEmail != null,
@@ -498,9 +435,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(), input.notify, accountsToNotify);
+ op = addReviewersOpFactory.create(this.reviewers, this.reviewersByEmail, state());
this.exactMatchFound = exactMatchFound;
}
@@ -512,14 +447,14 @@ public class ReviewerAdder {
: ImmutableSet.of();
}
- public void gatherResults(ChangeData cd) throws OrmException, PermissionBackendException {
+ public void gatherResults(ChangeData cd) throws PermissionBackendException {
checkState(op != null, "addition did not result in an update op");
checkState(op.getResult() != null, "op did not return a result");
// Generate result details and fill AccountLoader. This occurs outside
// the Op because the accounts are in a different table.
AddReviewersOp.Result opResult = op.getResult();
- if (migration.readChanges() && state() == CC) {
+ if (state() == CC) {
result.ccs = Lists.newArrayListWithCapacity(opResult.addedCCs().size());
for (Account.Id accountId : opResult.addedCCs()) {
result.ccs.add(json.format(new ReviewerInfo(accountId.get()), accountId, cd));
@@ -569,12 +504,11 @@ public class ReviewerAdder {
}
public ReviewerAdditionList prepare(
- ReviewDb db,
ChangeNotes notes,
CurrentUser user,
Iterable<? extends AddReviewerInput> inputs,
boolean allowGroup)
- throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
+ throws IOException, PermissionBackendException, ConfigInvalidException {
// Process CC ops before reviewer ops, so a user that appears in both lists ends up as a
// reviewer; the last call to ChangeUpdate#putReviewer wins. This can happen if the caller
// specifies the same string twice, or less obviously if they specify multiple groups with
@@ -586,11 +520,17 @@ public class ReviewerAdder {
Streams.stream(inputs)
.sorted(
comparing(
- i -> i.state(), Ordering.explicit(ReviewerState.CC, ReviewerState.REVIEWER)))
+ AddReviewerInput::state,
+ Ordering.explicit(ReviewerState.CC, ReviewerState.REVIEWER)))
.collect(toImmutableList());
List<ReviewerAddition> additions = new ArrayList<>();
for (AddReviewerInput input : sorted) {
- additions.add(prepare(db, notes, user, input, allowGroup));
+ ReviewerAddition addition = prepare(notes, user, input, allowGroup);
+ if (addition.op != null) {
+ // Assume any callers preparing a list of batch insertions are handling their own email.
+ addition.op.suppressEmail();
+ }
+ additions.add(addition);
}
return new ReviewerAdditionList(additions);
}
@@ -616,7 +556,7 @@ public class ReviewerAdder {
// We never call updateRepo on the addition ops, which is only ok because it's a no-op.
public void updateChange(ChangeContext ctx, PatchSet patchSet)
- throws OrmException, RestApiException, IOException {
+ throws RestApiException, IOException {
for (ReviewerAddition addition : additions()) {
addition.op.setPatchSet(patchSet);
addition.op.updateChange(ctx);
diff --git a/java/com/google/gerrit/server/change/ReviewerJson.java b/java/com/google/gerrit/server/change/ReviewerJson.java
index ef2c926393..2742bb944d 100644
--- a/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -27,7 +27,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.permissions.LabelPermission;
@@ -36,9 +35,7 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Collection;
import java.util.List;
@@ -46,7 +43,6 @@ import java.util.TreeMap;
@Singleton
public class ReviewerJson {
- private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
private final ChangeData.Factory changeDataFactory;
private final ApprovalsUtil approvalsUtil;
@@ -55,13 +51,11 @@ public class ReviewerJson {
@Inject
ReviewerJson(
- Provider<ReviewDb> db,
PermissionBackend permissionBackend,
ChangeData.Factory changeDataFactory,
ApprovalsUtil approvalsUtil,
AccountLoader.Factory accountLoaderFactory,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
- this.db = db;
this.permissionBackend = permissionBackend;
this.changeDataFactory = changeDataFactory;
this.approvalsUtil = approvalsUtil;
@@ -70,13 +64,13 @@ public class ReviewerJson {
}
public List<ReviewerInfo> format(Collection<ReviewerResource> rsrcs)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
List<ReviewerInfo> infos = Lists.newArrayListWithCapacity(rsrcs.size());
AccountLoader loader = accountLoaderFactory.create(true);
ChangeData cd = null;
for (ReviewerResource rsrc : rsrcs) {
if (cd == null || !cd.getId().equals(rsrc.getChangeId())) {
- cd = changeDataFactory.create(db.get(), rsrc.getChangeResource().getNotes());
+ cd = changeDataFactory.create(rsrc.getChangeResource().getNotes());
}
ReviewerInfo info;
if (rsrc.isByEmail()) {
@@ -93,19 +87,18 @@ public class ReviewerJson {
return infos;
}
- public List<ReviewerInfo> format(ReviewerResource rsrc)
- throws OrmException, PermissionBackendException {
- return format(ImmutableList.<ReviewerResource>of(rsrc));
+ public List<ReviewerInfo> format(ReviewerResource rsrc) throws PermissionBackendException {
+ return format(ImmutableList.of(rsrc));
}
public ReviewerInfo format(ReviewerInfo out, Account.Id reviewerAccountId, ChangeData cd)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
PatchSet.Id psId = cd.change().currentPatchSetId();
return format(
out,
reviewerAccountId,
cd,
- approvalsUtil.byPatchSetUser(db.get(), cd.notes(), psId, reviewerAccountId, null, null));
+ approvalsUtil.byPatchSetUser(cd.notes(), psId, reviewerAccountId, null, null));
}
public ReviewerInfo format(
@@ -113,7 +106,7 @@ public class ReviewerJson {
Account.Id reviewerAccountId,
ChangeData cd,
Iterable<PatchSetApproval> approvals)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
LabelTypes labelTypes = cd.getLabelTypes();
out.approvals = new TreeMap<>(labelTypes.nameComparator());
@@ -128,8 +121,7 @@ public class ReviewerJson {
// do not exist in the DB.
PatchSet ps = cd.currentPatchSet();
if (ps != null) {
- PermissionBackend.ForChange perm =
- permissionBackend.absentUser(reviewerAccountId).database(db).change(cd);
+ PermissionBackend.ForChange perm = permissionBackend.absentUser(reviewerAccountId).change(cd);
for (SubmitRecord rec : submitRuleEvaluator.evaluate(cd)) {
if (rec.labels == null) {
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index b67028da60..aa733cdfe3 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -47,7 +47,6 @@ 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.PatchSet.Id;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
@@ -66,7 +65,6 @@ 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.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -156,8 +154,7 @@ public class RevisionJson {
* depending on the options provided when constructing this instance.
*/
public RevisionInfo getRevisionInfo(ChangeData cd, PatchSet in)
- throws PatchListNotAvailableException, GpgException, OrmException, IOException,
- PermissionBackendException {
+ throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
AccountLoader accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
try (Repository repo = openRepoIfNecessary(cd.project());
RevWalk rw = newRevWalk(repo)) {
@@ -212,10 +209,9 @@ public class RevisionJson {
AccountLoader accountLoader,
ChangeData cd,
Map<PatchSet.Id, PatchSet> map,
- Optional<Id> limitToPsId,
+ Optional<PatchSet.Id> limitToPsId,
ChangeInfo changeInfo)
- throws PatchListNotAvailableException, GpgException, OrmException, IOException,
- PermissionBackendException {
+ throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
Map<String, RevisionInfo> res = new LinkedHashMap<>();
try (Repository repo = openRepoIfNecessary(cd.project());
RevWalk rw = newRevWalk(repo)) {
@@ -240,7 +236,7 @@ public class RevisionJson {
}
private Map<String, FetchInfo> makeFetchMap(ChangeData cd, PatchSet in)
- throws PermissionBackendException, OrmException, IOException {
+ throws PermissionBackendException, IOException {
Map<String, FetchInfo> r = new LinkedHashMap<>();
for (Extension<DownloadScheme> e : downloadSchemes) {
String schemeName = e.getExportName();
@@ -276,8 +272,7 @@ public class RevisionJson {
@Nullable RevWalk rw,
boolean fillCommit,
@Nullable ChangeInfo changeInfo)
- throws PatchListNotAvailableException, GpgException, OrmException, IOException,
- PermissionBackendException {
+ throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
Change c = cd.change();
RevisionInfo out = new RevisionInfo();
out.isCurrent = in.getId().equals(c.currentPatchSetId());
@@ -351,14 +346,13 @@ public class RevisionJson {
* lazyload}.
*/
private PermissionBackend.ForChange permissionBackendForChange(
- PermissionBackend.WithUser withUser, ChangeData cd) throws OrmException {
+ PermissionBackend.WithUser withUser, ChangeData cd) {
return lazyLoad
? withUser.change(cd)
: withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
}
- private boolean isWorldReadable(ChangeData cd)
- throws OrmException, PermissionBackendException, IOException {
+ private boolean isWorldReadable(ChangeData cd) throws PermissionBackendException, IOException {
try {
permissionBackendForChange(permissionBackend.user(anonymous), cd)
.check(ChangePermission.READ);
diff --git a/java/com/google/gerrit/server/change/RevisionResource.java b/java/com/google/gerrit/server/change/RevisionResource.java
index deb5022294..caafe24a57 100644
--- a/java/com/google/gerrit/server/change/RevisionResource.java
+++ b/java/com/google/gerrit/server/change/RevisionResource.java
@@ -34,7 +34,7 @@ public class RevisionResource implements RestResource, HasETag {
public static final TypeLiteral<RestView<RevisionResource>> REVISION_KIND =
new TypeLiteral<RestView<RevisionResource>>() {};
- public static RevisionResource createNonCachable(ChangeResource change, PatchSet ps) {
+ public static RevisionResource createNonCacheable(ChangeResource change, PatchSet ps) {
return new RevisionResource(change, ps, Optional.empty(), false);
}
@@ -52,11 +52,11 @@ public class RevisionResource implements RestResource, HasETag {
}
private RevisionResource(
- ChangeResource change, PatchSet ps, Optional<ChangeEdit> edit, boolean cachable) {
+ ChangeResource change, PatchSet ps, Optional<ChangeEdit> edit, boolean cacheable) {
this.change = change;
this.ps = ps;
this.edit = edit;
- this.cacheable = cachable;
+ this.cacheable = cacheable;
}
public boolean isCacheable() {
diff --git a/java/com/google/gerrit/server/change/SetAssigneeOp.java b/java/com/google/gerrit/server/change/SetAssigneeOp.java
index f61e95fa0a..8d350c32bd 100644
--- a/java/com/google/gerrit/server/change/SetAssigneeOp.java
+++ b/java/com/google/gerrit/server/change/SetAssigneeOp.java
@@ -32,7 +32,6 @@ import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.validators.AssigneeValidationListener;
import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -74,7 +73,7 @@ public class SetAssigneeOp implements BatchUpdateOp {
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException, RestApiException {
+ public boolean updateChange(ChangeContext ctx) throws RestApiException {
change = ctx.getChange();
if (newAssignee.getAccountId().equals(change.getAssignee())) {
return false;
@@ -99,7 +98,7 @@ public class SetAssigneeOp implements BatchUpdateOp {
return true;
}
- private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+ private void addMessage(ChangeContext ctx, ChangeUpdate update) {
StringBuilder msg = new StringBuilder();
msg.append("Assignee ");
if (oldAssignee == null) {
@@ -113,11 +112,11 @@ public class SetAssigneeOp implements BatchUpdateOp {
}
ChangeMessage cmsg =
ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_SET_ASSIGNEE);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
try {
SetAssigneeSender cm =
setAssigneeSenderFactory.create(
diff --git a/java/com/google/gerrit/server/change/SetHashtagsOp.java b/java/com/google/gerrit/server/change/SetHashtagsOp.java
index d11b2df4c1..abc4eeec14 100644
--- a/java/com/google/gerrit/server/change/SetHashtagsOp.java
+++ b/java/com/google/gerrit/server/change/SetHashtagsOp.java
@@ -32,14 +32,12 @@ import com.google.gerrit.server.change.HashtagsUtil.InvalidHashtagException;
import com.google.gerrit.server.extensions.events.HashtagsEdited;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.validators.HashtagValidationListener;
import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -52,7 +50,6 @@ public class SetHashtagsOp implements BatchUpdateOp {
SetHashtagsOp create(HashtagsInput input);
}
- private final NotesMigration notesMigration;
private final ChangeMessagesUtil cmUtil;
private final PluginSetContext<HashtagValidationListener> validationListeners;
private final HashtagsEdited hashtagsEdited;
@@ -67,12 +64,10 @@ public class SetHashtagsOp implements BatchUpdateOp {
@Inject
SetHashtagsOp(
- NotesMigration notesMigration,
ChangeMessagesUtil cmUtil,
PluginSetContext<HashtagValidationListener> validationListeners,
HashtagsEdited hashtagsEdited,
@Assisted @Nullable HashtagsInput input) {
- this.notesMigration = notesMigration;
this.cmUtil = cmUtil;
this.validationListeners = validationListeners;
this.hashtagsEdited = hashtagsEdited;
@@ -86,11 +81,7 @@ public class SetHashtagsOp implements BatchUpdateOp {
@Override
public boolean updateChange(ChangeContext ctx)
- throws AuthException, BadRequestException, MethodNotAllowedException, OrmException,
- IOException {
- if (!notesMigration.readChanges()) {
- throw new MethodNotAllowedException("Cannot add hashtags; NoteDb is disabled");
- }
+ throws AuthException, BadRequestException, MethodNotAllowedException, IOException {
if (input == null || (input.add == null && input.remove == null)) {
updatedHashtags = ImmutableSortedSet.of();
return false;
@@ -125,13 +116,13 @@ public class SetHashtagsOp implements BatchUpdateOp {
}
}
- private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+ private void addMessage(ChangeContext ctx, ChangeUpdate update) {
StringBuilder msg = new StringBuilder();
appendHashtagMessage(msg, "added", toAdd);
appendHashtagMessage(msg, "removed", toRemove);
ChangeMessage cmsg =
ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_SET_HASHTAGS);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
}
private void appendHashtagMessage(StringBuilder b, String action, Set<String> hashtags) {
@@ -153,7 +144,7 @@ public class SetHashtagsOp implements BatchUpdateOp {
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
if (updated() && fireEvent) {
hashtagsEdited.fire(
change, ctx.getAccount(), updatedHashtags, toAdd, toRemove, ctx.getWhen());
diff --git a/java/com/google/gerrit/server/change/SetPrivateOp.java b/java/com/google/gerrit/server/change/SetPrivateOp.java
index b7f444c27b..1600fd592a 100644
--- a/java/com/google/gerrit/server/change/SetPrivateOp.java
+++ b/java/com/google/gerrit/server/change/SetPrivateOp.java
@@ -16,11 +16,13 @@ package com.google.gerrit.server.change;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
+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;
import com.google.gerrit.server.extensions.events.PrivateStateChanged;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -28,7 +30,6 @@ import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -44,7 +45,7 @@ public class SetPrivateOp implements BatchUpdateOp {
}
public interface Factory {
- SetPrivateOp create(ChangeMessagesUtil cmUtil, boolean isPrivate, @Nullable Input input);
+ SetPrivateOp create(boolean isPrivate, @Nullable Input input);
}
private final PrivateStateChanged privateStateChanged;
@@ -55,12 +56,13 @@ public class SetPrivateOp implements BatchUpdateOp {
private Change change;
private PatchSet ps;
+ private boolean isNoOp;
@Inject
SetPrivateOp(
PrivateStateChanged privateStateChanged,
PatchSetUtil psUtil,
- @Assisted ChangeMessagesUtil cmUtil,
+ ChangeMessagesUtil cmUtil,
@Assisted boolean isPrivate,
@Assisted @Nullable Input input) {
this.privateStateChanged = privateStateChanged;
@@ -71,10 +73,21 @@ public class SetPrivateOp implements BatchUpdateOp {
}
@Override
- public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, OrmException {
+ public boolean updateChange(ChangeContext ctx)
+ throws ResourceConflictException, BadRequestException {
change = ctx.getChange();
+ if (ctx.getChange().isPrivate() == isPrivate) {
+ // No-op
+ isNoOp = true;
+ return false;
+ }
+
+ if (isPrivate && !change.isNew()) {
+ throw new BadRequestException(
+ String.format("cannot set %s change to private", ChangeUtil.status(change)));
+ }
ChangeNotes notes = ctx.getNotes();
- ps = psUtil.get(ctx.getDb(), notes, change.currentPatchSetId());
+ ps = psUtil.get(notes, change.currentPatchSetId());
ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
change.setPrivate(isPrivate);
change.setLastUpdatedOn(ctx.getWhen());
@@ -85,10 +98,12 @@ public class SetPrivateOp implements BatchUpdateOp {
@Override
public void postUpdate(Context ctx) {
- privateStateChanged.fire(change, ps, ctx.getAccount(), ctx.getWhen());
+ if (!isNoOp) {
+ privateStateChanged.fire(change, ps, ctx.getAccount(), ctx.getWhen());
+ }
}
- private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+ private void addMessage(ChangeContext ctx, ChangeUpdate update) {
Change c = ctx.getChange();
StringBuilder buf = new StringBuilder(c.isPrivate() ? "Set private" : "Unset private");
@@ -105,6 +120,6 @@ public class SetPrivateOp implements BatchUpdateOp {
c.isPrivate()
? ChangeMessagesUtil.TAG_SET_PRIVATE
: ChangeMessagesUtil.TAG_UNSET_PRIVATE);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
}
}
diff --git a/java/com/google/gerrit/server/change/TestSubmitInput.java b/java/com/google/gerrit/server/change/TestSubmitInput.java
index b681bf8d06..bb85e66547 100644
--- a/java/com/google/gerrit/server/change/TestSubmitInput.java
+++ b/java/com/google/gerrit/server/change/TestSubmitInput.java
@@ -1,3 +1,17 @@
+// 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.change;
import com.google.common.annotations.VisibleForTesting;
diff --git a/java/com/google/gerrit/server/change/WalkSorter.java b/java/com/google/gerrit/server/change/WalkSorter.java
index 916a62bb36..5945a0cb9d 100644
--- a/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/java/com/google/gerrit/server/change/WalkSorter.java
@@ -24,11 +24,11 @@ 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.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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayDeque;
@@ -73,7 +73,7 @@ public class WalkSorter {
}
try {
return in.get(0).data().change().getProject();
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new IllegalStateException(e);
}
});
@@ -98,7 +98,7 @@ public class WalkSorter {
return this;
}
- public Iterable<PatchSetData> sort(Iterable<ChangeData> in) throws OrmException, IOException {
+ public Iterable<PatchSetData> sort(Iterable<ChangeData> in) throws IOException {
ListMultimap<Project.NameKey, ChangeData> byProject =
MultimapBuilder.hashKeys().arrayListValues().build();
for (ChangeData cd : in) {
@@ -114,7 +114,7 @@ public class WalkSorter {
}
private List<PatchSetData> sortProject(Project.NameKey project, Collection<ChangeData> in)
- throws OrmException, IOException {
+ throws IOException {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
rw.setRetainBody(retainBody);
@@ -217,7 +217,7 @@ public class WalkSorter {
}
private ListMultimap<RevCommit, PatchSetData> byCommit(RevWalk rw, Collection<ChangeData> in)
- throws OrmException, IOException {
+ throws IOException {
ListMultimap<RevCommit, PatchSetData> byCommit =
MultimapBuilder.hashKeys(in.size()).arrayListValues(1).build();
for (ChangeData cd : in) {
diff --git a/java/com/google/gerrit/server/change/WorkInProgressOp.java b/java/com/google/gerrit/server/change/WorkInProgressOp.java
index 71cb4c4639..f3f1a2974d 100644
--- a/java/com/google/gerrit/server/change/WorkInProgressOp.java
+++ b/java/com/google/gerrit/server/change/WorkInProgressOp.java
@@ -14,30 +14,21 @@
package com.google.gerrit.server.change;
-import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.restapi.AuthException;
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.PatchSetUtil;
import com.google.gerrit.server.extensions.events.WorkInProgressStateChanged;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-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.ProjectPermission;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -59,42 +50,14 @@ public class WorkInProgressOp implements BatchUpdateOp {
WorkInProgressOp create(boolean workInProgress, Input in);
}
- public static void checkPermissions(
- PermissionBackend permissionBackend, CurrentUser user, Change change)
- throws PermissionBackendException, AuthException {
- if (!user.isIdentifiedUser()) {
- throw new AuthException("Authentication required");
- }
-
- if (change.getOwner().equals(user.asIdentifiedUser().getAccountId())) {
- return;
- }
-
- try {
- permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
- return;
- } catch (AuthException e) {
- // Skip.
- }
-
- try {
- permissionBackend
- .user(user)
- .project(change.getProject())
- .check(ProjectPermission.WRITE_CONFIG);
- } catch (AuthException exp) {
- throw new AuthException("not allowed to toggle work in progress", exp);
- }
- }
-
private final ChangeMessagesUtil cmUtil;
private final EmailReviewComments.Factory email;
private final PatchSetUtil psUtil;
private final boolean workInProgress;
private final Input in;
- private final NotifyHandling notify;
private final WorkInProgressStateChanged stateChanged;
+ private boolean sendEmail = true;
private Change change;
private ChangeNotes notes;
private PatchSet ps;
@@ -114,16 +77,17 @@ public class WorkInProgressOp implements BatchUpdateOp {
this.stateChanged = stateChanged;
this.workInProgress = workInProgress;
this.in = in;
- notify =
- MoreObjects.firstNonNull(
- in.notify, workInProgress ? NotifyHandling.NONE : NotifyHandling.ALL);
+ }
+
+ public void suppressEmail() {
+ this.sendEmail = false;
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
change = ctx.getChange();
notes = ctx.getNotes();
- ps = psUtil.get(ctx.getDb(), ctx.getNotes(), change.currentPatchSetId());
+ ps = psUtil.get(ctx.getNotes(), change.currentPatchSetId());
ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
change.setWorkInProgress(workInProgress);
if (!change.hasReviewStarted() && !workInProgress) {
@@ -135,7 +99,7 @@ public class WorkInProgressOp implements BatchUpdateOp {
return true;
}
- private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
+ private void addMessage(ChangeContext ctx, ChangeUpdate update) {
Change c = ctx.getChange();
StringBuilder buf =
new StringBuilder(c.isWorkInProgress() ? "Set Work In Progress" : "Set Ready For Review");
@@ -154,19 +118,21 @@ public class WorkInProgressOp implements BatchUpdateOp {
? ChangeMessagesUtil.TAG_SET_WIP
: ChangeMessagesUtil.TAG_SET_READY);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
}
@Override
public void postUpdate(Context ctx) {
stateChanged.fire(change, ps, ctx.getAccount(), ctx.getWhen());
- if (workInProgress || notify.ordinal() < NotifyHandling.OWNER_REVIEWERS.ordinal()) {
+ NotifyResolver.Result notify = ctx.getNotify(change.getId());
+ if (workInProgress
+ || notify.handling().compareTo(NotifyHandling.OWNER_REVIEWERS) < 0
+ || !sendEmail) {
return;
}
email
.create(
notify,
- ImmutableListMultimap.of(),
notes,
ps,
ctx.getIdentifiedUser(),
diff --git a/java/com/google/gerrit/server/config/AuthConfig.java b/java/com/google/gerrit/server/config/AuthConfig.java
index d3f9186106..de57d0430c 100644
--- a/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/java/com/google/gerrit/server/config/AuthConfig.java
@@ -22,8 +22,8 @@ import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.auth.openid.OpenIdProviderPattern;
-import com.google.gwtjsonrpc.server.SignedToken;
-import com.google.gwtjsonrpc.server.XsrfException;
+import com.google.gerrit.server.mail.SignedToken;
+import com.google.gerrit.server.mail.XsrfException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
diff --git a/java/com/google/gerrit/server/config/CacheResource.java b/java/com/google/gerrit/server/config/CacheResource.java
index ffa7b5aa5f..7a835b1391 100644
--- a/java/com/google/gerrit/server/config/CacheResource.java
+++ b/java/com/google/gerrit/server/config/CacheResource.java
@@ -33,15 +33,7 @@ public class CacheResource extends ConfigResource {
}
public CacheResource(String pluginName, String cacheName, Cache<?, ?> cache) {
- this(
- pluginName,
- cacheName,
- new Provider<Cache<?, ?>>() {
- @Override
- public Cache<?, ?> get() {
- return cache;
- }
- });
+ this(pluginName, cacheName, () -> cache);
}
public String getName() {
diff --git a/java/com/google/gerrit/server/config/ChangeCleanupConfig.java b/java/com/google/gerrit/server/config/ChangeCleanupConfig.java
index 05594aac74..0a38ee8326 100644
--- a/java/com/google/gerrit/server/config/ChangeCleanupConfig.java
+++ b/java/com/google/gerrit/server/config/ChangeCleanupConfig.java
@@ -29,6 +29,7 @@ public class ChangeCleanupConfig {
private static final String KEY_ABANDON_AFTER = "abandonAfter";
private static final String KEY_ABANDON_IF_MERGEABLE = "abandonIfMergeable";
private static final String KEY_ABANDON_MESSAGE = "abandonMessage";
+ private static final String KEY_CLEANUP_ACCOUNT_PATCH_REVIEW = "cleanupAccountPatchReview";
private static final String DEFAULT_ABANDON_MESSAGE =
"Auto-Abandoned due to inactivity, see "
+ "${URL}\n"
@@ -39,6 +40,7 @@ public class ChangeCleanupConfig {
private final Optional<Schedule> schedule;
private final long abandonAfter;
private final boolean abandonIfMergeable;
+ private final boolean cleanupAccountPatchReview;
private final String abandonMessage;
@Inject
@@ -47,6 +49,8 @@ public class ChangeCleanupConfig {
schedule = ScheduleConfig.createSchedule(cfg, SECTION);
abandonAfter = readAbandonAfter(cfg);
abandonIfMergeable = cfg.getBoolean(SECTION, null, KEY_ABANDON_IF_MERGEABLE, true);
+ cleanupAccountPatchReview =
+ cfg.getBoolean(SECTION, null, KEY_CLEANUP_ACCOUNT_PATCH_REVIEW, false);
abandonMessage = readAbandonMessage(cfg);
}
@@ -73,6 +77,10 @@ public class ChangeCleanupConfig {
return abandonIfMergeable;
}
+ public boolean getCleanupAccountPatchReview() {
+ return cleanupAccountPatchReview;
+ }
+
public String getAbandonMessage() {
String docUrl =
urlFormatter.get().getDocUrl("user-change-cleanup.html", "auto-abandon").orElse("");
diff --git a/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java b/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java
deleted file mode 100644
index 4c9e5f034d..0000000000
--- a/java/com/google/gerrit/server/config/ChangeUpdateExecutor.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2012 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.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.inject.BindingAnnotation;
-import java.lang.annotation.Retention;
-
-/**
- * Marker on the global {@link ListeningExecutorService} used by asynchronous {@link BatchUpdate}s.
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface ChangeUpdateExecutor {}
diff --git a/java/com/google/gerrit/server/config/ConfigResource.java b/java/com/google/gerrit/server/config/ConfigResource.java
index ec0e0c2c9e..f2b7c8e0d9 100644
--- a/java/com/google/gerrit/server/config/ConfigResource.java
+++ b/java/com/google/gerrit/server/config/ConfigResource.java
@@ -14,11 +14,22 @@
package com.google.gerrit.server.config;
+import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.TypeLiteral;
+import java.util.concurrent.TimeUnit;
public class ConfigResource implements RestResource {
public static final TypeLiteral<RestView<ConfigResource>> CONFIG_KIND =
new TypeLiteral<RestView<ConfigResource>>() {};
+
+ /**
+ * Default cache control that gets set on the 'Cache-Control' header for responses on this
+ * resource that are cacheable.
+ *
+ * <p>Not all resources are cacheable and in fact the vast majority might not be. Caching is a
+ * trade-off between the freshness of data and the number of QPS that the web UI sends.
+ */
+ public static CacheControl DEFAULT_CACHE_CONTROL = CacheControl.PRIVATE(300, TimeUnit.SECONDS);
}
diff --git a/java/com/google/gerrit/server/config/DisableReverseDnsLookup.java b/java/com/google/gerrit/server/config/DisableReverseDnsLookup.java
deleted file mode 100644
index 336edeb491..0000000000
--- a/java/com/google/gerrit/server/config/DisableReverseDnsLookup.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2014 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;
-
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface DisableReverseDnsLookup {}
diff --git a/java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java b/java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java
deleted file mode 100644
index 87d6bac24c..0000000000
--- a/java/com/google/gerrit/server/config/DisableReverseDnsLookupProvider.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2014 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 org.eclipse.jgit.lib.Config;
-
-public class DisableReverseDnsLookupProvider implements Provider<Boolean> {
- private final boolean disableReverseDnsLookup;
-
- @Inject
- DisableReverseDnsLookupProvider(@GerritServerConfig Config config) {
- disableReverseDnsLookup = config.getBoolean("gerrit", null, "disableReverseDnsLookup", false);
- }
-
- @Override
- public Boolean get() {
- return disableReverseDnsLookup;
- }
-}
diff --git a/java/com/google/gerrit/server/config/EnableReverseDnsLookup.java b/java/com/google/gerrit/server/config/EnableReverseDnsLookup.java
new file mode 100644
index 0000000000..ec57338d14
--- /dev/null
+++ b/java/com/google/gerrit/server/config/EnableReverseDnsLookup.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2014 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;
+
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface EnableReverseDnsLookup {}
diff --git a/java/com/google/gerrit/server/config/EnableReverseDnsLookupProvider.java b/java/com/google/gerrit/server/config/EnableReverseDnsLookupProvider.java
new file mode 100644
index 0000000000..71086a95eb
--- /dev/null
+++ b/java/com/google/gerrit/server/config/EnableReverseDnsLookupProvider.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2014 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 org.eclipse.jgit.lib.Config;
+
+public class EnableReverseDnsLookupProvider implements Provider<Boolean> {
+ private final Boolean enableReverseDnsLookup;
+
+ @Inject
+ EnableReverseDnsLookupProvider(@GerritServerConfig Config config) {
+ enableReverseDnsLookup = config.getBoolean("gerrit", null, "enableReverseDnsLookup", false);
+ }
+
+ @Override
+ public Boolean get() {
+ return enableReverseDnsLookup;
+ }
+}
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 7882ec71fb..5dad6a80f8 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -29,6 +29,7 @@ import com.google.gerrit.extensions.config.DownloadCommand;
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.AccountIndexedListener;
import com.google.gerrit.extensions.events.AgreementSignupListener;
import com.google.gerrit.extensions.events.AssigneeChangedListener;
@@ -60,7 +61,6 @@ 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.systemstatus.MessageOfTheDay;
import com.google.gerrit.extensions.webui.BranchWebLink;
import com.google.gerrit.extensions.webui.DiffWebLink;
import com.google.gerrit.extensions.webui.FileHistoryWebLink;
@@ -75,14 +75,13 @@ import com.google.gerrit.server.AnonymousUser;
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.IdentifiedUser;
-import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountDeactivator;
import com.google.gerrit.server.account.AccountExternalIdCreator;
import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.AccountVisibilityProvider;
import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.account.EmailExpander;
@@ -98,6 +97,7 @@ import com.google.gerrit.server.avatar.AvatarProvider;
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.ChangeFinder;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeKindCacheImpl;
@@ -116,6 +116,7 @@ import com.google.gerrit.server.git.GitModule;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.MergedByPushOp;
import com.google.gerrit.server.git.NotesBranchUtil;
+import com.google.gerrit.server.git.PureRevertCache;
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.TransferConfig;
@@ -159,9 +160,10 @@ import com.google.gerrit.server.project.ProjectNameLockManager;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
-import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.query.change.ConflictsCacheImpl;
+import com.google.gerrit.server.quota.QuotaEnforcer;
import com.google.gerrit.server.restapi.change.SuggestReviewers;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
@@ -213,7 +215,6 @@ public class GerritGlobalModule extends FactoryModule {
bind(IdGenerator.class);
bind(RulesCache.class);
bind(BlameCache.class).to(BlameCacheImpl.class);
- bind(Sequences.class);
install(authModule);
install(AccountCacheImpl.module());
install(BatchUpdate.module());
@@ -229,6 +230,7 @@ public class GerritGlobalModule extends FactoryModule {
install(SubmitStrategy.module());
install(TagCache.module());
install(OAuthTokenCache.module());
+ install(PureRevertCache.module());
install(new AccessControlModule());
install(new CmdLineParserModule());
@@ -237,7 +239,7 @@ public class GerritGlobalModule extends FactoryModule {
install(new GitModule());
install(new GroupDbModule());
install(new GroupModule());
- install(new NoteDbModule(cfg));
+ install(new NoteDbModule());
install(new PrologModule());
install(new DefaultSubmitRule.Module());
install(new IgnoreSelfApprovalRule.Module());
@@ -245,11 +247,10 @@ public class GerritGlobalModule extends FactoryModule {
install(new SshAddressesModule());
install(ThreadLocalRequestContext.module());
- bind(AccountResolver.class);
-
factory(CapabilityCollection.Factory.class);
factory(ChangeData.AssistedFactory.class);
factory(ChangeJson.AssistedFactory.class);
+ factory(ChangeIsVisibleToPredicate.Factory.class);
factory(LabelsJson.Factory.class);
factory(MergeUtil.Factory.class);
factory(PatchScriptFactory.Factory.class);
@@ -283,8 +284,8 @@ public class GerritGlobalModule extends FactoryModule {
bind(SoyTofu.class).annotatedWith(MailTemplates.class).toProvider(MailSoyTofuProvider.class);
bind(FromAddressGenerator.class).toProvider(FromAddressGeneratorProvider.class).in(SINGLETON);
bind(Boolean.class)
- .annotatedWith(DisableReverseDnsLookup.class)
- .toProvider(DisableReverseDnsLookupProvider.class)
+ .annotatedWith(EnableReverseDnsLookup.class)
+ .toProvider(EnableReverseDnsLookupProvider.class)
.in(SINGLETON);
bind(PatchSetInfoFactory.class);
@@ -297,6 +298,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicMap.mapOf(binder(), new TypeLiteral<Cache<?, ?>>() {});
DynamicSet.setOf(binder(), CacheRemovalListener.class);
DynamicMap.mapOf(binder(), CapabilityDefinition.class);
+ DynamicMap.mapOf(binder(), PluginProjectPermissionDefinition.class);
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
DynamicSet.setOf(binder(), AssigneeChangedListener.class);
DynamicSet.setOf(binder(), ChangeAbandonedListener.class);
@@ -351,7 +353,6 @@ public class GerritGlobalModule extends FactoryModule {
DynamicItem.itemOf(binder(), AvatarProvider.class);
DynamicSet.setOf(binder(), LifecycleListener.class);
DynamicSet.setOf(binder(), TopMenu.class);
- DynamicSet.setOf(binder(), MessageOfTheDay.class);
DynamicMap.mapOf(binder(), DownloadScheme.class);
DynamicMap.mapOf(binder(), DownloadCommand.class);
DynamicMap.mapOf(binder(), CloneCommand.class);
@@ -378,6 +379,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicItem.itemOf(binder(), MergeSuperSetComputation.class);
DynamicItem.itemOf(binder(), ProjectNameLockManager.class);
DynamicSet.setOf(binder(), SubmitRule.class);
+ DynamicSet.setOf(binder(), QuotaEnforcer.class);
DynamicMap.mapOf(binder(), MailFilter.class);
bind(MailFilter.class).annotatedWith(Exports.named("ListMailFilter")).to(ListMailFilter.class);
@@ -388,9 +390,10 @@ public class GerritGlobalModule extends FactoryModule {
factory(UploadValidators.Factory.class);
DynamicSet.setOf(binder(), UploadValidationListener.class);
+ DynamicMap.mapOf(binder(), DynamicOptions.DynamicBean.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
- DynamicMap.mapOf(binder(), ChangeQueryProcessor.ChangeAttributeFactory.class);
+ DynamicSet.setOf(binder(), ChangeAttributeFactory.class);
install(new GitwebConfig.LegacyModule(cfg));
diff --git a/java/com/google/gerrit/server/config/GerritOptions.java b/java/com/google/gerrit/server/config/GerritOptions.java
index 0192ddd824..17b65c9f21 100644
--- a/java/com/google/gerrit/server/config/GerritOptions.java
+++ b/java/com/google/gerrit/server/config/GerritOptions.java
@@ -14,29 +14,21 @@
package com.google.gerrit.server.config;
-import org.eclipse.jgit.lib.Config;
-
public class GerritOptions {
private final boolean headless;
private final boolean slave;
- private final boolean enableGwtUi;
private final boolean forcePolyGerritDev;
- public GerritOptions(Config cfg, boolean headless, boolean slave, boolean forcePolyGerritDev) {
+ public GerritOptions(boolean headless, boolean slave, boolean forcePolyGerritDev) {
+ this.headless = headless;
this.slave = slave;
- this.enableGwtUi = cfg.getBoolean("gerrit", null, "enableGwtUi", true);
this.forcePolyGerritDev = forcePolyGerritDev;
- this.headless = headless;
}
public boolean headless() {
return headless;
}
- public boolean enableGwtUi() {
- return !headless && enableGwtUi;
- }
-
public boolean enableMasterFeatures() {
return !slave;
}
diff --git a/java/com/google/gerrit/server/config/GerritRequestModule.java b/java/com/google/gerrit/server/config/GerritRequestModule.java
index 6a1103f157..6732513f0c 100644
--- a/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -26,7 +26,6 @@ public class GerritRequestModule extends FactoryModule {
@Override
protected void configure() {
bind(RequestCleanup.class).in(RequestScoped.class);
- bind(RequestScopedReviewDbProvider.class);
bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
}
}
diff --git a/java/com/google/gerrit/server/config/GerritServerConfigProvider.java b/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
index 8df21da1b3..952f7d38ee 100644
--- a/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
+++ b/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
@@ -18,7 +18,6 @@ import static java.util.stream.Collectors.joining;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.securestore.SecureStore;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -99,7 +98,7 @@ public class GerritServerConfigProvider implements Provider<Config> {
private static void checkNoteDbConfig(FileBasedConfig noteDbConfig) {
List<String> bad = new ArrayList<>();
for (String section : noteDbConfig.getSections()) {
- if (section.equals(NotesMigration.SECTION_NOTE_DB)) {
+ if (section.equals("noteDb")) {
continue;
}
for (String subsection : noteDbConfig.getSubsections(section)) {
diff --git a/java/com/google/gerrit/server/config/GerritServerConfigReloader.java b/java/com/google/gerrit/server/config/GerritServerConfigReloader.java
index 09c1074054..5ecf6ed43b 100644
--- a/java/com/google/gerrit/server/config/GerritServerConfigReloader.java
+++ b/java/com/google/gerrit/server/config/GerritServerConfigReloader.java
@@ -17,9 +17,9 @@ package com.google.gerrit.server.config;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -29,11 +29,12 @@ public class GerritServerConfigReloader {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final GerritServerConfigProvider configProvider;
- private final DynamicSet<GerritConfigListener> configListeners;
+ private final PluginSetContext<GerritConfigListener> configListeners;
@Inject
GerritServerConfigReloader(
- GerritServerConfigProvider configProvider, DynamicSet<GerritConfigListener> configListeners) {
+ GerritServerConfigProvider configProvider,
+ PluginSetContext<GerritConfigListener> configListeners) {
this.configProvider = configProvider;
this.configListeners = configListeners;
}
@@ -53,9 +54,7 @@ public class GerritServerConfigReloader {
public Multimap<UpdateResult, ConfigUpdateEntry> fireUpdatedConfigEvent(
ConfigUpdatedEvent event) {
Multimap<UpdateResult, ConfigUpdateEntry> updates = ArrayListMultimap.create();
- for (GerritConfigListener configListener : configListeners) {
- updates.putAll(configListener.configUpdated(event));
- }
+ configListeners.runEach(l -> updates.putAll(l.configUpdated(event)));
return updates;
}
}
diff --git a/java/com/google/gerrit/server/config/GerritServerIdProvider.java b/java/com/google/gerrit/server/config/GerritServerIdProvider.java
index c609cc4dca..4898f55f31 100644
--- a/java/com/google/gerrit/server/config/GerritServerIdProvider.java
+++ b/java/com/google/gerrit/server/config/GerritServerIdProvider.java
@@ -48,9 +48,7 @@ public class GerritServerIdProvider implements Provider<String> {
// We're not generally supposed to do work in provider constructors, but this is a bit of a
// special case because we really need to have the ID available by the time the dbInjector
- // is created. This even applies during MigrateToNoteDb, which otherwise would have been a
- // reasonable place to do the ID generation. Fortunately, it's not much work, and it happens
- // once.
+ // is created. Fortunately, it's not much work, and it happens once.
id = generate();
Config newCfg = readGerritConfig(sitePaths);
newCfg.setString(SECTION, null, KEY, id);
diff --git a/java/com/google/gerrit/server/config/ProjectConfigEntry.java b/java/com/google/gerrit/server/config/ProjectConfigEntry.java
index 5515f0ebc3..92ae10ade5 100644
--- a/java/com/google/gerrit/server/config/ProjectConfigEntry.java
+++ b/java/com/google/gerrit/server/config/ProjectConfigEntry.java
@@ -302,12 +302,16 @@ public class ProjectConfigEntry {
private final GitRepositoryManager repoManager;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
UpdateChecker(
- GitRepositoryManager repoManager, DynamicMap<ProjectConfigEntry> pluginConfigEntries) {
+ GitRepositoryManager repoManager,
+ DynamicMap<ProjectConfigEntry> pluginConfigEntries,
+ ProjectConfig.Factory projectConfigFactory) {
this.repoManager = repoManager;
this.pluginConfigEntries = pluginConfigEntries;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
@@ -361,7 +365,7 @@ public class ProjectConfigEntry {
return null;
}
try (Repository repo = repoManager.openRepository(p)) {
- ProjectConfig pc = new ProjectConfig(p);
+ ProjectConfig pc = projectConfigFactory.create(p);
pc.load(repo, id);
return pc;
}
diff --git a/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java b/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
deleted file mode 100644
index fdb400bda2..0000000000
--- a/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
+++ /dev/null
@@ -1,66 +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.server.config;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.RequestCleanup;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.servlet.RequestScoped;
-
-/** Provides {@link ReviewDb} database handle live only for this request. */
-@RequestScoped
-public class RequestScopedReviewDbProvider implements Provider<ReviewDb> {
- private final SchemaFactory<ReviewDb> schema;
- private final Provider<RequestCleanup> cleanup;
- private ReviewDb db;
-
- @Inject
- public RequestScopedReviewDbProvider(
- final SchemaFactory<ReviewDb> schema, Provider<RequestCleanup> cleanup) {
- this.schema = schema;
- this.cleanup = cleanup;
- }
-
- @SuppressWarnings("resource")
- @Override
- public ReviewDb get() {
- if (db == null) {
- ReviewDb c;
- try {
- c = schema.open();
- } catch (OrmException e) {
- throw new ProvisionException("Cannot open ReviewDb", e);
- }
- try {
- cleanup
- .get()
- .add(
- () -> {
- c.close();
- db = null;
- });
- } catch (Throwable e) {
- c.close();
- throw new ProvisionException("Cannot defer cleanup of ReviewDb", e);
- }
- db = c;
- }
- return db;
- }
-}
diff --git a/java/com/google/gerrit/server/config/SitePaths.java b/java/com/google/gerrit/server/config/SitePaths.java
index 11ec50cf04..47b6336ff3 100644
--- a/java/com/google/gerrit/server/config/SitePaths.java
+++ b/java/com/google/gerrit/server/config/SitePaths.java
@@ -43,7 +43,6 @@ public final class SitePaths {
public final Path mail_dir;
public final Path hooks_dir;
public final Path static_dir;
- public final Path themes_dir;
public final Path index_dir;
public final Path gerrit_sh;
@@ -67,8 +66,7 @@ public final class SitePaths {
public final Path site_css;
public final Path site_header;
public final Path site_footer;
- // For PolyGerrit UI only.
- public final Path site_theme;
+ public final Path site_theme; // For PolyGerrit UI only.
public final Path site_gitweb;
/** {@code true} if {@link #site_path} has not been initialized. */
@@ -90,7 +88,6 @@ public final class SitePaths {
mail_dir = etc_dir.resolve("mail");
hooks_dir = p.resolve("hooks");
static_dir = p.resolve("static");
- themes_dir = p.resolve("themes");
index_dir = p.resolve("index");
gerrit_sh = bin_dir.resolve("gerrit.sh");
diff --git a/java/com/google/gerrit/server/config/SysExecutorModule.java b/java/com/google/gerrit/server/config/SysExecutorModule.java
index f552434395..e7f454056b 100644
--- a/java/com/google/gerrit/server/config/SysExecutorModule.java
+++ b/java/com/google/gerrit/server/config/SysExecutorModule.java
@@ -14,19 +14,13 @@
package com.google.gerrit.server.config;
-import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
-import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
/**
@@ -73,28 +67,4 @@ public class SysExecutorModule extends AbstractModule {
}
return queues.createQueue(poolSize, "FanOut");
}
-
- @Provides
- @Singleton
- @ChangeUpdateExecutor
- public ListeningExecutorService createChangeUpdateExecutor(@GerritServerConfig Config config) {
- int poolSize = config.getInt("receive", null, "changeUpdateThreads", 1);
- if (poolSize <= 1) {
- return MoreExecutors.newDirectExecutorService();
- }
- return MoreExecutors.listeningDecorator(
- new LoggingContextAwareExecutorService(
- MoreExecutors.getExitingExecutorService(
- new ThreadPoolExecutor(
- 1,
- poolSize,
- 10,
- TimeUnit.MINUTES,
- new ArrayBlockingQueue<Runnable>(poolSize),
- new ThreadFactoryBuilder()
- .setNameFormat("ChangeUpdate-%d")
- .setDaemon(true)
- .build(),
- new ThreadPoolExecutor.CallerRunsPolicy()))));
- }
}
diff --git a/java/com/google/gerrit/server/config/TrackingFootersProvider.java b/java/com/google/gerrit/server/config/TrackingFootersProvider.java
index 6010870e70..1611da902d 100644
--- a/java/com/google/gerrit/server/config/TrackingFootersProvider.java
+++ b/java/com/google/gerrit/server/config/TrackingFootersProvider.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.config;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.TrackingId;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -33,6 +32,8 @@ import org.eclipse.jgit.lib.Config;
public class TrackingFootersProvider implements Provider<TrackingFooters> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final int MAX_LENGTH = 10;
+
private static final String TRACKING_ID_TAG = "trackingid";
private static final String FOOTER_TAG = "footer";
private static final String SYSTEM_TAG = "system";
@@ -59,11 +60,11 @@ public class TrackingFootersProvider implements Provider<TrackingFooters> {
configValid = false;
logger.atSevere().log(
"Missing %s.%s.%s in gerrit.config", TRACKING_ID_TAG, name, SYSTEM_TAG);
- } else if (system.length() > TrackingId.TRACKING_SYSTEM_MAX_CHAR) {
+ } else if (system.length() > MAX_LENGTH) {
configValid = false;
logger.atSevere().log(
"String too long \"%s\" in gerrit.config %s.%s.%s (max %d char)",
- system, TRACKING_ID_TAG, name, SYSTEM_TAG, TrackingId.TRACKING_SYSTEM_MAX_CHAR);
+ system, TRACKING_ID_TAG, name, SYSTEM_TAG, MAX_LENGTH);
}
String match = cfg.getString(TRACKING_ID_TAG, name, REGEX_TAG);
diff --git a/java/com/google/gerrit/server/documentation/MarkdownFormatterHeader.java b/java/com/google/gerrit/server/documentation/MarkdownFormatterHeader.java
index 00471fd548..00f7ec125d 100644
--- a/java/com/google/gerrit/server/documentation/MarkdownFormatterHeader.java
+++ b/java/com/google/gerrit/server/documentation/MarkdownFormatterHeader.java
@@ -18,7 +18,6 @@ import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.anchorlink.AnchorLink;
import com.vladsch.flexmark.ext.anchorlink.internal.AnchorLinkNodeRenderer;
-import com.vladsch.flexmark.html.CustomNodeRenderer;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.html.HtmlRenderer.HtmlRendererExtension;
import com.vladsch.flexmark.html.HtmlWriter;
@@ -57,25 +56,12 @@ public class MarkdownFormatterHeader {
@Override
public Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() {
- return new HashSet<NodeRenderingHandler<? extends Node>>(
+ return new HashSet<>(
Arrays.asList(
new NodeRenderingHandler<>(
AnchorLink.class,
- new CustomNodeRenderer<AnchorLink>() {
- @Override
- public void render(
- AnchorLink node, NodeRendererContext context, HtmlWriter html) {
- HeadingNodeRenderer.this.render(node, context);
- }
- }),
- new NodeRenderingHandler<>(
- Heading.class,
- new CustomNodeRenderer<Heading>() {
- @Override
- public void render(Heading node, NodeRendererContext context, HtmlWriter html) {
- HeadingNodeRenderer.this.render(node, context, html);
- }
- })));
+ (node, context, html) -> HeadingNodeRenderer.this.render(node, context)),
+ new NodeRenderingHandler<>(Heading.class, HeadingNodeRenderer.this::render)));
}
void render(final AnchorLink node, final NodeRendererContext context) {
@@ -116,25 +102,15 @@ public class MarkdownFormatterHeader {
.withAttr()
.tagLine(
"h" + node.getLevel(),
- new Runnable() {
- @Override
- public void run() {
- html.srcPos(node.getText()).withAttr().tag("span");
- context.renderChildren(node);
- html.tag("/span");
- }
+ () -> {
+ html.srcPos(node.getText()).withAttr().tag("span");
+ context.renderChildren(node);
+ html.tag("/span");
});
} else {
html.srcPos(node.getText())
.withAttr()
- .tagLine(
- "h" + node.getLevel(),
- new Runnable() {
- @Override
- public void run() {
- context.renderChildren(node);
- }
- });
+ .tagLine("h" + node.getLevel(), () -> context.renderChildren(node));
}
} else {
context.delegateRender();
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index ce359a9b14..246a53f124 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -23,7 +23,7 @@ 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.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -43,7 +43,6 @@ import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -52,6 +51,7 @@ import java.sql.Timestamp;
import java.util.List;
import java.util.Optional;
import java.util.TimeZone;
+import org.eclipse.jgit.dircache.InvalidPathException;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -80,7 +80,6 @@ public class ChangeEditModifier {
private final TimeZone tz;
private final ChangeIndexer indexer;
- private final Provider<ReviewDb> reviewDb;
private final Provider<CurrentUser> currentUser;
private final PermissionBackend permissionBackend;
private final ChangeEditUtil changeEditUtil;
@@ -91,14 +90,12 @@ public class ChangeEditModifier {
ChangeEditModifier(
@GerritPersonIdent PersonIdent gerritIdent,
ChangeIndexer indexer,
- Provider<ReviewDb> reviewDb,
Provider<CurrentUser> currentUser,
PermissionBackend permissionBackend,
ChangeEditUtil changeEditUtil,
PatchSetUtil patchSetUtil,
ProjectCache projectCache) {
this.indexer = indexer;
- this.reviewDb = reviewDb;
this.currentUser = currentUser;
this.permissionBackend = permissionBackend;
this.tz = gerritIdent.getTimeZone();
@@ -117,7 +114,7 @@ public class ChangeEditModifier {
* @throws PermissionBackendException
*/
public void createEdit(Repository repository, ChangeNotes notes)
- throws AuthException, IOException, InvalidChangeOperationException, OrmException,
+ throws AuthException, IOException, InvalidChangeOperationException,
PermissionBackendException, ResourceConflictException {
assertCanEdit(notes);
@@ -145,8 +142,8 @@ public class ChangeEditModifier {
* @throws PermissionBackendException
*/
public void rebaseEdit(Repository repository, ChangeNotes notes)
- throws AuthException, InvalidChangeOperationException, IOException, OrmException,
- MergeConflictException, PermissionBackendException, ResourceConflictException {
+ throws AuthException, InvalidChangeOperationException, IOException, MergeConflictException,
+ PermissionBackendException, ResourceConflictException {
assertCanEdit(notes);
Optional<ChangeEdit> optionalChangeEdit = lookupChangeEdit(notes);
@@ -168,7 +165,7 @@ public class ChangeEditModifier {
}
private void rebase(Repository repository, ChangeEdit changeEdit, PatchSet currentPatchSet)
- throws IOException, MergeConflictException, InvalidChangeOperationException, OrmException {
+ throws IOException, MergeConflictException, InvalidChangeOperationException {
RevCommit currentEditCommit = changeEdit.getEditCommit();
if (currentEditCommit.getParentCount() == 0) {
throw new InvalidChangeOperationException(
@@ -210,7 +207,7 @@ public class ChangeEditModifier {
* @throws BadRequestException if the commit message is malformed
*/
public void modifyMessage(Repository repository, ChangeNotes notes, String newCommitMessage)
- throws AuthException, IOException, UnchangedCommitMessageException, OrmException,
+ throws AuthException, IOException, UnchangedCommitMessageException,
PermissionBackendException, BadRequestException, ResourceConflictException {
assertCanEdit(notes);
newCommitMessage = CommitMessageUtil.checkAndSanitizeCommitMessage(newCommitMessage);
@@ -247,13 +244,14 @@ public class ChangeEditModifier {
* @param filePath the path of the file whose contents should be modified
* @param newContent the new file content
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
+ * @throws BadRequestException if the user provided bad input (e.g. invalid file paths)
* @throws InvalidChangeOperationException if the file already had the specified content
* @throws PermissionBackendException
* @throws ResourceConflictException if the project state does not permit the operation
*/
public void modifyFile(
Repository repository, ChangeNotes notes, String filePath, RawInput newContent)
- throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+ throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
PermissionBackendException, ResourceConflictException {
modifyTree(repository, notes, new ChangeFileContentModification(filePath, newContent));
}
@@ -266,12 +264,13 @@ public class ChangeEditModifier {
* @param notes the {@link ChangeNotes} of the change whose change edit should be modified
* @param file path of the file which should be deleted
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
+ * @throws BadRequestException if the user provided bad input (e.g. invalid file paths)
* @throws InvalidChangeOperationException if the file does not exist
* @throws PermissionBackendException
* @throws ResourceConflictException if the project state does not permit the operation
*/
public void deleteFile(Repository repository, ChangeNotes notes, String file)
- throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+ throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
PermissionBackendException, ResourceConflictException {
modifyTree(repository, notes, new DeleteFileModification(file));
}
@@ -285,6 +284,7 @@ public class ChangeEditModifier {
* @param currentFilePath the current path/name of the file
* @param newFilePath the desired path/name of the file
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
+ * @throws BadRequestException if the user provided bad input (e.g. invalid file paths)
* @throws InvalidChangeOperationException if the file was already renamed to the specified new
* name
* @throws PermissionBackendException
@@ -292,7 +292,7 @@ public class ChangeEditModifier {
*/
public void renameFile(
Repository repository, ChangeNotes notes, String currentFilePath, String newFilePath)
- throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+ throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
PermissionBackendException, ResourceConflictException {
modifyTree(repository, notes, new RenameFileModification(currentFilePath, newFilePath));
}
@@ -310,14 +310,14 @@ public class ChangeEditModifier {
* @throws PermissionBackendException
*/
public void restoreFile(Repository repository, ChangeNotes notes, String file)
- throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+ throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
PermissionBackendException, ResourceConflictException {
modifyTree(repository, notes, new RestoreFileModification(file));
}
private void modifyTree(
Repository repository, ChangeNotes notes, TreeModification treeModification)
- throws AuthException, IOException, OrmException, InvalidChangeOperationException,
+ throws AuthException, BadRequestException, IOException, InvalidChangeOperationException,
PermissionBackendException, ResourceConflictException {
assertCanEdit(notes);
@@ -362,8 +362,8 @@ public class ChangeEditModifier {
ChangeNotes notes,
PatchSet patchSet,
List<TreeModification> treeModifications)
- throws AuthException, IOException, InvalidChangeOperationException, MergeConflictException,
- OrmException, PermissionBackendException, ResourceConflictException {
+ throws AuthException, BadRequestException, IOException, InvalidChangeOperationException,
+ MergeConflictException, PermissionBackendException, ResourceConflictException {
assertCanEdit(notes);
Optional<ChangeEdit> optionalChangeEdit = lookupChangeEdit(notes);
@@ -394,27 +394,21 @@ public class ChangeEditModifier {
}
private void assertCanEdit(ChangeNotes notes)
- throws AuthException, PermissionBackendException, IOException, ResourceConflictException,
- OrmException {
+ throws AuthException, PermissionBackendException, IOException, ResourceConflictException {
if (!currentUser.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
Change c = notes.getChange();
- if (!c.getStatus().isOpen()) {
+ if (!c.isNew()) {
throw new ResourceConflictException(
- String.format(
- "change %s is %s", c.getChangeId(), c.getStatus().toString().toLowerCase()));
+ String.format("change %s is %s", c.getChangeId(), ChangeUtil.status(c)));
}
// Not allowed to edit if the current patch set is locked.
patchSetUtil.checkPatchSetNotLocked(notes);
try {
- permissionBackend
- .currentUser()
- .database(reviewDb)
- .change(notes)
- .check(ChangePermission.ADD_PATCH_SET);
+ permissionBackend.currentUser().change(notes).check(ChangePermission.ADD_PATCH_SET);
projectCache.checkedGet(notes.getProjectName()).checkStatePermitsWrite();
} catch (AuthException denied) {
throw new AuthException("edit not permitted", denied);
@@ -450,14 +444,13 @@ public class ChangeEditModifier {
return changeEditUtil.byChange(notes);
}
- private PatchSet getBasePatchSet(Optional<ChangeEdit> optionalChangeEdit, ChangeNotes notes)
- throws OrmException {
+ private PatchSet getBasePatchSet(Optional<ChangeEdit> optionalChangeEdit, ChangeNotes notes) {
Optional<PatchSet> editBasePatchSet = optionalChangeEdit.map(ChangeEdit::getBasePatchSet);
return editBasePatchSet.isPresent() ? editBasePatchSet.get() : lookupCurrentPatchSet(notes);
}
- private PatchSet lookupCurrentPatchSet(ChangeNotes notes) throws OrmException {
- return patchSetUtil.current(reviewDb.get(), notes);
+ private PatchSet lookupCurrentPatchSet(ChangeNotes notes) {
+ return patchSetUtil.current(notes);
}
private static boolean isBasedOn(ChangeEdit changeEdit, PatchSet patchSet) {
@@ -480,10 +473,15 @@ public class ChangeEditModifier {
private static ObjectId createNewTree(
Repository repository, RevCommit baseCommit, List<TreeModification> treeModifications)
- throws IOException, InvalidChangeOperationException {
- TreeCreator treeCreator = new TreeCreator(baseCommit);
- treeCreator.addTreeModifications(treeModifications);
- ObjectId newTreeId = treeCreator.createNewTreeAndGetId(repository);
+ throws BadRequestException, IOException, InvalidChangeOperationException {
+ ObjectId newTreeId;
+ try {
+ TreeCreator treeCreator = new TreeCreator(baseCommit);
+ treeCreator.addTreeModifications(treeModifications);
+ newTreeId = treeCreator.createNewTreeAndGetId(repository);
+ } catch (InvalidPathException e) {
+ throw new BadRequestException(e.getMessage());
+ }
if (ObjectId.equals(newTreeId, baseCommit.getTree())) {
throw new InvalidChangeOperationException("no changes were made");
@@ -543,7 +541,7 @@ public class ChangeEditModifier {
PatchSet basePatchSet,
ObjectId newEditCommitId,
Timestamp timestamp)
- throws IOException, OrmException {
+ throws IOException {
Change change = notes.getChange();
String editRefName = getEditRefName(change, basePatchSet);
updateReference(repository, editRefName, ObjectId.zeroId(), newEditCommitId, timestamp);
@@ -560,7 +558,7 @@ public class ChangeEditModifier {
private ChangeEdit updateEdit(
Repository repository, ChangeEdit changeEdit, ObjectId newEditCommitId, Timestamp timestamp)
- throws IOException, OrmException {
+ throws IOException {
String editRefName = changeEdit.getRefName();
RevCommit currentEditCommit = changeEdit.getEditCommit();
updateReference(repository, editRefName, currentEditCommit, newEditCommitId, timestamp);
@@ -627,7 +625,7 @@ public class ChangeEditModifier {
return user.newRefLogIdent(timestamp, tz);
}
- private void reindex(Change change) throws IOException, OrmException {
- indexer.index(reviewDb.get(), change);
+ private void reindex(Change change) {
+ indexer.index(change);
}
}
diff --git a/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index 39cf44f255..ef1d8803dd 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -16,23 +16,20 @@ package com.google.gerrit.server.edit;
import static com.google.common.base.Preconditions.checkArgument;
-import com.google.common.collect.ListMultimap;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.api.changes.RecipientType;
+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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeKindCache;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.ChangeIndexer;
@@ -42,7 +39,6 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -69,7 +65,6 @@ public class ChangeEditUtil {
private final GitRepositoryManager gitManager;
private final PatchSetInserter.Factory patchSetInserterFactory;
private final ChangeIndexer indexer;
- private final Provider<ReviewDb> db;
private final Provider<CurrentUser> userProvider;
private final ChangeKindCache changeKindCache;
private final PatchSetUtil psUtil;
@@ -79,14 +74,12 @@ public class ChangeEditUtil {
GitRepositoryManager gitManager,
PatchSetInserter.Factory patchSetInserterFactory,
ChangeIndexer indexer,
- Provider<ReviewDb> db,
Provider<CurrentUser> userProvider,
ChangeKindCache changeKindCache,
PatchSetUtil psUtil) {
this.gitManager = gitManager;
this.patchSetInserterFactory = patchSetInserterFactory;
this.indexer = indexer;
- this.db = db;
this.userProvider = userProvider;
this.changeKindCache = changeKindCache;
this.psUtil = psUtil;
@@ -152,9 +145,7 @@ public class ChangeEditUtil {
* @param edit change edit to publish
* @param notify Notify handling that defines to whom email notifications should be sent after the
* change edit is published.
- * @param accountsToNotify Accounts that should be notified after the change edit is published.
* @throws IOException
- * @throws OrmException
* @throws UpdateException
* @throws RestApiException
*/
@@ -162,10 +153,9 @@ public class ChangeEditUtil {
BatchUpdate.Factory updateFactory,
ChangeNotes notes,
CurrentUser user,
- final ChangeEdit edit,
- NotifyHandling notify,
- ListMultimap<RecipientType, Account.Id> accountsToNotify)
- throws IOException, OrmException, RestApiException, UpdateException {
+ ChangeEdit edit,
+ NotifyResolver.Result notify)
+ throws IOException, RestApiException, UpdateException {
Change change = edit.getChange();
try (Repository repo = gitManager.openRepository(change.getProject());
ObjectInserter oi = repo.newObjectInserter();
@@ -181,8 +171,6 @@ public class ChangeEditUtil {
PatchSetInserter inserter =
patchSetInserterFactory
.create(notes, psId, squashed)
- .setNotify(notify)
- .setAccountsToNotify(accountsToNotify)
.setSendEmail(!change.isWorkInProgress());
StringBuilder message =
@@ -202,9 +190,9 @@ public class ChangeEditUtil {
.append(".");
}
- try (BatchUpdate bu =
- updateFactory.create(db.get(), change.getProject(), user, TimeUtil.nowTs())) {
+ try (BatchUpdate bu = updateFactory.create(change.getProject(), user, TimeUtil.nowTs())) {
bu.setRepository(repo, rw, oi);
+ bu.setNotify(notify);
bu.addOp(change.getId(), inserter.setMessage(message.toString()));
bu.addOp(
change.getId(),
@@ -224,14 +212,13 @@ public class ChangeEditUtil {
*
* @param edit change edit to delete
* @throws IOException
- * @throws OrmException
*/
- public void delete(ChangeEdit edit) throws IOException, OrmException {
+ public void delete(ChangeEdit edit) throws IOException {
Change change = edit.getChange();
try (Repository repo = gitManager.openRepository(change.getProject())) {
deleteRef(repo, edit);
}
- indexer.index(db.get(), change);
+ indexer.index(change);
}
private PatchSet getBasePatchSet(ChangeNotes notes, Ref ref) throws IOException {
@@ -239,9 +226,8 @@ 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(
- db.get(), notes, new PatchSet.Id(notes.getChange().getId(), Integer.parseInt(psId)));
- } catch (OrmException | NumberFormatException e) {
+ return psUtil.get(notes, new PatchSet.Id(notes.getChange().getId(), Integer.parseInt(psId)));
+ } catch (StorageException | NumberFormatException e) {
throw new IOException(e);
}
}
diff --git a/java/com/google/gerrit/server/events/EventBroker.java b/java/com/google/gerrit/server/events/EventBroker.java
index 94e9bb1fe4..d5f548ff4b 100644
--- a/java/com/google/gerrit/server/events/EventBroker.java
+++ b/java/com/google/gerrit/server/events/EventBroker.java
@@ -22,7 +22,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -35,9 +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.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
/** Distributes Events to listeners if they are allowed to see them */
@@ -64,27 +61,22 @@ public class EventBroker implements EventDispatcher {
protected final ChangeNotes.Factory notesFactory;
- protected final Provider<ReviewDb> dbProvider;
-
@Inject
public EventBroker(
PluginSetContext<UserScopedEventListener> listeners,
PluginSetContext<EventListener> unrestrictedListeners,
PermissionBackend permissionBackend,
ProjectCache projectCache,
- ChangeNotes.Factory notesFactory,
- Provider<ReviewDb> dbProvider) {
+ ChangeNotes.Factory notesFactory) {
this.listeners = listeners;
this.unrestrictedListeners = unrestrictedListeners;
this.permissionBackend = permissionBackend;
this.projectCache = projectCache;
this.notesFactory = notesFactory;
- this.dbProvider = dbProvider;
}
@Override
- public void postEvent(Change change, ChangeEvent event)
- throws OrmException, PermissionBackendException {
+ public void postEvent(Change change, ChangeEvent event) throws PermissionBackendException {
fireEvent(change, event);
}
@@ -100,7 +92,7 @@ public class EventBroker implements EventDispatcher {
}
@Override
- public void postEvent(Event event) throws OrmException, PermissionBackendException {
+ public void postEvent(Event event) throws PermissionBackendException {
fireEvent(event);
}
@@ -108,10 +100,9 @@ public class EventBroker implements EventDispatcher {
unrestrictedListeners.runEach(l -> l.onEvent(event));
}
- protected void fireEvent(Change change, ChangeEvent event)
- throws OrmException, PermissionBackendException {
+ protected void fireEvent(Change change, ChangeEvent event) throws PermissionBackendException {
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
- CurrentUser user = c.call(l -> l.getUser());
+ CurrentUser user = c.call(UserScopedEventListener::getUser);
if (isVisibleTo(change, user)) {
c.run(l -> l.onEvent(event));
}
@@ -121,7 +112,7 @@ public class EventBroker implements EventDispatcher {
protected void fireEvent(Project.NameKey project, ProjectEvent event) {
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
- CurrentUser user = c.call(l -> l.getUser());
+ CurrentUser user = c.call(UserScopedEventListener::getUser);
if (isVisibleTo(project, user)) {
c.run(l -> l.onEvent(event));
}
@@ -132,7 +123,7 @@ public class EventBroker implements EventDispatcher {
protected void fireEvent(Branch.NameKey branchName, RefEvent event)
throws PermissionBackendException {
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
- CurrentUser user = c.call(l -> l.getUser());
+ CurrentUser user = c.call(UserScopedEventListener::getUser);
if (isVisibleTo(branchName, user)) {
c.run(l -> l.onEvent(event));
}
@@ -140,9 +131,9 @@ public class EventBroker implements EventDispatcher {
fireEventForUnrestrictedListeners(event);
}
- protected void fireEvent(Event event) throws OrmException, PermissionBackendException {
+ protected void fireEvent(Event event) throws PermissionBackendException {
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
- CurrentUser user = c.call(l -> l.getUser());
+ CurrentUser user = c.call(UserScopedEventListener::getUser);
if (isVisibleTo(event, user)) {
c.run(l -> l.onEvent(event));
}
@@ -164,8 +155,7 @@ public class EventBroker implements EventDispatcher {
}
}
- protected boolean isVisibleTo(Change change, CurrentUser user)
- throws OrmException, PermissionBackendException {
+ protected boolean isVisibleTo(Change change, CurrentUser user) throws PermissionBackendException {
if (change == null) {
return false;
}
@@ -173,12 +163,10 @@ public class EventBroker implements EventDispatcher {
if (pe == null || !pe.statePermitsRead()) {
return false;
}
- ReviewDb db = dbProvider.get();
try {
permissionBackend
.user(user)
- .change(notesFactory.createChecked(db, change))
- .database(db)
+ .change(notesFactory.createChecked(change))
.check(ChangePermission.READ);
return true;
} catch (AuthException e) {
@@ -201,18 +189,14 @@ public class EventBroker implements EventDispatcher {
}
}
- protected boolean isVisibleTo(Event event, CurrentUser user)
- throws OrmException, PermissionBackendException {
+ protected boolean isVisibleTo(Event event, CurrentUser user) throws PermissionBackendException {
if (event instanceof RefEvent) {
RefEvent refEvent = (RefEvent) event;
String ref = refEvent.getRefName();
if (PatchSet.isChangeRef(ref)) {
Change.Id cid = PatchSet.Id.fromRef(ref).getParentKey();
try {
- Change change =
- notesFactory
- .createChecked(dbProvider.get(), refEvent.getProjectNameKey(), cid)
- .getChange();
+ Change change = notesFactory.createChecked(refEvent.getProjectNameKey(), cid).getChange();
return isVisibleTo(change, user);
} catch (NoSuchChangeException e) {
logger.atFine().log(
diff --git a/java/com/google/gerrit/server/events/EventDispatcher.java b/java/com/google/gerrit/server/events/EventDispatcher.java
index cbf547e939..e6735f2960 100644
--- a/java/com/google/gerrit/server/events/EventDispatcher.java
+++ b/java/com/google/gerrit/server/events/EventDispatcher.java
@@ -18,7 +18,6 @@ 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.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
/** Interface for posting (dispatching) Events */
public interface EventDispatcher {
@@ -27,10 +26,9 @@ public interface EventDispatcher {
*
* @param change The change that the event is related to
* @param event The event to post
- * @throws OrmException on failure to post the event due to DB error
* @throws PermissionBackendException on failure of permission checks
*/
- void postEvent(Change change, ChangeEvent event) throws OrmException, PermissionBackendException;
+ void postEvent(Change change, ChangeEvent event) throws PermissionBackendException;
/**
* Post a stream event that is related to a branch
@@ -56,8 +54,7 @@ public interface EventDispatcher {
* specific postEvent methods for those use cases.
*
* @param event The event to post.
- * @throws OrmException on failure to post the event due to DB error
* @throws PermissionBackendException on failure of permission checks
*/
- void postEvent(Event event) throws OrmException, PermissionBackendException;
+ void postEvent(Event event) throws PermissionBackendException;
}
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 2550efd17b..efd9bb9cea 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -24,6 +24,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.reviewdb.client.Account;
@@ -35,7 +36,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache;
@@ -64,8 +64,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -94,7 +92,6 @@ public class EventFactory {
private final ApprovalsUtil approvalsUtil;
private final ChangeKindCache changeKindCache;
private final Provider<InternalChangeQuery> queryProvider;
- private final SchemaFactory<ReviewDb> schema;
private final IndexConfig indexConfig;
@Inject
@@ -108,7 +105,6 @@ public class EventFactory {
ApprovalsUtil approvalsUtil,
ChangeKindCache changeKindCache,
Provider<InternalChangeQuery> queryProvider,
- SchemaFactory<ReviewDb> schema,
IndexConfig indexConfig) {
this.accountCache = accountCache;
this.urlFormatter = urlFormatter;
@@ -119,7 +115,6 @@ public class EventFactory {
this.approvalsUtil = approvalsUtil;
this.changeKindCache = changeKindCache;
this.queryProvider = queryProvider;
- this.schema = schema;
this.indexConfig = indexConfig;
}
@@ -129,23 +124,7 @@ public class EventFactory {
* @param change
* @return object suitable for serialization to JSON
*/
- public ChangeAttribute asChangeAttribute(Change change, ChangeNotes notes) {
- try (ReviewDb db = schema.open()) {
- return asChangeAttribute(db, change, notes);
- } catch (OrmException e) {
- logger.atSevere().withCause(e).log("Cannot open database connection");
- return new ChangeAttribute();
- }
- }
-
- /**
- * Create a ChangeAttribute for the given change suitable for serialization to JSON.
- *
- * @param db Review database
- * @param change
- * @return object suitable for serialization to JSON
- */
- public ChangeAttribute asChangeAttribute(ReviewDb db, Change change) {
+ public ChangeAttribute asChangeAttribute(Change change) {
ChangeAttribute a = new ChangeAttribute();
a.project = change.getProject().get();
a.branch = change.getDest().getShortName();
@@ -154,7 +133,7 @@ public class EventFactory {
a.number = change.getId().get();
a.subject = change.getSubject();
try {
- a.commitMessage = changeDataFactory.create(db, change).commitMessage();
+ a.commitMessage = changeDataFactory.create(change).commitMessage();
} catch (Exception e) {
logger.atSevere().withCause(e).log(
"Error while getting full commit message for change %d", a.number);
@@ -172,14 +151,12 @@ public class EventFactory {
/**
* Create a ChangeAttribute for the given change suitable for serialization to JSON.
*
- * @param db Review database
* @param change
* @param notes
* @return object suitable for serialization to JSON
*/
- public ChangeAttribute asChangeAttribute(ReviewDb db, Change change, ChangeNotes notes)
- throws OrmException {
- ChangeAttribute a = asChangeAttribute(db, change);
+ public ChangeAttribute asChangeAttribute(Change change, ChangeNotes notes) {
+ ChangeAttribute a = asChangeAttribute(change);
Set<String> hashtags = notes.load().getHashtags();
if (!hashtags.isEmpty()) {
a.hashtags = new ArrayList<>(hashtags.size());
@@ -214,7 +191,7 @@ public class EventFactory {
*/
public void extend(ChangeAttribute a, Change change) {
a.lastUpdated = change.getLastUpdatedOn().getTime() / 1000L;
- a.open = change.getStatus().isOpen();
+ a.open = change.isNew();
}
/**
@@ -223,9 +200,8 @@ public class EventFactory {
* @param a
* @param notes
*/
- public void addAllReviewers(ReviewDb db, ChangeAttribute a, ChangeNotes notes)
- throws OrmException {
- Collection<Account.Id> reviewers = approvalsUtil.getReviewers(db, notes).all();
+ public void addAllReviewers(ChangeAttribute a, ChangeNotes notes) {
+ Collection<Account.Id> reviewers = approvalsUtil.getReviewers(notes).all();
if (!reviewers.isEmpty()) {
a.allReviewers = Lists.newArrayListWithCapacity(reviewers.size());
for (Account.Id id : reviewers) {
@@ -295,7 +271,7 @@ public class EventFactory {
try {
addDependsOn(rw, ca, change, currentPs);
addNeededBy(rw, ca, change, currentPs);
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
// Squash DB exceptions and leave dependency lists partially filled.
}
// Remove empty lists so a confusing label won't be displayed in the output.
@@ -308,7 +284,7 @@ public class EventFactory {
}
private void addDependsOn(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs)
- throws OrmException, IOException {
+ throws IOException {
RevCommit commit = rw.parseCommit(ObjectId.fromString(currentPs.getRevision().get()));
final List<String> parentNames = new ArrayList<>(commit.getParentCount());
for (RevCommit p : commit.getParents()) {
@@ -341,7 +317,7 @@ public class EventFactory {
}
private void addNeededBy(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs)
- throws OrmException, IOException {
+ throws IOException {
if (currentPs.getGroups().isEmpty()) {
return;
}
@@ -403,17 +379,15 @@ public class EventFactory {
}
public void addPatchSets(
- ReviewDb db,
RevWalk revWalk,
ChangeAttribute ca,
Collection<PatchSet> ps,
Map<PatchSet.Id, Collection<PatchSetApproval>> approvals,
LabelTypes labelTypes) {
- addPatchSets(db, revWalk, ca, ps, approvals, false, null, labelTypes);
+ addPatchSets(revWalk, ca, ps, approvals, false, null, labelTypes);
}
public void addPatchSets(
- ReviewDb db,
RevWalk revWalk,
ChangeAttribute ca,
Collection<PatchSet> ps,
@@ -424,7 +398,7 @@ public class EventFactory {
if (!ps.isEmpty()) {
ca.patchSets = new ArrayList<>(ps.size());
for (PatchSet p : ps) {
- PatchSetAttribute psa = asPatchSetAttribute(db, revWalk, change, p);
+ PatchSetAttribute psa = asPatchSetAttribute(revWalk, change, p);
if (approvals != null) {
addApprovals(psa, p.getId(), approvals, labelTypes);
}
@@ -484,28 +458,10 @@ public class EventFactory {
/**
* Create a PatchSetAttribute for the given patchset suitable for serialization to JSON.
*
- * @param revWalk
* @param patchSet
* @return object suitable for serialization to JSON
*/
public PatchSetAttribute asPatchSetAttribute(RevWalk revWalk, Change change, PatchSet patchSet) {
- try (ReviewDb db = schema.open()) {
- return asPatchSetAttribute(db, revWalk, change, patchSet);
- } catch (OrmException e) {
- logger.atSevere().withCause(e).log("Cannot open database connection");
- return new PatchSetAttribute();
- }
- }
-
- /**
- * Create a PatchSetAttribute for the given patchset suitable for serialization to JSON.
- *
- * @param db Review database
- * @param patchSet
- * @return object suitable for serialization to JSON
- */
- public PatchSetAttribute asPatchSetAttribute(
- ReviewDb db, RevWalk revWalk, Change change, PatchSet patchSet) {
PatchSetAttribute p = new PatchSetAttribute();
p.revision = patchSet.getRevision().get();
p.number = patchSet.getPatchSetId();
@@ -537,8 +493,8 @@ public class EventFactory {
p.sizeInsertions += pe.getInsertions();
}
}
- p.kind = changeKindCache.getChangeKind(db, change, patchSet);
- } catch (IOException | OrmException e) {
+ p.kind = changeKindCache.getChangeKind(change, patchSet);
+ } catch (IOException | StorageException e) {
logger.atSevere().withCause(e).log("Cannot load patch set data for %s", patchSet.getId());
} catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Cannot get size information for %s: %s", pId, e.getMessage());
@@ -550,7 +506,7 @@ public class EventFactory {
// TODO: The same method exists in PatchSetInfoFactory, find a common place
// for it
- private UserIdentity toUserIdentity(PersonIdent who) throws IOException, OrmException {
+ private UserIdentity toUserIdentity(PersonIdent who) throws IOException {
UserIdentity u = new UserIdentity();
u.setName(who.getName());
u.setEmail(who.getEmailAddress());
diff --git a/java/com/google/gerrit/server/events/ReviewerAddedEvent.java b/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
index 9644456d4f..ea6bda3e74 100644
--- a/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
+++ b/java/com/google/gerrit/server/events/ReviewerAddedEvent.java
@@ -21,6 +21,7 @@ import com.google.gerrit.server.data.AccountAttribute;
public class ReviewerAddedEvent extends PatchSetEvent {
static final String TYPE = "reviewer-added";
public Supplier<AccountAttribute> reviewer;
+ public Supplier<AccountAttribute> adder;
public ReviewerAddedEvent(Change change) {
super(TYPE, change);
diff --git a/java/com/google/gerrit/server/events/StreamEventsApiListener.java b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
index 4f43fe60ad..f6f1d57850 100644
--- a/java/com/google/gerrit/server/events/StreamEventsApiListener.java
+++ b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
@@ -20,6 +20,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -45,28 +46,23 @@ 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.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
-import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import java.util.Map.Entry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -117,7 +113,6 @@ public class StreamEventsApiListener
}
private final PluginItemContext<EventDispatcher> dispatcher;
- private final Provider<ReviewDb> db;
private final EventFactory eventFactory;
private final ProjectCache projectCache;
private final GitRepositoryManager repoManager;
@@ -127,14 +122,12 @@ public class StreamEventsApiListener
@Inject
StreamEventsApiListener(
PluginItemContext<EventDispatcher> dispatcher,
- Provider<ReviewDb> db,
EventFactory eventFactory,
ProjectCache projectCache,
GitRepositoryManager repoManager,
PatchSetUtil psUtil,
ChangeNotes.Factory changeNotesFactory) {
this.dispatcher = dispatcher;
- this.db = db;
this.eventFactory = eventFactory;
this.projectCache = projectCache;
this.repoManager = repoManager;
@@ -142,59 +135,53 @@ public class StreamEventsApiListener
this.changeNotesFactory = changeNotesFactory;
}
- private ChangeNotes getNotes(ChangeInfo info) throws OrmException {
+ private ChangeNotes getNotes(ChangeInfo info) {
try {
return changeNotesFactory.createChecked(new Change.Id(info._number));
} catch (NoSuchChangeException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
- private PatchSet getPatchSet(ChangeNotes notes, RevisionInfo info) throws OrmException {
- return psUtil.get(db.get(), notes, PatchSet.Id.fromRef(info.ref));
+ private PatchSet getPatchSet(ChangeNotes notes, RevisionInfo info) {
+ return psUtil.get(notes, PatchSet.Id.fromRef(info.ref));
}
private Supplier<ChangeAttribute> changeAttributeSupplier(Change change, ChangeNotes notes) {
return Suppliers.memoize(
- new Supplier<ChangeAttribute>() {
- @Override
- public ChangeAttribute get() {
+ () -> {
+ try {
return eventFactory.asChangeAttribute(change, notes);
+ } catch (StorageException e) {
+ throw new RuntimeException(e);
}
});
}
private Supplier<AccountAttribute> accountAttributeSupplier(AccountInfo account) {
return Suppliers.memoize(
- new Supplier<AccountAttribute>() {
- @Override
- public AccountAttribute get() {
- return account != null
+ () ->
+ account != null
? eventFactory.asAccountAttribute(new Account.Id(account._accountId))
- : null;
- }
- });
+ : null);
}
private Supplier<PatchSetAttribute> patchSetAttributeSupplier(
final Change change, PatchSet patchSet) {
return Suppliers.memoize(
- new Supplier<PatchSetAttribute>() {
- @Override
- public PatchSetAttribute get() {
- try (Repository repo = repoManager.openRepository(change.getProject());
- RevWalk revWalk = new RevWalk(repo)) {
- return eventFactory.asPatchSetAttribute(revWalk, change, patchSet);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ () -> {
+ try (Repository repo = repoManager.openRepository(change.getProject());
+ RevWalk revWalk = new RevWalk(repo)) {
+ return eventFactory.asPatchSetAttribute(revWalk, change, patchSet);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
});
}
private static Map<String, Short> convertApprovalsMap(Map<String, ApprovalInfo> approvals) {
Map<String, Short> result = new HashMap<>();
- for (Entry<String, ApprovalInfo> e : approvals.entrySet()) {
+ for (Map.Entry<String, ApprovalInfo> e : approvals.entrySet()) {
Short value = e.getValue().value == null ? null : e.getValue().value.shortValue();
result.put(e.getKey(), value);
}
@@ -202,7 +189,7 @@ public class StreamEventsApiListener
}
private ApprovalAttribute getApprovalAttribute(
- LabelTypes labelTypes, Entry<String, Short> approval, Map<String, Short> oldApprovals) {
+ LabelTypes labelTypes, Map.Entry<String, Short> approval, Map<String, Short> oldApprovals) {
ApprovalAttribute a = new ApprovalAttribute();
a.type = approval.getKey();
@@ -227,21 +214,18 @@ public class StreamEventsApiListener
final Map<String, ApprovalInfo> oldApprovals) {
final Map<String, Short> approvals = convertApprovalsMap(newApprovals);
return Suppliers.memoize(
- new Supplier<ApprovalAttribute[]>() {
- @Override
- public ApprovalAttribute[] get() {
- LabelTypes labelTypes = projectCache.get(change.getProject()).getLabelTypes();
- if (approvals.size() > 0) {
- ApprovalAttribute[] r = new ApprovalAttribute[approvals.size()];
- int i = 0;
- for (Map.Entry<String, Short> approval : approvals.entrySet()) {
- r[i++] =
- getApprovalAttribute(labelTypes, approval, convertApprovalsMap(oldApprovals));
- }
- return r;
+ () -> {
+ LabelTypes labelTypes = projectCache.get(change.getProject()).getLabelTypes();
+ if (approvals.size() > 0) {
+ ApprovalAttribute[] r = new ApprovalAttribute[approvals.size()];
+ int i = 0;
+ for (Map.Entry<String, Short> approval : approvals.entrySet()) {
+ r[i++] =
+ getApprovalAttribute(labelTypes, approval, convertApprovalsMap(oldApprovals));
}
- return null;
+ return r;
}
+ return null;
});
}
@@ -264,7 +248,7 @@ public class StreamEventsApiListener
event.oldAssignee = accountAttributeSupplier(ev.getOldAssignee());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -281,7 +265,7 @@ public class StreamEventsApiListener
event.oldTopic = ev.getOldTopic();
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -299,7 +283,7 @@ public class StreamEventsApiListener
event.uploader = accountAttributeSupplier(ev.getWho());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -311,7 +295,7 @@ public class StreamEventsApiListener
Change change = notes.getChange();
ReviewerDeletedEvent event = new ReviewerDeletedEvent(change);
event.change = changeAttributeSupplier(change, notes);
- event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
+ event.patchSet = patchSetAttributeSupplier(change, psUtil.current(notes));
event.reviewer = accountAttributeSupplier(ev.getReviewer());
event.remover = accountAttributeSupplier(ev.getWho());
event.comment = ev.getComment();
@@ -319,7 +303,7 @@ public class StreamEventsApiListener
approvalsAttributeSupplier(change, ev.getNewApprovals(), ev.getOldApprovals());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -332,12 +316,13 @@ public class StreamEventsApiListener
ReviewerAddedEvent event = new ReviewerAddedEvent(change);
event.change = changeAttributeSupplier(change, notes);
- event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
+ event.patchSet = patchSetAttributeSupplier(change, psUtil.current(notes));
+ event.adder = accountAttributeSupplier(ev.getWho());
for (AccountInfo reviewer : ev.getReviewers()) {
event.reviewer = accountAttributeSupplier(reviewer);
dispatcher.run(d -> d.postEvent(event));
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -365,7 +350,7 @@ public class StreamEventsApiListener
event.removed = hashtagArray(ev.getRemovedHashtags());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -379,15 +364,11 @@ public class StreamEventsApiListener
final Branch.NameKey refName = new Branch.NameKey(ev.getProjectName(), ev.getRefName());
event.refUpdate =
Suppliers.memoize(
- new Supplier<RefUpdateAttribute>() {
- @Override
- public RefUpdateAttribute get() {
- return eventFactory.asRefUpdateAttribute(
+ () ->
+ eventFactory.asRefUpdateAttribute(
ObjectId.fromString(ev.getOldObjectId()),
ObjectId.fromString(ev.getNewObjectId()),
- refName);
- }
- });
+ refName));
dispatcher.run(d -> d.postEvent(refName, event));
}
@@ -406,7 +387,7 @@ public class StreamEventsApiListener
event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -420,11 +401,11 @@ public class StreamEventsApiListener
event.change = changeAttributeSupplier(change, notes);
event.restorer = accountAttributeSupplier(ev.getWho());
- event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
+ event.patchSet = patchSetAttributeSupplier(change, psUtil.current(notes));
event.reason = ev.getReason();
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -438,11 +419,11 @@ public class StreamEventsApiListener
event.change = changeAttributeSupplier(change, notes);
event.submitter = accountAttributeSupplier(ev.getWho());
- event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
+ event.patchSet = patchSetAttributeSupplier(change, psUtil.current(notes));
event.newRev = ev.getNewRevisionId();
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -456,11 +437,11 @@ public class StreamEventsApiListener
event.change = changeAttributeSupplier(change, notes);
event.abandoner = accountAttributeSupplier(ev.getWho());
- event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
+ event.patchSet = patchSetAttributeSupplier(change, psUtil.current(notes));
event.reason = ev.getReason();
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -478,7 +459,7 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, patchSet);
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -496,7 +477,7 @@ public class StreamEventsApiListener
event.patchSet = patchSetAttributeSupplier(change, patchSet);
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -509,14 +490,14 @@ public class StreamEventsApiListener
VoteDeletedEvent event = new VoteDeletedEvent(change);
event.change = changeAttributeSupplier(change, notes);
- event.patchSet = patchSetAttributeSupplier(change, psUtil.current(db.get(), notes));
+ event.patchSet = patchSetAttributeSupplier(change, psUtil.current(notes));
event.comment = ev.getMessage();
event.reviewer = accountAttributeSupplier(ev.getReviewer());
event.remover = accountAttributeSupplier(ev.getWho());
event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
@@ -532,7 +513,7 @@ public class StreamEventsApiListener
event.deleter = accountAttributeSupplier(ev.getWho());
dispatcher.run(d -> d.postEvent(change, event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to dispatch event");
}
}
diff --git a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
index 513a5dee06..fdce1da8f6 100644
--- a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -22,7 +23,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
@@ -53,7 +53,7 @@ public class AssigneeChanged {
util.accountInfo(oldAssignee),
when);
listeners.runEach(l -> l.onAssigneeChanged(event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
}
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
index 3d6700e5a1..a8c08b991f 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -28,7 +29,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -72,7 +72,7 @@ public class ChangeAbandoned {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
index d9eb9f98b6..9e3e979196 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -22,7 +23,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
@@ -47,7 +47,7 @@ public class ChangeDeleted {
try {
Event event = new Event(util.changeInfo(change), util.accountInfo(deleter), when);
listeners.runEach(l -> l.onChangeDeleted(event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
}
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
index 7b814aed0f..756f3832ff 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -28,7 +29,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -66,7 +66,7 @@ public class ChangeMerged {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
index 81b04cdfc8..e8bed56854 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -28,7 +29,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -66,7 +66,7 @@ public class ChangeRestored {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
index ac7aac01f5..ccb17d5cfd 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
@@ -15,12 +15,12 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
@@ -45,7 +45,7 @@ public class ChangeReverted {
try {
Event event = new Event(util.changeInfo(change), util.changeInfo(revertChange), when);
listeners.runEach(l -> l.onChangeReverted(event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
}
diff --git a/java/com/google/gerrit/server/extensions/events/CommentAdded.java b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
index e224540dab..ea9ae312a4 100644
--- a/java/com/google/gerrit/server/extensions/events/CommentAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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.ApprovalInfo;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -76,7 +76,7 @@ public class CommentAdded {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/EventUtil.java b/java/com/google/gerrit/server/extensions/events/EventUtil.java
index 485ed50e4a..d00eb31721 100644
--- a/java/com/google/gerrit/server/extensions/events/EventUtil.java
+++ b/java/com/google/gerrit/server/extensions/events/EventUtil.java
@@ -25,7 +25,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.GpgException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.ChangeJson;
@@ -33,9 +32,7 @@ import com.google.gerrit.server.change.RevisionJson;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.sql.Timestamp;
@@ -63,7 +60,6 @@ public class EventUtil {
}
private final ChangeData.Factory changeDataFactory;
- private final Provider<ReviewDb> db;
private final ChangeJson.Factory changeJsonFactory;
private final RevisionJson.Factory revisionJsonFactory;
@@ -71,28 +67,24 @@ public class EventUtil {
EventUtil(
ChangeJson.Factory changeJsonFactory,
RevisionJson.Factory revisionJsonFactory,
- ChangeData.Factory changeDataFactory,
- Provider<ReviewDb> db) {
+ ChangeData.Factory changeDataFactory) {
this.changeDataFactory = changeDataFactory;
- this.db = db;
this.changeJsonFactory = changeJsonFactory;
this.revisionJsonFactory = revisionJsonFactory;
}
- public ChangeInfo changeInfo(Change change) throws OrmException {
+ public ChangeInfo changeInfo(Change change) {
return changeJsonFactory.create(CHANGE_OPTIONS).format(change);
}
public RevisionInfo revisionInfo(Project project, PatchSet ps)
- throws OrmException, PatchListNotAvailableException, GpgException, IOException,
- PermissionBackendException {
+ throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
return revisionInfo(project.getNameKey(), ps);
}
public RevisionInfo revisionInfo(Project.NameKey project, PatchSet ps)
- throws OrmException, PatchListNotAvailableException, GpgException, IOException,
- PermissionBackendException {
- ChangeData cd = changeDataFactory.create(db.get(), project, ps.getId().getParentKey());
+ throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
+ ChangeData cd = changeDataFactory.create(project, ps.getId().getParentKey());
return revisionJsonFactory.create(CHANGE_OPTIONS).getRevisionInfo(cd, ps);
}
diff --git a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
index ca0edaba9d..65f5b8bfa2 100644
--- a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.extensions.events;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.flogger.FluentLogger;
+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;
@@ -23,7 +24,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
@@ -58,7 +58,7 @@ public class HashtagsEdited {
new Event(
util.changeInfo(change), util.accountInfo(editor), hashtags, added, removed, when);
listeners.runEach(l -> l.onHashtagsEdited(event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
}
diff --git a/java/com/google/gerrit/server/extensions/events/PluginEvent.java b/java/com/google/gerrit/server/extensions/events/PluginEvent.java
index 8680ab1528..60d27c9529 100644
--- a/java/com/google/gerrit/server/extensions/events/PluginEvent.java
+++ b/java/com/google/gerrit/server/extensions/events/PluginEvent.java
@@ -15,16 +15,16 @@
package com.google.gerrit.server.extensions.events;
import com.google.gerrit.extensions.events.PluginEventListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
public class PluginEvent {
- private final DynamicSet<PluginEventListener> listeners;
+ private final PluginSetContext<PluginEventListener> listeners;
@Inject
- PluginEvent(DynamicSet<PluginEventListener> listeners) {
+ PluginEvent(PluginSetContext<PluginEventListener> listeners) {
this.listeners = listeners;
}
@@ -33,9 +33,7 @@ public class PluginEvent {
return;
}
Event e = new Event(pluginName, type, data);
- for (PluginEventListener l : listeners) {
- l.onPluginEvent(e);
- }
+ listeners.runEach(l -> l.onPluginEvent(e));
}
private static class Event extends AbstractNoNotifyEvent implements PluginEventListener.Event {
diff --git a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
index 358667fe0b..49a609124e 100644
--- a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -27,7 +28,6 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -58,7 +58,7 @@ public class PrivateStateChanged {
util.accountInfo(account),
when);
listeners.runEach(l -> l.onPrivateStateChanged(event));
- } catch (OrmException
+ } catch (StorageException
| PatchListNotAvailableException
| GpgException
| IOException
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
index 8e5259c34e..f9f67f6c4b 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.extensions.events;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
+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;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -73,7 +73,7 @@ public class ReviewerAdded {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
index 89c8f187be..b92f3e6937 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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.ApprovalInfo;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -80,7 +80,7 @@ public class ReviewerDeleted {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
index e043e9fcd2..6fddcfe4ad 100644
--- a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
+++ b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -24,11 +25,11 @@ 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;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -46,7 +47,7 @@ public class RevisionCreated {
PatchSet patchSet,
AccountState uploader,
Timestamp when,
- NotifyHandling notify) {}
+ NotifyResolver.Result notify) {}
};
private final PluginSetContext<RevisionCreatedListener> listeners;
@@ -68,7 +69,7 @@ public class RevisionCreated {
PatchSet patchSet,
AccountState uploader,
Timestamp when,
- NotifyHandling notify) {
+ NotifyResolver.Result notify) {
if (listeners.isEmpty()) {
return;
}
@@ -79,14 +80,14 @@ public class RevisionCreated {
util.revisionInfo(change.getProject(), patchSet),
util.accountInfo(uploader),
when,
- notify);
+ notify.handling());
listeners.runEach(l -> l.onRevisionCreated(event));
} catch (PatchListObjectTooLargeException e) {
logger.atWarning().log("Couldn't fire event: %s", e.getMessage());
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/TopicEdited.java b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
index 8568c0f404..9e1ae44560 100644
--- a/java/com/google/gerrit/server/extensions/events/TopicEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -22,7 +23,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.Timestamp;
@@ -48,7 +48,7 @@ public class TopicEdited {
Event event =
new Event(util.changeInfo(change), util.accountInfo(account), oldTopicName, when);
listeners.runEach(l -> l.onTopicEdited(event));
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
}
diff --git a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
index b750851852..bd6873a65c 100644
--- a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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.ApprovalInfo;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -80,7 +80,7 @@ public class VoteDeleted {
} catch (PatchListNotAvailableException
| GpgException
| IOException
- | OrmException
+ | StorageException
| PermissionBackendException e) {
logger.atSevere().withCause(e).log("Couldn't fire event");
}
diff --git a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
index 6273ad6186..785d6feae8 100644
--- a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.extensions.events;
import com.google.common.flogger.FluentLogger;
+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;
@@ -27,7 +28,6 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -59,7 +59,7 @@ public class WorkInProgressStateChanged {
util.accountInfo(account),
when);
listeners.runEach(l -> l.onWorkInProgressStateChanged(event));
- } catch (OrmException
+ } catch (StorageException
| PatchListNotAvailableException
| GpgException
| IOException
diff --git a/java/com/google/gerrit/server/git/BanCommit.java b/java/com/google/gerrit/server/git/BanCommit.java
index aba4c53e5f..d8aeeceb86 100644
--- a/java/com/google/gerrit/server/git/BanCommit.java
+++ b/java/com/google/gerrit/server/git/BanCommit.java
@@ -18,6 +18,7 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_REJECT_COMMITS;
import static java.nio.charset.StandardCharsets.UTF_8;
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;
diff --git a/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java
index ef5e65bdf8..a8594e7622 100644
--- a/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java
+++ b/java/com/google/gerrit/server/git/DefaultAdvertiseRefsHook.java
@@ -14,10 +14,14 @@
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;
@@ -28,7 +32,6 @@ import org.eclipse.jgit.transport.ServiceMayNotContinueException;
* implements {@link org.eclipse.jgit.transport.AdvertiseRefsHook}.
*/
public class DefaultAdvertiseRefsHook extends AbstractAdvertiseRefsHook {
-
private final PermissionBackend.ForProject perm;
private final PermissionBackend.RefFilterOptions opts;
@@ -42,9 +45,14 @@ public class DefaultAdvertiseRefsHook extends AbstractAdvertiseRefsHook {
protected Map<String, Ref> getAdvertisedRefs(Repository repo, RevWalk revWalk)
throws ServiceMayNotContinueException {
try {
- return perm.filter(repo.getAllRefs(), repo, opts);
- } catch (PermissionBackendException e) {
- throw new ServiceMayNotContinueException(e);
+ 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 44b4921eb0..5b9dffce4e 100644
--- a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
+++ b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
@@ -27,6 +27,7 @@ public class DefaultChangeReportFormatter implements ChangeReportFormatter {
private static final int SUBJECT_MAX_LENGTH = 80;
private static final String SUBJECT_CROP_APPENDIX = "...";
private static final int SUBJECT_CROP_RANGE = 10;
+ private static final String NEW_CHANGE_INDICATOR = " [NEW]";
protected final DynamicItem<UrlFormatter> urlFormatter;
@@ -37,7 +38,7 @@ public class DefaultChangeReportFormatter implements ChangeReportFormatter {
@Override
public String newChange(ChangeReportFormatter.Input input) {
- return formatChangeUrl(input);
+ return formatChangeUrl(input) + NEW_CHANGE_INDICATOR;
}
@Override
diff --git a/java/com/google/gerrit/server/git/GarbageCollection.java b/java/com/google/gerrit/server/git/GarbageCollection.java
index 3624695284..75c90126ef 100644
--- a/java/com/google/gerrit/server/git/GarbageCollection.java
+++ b/java/com/google/gerrit/server/git/GarbageCollection.java
@@ -18,10 +18,10 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GarbageCollectionResult;
import com.google.gerrit.extensions.events.GarbageCollectorListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
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;
import com.google.inject.Inject;
import java.io.PrintWriter;
import java.util.List;
@@ -43,7 +43,7 @@ public class GarbageCollection {
private final GitRepositoryManager repoManager;
private final GarbageCollectionQueue gcQueue;
private final GcConfig gcConfig;
- private final DynamicSet<GarbageCollectorListener> listeners;
+ private final PluginSetContext<GarbageCollectorListener> listeners;
public interface Factory {
GarbageCollection create();
@@ -54,7 +54,7 @@ public class GarbageCollection {
GitRepositoryManager repoManager,
GarbageCollectionQueue gcQueue,
GcConfig config,
- DynamicSet<GarbageCollectorListener> listeners) {
+ PluginSetContext<GarbageCollectorListener> listeners) {
this.repoManager = repoManager;
this.gcQueue = gcQueue;
this.gcConfig = config;
@@ -113,13 +113,7 @@ public class GarbageCollection {
return;
}
Event event = new Event(p, statistics);
- for (GarbageCollectorListener l : listeners) {
- try {
- l.onGarbageCollected(event);
- } catch (RuntimeException e) {
- logger.atWarning().withCause(e).log("Failure in GarbageCollectorListener");
- }
- }
+ listeners.runEach(l -> l.onGarbageCollected(event));
}
private static void logGcInfo(Project.NameKey projectName, String msg) {
diff --git a/java/com/google/gerrit/server/git/GroupCollector.java b/java/com/google/gerrit/server/git/GroupCollector.java
index 068030db40..e4fd803b27 100644
--- a/java/com/google/gerrit/server/git/GroupCollector.java
+++ b/java/com/google/gerrit/server/git/GroupCollector.java
@@ -30,11 +30,9 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
@@ -76,10 +74,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
public class GroupCollector {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- public static List<String> getDefaultGroups(PatchSet ps) {
- return ImmutableList.of(ps.getRevision().get());
- }
-
public static List<String> getDefaultGroups(ObjectId commit) {
return ImmutableList.of(commit.name());
}
@@ -94,7 +88,7 @@ public class GroupCollector {
}
private interface Lookup {
- List<String> lookup(PatchSet.Id psId) throws OrmException;
+ List<String> lookup(PatchSet.Id psId);
}
private final ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha;
@@ -106,33 +100,16 @@ public class GroupCollector {
public static GroupCollector create(
ListMultimap<ObjectId, Ref> changeRefsById,
- ReviewDb db,
PatchSetUtil psUtil,
ChangeNotes.Factory notesFactory,
Project.NameKey project) {
return new GroupCollector(
transformRefs(changeRefsById),
- new Lookup() {
- @Override
- public List<String> lookup(PatchSet.Id psId) throws OrmException {
- // TODO(dborowitz): Reuse open repository from caller.
- ChangeNotes notes = notesFactory.createChecked(db, project, psId.getParentKey());
- PatchSet ps = psUtil.get(db, notes, psId);
- return ps != null ? ps.getGroups() : null;
- }
- });
- }
-
- public static GroupCollector createForSchemaUpgradeOnly(
- ListMultimap<ObjectId, Ref> changeRefsById, ReviewDb db) {
- return new GroupCollector(
- transformRefs(changeRefsById),
- new Lookup() {
- @Override
- public List<String> lookup(PatchSet.Id psId) throws OrmException {
- PatchSet ps = db.patchSets().get(psId);
- return ps != null ? ps.getGroups() : null;
- }
+ psId -> {
+ // TODO(dborowitz): Reuse open repository from caller.
+ ChangeNotes notes = notesFactory.createChecked(project, psId.getParentKey());
+ PatchSet ps = psUtil.get(notes, psId);
+ return ps != null ? ps.getGroups() : null;
});
}
@@ -154,12 +131,9 @@ public class GroupCollector {
ListMultimap<PatchSet.Id, String> groupLookup) {
this(
patchSetsBySha,
- new Lookup() {
- @Override
- public List<String> lookup(PatchSet.Id psId) {
- List<String> groups = groupLookup.get(psId);
- return !groups.isEmpty() ? groups : null;
- }
+ psId -> {
+ List<String> groups = groupLookup.get(psId);
+ return !groups.isEmpty() ? groups : null;
});
}
@@ -223,7 +197,7 @@ public class GroupCollector {
}
}
- public SortedSetMultimap<ObjectId, String> getGroups() throws OrmException {
+ public SortedSetMultimap<ObjectId, String> getGroups() {
done = true;
SortedSetMultimap<ObjectId, String> result =
MultimapBuilder.hashKeys(groups.keySet().size()).treeSetValues().build();
@@ -249,8 +223,7 @@ public class GroupCollector {
return id != null && patchSetsBySha.containsKey(id);
}
- private Set<String> resolveGroups(ObjectId forCommit, Collection<String> candidates)
- throws OrmException {
+ private Set<String> resolveGroups(ObjectId forCommit, Collection<String> candidates) {
Set<String> actual = Sets.newTreeSet();
Set<String> done = Sets.newHashSetWithExpectedSize(candidates.size());
Set<String> seen = Sets.newHashSetWithExpectedSize(candidates.size());
@@ -285,7 +258,7 @@ public class GroupCollector {
}
}
- private Iterable<String> resolveGroup(ObjectId forCommit, String group) throws OrmException {
+ private Iterable<String> resolveGroup(ObjectId forCommit, String group) {
ObjectId id = parseGroup(forCommit, group);
if (id != null) {
PatchSet.Id psId = Iterables.getFirst(patchSetsBySha.get(id), null);
diff --git a/java/com/google/gerrit/server/git/HookUtil.java b/java/com/google/gerrit/server/git/HookUtil.java
index 42d3f69d1d..3bef7cccb2 100644
--- a/java/com/google/gerrit/server/git/HookUtil.java
+++ b/java/com/google/gerrit/server/git/HookUtil.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.git;
+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.lib.RefDatabase;
import org.eclipse.jgit.transport.BaseReceivePack;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
@@ -31,6 +32,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)
throws ServiceMayNotContinueException {
Map<String, Ref> refs = rp.getAdvertisedRefs();
@@ -38,7 +40,9 @@ public class HookUtil {
return refs;
}
try {
- refs = rp.getRepository().getRefDatabase().getRefs(RefDatabase.ALL);
+ refs =
+ rp.getRepository().getRefDatabase().getRefs().stream()
+ .collect(toMap(Ref::getName, r -> r));
} catch (ServiceMayNotContinueException e) {
throw e;
} catch (IOException e) {
diff --git a/java/com/google/gerrit/server/git/LockFailureException.java b/java/com/google/gerrit/server/git/LockFailureException.java
deleted file mode 100644
index 503c0d9008..0000000000
--- a/java/com/google/gerrit/server/git/LockFailureException.java
+++ /dev/null
@@ -1,49 +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.server.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 {
- private static final long serialVersionUID = 1L;
-
- private final ImmutableList<String> refs;
-
- public LockFailureException(String message, RefUpdate refUpdate) {
- super(message);
- refs = ImmutableList.of(refUpdate.getName());
- }
-
- 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;
- }
-}
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index dcc36b6203..c1d8ddd1b4 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -30,8 +30,10 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
@@ -42,9 +44,7 @@ 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.PatchSet.Id;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -57,9 +57,7 @@ import com.google.gerrit.server.submit.CommitMergeStatus;
import com.google.gerrit.server.submit.IntegrationException;
import com.google.gerrit.server.submit.MergeIdenticalTreeException;
import com.google.gerrit.server.submit.MergeSorter;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -128,20 +126,31 @@ public class MergeUtil {
}
public String generate(
- RevCommit original, RevCommit mergeTip, Branch.NameKey dest, String current) {
+ RevCommit original, RevCommit mergeTip, Branch.NameKey dest, String originalMessage) {
requireNonNull(original.getRawBuffer());
if (mergeTip != null) {
requireNonNull(mergeTip.getRawBuffer());
}
- for (ChangeMessageModifier changeMessageModifier : changeMessageModifiers) {
+
+ int count = 0;
+ String current = originalMessage;
+ for (Extension<ChangeMessageModifier> ext : changeMessageModifiers.entries()) {
+ ChangeMessageModifier changeMessageModifier = ext.get();
+ String className = changeMessageModifier.getClass().getName();
current = changeMessageModifier.onSubmit(current, original, mergeTip, dest);
- requireNonNull(
- current,
- () ->
- String.format(
- "%s.OnSubmit returned null instead of new commit message",
- changeMessageModifier.getClass().getName()));
+ checkState(
+ current != null,
+ "%s.onSubmit from plugin %s returned null instead of new commit message",
+ className,
+ ext.getPluginName());
+ count++;
+ logger.atFine().log(
+ "Invoked %s from plugin %s, message length now %d",
+ className, ext.getPluginName(), current.length());
}
+ logger.atFine().log(
+ "Invoked %d ChangeMessageModifiers on message with original length %d",
+ count, originalMessage.length());
return current;
}
}
@@ -162,7 +171,6 @@ public class MergeUtil {
MergeUtil create(ProjectState project, boolean useContentMerge);
}
- private final Provider<ReviewDb> db;
private final IdentifiedUser.GenericFactory identifiedUserFactory;
private final DynamicItem<UrlFormatter> urlFormatter;
private final ApprovalsUtil approvalsUtil;
@@ -174,7 +182,6 @@ public class MergeUtil {
@AssistedInject
MergeUtil(
@GerritServerConfig Config serverConfig,
- Provider<ReviewDb> db,
IdentifiedUser.GenericFactory identifiedUserFactory,
DynamicItem<UrlFormatter> urlFormatter,
ApprovalsUtil approvalsUtil,
@@ -182,7 +189,6 @@ public class MergeUtil {
@Assisted ProjectState project) {
this(
serverConfig,
- db,
identifiedUserFactory,
urlFormatter,
approvalsUtil,
@@ -194,14 +200,12 @@ public class MergeUtil {
@AssistedInject
MergeUtil(
@GerritServerConfig Config serverConfig,
- Provider<ReviewDb> db,
IdentifiedUser.GenericFactory identifiedUserFactory,
DynamicItem<UrlFormatter> urlFormatter,
ApprovalsUtil approvalsUtil,
@Assisted ProjectState project,
PluggableCommitMessageGenerator commitMessageGenerator,
@Assisted boolean useContentMerge) {
- this.db = db;
this.identifiedUserFactory = identifiedUserFactory;
this.urlFormatter = urlFormatter;
this.approvalsUtil = approvalsUtil;
@@ -233,7 +237,7 @@ public class MergeUtil {
List<CodeReviewCommit> result = new ArrayList<>();
try {
result.addAll(mergeSorter.sort(toSort));
- } catch (IOException | OrmException e) {
+ } catch (IOException | StorageException e) {
throw new IntegrationException("Branch head sorting failed", e);
}
result.sort(CodeReviewCommit.ORDER);
@@ -350,8 +354,8 @@ public class MergeUtil {
try {
// TODO(dborowitz): Respect inCoreLimit here.
buf = new TemporaryBuffer.LocalFile(null, 10 * 1024 * 1024);
- fmt.formatMerge(buf, p, "BASE", oursNameFormatted, theirsNameFormatted, UTF_8.name());
- buf.close();
+ fmt.formatMerge(buf, p, "BASE", oursNameFormatted, theirsNameFormatted, UTF_8);
+ buf.close(); // Flush file and close for writes, but leave available for reading.
try (InputStream in = buf.openInputStream()) {
resolved.put(entry.getKey(), ins.insert(Constants.OBJ_BLOB, buf.length(), in));
@@ -589,7 +593,7 @@ public class MergeUtil {
* @return new message
*/
public String createCommitMessageOnSubmit(
- RevCommit n, RevCommit mergeTip, ChangeNotes notes, Id id) {
+ RevCommit n, RevCommit mergeTip, ChangeNotes notes, PatchSet.Id id) {
return commitMessageGenerator.generate(
n, mergeTip, notes.getChange().getDest(), createDetailedCommitMessage(n, notes, id));
}
@@ -604,8 +608,8 @@ public class MergeUtil {
private Iterable<PatchSetApproval> safeGetApprovals(ChangeNotes notes, PatchSet.Id psId) {
try {
- return approvalsUtil.byPatchSet(db.get(), notes, psId, null, null);
- } catch (OrmException e) {
+ return approvalsUtil.byPatchSet(notes, psId, null, null);
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Can't read approval records for %s", psId);
return Collections.emptyList();
}
@@ -718,7 +722,7 @@ public class MergeUtil {
throws IntegrationException {
try {
return !mergeSorter.sort(Collections.singleton(toMerge)).contains(toMerge);
- } catch (IOException | OrmException e) {
+ } catch (IOException | StorageException e) {
throw new IntegrationException("Branch head sorting failed", e);
}
}
diff --git a/java/com/google/gerrit/server/git/MergedByPushOp.java b/java/com/google/gerrit/server/git/MergedByPushOp.java
index 7f18e1b8ce..3d64b82542 100644
--- a/java/com/google/gerrit/server/git/MergedByPushOp.java
+++ b/java/com/google/gerrit/server/git/MergedByPushOp.java
@@ -35,12 +35,10 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
-import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.Constants;
@@ -112,7 +110,7 @@ public class MergedByPushOp implements BatchUpdateOp {
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException, IOException {
+ public boolean updateChange(ChangeContext ctx) throws IOException {
change = ctx.getChange();
correctBranch = refName.equals(change.getDest().get());
if (!correctBranch) {
@@ -126,14 +124,13 @@ public class MergedByPushOp implements BatchUpdateOp {
} else {
patchSet =
requireNonNull(
- psUtil.get(ctx.getDb(), ctx.getNotes(), psId),
+ psUtil.get(ctx.getNotes(), psId),
() -> String.format("patch set %s not found", psId));
}
info = getPatchSetInfo(ctx);
ChangeUpdate update = ctx.getUpdate(psId);
- Change.Status status = change.getStatus();
- if (status == Change.Status.MERGED) {
+ if (change.isMerged()) {
return true;
}
change.setCurrentPatchSet(info);
@@ -161,13 +158,12 @@ public class MergedByPushOp implements BatchUpdateOp {
ChangeMessage msg =
ChangeMessagesUtil.newMessage(
psId, ctx.getUser(), ctx.getWhen(), msgBuf.toString(), ChangeMessagesUtil.TAG_MERGED);
- cmUtil.addChangeMessage(ctx.getDb(), update, msg);
+ cmUtil.addChangeMessage(update, msg);
PatchSetApproval submitter =
ApprovalsUtil.newApproval(
change.currentPatchSetId(), ctx.getUser(), LabelId.legacySubmit(), 1, ctx.getWhen());
update.putApproval(submitter.getLabel(), submitter.getValue());
- ctx.getDb().patchSetApprovals().upsert(Collections.singleton(submitter));
return true;
}
@@ -205,7 +201,7 @@ public class MergedByPushOp implements BatchUpdateOp {
changeMerged.fire(change, patchSet, ctx.getAccount(), mergeResultRevId, ctx.getWhen());
}
- private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException, OrmException {
+ private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException {
RevWalk rw = ctx.getRevWalk();
RevCommit commit =
rw.parseCommit(ObjectId.fromString(requireNonNull(patchSet).getRevision().get()));
diff --git a/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java b/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
index 6fafe4e0cc..01e85cf332 100644
--- a/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
+++ b/java/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManager.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkState;
import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.Project.NameKey;
+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;
@@ -54,7 +54,7 @@ public class MultiBaseLocalDiskRepositoryManager extends LocalDiskRepositoryMana
}
@Override
- public Path getBasePath(NameKey name) {
+ public Path getBasePath(Project.NameKey name) {
Path alternateBasePath = config.getBasePath(name);
return alternateBasePath != null ? alternateBasePath : super.getBasePath(name);
}
diff --git a/java/com/google/gerrit/server/git/NotesBranchUtil.java b/java/com/google/gerrit/server/git/NotesBranchUtil.java
index 24b372772a..1636b85571 100644
--- a/java/com/google/gerrit/server/git/NotesBranchUtil.java
+++ b/java/com/google/gerrit/server/git/NotesBranchUtil.java
@@ -16,10 +16,11 @@ package com.google.gerrit.server.git;
import static com.google.common.base.MoreObjects.firstNonNull;
+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.gerrit.server.update.RefUpdateUtil;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/git/PerThreadRequestScope.java b/java/com/google/gerrit/server/git/PerThreadRequestScope.java
index a4719a911e..b7db542c22 100644
--- a/java/com/google/gerrit/server/git/PerThreadRequestScope.java
+++ b/java/com/google/gerrit/server/git/PerThreadRequestScope.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.git;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
@@ -52,10 +51,8 @@ public class PerThreadRequestScope {
public static class Propagator extends ThreadLocalRequestScopePropagator<Context> {
@Inject
- Propagator(
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
- super(REQUEST, current, local, dbProviderProvider);
+ Propagator(ThreadLocalRequestContext local) {
+ super(REQUEST, current, local);
}
@Override
diff --git a/java/com/google/gerrit/server/git/PureRevertCache.java b/java/com/google/gerrit/server/git/PureRevertCache.java
new file mode 100644
index 0000000000..16ac87ff38
--- /dev/null
+++ b/java/com/google/gerrit/server/git/PureRevertCache.java
@@ -0,0 +1,200 @@
+// 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.annotations.VisibleForTesting;
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+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.TraceContext;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.inject.Inject;
+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;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.merge.ThreeWayMerger;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/** Computes and caches if a change is a pure revert of another change. */
+@Singleton
+public class PureRevertCache {
+ private static final String ID_CACHE = "pure_revert";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ persist(ID_CACHE, Cache.PureRevertKeyProto.class, Boolean.class)
+ .maximumWeight(100)
+ .loader(Loader.class)
+ .version(1)
+ .keySerializer(new ProtobufSerializer<>(Cache.PureRevertKeyProto.parser()))
+ .valueSerializer(BooleanCacheSerializer.INSTANCE);
+ }
+ };
+ }
+
+ private final LoadingCache<PureRevertKeyProto, Boolean> cache;
+ private final ChangeNotes.Factory notesFactory;
+
+ @Inject
+ PureRevertCache(
+ @Named(ID_CACHE) LoadingCache<PureRevertKeyProto, Boolean> cache,
+ ChangeNotes.Factory notesFactory) {
+ this.cache = cache;
+ this.notesFactory = notesFactory;
+ }
+
+ /**
+ * Returns {@code true} if {@code claimedRevert} is a pure (clean) revert of the change that is
+ * referenced in {@link Change#getRevertOf()}.
+ *
+ * @return {@code true} if {@code claimedRevert} is a pure (clean) revert.
+ * @throws IOException if there was a problem with the storage layer
+ * @throws BadRequestException if there is a problem with the provided {@link ChangeNotes}
+ */
+ public boolean isPureRevert(ChangeNotes claimedRevert) throws IOException, BadRequestException {
+ if (claimedRevert.getChange().getRevertOf() == null) {
+ throw new BadRequestException("revertOf not set");
+ }
+ ChangeNotes claimedOriginal =
+ notesFactory.createChecked(
+ claimedRevert.getProjectName(), claimedRevert.getChange().getRevertOf());
+ return isPureRevert(
+ claimedRevert.getProjectName(),
+ ObjectId.fromString(claimedRevert.getCurrentPatchSet().getRevision().get()),
+ ObjectId.fromString(claimedOriginal.getCurrentPatchSet().getRevision().get()));
+ }
+
+ /**
+ * Returns {@code true} if {@code claimedRevert} is a pure (clean) revert of {@code
+ * claimedOriginal}.
+ *
+ * @return {@code true} if {@code claimedRevert} is a pure (clean) revert of {@code
+ * claimedOriginal}.
+ * @throws IOException if there was a problem with the storage layer
+ * @throws BadRequestException if there is a problem with the provided {@link ObjectId}s
+ */
+ public boolean isPureRevert(
+ Project.NameKey project, ObjectId claimedRevert, ObjectId claimedOriginal)
+ throws IOException, BadRequestException {
+ try {
+ return cache.get(key(project, claimedRevert, claimedOriginal));
+ } catch (ExecutionException e) {
+ Throwables.throwIfInstanceOf(e.getCause(), BadRequestException.class);
+ throw new IOException(e);
+ }
+ }
+
+ @VisibleForTesting
+ static PureRevertKeyProto key(
+ Project.NameKey project, ObjectId claimedRevert, ObjectId claimedOriginal) {
+ ByteString original = ObjectIdConverter.create().toByteString(claimedOriginal);
+ ByteString revert = ObjectIdConverter.create().toByteString(claimedRevert);
+ return PureRevertKeyProto.newBuilder()
+ .setProject(project.get())
+ .setClaimedOriginal(original)
+ .setClaimedRevert(revert)
+ .build();
+ }
+
+ static class Loader extends CacheLoader<PureRevertKeyProto, Boolean> {
+ private final GitRepositoryManager repoManager;
+ private final MergeUtil.Factory mergeUtilFactory;
+ private final ProjectCache projectCache;
+
+ @Inject
+ Loader(
+ GitRepositoryManager repoManager,
+ MergeUtil.Factory mergeUtilFactory,
+ ProjectCache projectCache) {
+ this.repoManager = repoManager;
+ this.mergeUtilFactory = mergeUtilFactory;
+ this.projectCache = projectCache;
+ }
+
+ @Override
+ public Boolean load(PureRevertKeyProto key) throws BadRequestException, IOException {
+ try (TraceContext.TraceTimer ignored =
+ TraceContext.newTimer("Loading pure revert for %s", key)) {
+ ObjectId original = ObjectIdConverter.create().fromByteString(key.getClaimedOriginal());
+ ObjectId revert = ObjectIdConverter.create().fromByteString(key.getClaimedRevert());
+ Project.NameKey project = new Project.NameKey(key.getProject());
+
+ try (Repository repo = repoManager.openRepository(project);
+ ObjectInserter oi = repo.newObjectInserter();
+ RevWalk rw = new RevWalk(repo)) {
+ RevCommit claimedOriginalCommit;
+ try {
+ claimedOriginalCommit = rw.parseCommit(original);
+ } catch (InvalidObjectIdException | MissingObjectException e) {
+ throw new BadRequestException("invalid object ID", e);
+ }
+ if (claimedOriginalCommit.getParentCount() == 0) {
+ throw new BadRequestException("can't check against initial commit");
+ }
+ RevCommit claimedRevertCommit = rw.parseCommit(revert);
+ if (claimedRevertCommit.getParentCount() == 0) {
+ return false;
+ }
+ // Rebase claimed revert onto claimed original
+ ThreeWayMerger merger =
+ mergeUtilFactory
+ .create(projectCache.checkedGet(project))
+ .newThreeWayMerger(oi, repo.getConfig());
+ merger.setBase(claimedRevertCommit.getParent(0));
+ boolean success = merger.merge(claimedRevertCommit, claimedOriginalCommit);
+ if (!success || merger.getResultTreeId() == null) {
+ // Merge conflict during rebase
+ return false;
+ }
+
+ // 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())) {
+ df.setReader(oi.newReader(), repo.getConfig());
+ List<DiffEntry> entries =
+ df.scan(claimedOriginalCommit.getParent(0), merger.getResultTreeId());
+ return entries.isEmpty();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
index d5e19cc613..d7f8982e40 100644
--- a/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
+++ b/java/com/google/gerrit/server/git/SearchingChangeCacheImpl.java
@@ -19,12 +19,12 @@ import com.google.common.cache.CacheLoader;
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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.index.change.ChangeField;
@@ -45,6 +45,13 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
+/**
+ * Cache based on an index query of the most recent changes. The number of cached items depends on
+ * the index implementation and configuration.
+ *
+ * <p>This cache is intended to be used when filtering references. By design it returns only a
+ * fraction of all changes. These are the changes that were modified last.
+ */
@Singleton
public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -65,8 +72,7 @@ public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener {
@Override
protected void configure() {
if (slave) {
- bind(SearchingChangeCacheImpl.class)
- .toProvider(Providers.<SearchingChangeCacheImpl>of(null));
+ bind(SearchingChangeCacheImpl.class).toProvider(Providers.of(null));
} else {
cache(ID_CACHE, Project.NameKey.class, new TypeLiteral<List<CachedChange>>() {})
.maximumWeight(0)
@@ -80,7 +86,8 @@ public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener {
}
@AutoValue
- abstract static class CachedChange {
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public abstract static class CachedChange {
// Subset of fields in ChangeData, specifically fields needed to serve
// VisibleRefFilter without touching the database. More can be added as
// necessary.
@@ -107,16 +114,15 @@ public class SearchingChangeCacheImpl implements GitReferenceUpdatedListener {
* <p>Returned changes only include the {@code Change} object (with id, branch) and the reviewers.
* Additional stored fields are not loaded from the index.
*
- * @param db database handle to populate missing change data (probably unused).
* @param project project to read.
* @return list of known changes; empty if no changes.
*/
- public List<ChangeData> getChangeData(ReviewDb db, Project.NameKey project) {
+ public List<ChangeData> getChangeData(Project.NameKey project) {
try {
List<CachedChange> cached = cache.get(project);
List<ChangeData> cds = new ArrayList<>(cached.size());
for (CachedChange cc : cached) {
- ChangeData cd = changeDataFactory.create(db, cc.change());
+ ChangeData cd = changeDataFactory.create(cc.change());
cd.setReviewers(cc.reviewers());
cds.add(cd);
}
diff --git a/java/com/google/gerrit/server/git/TagMatcher.java b/java/com/google/gerrit/server/git/TagMatcher.java
index 58b4b8b2b8..f003b6f9b4 100644
--- a/java/com/google/gerrit/server/git/TagMatcher.java
+++ b/java/com/google/gerrit/server/git/TagMatcher.java
@@ -51,13 +51,8 @@ public class TagMatcher {
this.updated = updated;
}
- public boolean isReachable(Ref tagRef) {
- try {
- tagRef = db.getRefDatabase().peel(tagRef);
- } catch (IOException e) {
- // Ignore
- }
-
+ public boolean isReachable(Ref tagRef) throws IOException {
+ tagRef = db.getRefDatabase().peel(tagRef);
ObjectId tagObj = tagRef.getPeeledObjectId();
if (tagObj == null) {
tagObj = tagRef.getObjectId();
diff --git a/java/com/google/gerrit/server/git/TagSet.java b/java/com/google/gerrit/server/git/TagSet.java
index ce8814f849..57637c897b 100644
--- a/java/com/google/gerrit/server/git/TagSet.java
+++ b/java/com/google/gerrit/server/git/TagSet.java
@@ -24,7 +24,7 @@ import com.google.gerrit.reviewdb.client.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;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.BitSet;
diff --git a/java/com/google/gerrit/server/git/TagSetHolder.java b/java/com/google/gerrit/server/git/TagSetHolder.java
index 4c0c035e02..194283e7af 100644
--- a/java/com/google/gerrit/server/git/TagSetHolder.java
+++ b/java/com/google/gerrit/server/git/TagSetHolder.java
@@ -17,10 +17,10 @@ package com.google.gerrit.server.git;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.common.Nullable;
+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 com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
import java.util.Collection;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -111,13 +111,12 @@ public class TagSetHolder {
if (tags != null) {
b.setTags(tags.toProto());
}
- return ProtoCacheSerializers.toByteArray(b.build());
+ return Protos.toByteArray(b.build());
}
@Override
public TagSetHolder deserialize(byte[] in) {
- TagSetHolderProto proto =
- ProtoCacheSerializers.parseUnchecked(TagSetHolderProto.parser(), in);
+ TagSetHolderProto proto = Protos.parseUnchecked(TagSetHolderProto.parser(), in);
TagSetHolder holder = new TagSetHolder(new Project.NameKey(proto.getProjectName()));
if (proto.hasTags()) {
holder.tags = TagSet.fromProto(proto.getTags());
diff --git a/java/com/google/gerrit/server/git/UserConfigSections.java b/java/com/google/gerrit/server/git/UserConfigSections.java
index 859e40d3c6..0ef908c6a2 100644
--- a/java/com/google/gerrit/server/git/UserConfigSections.java
+++ b/java/com/google/gerrit/server/git/UserConfigSections.java
@@ -25,9 +25,6 @@ public class UserConfigSections {
public static final String KEY_URL = "url";
public static final String KEY_TARGET = "target";
public static final String KEY_ID = "id";
- public static final String URL_ALIAS = "urlAlias";
- public static final String KEY_MATCH = "match";
- public static final String KEY_TOKEN = "token";
/** The table column user preferences. */
public static final String CHANGE_TABLE = "changeTable";
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java
index 2d97d04f7e..4522b0d80f 100644
--- a/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -17,7 +17,6 @@ package com.google.gerrit.server.git;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CaseFormat;
-import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
@@ -86,12 +85,8 @@ public class WorkQueue {
}
private static final UncaughtExceptionHandler LOG_UNCAUGHT_EXCEPTION =
- new UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
+ (t, e) ->
logger.atSevere().withCause(e).log("WorkQueue thread %s threw exception", t.getName());
- }
- };
private final ScheduledExecutorService defaultQueue;
private final IdGenerator idGenerator;
@@ -360,75 +355,44 @@ public class WorkQueue {
new Description("Maximum allowed number of threads in the pool")
.setGauge()
.setUnit("threads"),
- new Supplier<Long>() {
- @Override
- public Long get() {
- return (long) getMaximumPoolSize();
- }
- });
+ () -> (long) getMaximumPoolSize());
metrics.newCallbackMetric(
getMetricName(queueName, "pool_size"),
Long.class,
new Description("Current number of threads in the pool").setGauge().setUnit("threads"),
- new Supplier<Long>() {
- @Override
- public Long get() {
- return (long) getPoolSize();
- }
- });
+ () -> (long) getPoolSize());
metrics.newCallbackMetric(
getMetricName(queueName, "active_threads"),
Long.class,
new Description("Number number of threads that are actively executing tasks")
.setGauge()
.setUnit("threads"),
- new Supplier<Long>() {
- @Override
- public Long get() {
- return (long) getActiveCount();
- }
- });
+ () -> (long) getActiveCount());
metrics.newCallbackMetric(
getMetricName(queueName, "scheduled_tasks"),
Integer.class,
new Description("Number of scheduled tasks in the queue").setGauge().setUnit("tasks"),
- new Supplier<Integer>() {
- @Override
- public Integer get() {
- return getQueue().size();
- }
- });
+ () -> getQueue().size());
metrics.newCallbackMetric(
getMetricName(queueName, "total_scheduled_tasks_count"),
Long.class,
new Description("Total number of tasks that have been scheduled for execution")
.setCumulative()
.setUnit("tasks"),
- new Supplier<Long>() {
- @Override
- public Long get() {
- return (long) getTaskCount();
- }
- });
+ this::getTaskCount);
metrics.newCallbackMetric(
getMetricName(queueName, "total_completed_tasks_count"),
Long.class,
new Description("Total number of tasks that have completed execution")
.setCumulative()
.setUnit("tasks"),
- new Supplier<Long>() {
- @Override
- public Long get() {
- return (long) getCompletedTaskCount();
- }
- });
+ this::getCompletedTaskCount);
}
private String getMetricName(String queueName, String metricName) {
String name =
CaseFormat.UPPER_CAMEL.to(
- CaseFormat.LOWER_UNDERSCORE,
- queueName.replaceFirst("SSH", "Ssh").replaceAll("-", ""));
+ CaseFormat.LOWER_UNDERSCORE, queueName.replaceFirst("SSH", "Ssh").replace("-", ""));
return metrics.sanitizeMetricName(String.format("queue/%s/%s", name, metricName));
}
@@ -674,7 +638,7 @@ public class WorkQueue {
for (Field innerField : innerObj.getClass().getDeclaredFields()) {
if (innerField.getType().isAssignableFrom(Callable.class)) {
innerField.setAccessible(true);
- return ((Callable<?>) innerField.get(innerObj)).toString();
+ return innerField.get(innerObj).toString();
}
}
}
diff --git a/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java b/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java
index bbe0c628f6..97beefdeb2 100644
--- a/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java
+++ b/java/com/google/gerrit/server/git/meta/MetaDataUpdate.java
@@ -22,6 +22,7 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.Singleton;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -33,21 +34,22 @@ import org.eclipse.jgit.lib.Repository;
/** Helps with the updating of a {@link VersionedMetaData}. */
public class MetaDataUpdate implements AutoCloseable {
+ @Singleton
public static class User {
private final InternalFactory factory;
private final GitRepositoryManager mgr;
- private final PersonIdent serverIdent;
+ private final Provider<PersonIdent> serverIdentProvider;
private final Provider<IdentifiedUser> identifiedUser;
@Inject
User(
InternalFactory factory,
GitRepositoryManager mgr,
- @GerritPersonIdent PersonIdent serverIdent,
+ @GerritPersonIdent Provider<PersonIdent> serverIdentProvider,
Provider<IdentifiedUser> identifiedUser) {
this.factory = factory;
this.mgr = mgr;
- this.serverIdent = serverIdent;
+ this.serverIdentProvider = serverIdentProvider;
this.identifiedUser = identifiedUser;
}
@@ -126,29 +128,31 @@ public class MetaDataUpdate implements AutoCloseable {
public MetaDataUpdate create(
Project.NameKey name, Repository repository, IdentifiedUser user, BatchRefUpdate batch) {
MetaDataUpdate md = factory.create(name, repository, batch);
- md.getCommitBuilder().setCommitter(serverIdent);
+ md.getCommitBuilder().setCommitter(serverIdentProvider.get());
md.setAuthor(user);
return md;
}
private PersonIdent createPersonIdent(IdentifiedUser user) {
+ PersonIdent serverIdent = serverIdentProvider.get();
return user.newCommitterIdent(serverIdent.getWhen(), serverIdent.getTimeZone());
}
}
+ @Singleton
public static class Server {
private final InternalFactory factory;
private final GitRepositoryManager mgr;
- private final PersonIdent serverIdent;
+ private final Provider<PersonIdent> serverIdentProvider;
@Inject
Server(
InternalFactory factory,
GitRepositoryManager mgr,
- @GerritPersonIdent PersonIdent serverIdent) {
+ @GerritPersonIdent Provider<PersonIdent> serverIdentProvider) {
this.factory = factory;
this.mgr = mgr;
- this.serverIdent = serverIdent;
+ this.serverIdentProvider = serverIdentProvider;
}
public MetaDataUpdate create(Project.NameKey name)
@@ -162,6 +166,7 @@ public class MetaDataUpdate implements AutoCloseable {
Repository repo = mgr.openRepository(name);
MetaDataUpdate md = factory.create(name, repo, batch);
md.setCloseRepository(true);
+ PersonIdent serverIdent = serverIdentProvider.get();
md.getCommitBuilder().setAuthor(serverIdent);
md.getCommitBuilder().setCommitter(serverIdent);
return md;
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
index d38a33e9eb..86942998a6 100644
--- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
+++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -19,8 +19,8 @@ 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.git.LockFailureException;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.git.LockFailureException;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.util.CommitMessageUtil;
@@ -457,7 +457,12 @@ public abstract class VersionedMetaData {
}
protected Config readConfig(String fileName) throws IOException, ConfigInvalidException {
- Config rc = new Config();
+ return readConfig(fileName, null);
+ }
+
+ protected Config readConfig(String fileName, Config baseConfig)
+ throws IOException, ConfigInvalidException {
+ Config rc = new Config(baseConfig);
String text = readUTF8(fileName);
if (!text.isEmpty()) {
try {
diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index 882f2083a4..da2887f907 100644
--- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -14,10 +14,12 @@
package com.google.gerrit.server.git.receive;
+import static com.google.gerrit.server.quota.QuotaGroupDefinitions.REPOSITORY_SIZE_GROUP;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
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.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -31,7 +33,6 @@ 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.UsedAt;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ReceiveCommitsExecutor;
@@ -46,6 +47,9 @@ import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.quota.QuotaBackend;
+import com.google.gerrit.server.quota.QuotaException;
+import com.google.gerrit.server.quota.QuotaResponse;
import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.inject.Inject;
@@ -96,6 +100,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
public static class Module extends PrivateModule {
@Override
public void configure() {
+ install(new FactoryModuleBuilder().build(LazyPostReceiveHookChain.Factory.class));
install(new FactoryModuleBuilder().build(AsyncReceiveCommits.Factory.class));
expose(AsyncReceiveCommits.Factory.class);
// Don't expose the binding for ReceiveCommits.Factory. All callers should
@@ -115,17 +120,25 @@ public class AsyncReceiveCommits implements PreReceiveHook {
private class Worker implements ProjectRunnable {
final MultiProgressMonitor progress;
+ final String name;
private final Collection<ReceiveCommand> commands;
- private Worker(Collection<ReceiveCommand> commands) {
+ private Worker(Collection<ReceiveCommand> commands, String name) {
this.commands = commands;
+ this.name = name;
progress = new MultiProgressMonitor(new MessageSenderOutputStream(), "Processing changes");
}
@Override
public void run() {
- receiveCommits.processCommands(commands, progress);
+ String oldName = Thread.currentThread().getName();
+ Thread.currentThread().setName(oldName + "-for-" + name);
+ try {
+ receiveCommits.processCommands(commands, progress);
+ } finally {
+ Thread.currentThread().setName(oldName);
+ }
}
@Override
@@ -175,29 +188,45 @@ public class AsyncReceiveCommits implements PreReceiveHook {
}
}
+ private enum PushType {
+ CREATE_REPLACE,
+ NORMAL,
+ AUTOCLOSE,
+ }
+
@Singleton
private static class Metrics {
- private final Histogram1<ResultChangeIds.Key> changes;
- private final Timer1<String> latencyPerChange;
+ private final Histogram1<PushType> changes;
+ private final Timer1<PushType> latencyPerChange;
+ private final Timer1<PushType> latencyPerPush;
private final Counter0 timeouts;
@Inject
Metrics(MetricMaker metricMaker) {
changes =
metricMaker.newHistogram(
- "receivecommits/changes",
+ "receivecommits/changes_per_push",
new Description("number of changes uploaded in a single push.").setCumulative(),
- Field.ofEnum(
- ResultChangeIds.Key.class,
- "type",
- "type of update (replace, create, autoclose)"));
+ Field.ofEnum(PushType.class, "type", "type of push (create/replace, autoclose)"));
+
latencyPerChange =
metricMaker.newTimer(
- "receivecommits/latency",
- new Description("average delay per updated change")
+ "receivecommits/latency_per_push_per_change",
+ new Description(
+ "Processing delay per push divided by the number of changes in said push. "
+ + "(Only includes pushes which contain changes.)")
+ .setUnit(Units.MILLISECONDS)
+ .setCumulative(),
+ Field.ofEnum(PushType.class, "type", "type of push (create/replace, autoclose)"));
+
+ latencyPerPush =
+ metricMaker.newTimer(
+ "receivecommits/latency_per_push",
+ new Description("processing delay for a processing single push")
.setUnit(Units.MILLISECONDS)
.setCumulative(),
- Field.ofString("type", "type of update (create/replace, autoclose)"));
+ Field.ofEnum(
+ PushType.class, "type", "type of push (create/replace, autoclose, normal)"));
timeouts =
metricMaker.newCounter(
@@ -229,9 +258,10 @@ public class AsyncReceiveCommits implements PreReceiveHook {
RequestScopePropagator scopePropagator,
ReceiveConfig receiveConfig,
TransferConfig transferConfig,
- Provider<LazyPostReceiveHookChain> lazyPostReceive,
+ LazyPostReceiveHookChain.Factory lazyPostReceive,
ContributorAgreementsChecker contributorAgreements,
Metrics metrics,
+ QuotaBackend quotaBackend,
@Named(TIMEOUT_NAME) long timeoutMillis,
@Assisted ProjectState projectState,
@Assisted IdentifiedUser user,
@@ -260,7 +290,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
receivePack.setRefFilter(new ReceiveRefFilter());
receivePack.setAllowPushOptions(true);
receivePack.setPreReceiveHook(this);
- receivePack.setPostReceiveHook(lazyPostReceive.get());
+ 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.
@@ -287,6 +317,17 @@ public class AsyncReceiveCommits implements PreReceiveHook {
factory.create(
projectState, user, receivePack, allRefsWatcher, messageSender, resultChangeIds);
receiveCommits.init();
+ QuotaResponse.Aggregated availableTokens =
+ quotaBackend.user(user).project(projectName).availableTokens(REPOSITORY_SIZE_GROUP);
+ try {
+ availableTokens.throwOnError();
+ } catch (QuotaException e) {
+ logger.atWarning().withCause(e).log(
+ "Quota %s availableTokens request failed for project %s",
+ REPOSITORY_SIZE_GROUP, projectName);
+ throw new RuntimeException(e);
+ }
+ availableTokens.availableTokens().ifPresent(v -> receivePack.setMaxObjectSizeLimit(v));
}
/** Determine if the user can upload commits. */
@@ -318,7 +359,7 @@ public class AsyncReceiveCommits implements PreReceiveHook {
}
long startNanos = System.nanoTime();
- Worker w = new Worker(commands);
+ Worker w = new Worker(commands, Thread.currentThread().getName());
try {
w.progress.waitFor(
executor.submit(scopePropagator.wrap(w)), timeoutMillis, TimeUnit.MILLISECONDS);
@@ -341,20 +382,30 @@ public class AsyncReceiveCommits implements PreReceiveHook {
long deltaNanos = System.nanoTime() - startNanos;
int totalChanges = 0;
- for (ResultChangeIds.Key key : ResultChangeIds.Key.values()) {
- List<Change.Id> ids = resultChangeIds.get(key);
- metrics.changes.record(key, ids.size());
- totalChanges += ids.size();
+
+ PushType pushType;
+ if (resultChangeIds.isMagicPush()) {
+ pushType = PushType.CREATE_REPLACE;
+ List<Change.Id> created = resultChangeIds.get(ResultChangeIds.Key.CREATED);
+ List<Change.Id> replaced = resultChangeIds.get(ResultChangeIds.Key.REPLACED);
+ metrics.changes.record(pushType, created.size() + replaced.size());
+ totalChanges = replaced.size() + created.size();
+ } else {
+ List<Change.Id> autoclosed = resultChangeIds.get(ResultChangeIds.Key.AUTOCLOSED);
+ if (!autoclosed.isEmpty()) {
+ pushType = PushType.AUTOCLOSE;
+ metrics.changes.record(pushType, autoclosed.size());
+ totalChanges = autoclosed.size();
+ } else {
+ pushType = PushType.NORMAL;
+ }
}
if (totalChanges > 0) {
- metrics.latencyPerChange.record(
- resultChangeIds.get(ResultChangeIds.Key.AUTOCLOSED).isEmpty()
- ? "CREATE_REPLACE"
- : ResultChangeIds.Key.AUTOCLOSED.name(),
- deltaNanos / totalChanges,
- NANOSECONDS);
+ metrics.latencyPerChange.record(pushType, deltaNanos / totalChanges, NANOSECONDS);
}
+
+ metrics.latencyPerPush.record(pushType, deltaNanos, NANOSECONDS);
}
/** Returns the Change.Ids that were processed in onPreReceive */
diff --git a/java/com/google/gerrit/server/git/receive/BUILD b/java/com/google/gerrit/server/git/receive/BUILD
index 6a51474e4c..b1bf933374 100644
--- a/java/com/google/gerrit/server/git/receive/BUILD
+++ b/java/com/google/gerrit/server/git/receive/BUILD
@@ -7,6 +7,7 @@ java_library(
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/metrics",
"//java/com/google/gerrit/reviewdb:server",
@@ -16,7 +17,6 @@ java_library(
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
- "//lib:gwtorm",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java b/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java
index bf3d270e43..36d0eb7add 100644
--- a/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java
+++ b/java/com/google/gerrit/server/git/receive/HackPushNegotiateHook.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git.receive;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
+import static java.util.stream.Collectors.toMap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
@@ -73,12 +73,15 @@ public class HackPushNegotiateHook implements AdvertiseRefsHook {
throw new UnsupportedOperationException("HackPushNegotiateHook cannot be used for UploadPack");
}
+ @SuppressWarnings("deprecation")
@Override
public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
Map<String, Ref> r = rp.getAdvertisedRefs();
if (r == null) {
try {
- r = rp.getRepository().getRefDatabase().getRefs(ALL);
+ r =
+ rp.getRepository().getRefDatabase().getRefs().stream()
+ .collect(toMap(Ref::getName, x -> x));
} catch (ServiceMayNotContinueException e) {
throw e;
} catch (IOException e) {
@@ -100,6 +103,7 @@ public class HackPushNegotiateHook implements AdvertiseRefsHook {
}
// Scan history until the advertisement is full.
+ @SuppressWarnings("deprecation")
RevWalk rw = rp.getRevWalk();
rw.reset();
try {
diff --git a/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java b/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
index 7adb21b97c..17d4a38173 100644
--- a/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
+++ b/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
@@ -14,25 +14,85 @@
package com.google.gerrit.server.git.receive;
-import com.google.gerrit.extensions.registration.DynamicSet;
+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.server.CurrentUser;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.gerrit.server.quota.QuotaBackend;
+import com.google.gerrit.server.quota.QuotaResponse;
import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
import java.util.Collection;
import org.eclipse.jgit.transport.PostReceiveHook;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceivePack;
-class LazyPostReceiveHookChain implements PostReceiveHook {
- private final DynamicSet<PostReceiveHook> hooks;
+/**
+ * Class is responsible for calling all registered post-receive hooks. In addition, in case when
+ * repository size quota is defined, it requests tokens (pack size) that were received. This is the
+ * final step of enforcing repository size quota that deducts token from available tokens.
+ */
+public class LazyPostReceiveHookChain implements PostReceiveHook {
+ interface Factory {
+ LazyPostReceiveHookChain create(CurrentUser user, Project.NameKey project);
+ }
+
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final PluginSetContext<PostReceiveHook> hooks;
+ private final QuotaBackend quotaBackend;
+ private final CurrentUser user;
+ private final Project.NameKey project;
@Inject
- LazyPostReceiveHookChain(DynamicSet<PostReceiveHook> hooks) {
+ LazyPostReceiveHookChain(
+ PluginSetContext<PostReceiveHook> hooks,
+ QuotaBackend quotaBackend,
+ @Assisted CurrentUser user,
+ @Assisted Project.NameKey project) {
this.hooks = hooks;
+ this.quotaBackend = quotaBackend;
+ this.user = user;
+ this.project = project;
}
@Override
public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
- for (PostReceiveHook h : hooks) {
- h.onPostReceive(rp, commands);
+ hooks.runEach(h -> h.onPostReceive(rp, commands));
+ if (affectsSize(rp, commands)) {
+ QuotaResponse.Aggregated a =
+ quotaBackend
+ .user(user)
+ .project(project)
+ .requestTokens(REPOSITORY_SIZE_GROUP, rp.getPackSize());
+ if (a.hasError()) {
+ String msg =
+ String.format(
+ "%s request failed for project %s with [%s]",
+ REPOSITORY_SIZE_GROUP, project, a.errorMessage());
+ logger.atWarning().log(msg);
+ throw new RuntimeException(msg);
+ }
+ }
+ }
+
+ 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;
}
}
diff --git a/java/com/google/gerrit/server/git/receive/MessageSender.java b/java/com/google/gerrit/server/git/receive/MessageSender.java
index 1f665703e2..4fa5451024 100644
--- a/java/com/google/gerrit/server/git/receive/MessageSender.java
+++ b/java/com/google/gerrit/server/git/receive/MessageSender.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.git.receive;
-import com.google.gerrit.server.UsedAt;
+import com.google.gerrit.common.UsedAt;
/**
* Interface used by {@link ReceiveCommits} for send messages over the wire during {@code
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 77f1e1ec5e..2b127b63c1 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -18,6 +18,7 @@ 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.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;
@@ -30,7 +31,6 @@ import static com.google.gerrit.server.git.receive.ReceiveConstants.SAME_CHANGE_
import static com.google.gerrit.server.git.validators.CommitValidators.NEW_PATCHSET_PATTERN;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Comparator.comparingInt;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
@@ -49,6 +49,7 @@ import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
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.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
@@ -63,6 +64,7 @@ import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
+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.RecipientType;
@@ -86,16 +88,16 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CreateGroupPermissionSyncer;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.change.ChangeInserter;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.SetHashtagsOp;
+import com.google.gerrit.server.change.SetPrivateOp;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.PluginConfig;
@@ -111,7 +113,6 @@ import com.google.gerrit.server.git.MultiProgressMonitor.Task;
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.ValidationError;
-import com.google.gerrit.server.git.receive.ResultChangeIds.Key;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.git.validators.RefOperationValidationException;
import com.google.gerrit.server.git.validators.RefOperationValidators;
@@ -121,7 +122,7 @@ import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -158,7 +159,6 @@ import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.util.cli.CmdLineParser;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -175,14 +175,17 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.TreeSet;
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;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -304,7 +307,6 @@ class ReceiveCommits {
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
private final PluginSetContext<ReceivePackInitializer> initializers;
private final MergedByPushOp.Factory mergedByPushOpFactory;
- private final NotesMigration notesMigration;
private final PatchSetInfoFactory patchSetInfoFactory;
private final PatchSetUtil psUtil;
private final PermissionBackend permissionBackend;
@@ -317,11 +319,12 @@ class ReceiveCommits {
private final ReplaceOp.Factory replaceOpFactory;
private final RetryHelper retryHelper;
private final RequestScopePropagator requestScopePropagator;
- private final ReviewDb db;
private final Sequences seq;
private final SetHashtagsOp.Factory hashtagsFactory;
private final SubmoduleOp.Factory subOpFactory;
private final TagCache tagCache;
+ private final ProjectConfig.Factory projectConfigFactory;
+ private final SetPrivateOp.Factory setPrivateOpFactory;
// Assisted injected fields.
private final AllRefsWatcher allRefsWatcher;
@@ -366,6 +369,7 @@ class ReceiveCommits {
AccountResolver accountResolver,
AllProjectsName allProjectsName,
BatchUpdate.Factory batchUpdateFactory,
+ ProjectConfig.Factory projectConfigFactory,
@GerritServerConfig Config cfg,
ChangeEditUtil editUtil,
ChangeIndexer indexer,
@@ -379,7 +383,6 @@ class ReceiveCommits {
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginSetContext<ReceivePackInitializer> initializers,
MergedByPushOp.Factory mergedByPushOpFactory,
- NotesMigration notesMigration,
PatchSetInfoFactory patchSetInfoFactory,
PatchSetUtil psUtil,
PermissionBackend permissionBackend,
@@ -392,11 +395,11 @@ class ReceiveCommits {
ReplaceOp.Factory replaceOpFactory,
RetryHelper retryHelper,
RequestScopePropagator requestScopePropagator,
- ReviewDb db,
Sequences seq,
SetHashtagsOp.Factory hashtagsFactory,
SubmoduleOp.Factory subOpFactory,
TagCache tagCache,
+ SetPrivateOp.Factory setPrivateOpFactory,
@Assisted ProjectState projectState,
@Assisted IdentifiedUser user,
@Assisted ReceivePack rp,
@@ -413,7 +416,6 @@ class ReceiveCommits {
this.commitValidatorFactory = commitValidatorFactory;
this.createRefControl = createRefControl;
this.createGroupPermissionSyncer = createGroupPermissionSyncer;
- this.db = db;
this.editUtil = editUtil;
this.hashtagsFactory = hashtagsFactory;
this.indexer = indexer;
@@ -421,7 +423,6 @@ class ReceiveCommits {
this.mergeOpProvider = mergeOpProvider;
this.mergedByPushOpFactory = mergedByPushOpFactory;
this.notesFactory = notesFactory;
- this.notesMigration = notesMigration;
this.optionParserFactory = optionParserFactory;
this.ormProvider = ormProvider;
this.patchSetInfoFactory = patchSetInfoFactory;
@@ -438,6 +439,8 @@ class ReceiveCommits {
this.seq = seq;
this.subOpFactory = subOpFactory;
this.tagCache = tagCache;
+ this.projectConfigFactory = projectConfigFactory;
+ this.setPrivateOpFactory = setPrivateOpFactory;
// Assisted injected fields.
this.allRefsWatcher = allRefsWatcher;
@@ -531,6 +534,8 @@ class ReceiveCommits {
(tagName, traceId) -> addMessage(tagName + ": " + traceId))) {
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());
@@ -608,14 +613,10 @@ class ReceiveCommits {
newProgress.end();
replaceProgress.end();
queueSuccessMessages(newChanges);
- refsPublishDeprecationWarning();
- }
- }
- private void refsPublishDeprecationWarning() {
- // TODO(xchangcheng): remove after migrating tools which are using this magic branch.
- if (magicBranch != null && magicBranch.publish) {
- addMessage("Pushing to refs/publish/* is deprecated, use refs/for/* instead.");
+ logger.atFine().log(
+ "Command results: %s",
+ lazy(() -> commands.stream().map(ReceiveCommits::commandToString).collect(joining(","))));
}
}
@@ -632,17 +633,18 @@ 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(
- db, project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
+ project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
ObjectInserter ins = repo.newObjectInserter();
ObjectReader reader = ins.newReader();
RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo, rw, ins).updateChangesInParallel();
+ bu.setRepository(repo, rw, ins);
bu.setRefLogMessage("push");
int added = 0;
@@ -687,7 +689,7 @@ class ReceiveCommits {
// Update superproject gitlinks if required.
if (!branches.isEmpty()) {
try (MergeOpRepoManager orm = ormProvider.get()) {
- orm.setContext(db, TimeUtil.nowTs(), user);
+ orm.setContext(TimeUtil.nowTs(), user, NotifyResolver.Result.none());
SubmoduleOp op = subOpFactory.create(branches, orm);
op.updateSuperProjects();
} catch (SubmoduleException e) {
@@ -698,13 +700,48 @@ class ReceiveCommits {
/** Appends messages for successful change creation/updates. */
private void queueSuccessMessages(List<CreateRequest> newChanges) {
- List<CreateRequest> created =
- newChanges.stream().filter(r -> r.change != null).collect(toList());
- List<ReplaceRequest> updated =
+ // adjacency list for commit => parent
+ Map<String, String> adjList = new HashMap<>();
+ for (CreateRequest cr : newChanges) {
+ String parent = cr.commit.getParentCount() == 0 ? null : cr.commit.getParent(0).name();
+ adjList.put(cr.commit.name(), parent);
+ }
+ for (ReplaceRequest rr : replaceByChange.values()) {
+ String parent = null;
+ if (rr.revCommit != null) {
+ parent = rr.revCommit.getParentCount() == 0 ? null : rr.revCommit.getParent(0).name();
+ }
+ adjList.put(rr.newCommitId.name(), parent);
+ }
+
+ // get commits that are not parents
+ Set<String> leafs = new TreeSet<>(adjList.keySet());
+ leafs.removeAll(adjList.values());
+ // go backwards from the last commit to its parent(s)
+ Set<String> ordered = new LinkedHashSet<>();
+ for (String leaf : leafs) {
+ if (ordered.contains(leaf)) {
+ continue;
+ }
+ while (leaf != null) {
+ if (!ordered.contains(leaf)) {
+ ordered.add(leaf);
+ }
+ leaf = adjList.get(leaf);
+ }
+ }
+ // reverse the order to start with earliest commit
+ List<String> orderedCommits = new ArrayList<>(ordered);
+ Collections.reverse(orderedCommits);
+
+ Map<String, CreateRequest> created =
+ newChanges.stream()
+ .filter(r -> r.change != null)
+ .collect(Collectors.toMap(r -> r.commit.name(), r -> r));
+ Map<String, ReplaceRequest> updated =
replaceByChange.values().stream()
.filter(r -> r.inputCommand.getResult() == OK)
- .sorted(comparingInt(r -> r.notes.getChangeId().get()))
- .collect(toList());
+ .collect(Collectors.toMap(r -> r.newCommitId.name(), r -> r));
if (created.isEmpty() && updated.isEmpty()) {
return;
@@ -712,63 +749,68 @@ class ReceiveCommits {
addMessage("");
addMessage("SUCCESS");
+ addMessage("");
- if (!created.isEmpty()) {
- addMessage("");
- addMessage("New Changes:");
- for (CreateRequest c : created) {
- addMessage(
- changeFormatter.newChange(
- ChangeReportFormatter.Input.builder().setChange(c.change).build()));
- }
- }
-
+ boolean edit = false;
+ Boolean isPrivate = null;
+ Boolean wip = null;
if (!updated.isEmpty()) {
- addMessage("");
- addMessage("Updated Changes:");
- boolean edit = magicBranch != null && (magicBranch.edit || magicBranch.draft);
- for (ReplaceRequest u : updated) {
- String subject;
- Change change = u.notes.getChange();
- if (edit) {
- try {
- subject = receivePack.getRevWalk().parseCommit(u.newCommitId).getShortMessage();
- } catch (IOException e) {
- // Log and fall back to original change subject
- logger.atWarning().withCause(e).log("failed to get subject for edit patch set");
- subject = change.getSubject();
- }
- } else {
- subject = u.info.getSubject();
+ edit = magicBranch != null && (magicBranch.edit || magicBranch.draft);
+ if (magicBranch != null) {
+ if (magicBranch.isPrivate) {
+ isPrivate = true;
+ } else if (magicBranch.removePrivate) {
+ isPrivate = false;
}
-
- boolean isPrivate = change.isPrivate();
- boolean wip = change.isWorkInProgress();
- if (magicBranch != null) {
- if (magicBranch.isPrivate) {
- isPrivate = true;
- } else if (magicBranch.removePrivate) {
- isPrivate = false;
- }
- if (magicBranch.workInProgress) {
- wip = true;
- } else if (magicBranch.ready) {
- wip = false;
- }
+ if (magicBranch.workInProgress) {
+ wip = true;
+ } else if (magicBranch.ready) {
+ wip = false;
}
+ }
+ }
- ChangeReportFormatter.Input input =
- ChangeReportFormatter.Input.builder()
- .setChange(change)
- .setSubject(subject)
- .setIsEdit(edit)
- .setIsPrivate(isPrivate)
- .setIsWorkInProgress(wip)
- .build();
- addMessage(changeFormatter.changeUpdated(input));
+ for (String commit : orderedCommits) {
+ if (created.get(commit) != null) {
+ addCreatedMessage(created.get(commit));
+ } else if (updated.get(commit) != null) {
+ addReplacedMessage(updated.get(commit), edit, isPrivate, wip);
}
- addMessage("");
}
+ addMessage("");
+ }
+
+ private void addCreatedMessage(CreateRequest c) {
+ addMessage(
+ changeFormatter.newChange(
+ ChangeReportFormatter.Input.builder().setChange(c.change).build()));
+ }
+
+ private void addReplacedMessage(ReplaceRequest u, boolean edit, Boolean isPrivate, Boolean wip) {
+ String subject;
+ if (edit) {
+ subject =
+ u.revCommit == null ? u.notes.getChange().getSubject() : u.revCommit.getShortMessage();
+ } else {
+ subject = u.info.getSubject();
+ }
+
+ if (isPrivate == null) {
+ isPrivate = u.notes.getChange().isPrivate();
+ }
+ if (wip == null) {
+ wip = u.notes.getChange().isWorkInProgress();
+ }
+
+ ChangeReportFormatter.Input input =
+ ChangeReportFormatter.Input.builder()
+ .setChange(u.notes.getChange())
+ .setSubject(subject)
+ .setIsEdit(edit)
+ .setIsPrivate(isPrivate)
+ .setIsWorkInProgress(wip)
+ .build();
+ addMessage(changeFormatter.changeUpdated(input));
}
private void insertChangesAndPatchSets(List<CreateRequest> newChanges, Task replaceProgress) {
@@ -784,15 +826,21 @@ class ReceiveCommits {
try (BatchUpdate bu =
batchUpdateFactory.create(
- db, project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
+ project.getNameKey(), user.materializedCopy(), TimeUtil.nowTs());
ObjectInserter ins = repo.newObjectInserter();
ObjectReader reader = ins.newReader();
RevWalk rw = new RevWalk(reader)) {
- bu.setRepository(repo, rw, ins).updateChangesInParallel();
+ bu.setRepository(repo, rw, ins);
bu.setRefLogMessage("push");
+ if (magicBranch != null) {
+ bu.setNotify(magicBranch.getNotifyForNewChange());
+ }
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);
}
@@ -812,8 +860,9 @@ class ReceiveCommits {
}
replaceByChange.values().stream()
- .forEach(req -> resultChangeIds.add(Key.REPLACED, req.ontoChange));
- newChanges.stream().forEach(req -> resultChangeIds.add(Key.CREATED, req.changeId));
+ .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);
@@ -834,7 +883,7 @@ class ReceiveCommits {
} catch (ResourceConflictException e) {
addError(e.getMessage());
reject(magicBranchCmd, "conflict");
- } catch (BadRequestException | UnprocessableEntityException e) {
+ } catch (BadRequestException | UnprocessableEntityException | AuthException e) {
logger.atFine().withCause(e).log("Rejecting due to client error");
reject(magicBranchCmd, e.getMessage());
} catch (RestApiException | IOException e) {
@@ -849,7 +898,7 @@ class ReceiveCommits {
addError(e.getMessage());
reject(magicBranchCmd, "conflict");
} catch (RestApiException
- | OrmException
+ | StorageException
| UpdateException
| IOException
| ConfigInvalidException
@@ -1047,7 +1096,7 @@ class ReceiveCommits {
case UPDATE:
case UPDATE_NONFASTFORWARD:
try {
- ProjectConfig cfg = new ProjectConfig(project.getNameKey());
+ ProjectConfig cfg = projectConfigFactory.create(project.getNameKey());
cfg.load(project.getNameKey(), receivePack.getRevWalk(), cmd.getNewId());
if (!cfg.getValidationErrors().isEmpty()) {
addError("Invalid project configuration:");
@@ -1312,7 +1361,6 @@ class ReceiveCommits {
boolean deprecatedTopicSeen;
final ReceiveCommand cmd;
final LabelTypes labelTypes;
- final NotesMigration notesMigration;
private final boolean defaultPublishComments;
Branch.NameKey dest;
PermissionBackend.ForRef perm;
@@ -1340,8 +1388,6 @@ class ReceiveCommits {
+ "for new changes and '--edit' for existing changes")
boolean draft;
- boolean publish;
-
@Option(name = "--private", usage = "mark new/updated change as private")
boolean isPrivate;
@@ -1384,7 +1430,7 @@ class ReceiveCommits {
"Notify handling that defines to whom email notifications "
+ "should be sent. Allowed values are NONE, OWNER, "
+ "OWNER_REVIEWERS, ALL. If not set, the default is ALL.")
- private NotifyHandling notify;
+ private NotifyHandling notifyHandling;
@Option(
name = "--notify-to",
@@ -1422,7 +1468,7 @@ class ReceiveCommits {
name = "--label",
aliases = {"-l"},
metaVar = "LABEL+VALUE",
- usage = "label(s) to assign (defaults to +1 if no value provided")
+ usage = "label(s) to assign (defaults to +1 if no value provided)")
void addLabel(String token) throws CmdLineException {
LabelVote v = LabelVote.parse(token);
try {
@@ -1460,10 +1506,7 @@ class ReceiveCommits {
aliases = {"-t"},
metaVar = "HASHTAG",
usage = "add hashtag to changes")
- void addHashtag(String token) throws CmdLineException {
- if (!notesMigration.readChanges()) {
- throw cmdLineParser.reject("cannot add hashtags; noteDb is disabled");
- }
+ void addHashtag(String token) {
String hashtag = cleanupHashtag(token);
if (!hashtag.isEmpty()) {
hashtags.add(hashtag);
@@ -1471,17 +1514,11 @@ class ReceiveCommits {
// TODO(dpursehouse): validate hashtags
}
- MagicBranchInput(
- IdentifiedUser user,
- ReceiveCommand cmd,
- LabelTypes labelTypes,
- NotesMigration notesMigration) {
+ MagicBranchInput(IdentifiedUser user, ReceiveCommand cmd, LabelTypes labelTypes) {
this.deprecatedTopicSeen = false;
this.cmd = cmd;
this.draft = cmd.getRefName().startsWith(MagicBranch.NEW_DRAFT_CHANGE);
- this.publish = cmd.getRefName().startsWith(MagicBranch.NEW_PUBLISH_CHANGE);
this.labelTypes = labelTypes;
- this.notesMigration = notesMigration;
GeneralPreferencesInfo prefs = user.state().getGeneralPreferences();
this.defaultPublishComments =
prefs != null
@@ -1523,15 +1560,6 @@ class ReceiveCommits {
.collect(toImmutableSet());
}
- ListMultimap<RecipientType, Account.Id> getAccountsToNotify() {
- ListMultimap<RecipientType, Account.Id> accountsToNotify =
- MultimapBuilder.hashKeys().arrayListValues().build();
- accountsToNotify.putAll(RecipientType.TO, notifyTo);
- accountsToNotify.putAll(RecipientType.CC, notifyCc);
- accountsToNotify.putAll(RecipientType.BCC, notifyBcc);
- return accountsToNotify;
- }
-
boolean shouldPublishComments() {
if (publishComments) {
return true;
@@ -1591,19 +1619,20 @@ class ReceiveCommits {
return ref.substring(0, split);
}
- NotifyHandling getNotify() {
- if (notify != null) {
- return notify;
- }
- if (workInProgress) {
- return NotifyHandling.OWNER;
- }
- return NotifyHandling.ALL;
+ NotifyResolver.Result getNotifyForNewChange() {
+ return NotifyResolver.Result.create(
+ firstNonNull(notifyHandling, workInProgress ? NotifyHandling.OWNER : NotifyHandling.ALL),
+ ImmutableSetMultimap.<RecipientType, Account.Id>builder()
+ .putAll(RecipientType.TO, notifyTo)
+ .putAll(RecipientType.CC, notifyCc)
+ .putAll(RecipientType.BCC, notifyBcc)
+ .build());
}
- NotifyHandling getNotify(ChangeNotes notes) {
- if (notify != null) {
- return notify;
+ NotifyHandling getNotifyHandling(ChangeNotes notes) {
+ requireNonNull(notes);
+ if (notifyHandling != null) {
+ return notifyHandling;
}
if (workInProgress || (!ready && notes.getChange().isWorkInProgress())) {
return NotifyHandling.OWNER;
@@ -1620,7 +1649,7 @@ class ReceiveCommits {
*/
private void parseMagicBranch(ReceiveCommand cmd) throws PermissionBackendException {
logger.atFine().log("Found magic branch %s", cmd.getRefName());
- MagicBranchInput magicBranch = new MagicBranchInput(user, cmd, labelTypes, notesMigration);
+ MagicBranchInput magicBranch = new MagicBranchInput(user, cmd, labelTypes);
String ref;
magicBranch.cmdLineParser = optionParserFactory.create(magicBranch);
@@ -1653,6 +1682,11 @@ class ReceiveCommits {
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)) {
@@ -1736,9 +1770,10 @@ class ReceiveCommits {
reject(cmd, "cannot use merged with base");
return;
}
- RevCommit branchTip = readBranchTip(cmd, magicBranch.dest);
+ RevCommit branchTip = readBranchTip(magicBranch.dest);
if (branchTip == null) {
- return; // readBranchTip already rejected cmd.
+ reject(cmd, magicBranch.dest.get() + " not found");
+ return;
}
if (!walk.isMergedInto(tip, branchTip)) {
reject(cmd, "not merged into branch");
@@ -1776,12 +1811,21 @@ class ReceiveCommits {
}
}
} else if (newChangeForAllNotInTarget) {
- RevCommit branchTip = readBranchTip(cmd, magicBranch.dest);
- if (branchTip == null) {
- return; // readBranchTip already rejected cmd.
+ 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;
+ }
}
- magicBranch.baseCommit = Collections.singletonList(branchTip);
- logger.atFine().log("Set baseCommit = %s", magicBranch.baseCommit.get(0).name());
}
} catch (IOException ex) {
logger.atWarning().withCause(ex).log(
@@ -1793,12 +1837,13 @@ class ReceiveCommits {
if (magicBranch.deprecatedTopicSeen) {
messages.add(
new ValidationMessage(
- "WARNING: deprecated topic syntax. Use %topic=TOPIC instead", false));
+ "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);
}
}
@@ -1846,17 +1891,18 @@ class ReceiveCommits {
private static String readHEAD(Repository repo) {
try {
- return repo.getFullBranch();
+ String head = repo.getFullBranch();
+ logger.atFine().log("HEAD = %s", head);
+ return head;
} catch (IOException e) {
logger.atSevere().withCause(e).log("Cannot read HEAD symref");
return null;
}
}
- private RevCommit readBranchTip(ReceiveCommand cmd, Branch.NameKey branch) throws IOException {
+ private RevCommit readBranchTip(Branch.NameKey branch) throws IOException {
Ref r = allRefs().get(branch.get());
if (r == null) {
- reject(cmd, branch.get() + " not found");
return null;
}
return receivePack.getRevWalk().parseCommit(r.getObjectId());
@@ -1882,12 +1928,12 @@ class ReceiveCommits {
Change changeEnt;
try {
- changeEnt = notesFactory.createChecked(db, project.getNameKey(), changeId).getChange();
+ 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 (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Cannot lookup existing change %s", changeId);
reject(cmd, "database error");
return;
@@ -1922,7 +1968,7 @@ class ReceiveCommits {
*/
private boolean requestReplace(
ReceiveCommand cmd, boolean checkMergedInto, Change change, RevCommit newCommit) {
- if (change.getStatus().isClosed()) {
+ if (change.isClosed()) {
reject(
cmd,
changeFormatter.changeClosed(
@@ -1962,7 +2008,7 @@ class ReceiveCommits {
ListMultimap<ObjectId, Ref> existing = changeRefsById();
GroupCollector groupCollector =
- GroupCollector.create(changeRefsById(), db, psUtil, notesFactory, project.getNameKey());
+ GroupCollector.create(changeRefsById(), psUtil, notesFactory, project.getNameKey());
BranchCommitValidator validator =
commitValidatorFactory.create(projectState, magicBranch.dest, user);
@@ -2186,7 +2232,7 @@ class ReceiveCommits {
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 (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Cannot query database to locate prior changes");
reject(magicBranch.cmd, "database error");
return Collections.emptyList();
@@ -2216,17 +2262,17 @@ class ReceiveCommits {
update.groups = ImmutableList.copyOf((groups.get(update.commit)));
}
logger.atFine().log("Finished updating groups from GroupCollector");
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Error collecting groups for changes");
reject(magicBranch.cmd, INTERNAL_SERVER_ERROR);
}
return newChanges;
}
- private boolean foundInExistingRef(Collection<Ref> existingRefs) throws OrmException {
+ private boolean foundInExistingRef(Collection<Ref> existingRefs) {
for (Ref ref : existingRefs) {
ChangeNotes notes =
- notesFactory.create(db, project.getNameKey(), Change.Id.fromRef(ref.getName()));
+ 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());
@@ -2343,11 +2389,11 @@ class ReceiveCommits {
}
}
- private ChangeLookup lookupByChangeKey(RevCommit c, Change.Key key) throws OrmException {
+ private ChangeLookup lookupByChangeKey(RevCommit c, Change.Key key) {
return new ChangeLookup(c, key, queryProvider.get().byBranchKey(magicBranch.dest, key));
}
- private ChangeLookup lookupByCommit(RevCommit c) throws OrmException {
+ private ChangeLookup lookupByCommit(RevCommit c) {
return new ChangeLookup(
c, null, queryProvider.get().byBranchCommit(magicBranch.dest, c.getName()));
}
@@ -2430,14 +2476,13 @@ class ReceiveCommits {
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())
- .setNotify(magicBranch.getNotify())
- .setAccountsToNotify(magicBranch.getAccountsToNotify())
.setRequestScopePropagator(requestScopePropagator)
.setSendMail(true)
.setPatchSetDescription(magicBranch.message));
@@ -2475,7 +2520,7 @@ class ReceiveCommits {
}
private void submit(Collection<CreateRequest> create, Collection<ReplaceRequest> replace)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ throws RestApiException, UpdateException, IOException, ConfigInvalidException,
PermissionBackendException {
Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
for (CreateRequest r : create) {
@@ -2496,7 +2541,7 @@ class ReceiveCommits {
logger.atFine().log(
"Processing submit with tip change %s (%s)", tipChange.getId(), magicBranch.cmd.getNewId());
try (MergeOp op = mergeOpProvider.get()) {
- op.merge(db, tipChange, user, false, new SubmitInput(), false);
+ op.merge(tipChange, user, false, new SubmitInput(), false);
}
}
@@ -2508,7 +2553,7 @@ class ReceiveCommits {
req.validateNewPatchSet();
}
}
- } catch (OrmException err) {
+ } catch (StorageException err) {
logger.atSevere().withCause(err).log(
"Cannot read database before replacement for project %s", project.getName());
rejectRemainingRequests(replaceByChange.values(), INTERNAL_SERVER_ERROR);
@@ -2532,10 +2577,10 @@ class ReceiveCommits {
}
}
- private void readChangesForReplace() throws OrmException {
+ private void readChangesForReplace() {
Collection<ChangeNotes> allNotes =
notesFactory.create(
- db, replaceByChange.values().stream().map(r -> r.ontoChange).collect(toList()));
+ replaceByChange.values().stream().map(r -> r.ontoChange).collect(toList()));
for (ChangeNotes notes : allNotes) {
replaceByChange.get(notes.getChangeId()).notes = notes;
}
@@ -2547,6 +2592,7 @@ class ReceiveCommits {
final ObjectId newCommitId;
final ReceiveCommand inputCommand;
final boolean checkMergedInto;
+ RevCommit revCommit;
ChangeNotes notes;
BiMap<RevCommit, PatchSet.Id> revisions;
PatchSet.Id psId;
@@ -2564,6 +2610,11 @@ class ReceiveCommits {
this.inputCommand = requireNonNull(cmd);
this.checkMergedInto = checkMergedInto;
+ try {
+ revCommit = receivePack.getRevWalk().parseCommit(newCommitId);
+ } catch (IOException e) {
+ revCommit = null;
+ }
revisions = HashBiMap.create();
for (Ref ref : refs(toChange)) {
try {
@@ -2590,10 +2641,9 @@ class ReceiveCommits {
*
* @return whether the new commit is valid
* @throws IOException
- * @throws OrmException
* @throws PermissionBackendException
*/
- boolean validateNewPatchSet() throws IOException, OrmException, PermissionBackendException {
+ boolean validateNewPatchSet() throws IOException, PermissionBackendException {
if (!validateNewPatchSetNoteDb()) {
return false;
}
@@ -2614,8 +2664,7 @@ class ReceiveCommits {
return true;
}
- boolean validateNewPatchSetForAutoClose()
- throws IOException, OrmException, PermissionBackendException {
+ boolean validateNewPatchSetForAutoClose() throws IOException, PermissionBackendException {
if (!validateNewPatchSetNoteDb()) {
return false;
}
@@ -2625,8 +2674,7 @@ class ReceiveCommits {
}
/** Validates the new PS against permissions and notedb status. */
- private boolean validateNewPatchSetNoteDb()
- throws IOException, OrmException, PermissionBackendException {
+ private boolean validateNewPatchSetNoteDb() throws IOException, PermissionBackendException {
if (notes == null) {
reject(inputCommand, "change " + ontoChange + " not found");
return false;
@@ -2648,13 +2696,13 @@ class ReceiveCommits {
}
try {
- permissions.change(notes).database(db).check(ChangePermission.ADD_PATCH_SET);
+ permissions.change(notes).check(ChangePermission.ADD_PATCH_SET);
} catch (AuthException no) {
reject(inputCommand, "cannot add patch set to " + ontoChange + ".");
return false;
}
- if (change.getStatus().isClosed()) {
+ if (change.isClosed()) {
reject(inputCommand, "change " + ontoChange + " closed");
return false;
} else if (revisions.containsKey(newCommit)) {
@@ -2789,7 +2837,7 @@ class ReceiveCommits {
}
/** Updates 'this' to add a new patchset. */
- private void newPatchSet() throws IOException, OrmException {
+ private void newPatchSet() throws IOException {
RevCommit newCommit = receivePack.getRevWalk().parseCommit(newCommitId);
psId =
ChangeUtil.nextPatchSetIdFromAllRefsMap(allRefs(), notes.getChange().currentPatchSetId());
@@ -2854,8 +2902,8 @@ class ReceiveCommits {
psId.getParentKey(),
new BatchUpdateOp() {
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
- PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ public boolean updateChange(ChangeContext ctx) {
+ PatchSet ps = psUtil.get(ctx.getNotes(), psId);
List<String> oldGroups = ps.getGroups();
if (oldGroups == null) {
if (groups == null) {
@@ -2864,7 +2912,7 @@ class ReceiveCommits {
} else if (sameGroups(oldGroups, groups)) {
return false;
}
- psUtil.setGroups(ctx.getDb(), ctx.getUpdate(psId), ps, groups);
+ psUtil.setGroups(ctx.getUpdate(psId), ps, groups);
return true;
}
});
@@ -2896,12 +2944,7 @@ class ReceiveCommits {
}
if (isConfig(cmd)) {
logger.atFine().log("Reloading project in cache");
- try {
- projectCache.evict(project);
- } catch (IOException e) {
- logger.atWarning().withCause(e).log(
- "Cannot evict from project cache, name key: %s", project.getName());
- }
+ projectCache.evict(project);
ProjectState ps = projectCache.get(project.getNameKey());
try {
logger.atFine().log("Updating project description");
@@ -3087,11 +3130,11 @@ class ReceiveCommits {
retryHelper.execute(
updateFactory -> {
try (BatchUpdate bu =
- updateFactory.create(db, projectState.getNameKey(), user, TimeUtil.nowTs());
+ 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).updateChangesInParallel();
+ bu.setRepository(repo, rw, ins);
// TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
RevCommit newTip = rw.parseCommit(cmd.getNewId());
@@ -3119,6 +3162,7 @@ class ReceiveCommits {
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(
@@ -3152,6 +3196,7 @@ class ReceiveCommits {
continue;
}
req.addOps(bu, null);
+ bu.addOp(id, setPrivateOpFactory.create(false, null));
bu.addOp(
id,
mergedByPushOpFactory
@@ -3165,7 +3210,7 @@ class ReceiveCommits {
"Auto-closing %s changes with existing patch sets and %s with new patch sets",
existingPatchSets, newPatchSets);
bu.execute();
- } catch (IOException | OrmException | PermissionBackendException e) {
+ } catch (IOException | StorageException | PermissionBackendException e) {
logger.atSevere().withCause(e).log("Failed to auto-close changes");
return null;
}
@@ -3173,7 +3218,7 @@ class ReceiveCommits {
// 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(Key.AUTOCLOSED, id));
+ ids.stream().forEach(id -> resultChangeIds.add(ResultChangeIds.Key.AUTOCLOSED, id));
return null;
},
@@ -3189,26 +3234,25 @@ class ReceiveCommits {
}
}
- private Optional<ChangeNotes> getChangeNotes(Change.Id changeId) throws OrmException {
+ private Optional<ChangeNotes> getChangeNotes(Change.Id changeId) {
try {
- return Optional.of(notesFactory.createChecked(db, project.getNameKey(), changeId));
+ return Optional.of(notesFactory.createChecked(project.getNameKey(), changeId));
} catch (NoSuchChangeException e) {
return Optional.empty();
}
}
- private <T> T executeIndexQuery(Action<T> action) throws OrmException {
+ private <T> T executeIndexQuery(Action<T> action) {
try {
- return retryHelper.execute(ActionType.INDEX_QUERY, action, OrmException.class::isInstance);
+ return retryHelper.execute(
+ ActionType.INDEX_QUERY, action, StorageException.class::isInstance);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
- Throwables.throwIfInstanceOf(e, OrmException.class);
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
- private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(Branch.NameKey branch)
- throws OrmException {
+ private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(Branch.NameKey branch) {
Map<Change.Key, ChangeNotes> r = new HashMap<>();
for (ChangeData cd : queryProvider.get().byBranchOpen(branch)) {
try {
@@ -3250,4 +3294,15 @@ class ReceiveCommits {
private static boolean isConfig(ReceiveCommand cmd) {
return cmd.getRefName().equals(RefNames.REFS_CONFIG);
}
+
+ private static String commandToString(ReceiveCommand cmd) {
+ StringBuilder b = new StringBuilder();
+ b.append(cmd);
+ b.append(" (").append(cmd.getResult());
+ if (cmd.getMessage() != null) {
+ b.append(": ").append(cmd.getMessage());
+ }
+ b.append(")\n");
+ return b.toString();
+ }
}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
index 8cbcc887d4..cfe06ea7bd 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
@@ -19,6 +19,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+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;
@@ -27,7 +28,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Provider;
import java.util.Collections;
import java.util.Map;
@@ -66,6 +66,7 @@ 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));
@@ -118,7 +119,7 @@ public class ReceiveCommitsAdvertiseRefsHook implements AdvertiseRefsHook {
}
}
return r;
- } catch (OrmException err) {
+ } catch (StorageException err) {
logger.atSevere().withCause(err).log("Cannot list open changes of %s", projectName);
return Collections.emptySet();
}
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index af4eab0da4..4d0d2c3559 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -25,7 +25,6 @@ import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
@@ -43,7 +42,6 @@ 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.ApprovalCopier;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -53,6 +51,7 @@ import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.change.AddReviewersOp;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.change.EmailReviewComments;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.ReviewerAdder;
import com.google.gerrit.server.change.ReviewerAdder.InternalAddReviewerInput;
import com.google.gerrit.server.change.ReviewerAdder.ReviewerAddition;
@@ -75,7 +74,6 @@ import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.RepoContext;
import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.util.Providers;
@@ -117,7 +115,6 @@ public class ReplaceOp implements BatchUpdateOp {
private static final String CHANGE_IS_CLOSED = "change is closed";
private final AccountResolver accountResolver;
- private final ApprovalCopier approvalCopier;
private final ApprovalsUtil approvalsUtil;
private final ChangeData.Factory changeDataFactory;
private final ChangeKindCache changeKindCache;
@@ -164,7 +161,6 @@ public class ReplaceOp implements BatchUpdateOp {
@Inject
ReplaceOp(
AccountResolver accountResolver,
- ApprovalCopier approvalCopier,
ApprovalsUtil approvalsUtil,
ChangeData.Factory changeDataFactory,
ChangeKindCache changeKindCache,
@@ -193,7 +189,6 @@ public class ReplaceOp implements BatchUpdateOp {
@Assisted @Nullable MagicBranchInput magicBranch,
@Assisted @Nullable PushCertificate pushCertificate) {
this.accountResolver = accountResolver;
- this.approvalCopier = approvalCopier;
this.approvalsUtil = approvalsUtil;
this.changeDataFactory = changeDataFactory;
this.changeKindCache = changeKindCache;
@@ -251,20 +246,19 @@ public class ReplaceOp implements BatchUpdateOp {
@Override
public boolean updateChange(ChangeContext ctx)
- throws RestApiException, OrmException, IOException, PermissionBackendException,
- ConfigInvalidException {
+ throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
notes = ctx.getNotes();
Change change = notes.getChange();
- if (change == null || change.getStatus().isClosed()) {
+ if (change == null || change.isClosed()) {
rejectMessage = CHANGE_IS_CLOSED;
return false;
}
if (groups.isEmpty()) {
- PatchSet prevPs = psUtil.current(ctx.getDb(), notes);
- groups = prevPs != null ? prevPs.getGroups() : ImmutableList.<String>of();
+ PatchSet prevPs = psUtil.current(notes);
+ groups = prevPs != null ? prevPs.getGroups() : ImmutableList.of();
}
- ChangeData cd = changeDataFactory.create(ctx.getDb(), ctx.getNotes());
+ ChangeData cd = changeDataFactory.create(ctx.getNotes());
oldRecipients = getRecipientsFromReviewers(cd.reviewers());
ChangeUpdate update = ctx.getUpdate(patchSetId);
@@ -310,7 +304,6 @@ public class ReplaceOp implements BatchUpdateOp {
newPatchSet =
psUtil.insert(
- ctx.getDb(),
ctx.getRevWalk(),
update,
patchSetId,
@@ -321,25 +314,11 @@ public class ReplaceOp implements BatchUpdateOp {
update.setPsDescription(psDescription);
MailRecipients fromFooters = getRecipientsFromFooters(accountResolver, commit.getFooterLines());
- Iterable<PatchSetApproval> newApprovals =
- approvalsUtil.addApprovalsForNewPatchSet(
- ctx.getDb(),
- update,
- projectState.getLabelTypes(),
- newPatchSet,
- ctx.getUser(),
- approvals);
- approvalCopier.copyInReviewDb(
- ctx.getDb(),
- ctx.getNotes(),
- newPatchSet,
- ctx.getRevWalk(),
- ctx.getRepoView().getConfig(),
- newApprovals);
+ approvalsUtil.addApprovalsForNewPatchSet(
+ update, projectState.getLabelTypes(), newPatchSet, ctx.getUser(), approvals);
reviewerAdditions =
reviewerAdder.prepare(
- ctx.getDb(),
ctx.getNotes(),
ctx.getUser(),
getReviewerInputs(magicBranch, fromFooters, ctx.getChange(), info),
@@ -358,7 +337,7 @@ public class ReplaceOp implements BatchUpdateOp {
}
msg = createChangeMessage(ctx, reviewMessage);
- cmUtil.addChangeMessage(ctx.getDb(), update, msg);
+ cmUtil.addChangeMessage(update, msg);
if (mergedByPushOp == null) {
resetChange(ctx);
@@ -411,7 +390,7 @@ public class ReplaceOp implements BatchUpdateOp {
}
private ChangeMessage createChangeMessage(ChangeContext ctx, String reviewMessage)
- throws OrmException, IOException {
+ throws IOException {
String approvalMessage =
ApprovalsUtil.renderMessageWithApprovals(
patchSetId.get(), approvals, scanLabels(ctx, approvals));
@@ -465,13 +444,12 @@ public class ReplaceOp implements BatchUpdateOp {
}
private Map<String, PatchSetApproval> scanLabels(ChangeContext ctx, Map<String, Short> approvals)
- throws OrmException, IOException {
+ throws IOException {
Map<String, PatchSetApproval> current = new HashMap<>();
// We optimize here and only retrieve current when approvals provided
if (!approvals.isEmpty()) {
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
- ctx.getDb(),
ctx.getNotes(),
priorPatchSetId,
ctx.getAccountId(),
@@ -510,10 +488,9 @@ public class ReplaceOp implements BatchUpdateOp {
}
}
- private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress)
- throws OrmException {
+ private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress) {
List<Comment> comments =
- commentsUtil.draftByChangeAuthor(ctx.getDb(), ctx.getNotes(), ctx.getUser().getAccountId());
+ commentsUtil.draftByChangeAuthor(ctx.getNotes(), ctx.getUser().getAccountId());
publishCommentUtil.publish(
ctx, patchSetId, comments, ChangeMessagesUtil.uploadedPatchSetTag(workInProgress));
return comments;
@@ -533,13 +510,11 @@ public class ReplaceOp implements BatchUpdateOp {
}
}
- NotifyHandling notify = magicBranch != null ? magicBranch.getNotify(notes) : NotifyHandling.ALL;
-
+ NotifyResolver.Result notify = ctx.getNotify(notes.getChangeId());
if (shouldPublishComments()) {
emailCommentsFactory
.create(
notify,
- magicBranch != null ? magicBranch.getAccountsToNotify() : ImmutableListMultimap.of(),
notes,
newPatchSet,
ctx.getUser().asIdentifiedUser(),
@@ -576,10 +551,7 @@ public class ReplaceOp implements BatchUpdateOp {
cm.setFrom(ctx.getAccount().getAccount().getId());
cm.setPatchSet(newPatchSet, info);
cm.setChangeMessage(msg.getMessage(), ctx.getWhen());
- if (magicBranch != null) {
- cm.setNotify(magicBranch.getNotify(notes));
- cm.setAccountsToNotify(magicBranch.getAccountsToNotify());
- }
+ cm.setNotify(ctx.getNotify(notes.getChangeId()));
cm.addReviewers(
Streams.concat(
oldRecipients.getReviewers().stream(),
diff --git a/java/com/google/gerrit/server/git/receive/ResultChangeIds.java b/java/com/google/gerrit/server/git/receive/ResultChangeIds.java
index bbf8d9569c..e326141a1f 100644
--- a/java/com/google/gerrit/server/git/receive/ResultChangeIds.java
+++ b/java/com/google/gerrit/server/git/receive/ResultChangeIds.java
@@ -33,6 +33,7 @@ public class ResultChangeIds {
AUTOCLOSED,
}
+ private boolean isMagicPush;
private final Map<Key, List<Change.Id>> ids;
ResultChangeIds() {
@@ -43,16 +44,24 @@ public class ResultChangeIds {
}
/** Record a change ID update as having completed. Thread-safe. */
- public void add(Key key, Change.Id id) {
- synchronized (this) {
- ids.get(key).add(id);
- }
+ public synchronized void add(Key key, Change.Id id) {
+ ids.get(key).add(id);
}
- /** Returns change IDs of the given type for which the BatchUpdate succeeded. Thread-safe. */
- public List<Change.Id> get(Key key) {
- synchronized (this) {
- return ImmutableList.copyOf(ids.get(key));
- }
+ /** Indicate that the ReceiveCommits call involved a magic branch. */
+ public synchronized void setMagicPush(boolean magic) {
+ isMagicPush = magic;
+ }
+
+ public synchronized boolean isMagicPush() {
+ return isMagicPush;
+ }
+
+ /**
+ * Returns change IDs of the given type for which the BatchUpdate succeeded, or empty list if
+ * there are none. Thread-safe.
+ */
+ public synchronized List<Change.Id> get(Key key) {
+ return ImmutableList.copyOf(ids.get(key));
}
}
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 51ed6eadf0..ff21b2dddc 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -21,9 +21,7 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
import static java.util.stream.Collectors.toList;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
@@ -33,7 +31,6 @@ 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.Branch.NameKey;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
@@ -99,6 +96,7 @@ public class CommitValidators {
private final AccountValidator accountValidator;
private final String installCommitMsgHookCommand;
private final ProjectCache projectCache;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
Factory(
@@ -111,7 +109,8 @@ public class CommitValidators {
AllProjectsName allProjects,
ExternalIdsConsistencyChecker externalIdsConsistencyChecker,
AccountValidator accountValidator,
- ProjectCache projectCache) {
+ ProjectCache projectCache,
+ ProjectConfig.Factory projectConfigFactory) {
this.gerritIdent = gerritIdent;
this.urlFormatter = urlFormatter;
this.pluginValidators = pluginValidators;
@@ -123,6 +122,7 @@ public class CommitValidators {
this.installCommitMsgHookCommand =
cfg != null ? cfg.getString("gerrit", null, "installCommitMsgHookCommand") : null;
this.projectCache = projectCache;
+ this.projectConfigFactory = projectConfigFactory;
}
public CommitValidators forReceiveCommits(
@@ -153,7 +153,7 @@ public class CommitValidators {
installCommitMsgHookCommand,
sshInfo,
change))
- .add(new ConfigValidator(branch, user, rw, allUsers, allProjects))
+ .add(new ConfigValidator(projectConfigFactory, branch, user, rw, allUsers, allProjects))
.add(new BannedCommitsValidator(rejectCommits))
.add(new PluginCommitValidationListener(pluginValidators, skipValidation))
.add(new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker))
@@ -164,7 +164,7 @@ public class CommitValidators {
public CommitValidators forGerritCommits(
PermissionBackend.ForProject forProject,
- NameKey branch,
+ Branch.NameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
RevWalk rw,
@@ -187,7 +187,7 @@ public class CommitValidators {
installCommitMsgHookCommand,
sshInfo,
change))
- .add(new ConfigValidator(branch, user, rw, allUsers, allProjects))
+ .add(new ConfigValidator(projectConfigFactory, branch, user, rw, allUsers, allProjects))
.add(new PluginCommitValidationListener(pluginValidators))
.add(new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker))
.add(new AccountCommitValidator(repoManager, allUsers, accountValidator))
@@ -250,6 +250,7 @@ public class CommitValidators {
private static final String MISSING_CHANGE_ID_MSG = "missing Change-Id in message footer";
private static final String MISSING_SUBJECT_MSG =
"missing subject; Change-Id must be in message footer";
+ private static final String CHANGE_ID_ABOVE_FOOTER_MSG = "Change-Id must be in message footer";
private static final String MULTIPLE_CHANGE_ID_MSG =
"multiple Change-Id lines in message footer";
private static final String INVALID_CHANGE_ID_MSG =
@@ -299,8 +300,20 @@ public class CommitValidators {
&& CHANGE_ID.matcher(shortMsg.substring(CHANGE_ID_PREFIX.length()).trim()).matches()) {
throw new CommitValidationException(MISSING_SUBJECT_MSG);
}
+ if (commit.getFullMessage().contains("\n" + CHANGE_ID_PREFIX)) {
+ messages.add(
+ new CommitValidationMessage(
+ CHANGE_ID_ABOVE_FOOTER_MSG
+ + "\n"
+ + "\n"
+ + "Hint: run\n"
+ + " git commit --amend\n"
+ + "and move 'Change-Id: Ixxx..' to the bottom on a separate line\n",
+ Type.ERROR));
+ throw new CommitValidationException(CHANGE_ID_ABOVE_FOOTER_MSG, messages);
+ }
if (projectState.is(BooleanProjectConfig.REQUIRE_CHANGE_ID)) {
- messages.add(getMissingChangeIdErrorMsg(MISSING_CHANGE_ID_MSG, commit));
+ messages.add(getMissingChangeIdErrorMsg(MISSING_CHANGE_ID_MSG));
throw new CommitValidationException(MISSING_CHANGE_ID_MSG, messages);
}
} else if (idList.size() > 1) {
@@ -310,7 +323,7 @@ public class CommitValidators {
// Reject Change-Ids with wrong format and invalid placeholder ID from
// Egit (I0000000000000000000000000000000000000000).
if (!CHANGE_ID.matcher(v).matches() || v.matches("^I00*$")) {
- messages.add(getMissingChangeIdErrorMsg(INVALID_CHANGE_ID_MSG, receiveEvent.commit));
+ messages.add(getMissingChangeIdErrorMsg(INVALID_CHANGE_ID_MSG));
throw new CommitValidationException(INVALID_CHANGE_ID_MSG, messages);
}
if (change != null && !v.equals(change.getKey().get())) {
@@ -326,31 +339,17 @@ public class CommitValidators {
|| NEW_PATCHSET_PATTERN.matcher(event.command.getRefName()).matches();
}
- private CommitValidationMessage getMissingChangeIdErrorMsg(String errMsg, RevCommit c) {
- StringBuilder sb = new StringBuilder();
- sb.append(errMsg).append("\n");
-
- boolean hinted = false;
- if (c.getFullMessage().contains(CHANGE_ID_PREFIX)) {
- String lastLine = Iterables.getLast(Splitter.on('\n').split(c.getFullMessage()), "");
- if (!lastLine.contains(CHANGE_ID_PREFIX)) {
- hinted = true;
- sb.append("\n")
- .append("Hint: run\n")
- .append(" git commit --amend\n")
- .append("and move 'Change-Id: Ixxx..' to the bottom on a separate line\n");
- }
- }
-
- // Print only one hint to avoid overwhelming the user.
- if (!hinted) {
- sb.append("\nHint: to automatically insert a Change-Id, install the hook:\n")
- .append(getCommitMessageHookInstallationHint())
- .append("\n")
- .append("and then amend the commit:\n")
- .append(" git commit --amend --no-edit\n");
- }
- return new CommitValidationMessage(sb.toString(), Type.ERROR);
+ private CommitValidationMessage getMissingChangeIdErrorMsg(String errMsg) {
+ return new CommitValidationMessage(
+ errMsg
+ + "\n"
+ + "\nHint: to automatically insert a Change-Id, install the hook:\n"
+ + getCommitMessageHookInstallationHint()
+ + "\n"
+ + "and then amend the commit:\n"
+ + " git commit --amend --no-edit\n"
+ + "Finally, push your changes again\n",
+ Type.ERROR);
}
private String getCommitMessageHookInstallationHint() {
@@ -395,6 +394,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 IdentifiedUser user;
private final RevWalk rw;
@@ -402,11 +402,13 @@ public class CommitValidators {
private final AllProjectsName allProjects;
public ConfigValidator(
+ ProjectConfig.Factory projectConfigFactory,
Branch.NameKey branch,
IdentifiedUser user,
RevWalk rw,
AllUsersName allUsers,
AllProjectsName allProjects) {
+ this.projectConfigFactory = projectConfigFactory;
this.branch = branch;
this.user = user;
this.rw = rw;
@@ -421,7 +423,7 @@ public class CommitValidators {
List<CommitValidationMessage> messages = new ArrayList<>();
try {
- ProjectConfig cfg = new ProjectConfig(receiveEvent.project.getNameKey());
+ ProjectConfig cfg = projectConfigFactory.create(receiveEvent.project.getNameKey());
cfg.load(rw, receiveEvent.command.getNewId());
if (!cfg.getValidationErrors().isEmpty()) {
addError("Invalid project configuration:", messages);
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index 425ab3fcf7..75be8f35bf 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -17,6 +17,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
@@ -26,7 +27,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountProperties;
import com.google.gerrit.server.config.AllProjectsName;
@@ -44,9 +44,7 @@ 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.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -127,6 +125,7 @@ public class MergeValidators {
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
+ private final ProjectConfig.Factory projectConfigFactory;
private final boolean allowProjectOwnersToChangeParent;
public interface Factory {
@@ -140,12 +139,14 @@ public class MergeValidators {
ProjectCache projectCache,
PermissionBackend permissionBackend,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
+ ProjectConfig.Factory projectConfigFactory,
@GerritServerConfig Config config) {
this.allProjectsName = allProjectsName;
this.allUsersName = allUsersName;
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.pluginConfigEntries = pluginConfigEntries;
+ this.projectConfigFactory = projectConfigFactory;
this.allowProjectOwnersToChangeParent =
config.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
}
@@ -162,7 +163,7 @@ public class MergeValidators {
if (RefNames.REFS_CONFIG.equals(destBranch.get())) {
final Project.NameKey newParent;
try {
- ProjectConfig cfg = new ProjectConfig(destProject.getNameKey());
+ ProjectConfig cfg = projectConfigFactory.create(destProject.getNameKey());
cfg.load(destProject.getNameKey(), repo, commit);
newParent = cfg.getProject().getParent(allProjectsName);
final Project.NameKey oldParent = destProject.getProject().getParent(allProjectsName);
@@ -265,18 +266,15 @@ public class MergeValidators {
AccountMergeValidator create();
}
- private final Provider<ReviewDb> dbProvider;
private final AllUsersName allUsersName;
private final ChangeData.Factory changeDataFactory;
private final AccountValidator accountValidator;
@Inject
public AccountMergeValidator(
- Provider<ReviewDb> dbProvider,
AllUsersName allUsersName,
ChangeData.Factory changeDataFactory,
AccountValidator accountValidator) {
- this.dbProvider = dbProvider;
this.allUsersName = allUsersName;
this.changeDataFactory = changeDataFactory;
this.accountValidator = accountValidator;
@@ -298,12 +296,12 @@ public class MergeValidators {
ChangeData cd =
changeDataFactory.create(
- dbProvider.get(), destProject.getProject().getNameKey(), patchSetId.getParentKey());
+ destProject.getProject().getNameKey(), patchSetId.getParentKey());
try {
if (!cd.currentFilePaths().contains(AccountProperties.ACCOUNT_CONFIG)) {
return;
}
- } catch (IOException | OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Cannot validate account update");
throw new MergeValidationException("account validation unavailable");
}
diff --git a/java/com/google/gerrit/server/git/validators/RefOperationValidators.java b/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
index 4eb317ced8..3c0208c787 100644
--- a/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
+++ b/java/com/google/gerrit/server/git/validators/RefOperationValidators.java
@@ -77,7 +77,8 @@ public class RefOperationValidators {
boolean withException = false;
try {
messages.addAll(
- new DisallowCreationAndDeletionOfUserBranches(perm, allUsersName).onRefOperation(event));
+ new DisallowCreationAndDeletionOfGerritMaintainedBranches(perm, allUsersName)
+ .onRefOperation(event));
refOperationValidationListeners.runEach(
l -> l.onRefOperation(event), ValidationException.class);
} catch (ValidationException e) {
@@ -110,12 +111,12 @@ public class RefOperationValidators {
}
}
- private static class DisallowCreationAndDeletionOfUserBranches
+ private static class DisallowCreationAndDeletionOfGerritMaintainedBranches
implements RefOperationValidationListener {
private final PermissionBackend.WithUser perm;
private final AllUsersName allUsersName;
- DisallowCreationAndDeletionOfUserBranches(
+ DisallowCreationAndDeletionOfGerritMaintainedBranches(
PermissionBackend.WithUser perm, AllUsersName allUsersName) {
this.perm = perm;
this.allUsersName = allUsersName;
diff --git a/java/com/google/gerrit/server/group/GroupAuditService.java b/java/com/google/gerrit/server/group/GroupAuditService.java
index 4b851ea0b8..4c02adaf5f 100644
--- a/java/com/google/gerrit/server/group/GroupAuditService.java
+++ b/java/com/google/gerrit/server/group/GroupAuditService.java
@@ -16,7 +16,6 @@ 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.Account.Id;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.AuditEvent;
import java.sql.Timestamp;
@@ -27,7 +26,7 @@ public interface GroupAuditService {
void dispatchAddMembers(
Account.Id actor,
AccountGroup.UUID updatedGroup,
- ImmutableSet<Id> addedMembers,
+ ImmutableSet<Account.Id> addedMembers,
Timestamp addedOn);
void dispatchDeleteMembers(
diff --git a/java/com/google/gerrit/server/group/db/AuditLogReader.java b/java/com/google/gerrit/server/group/db/AuditLogReader.java
index 61fdb60863..106ee6b472 100644
--- a/java/com/google/gerrit/server/group/db/AuditLogReader.java
+++ b/java/com/google/gerrit/server/group/db/AuditLogReader.java
@@ -59,8 +59,8 @@ public class AuditLogReader {
}
// Having separate methods for reading the two types of audit records mirrors the split in
- // ReviewDb. Once ReviewDb is gone, the audit record interface becomes more flexible and we can
- // revisit this, e.g. to do only a single walk, or even change the record types.
+ // ReviewDb. Now that ReviewDb is gone, the audit record interface is more flexible and this may
+ // be changed, e.g. to do only a single walk, or even change the record types.
public ImmutableList<AccountGroupMemberAudit> getMembersAudit(
Repository allUsersRepo, AccountGroup.UUID uuid) throws IOException, ConfigInvalidException {
@@ -134,8 +134,8 @@ public class AuditLogReader {
private Optional<ParsedCommit> parse(AccountGroup.UUID uuid, RevCommit c) {
Optional<Account.Id> authorId = NoteDbUtil.parseIdent(c.getAuthorIdent(), serverId);
if (!authorId.isPresent()) {
- // Only report audit events from identified users, since this is a non-nullable field in
- // ReviewDb. May be revisited after groups are fully migrated to NoteDb.
+ // Only report audit events from identified users, since this was a non-nullable field in
+ // ReviewDb. May be revisited.
return Optional.empty();
}
diff --git a/java/com/google/gerrit/server/group/db/GroupConfig.java b/java/com/google/gerrit/server/group/db/GroupConfig.java
index 66230ea417..903b9c08b0 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfig.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfig.java
@@ -24,6 +24,7 @@ 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.exceptions.DuplicateKeyException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
@@ -32,7 +33,6 @@ import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.Arrays;
@@ -57,13 +57,13 @@ import org.eclipse.jgit.revwalk.RevSort;
* AccountGroup.UUID)} or {@link #loadForGroupSnapshot(Project.NameKey, Repository,
* AccountGroup.UUID, ObjectId)}.
*
- * <p><strong>Note: </strong>Any modification (group creation or update) only becomes permanent (and
+ * <p><strong>Note:</strong> Any modification (group creation or update) only becomes permanent (and
* hence written to NoteDb) if {@link #commit(MetaDataUpdate)} is called.
*
- * <p><strong>Warning: </strong>This class is a low-level API for groups in NoteDb. Most code which
+ * <p><strong>Warning:</strong> This class is a low-level API for groups in NoteDb. Most code which
* deals with internal Gerrit groups should use {@link Groups} or {@link GroupsUpdate} instead.
*
- * <p><em>Internal details</em>
+ * <h2>Internal details</h2>
*
* <p>Each group is represented by a commit on a branch as defined by {@link
* RefNames#refsGroups(AccountGroup.UUID)}. Previous versions of the group exist as older commits on
@@ -99,7 +99,7 @@ public class GroupConfig extends VersionedMetaData {
* {@link #setGroupUpdate(InternalGroupUpdate, AuditLogFormatter)} on the returned {@code
* GroupConfig}.
*
- * <p><strong>Note: </strong>The returned {@code GroupConfig} has to be committed via {@link
+ * <p><strong>Note:</strong> The returned {@code GroupConfig} has to be committed via {@link
* #commit(MetaDataUpdate)} in order to create the group for real.
*
* @param projectName the name of the project which holds the NoteDb commits for groups
@@ -110,11 +110,11 @@ public class GroupConfig extends VersionedMetaData {
* @throws IOException if the repository can't be accessed for some reason
* @throws ConfigInvalidException if a group with the same UUID already exists but can't be read
* due to an invalid format
- * @throws OrmDuplicateKeyException if a group with the same UUID already exists
+ * @throws DuplicateKeyException if a group with the same UUID already exists
*/
public static GroupConfig createForNewGroup(
Project.NameKey projectName, Repository repository, InternalGroupCreation groupCreation)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException {
GroupConfig groupConfig = new GroupConfig(groupCreation.getGroupUUID());
groupConfig.load(projectName, repository);
groupConfig.setGroupCreation(groupCreation);
@@ -216,7 +216,7 @@ public class GroupConfig extends VersionedMetaData {
* <p>If the group is newly created, the {@code InternalGroupUpdate} can be used to specify
* optional properties.
*
- * <p><strong>Note: </strong>This method doesn't perform the update. It only contains the
+ * <p><strong>Note:</strong> This method doesn't perform the update. It only contains the
* instructions for the update. To apply the update for real and write the result back to NoteDb,
* call {@link #commit(MetaDataUpdate)} on this {@code GroupConfig}.
*
@@ -233,7 +233,7 @@ public class GroupConfig extends VersionedMetaData {
/**
* Allows the new name of a group to be empty during creation or update.
*
- * <p><strong>Note: </strong>This method exists only to support the migration of legacy groups
+ * <p><strong>Note:</strong> This method exists only to support the migration of legacy groups
* which don't always necessarily have a name. Nowadays, we enforce that groups always have names.
* When we remove the migration code, we can probably remove this method as well.
*/
@@ -241,11 +241,10 @@ public class GroupConfig extends VersionedMetaData {
this.allowSaveEmptyName = true;
}
- private void setGroupCreation(InternalGroupCreation groupCreation)
- throws OrmDuplicateKeyException {
+ private void setGroupCreation(InternalGroupCreation groupCreation) throws DuplicateKeyException {
checkLoaded();
if (loadedGroup.isPresent()) {
- throw new OrmDuplicateKeyException(String.format("Group %s already exists", groupUuid.get()));
+ throw new DuplicateKeyException(String.format("Group %s already exists", groupUuid.get()));
}
this.groupCreation = Optional.of(groupCreation);
diff --git a/java/com/google/gerrit/server/group/db/GroupConfigEntry.java b/java/com/google/gerrit/server/group/db/GroupConfigEntry.java
index eff34588d8..f7a104d7d9 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfigEntry.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfigEntry.java
@@ -25,7 +25,7 @@ import org.eclipse.jgit.lib.Config;
*
* <p>Each property knows how to read and write its value from/to a JGit {@link Config} file.
*
- * <p><strong>Warning: </strong>This class is a low-level API for properties of groups in NoteDb. It
+ * <p><strong>Warning:</strong> This class is a low-level API for properties of groups in NoteDb. It
* may only be used by {@link GroupConfig}. Other classes should use {@link InternalGroupUpdate} to
* modify the properties of a group.
*/
diff --git a/java/com/google/gerrit/server/group/db/GroupNameNotes.java b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
index e95e91fbd2..eda7153cf2 100644
--- a/java/com/google/gerrit/server/group/db/GroupNameNotes.java
+++ b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
@@ -29,11 +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.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.server.git.meta.VersionedMetaData;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
@@ -114,7 +114,7 @@ public class GroupNameNotes extends VersionedMetaData {
* @throws IOException if the repository can't be accessed for some reason
* @throws ConfigInvalidException if the note for the specified group doesn't exist or is in an
* invalid state
- * @throws OrmDuplicateKeyException if a group with the new name already exists
+ * @throws DuplicateKeyException if a group with the new name already exists
*/
public static GroupNameNotes forRename(
Project.NameKey projectName,
@@ -122,7 +122,7 @@ public class GroupNameNotes extends VersionedMetaData {
AccountGroup.UUID groupUuid,
AccountGroup.NameKey oldName,
AccountGroup.NameKey newName)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException {
requireNonNull(oldName);
requireNonNull(newName);
@@ -146,14 +146,14 @@ public class GroupNameNotes extends VersionedMetaData {
* @return an instance of {@code GroupNameNotes} configured for a specific group creation
* @throws IOException if the repository can't be accessed for some reason
* @throws ConfigInvalidException in no case so far
- * @throws OrmDuplicateKeyException if a group with the new name already exists
+ * @throws DuplicateKeyException if a group with the new name already exists
*/
public static GroupNameNotes forNewGroup(
Project.NameKey projectName,
Repository repository,
AccountGroup.UUID groupUuid,
AccountGroup.NameKey groupName)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException {
requireNonNull(groupName);
GroupNameNotes groupNameNotes = new GroupNameNotes(groupUuid, null, groupName);
@@ -363,9 +363,9 @@ public class GroupNameNotes extends VersionedMetaData {
}
}
- private void ensureNewNameIsNotUsed() throws OrmDuplicateKeyException {
+ private void ensureNewNameIsNotUsed() throws DuplicateKeyException {
if (newGroupName.isPresent() && nameConflicting) {
- throw new OrmDuplicateKeyException(
+ throw new DuplicateKeyException(
String.format("Name '%s' is already used", newGroupName.get().get()));
}
}
diff --git a/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java b/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
index 58b55ee043..c3ca60bf11 100644
--- a/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
+++ b/java/com/google/gerrit/server/group/db/GroupsNoteDbConsistencyChecker.java
@@ -87,8 +87,12 @@ public class GroupsNoteDbConsistencyChecker {
BiMap<AccountGroup.UUID, String> uuidNameBiMap = HashBiMap.create();
- // Get all refs in an attempt to avoid seeing half committed group updates.
- Map<String, Ref> refs = allUsersRepo.getAllRefs();
+ // Get group refs and group names ref using the most atomic API available, in an attempt to
+ // avoid seeing half-committed group updates.
+ List<Ref> refs =
+ allUsersRepo
+ .getRefDatabase()
+ .getRefsByPrefix(RefNames.REFS_GROUPS, RefNames.REFS_GROUPNAMES);
readGroups(allUsersRepo, refs, result);
readGroupNames(allUsersRepo, refs, result, uuidNameBiMap);
// The sequential IDs are not keys in NoteDb, so no need to check them.
@@ -103,22 +107,21 @@ public class GroupsNoteDbConsistencyChecker {
return result;
}
- private void readGroups(Repository allUsersRepo, Map<String, Ref> refs, Result result)
+ private void readGroups(Repository allUsersRepo, List<Ref> refs, Result result)
throws IOException {
- for (Map.Entry<String, Ref> entry : refs.entrySet()) {
- if (!entry.getKey().startsWith(RefNames.REFS_GROUPS)) {
+ for (Ref ref : refs) {
+ if (!ref.getName().startsWith(RefNames.REFS_GROUPS)) {
continue;
}
- AccountGroup.UUID uuid = AccountGroup.UUID.fromRef(entry.getKey());
+ AccountGroup.UUID uuid = AccountGroup.UUID.fromRef(ref.getName());
if (uuid == null) {
- result.problems.add(error("null UUID from %s", entry.getKey()));
+ result.problems.add(error("null UUID from %s", ref.getName()));
continue;
}
try {
GroupConfig cfg =
- GroupConfig.loadForGroupSnapshot(
- allUsersName, allUsersRepo, uuid, entry.getValue().getObjectId());
+ GroupConfig.loadForGroupSnapshot(allUsersName, allUsersRepo, uuid, ref.getObjectId());
result.uuidToGroupMap.put(uuid, cfg.getLoadedGroup().get());
} catch (ConfigInvalidException e) {
result.problems.add(error("group %s does not parse: %s", uuid, e.getMessage()));
@@ -128,16 +131,18 @@ public class GroupsNoteDbConsistencyChecker {
private void readGroupNames(
Repository repo,
- Map<String, Ref> refs,
+ List<Ref> refs,
Result result,
BiMap<AccountGroup.UUID, String> uuidNameBiMap)
throws IOException {
- Ref ref = refs.get(RefNames.REFS_GROUPNAMES);
- if (ref == null) {
+ Optional<Ref> maybeRef =
+ refs.stream().filter(r -> r.getName().equals(RefNames.REFS_GROUPNAMES)).findFirst();
+ if (!maybeRef.isPresent()) {
String msg = String.format("ref %s does not exist", RefNames.REFS_GROUPNAMES);
result.problems.add(error(msg));
return;
}
+ Ref ref = maybeRef.get();
try (RevWalk rw = new RevWalk(repo)) {
RevCommit c = rw.parseCommit(ref.getObjectId());
diff --git a/java/com/google/gerrit/server/group/db/GroupsUpdate.java b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
index ad3813a612..b450ab856e 100644
--- a/java/com/google/gerrit/server/group/db/GroupsUpdate.java
+++ b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
@@ -21,7 +21,10 @@ 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.common.errors.NoSuchGroupException;
+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;
@@ -35,20 +38,17 @@ import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LockFailureException;
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.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
-import com.google.gerrit.server.update.RefUpdateUtil;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.Objects;
@@ -78,13 +78,24 @@ public class GroupsUpdate {
* modifications executed by it. For NoteDb, this identity is used as author and committer for
* all related commits.
*
- * <p><strong>Note</strong>: Please use this method with care and rather consider to use the
- * correct annotation on the provider of a {@code GroupsUpdate} instead.
+ * <p><strong>Note</strong>: Please use this method with care and consider using the {@link
+ * com.google.gerrit.server.UserInitiated} annotation on the provider of a {@code GroupsUpdate}
+ * instead.
*
- * @param currentUser the user to which modifications should be attributed, or {@code null} if
- * the Gerrit server identity should be used
+ * @param currentUser the user to which modifications should be attributed
*/
- GroupsUpdate create(@Nullable IdentifiedUser currentUser);
+ GroupsUpdate create(IdentifiedUser currentUser);
+
+ /**
+ * Creates a {@code GroupsUpdate} which uses the server identity to mark database modifications
+ * executed by it. For NoteDb, this identity is used as author and committer for all related
+ * commits.
+ *
+ * <p><strong>Note</strong>: Please use this method with care and consider using the {@link
+ * com.google.gerrit.server.ServerInitiated} annotation on the provider of a {@code
+ * GroupsUpdate} instead.
+ */
+ GroupsUpdate createWithServerIdent();
}
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -96,14 +107,14 @@ public class GroupsUpdate {
private final Provider<GroupIndexer> indexer;
private final GroupAuditService groupAuditService;
private final RenameGroupOp.Factory renameGroupOpFactory;
- @Nullable private final IdentifiedUser currentUser;
+ private final Optional<IdentifiedUser> currentUser;
private final AuditLogFormatter auditLogFormatter;
private final PersonIdent authorIdent;
private final MetaDataUpdateFactory metaDataUpdateFactory;
private final GitReferenceUpdated gitRefUpdated;
private final RetryHelper retryHelper;
- @Inject
+ @AssistedInject
GroupsUpdate(
GitRepositoryManager repoManager,
AllUsersName allUsersName,
@@ -118,8 +129,76 @@ public class GroupsUpdate {
@GerritPersonIdent PersonIdent serverIdent,
MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory,
GitReferenceUpdated gitRefUpdated,
+ RetryHelper retryHelper) {
+ this(
+ repoManager,
+ allUsersName,
+ groupBackend,
+ groupCache,
+ groupIncludeCache,
+ indexer,
+ auditService,
+ accountCache,
+ renameGroupOpFactory,
+ serverId,
+ serverIdent,
+ metaDataUpdateInternalFactory,
+ gitRefUpdated,
+ retryHelper,
+ Optional.empty());
+ }
+
+ @AssistedInject
+ GroupsUpdate(
+ GitRepositoryManager repoManager,
+ AllUsersName allUsersName,
+ GroupBackend groupBackend,
+ GroupCache groupCache,
+ GroupIncludeCache groupIncludeCache,
+ Provider<GroupIndexer> indexer,
+ GroupAuditService auditService,
+ AccountCache accountCache,
+ RenameGroupOp.Factory renameGroupOpFactory,
+ @GerritServerId String serverId,
+ @GerritPersonIdent PersonIdent serverIdent,
+ MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory,
+ GitReferenceUpdated gitRefUpdated,
+ RetryHelper retryHelper,
+ @Assisted IdentifiedUser currentUser) {
+ this(
+ repoManager,
+ allUsersName,
+ groupBackend,
+ groupCache,
+ groupIncludeCache,
+ indexer,
+ auditService,
+ accountCache,
+ renameGroupOpFactory,
+ serverId,
+ serverIdent,
+ metaDataUpdateInternalFactory,
+ gitRefUpdated,
+ retryHelper,
+ Optional.of(currentUser));
+ }
+
+ private GroupsUpdate(
+ GitRepositoryManager repoManager,
+ AllUsersName allUsersName,
+ GroupBackend groupBackend,
+ GroupCache groupCache,
+ GroupIncludeCache groupIncludeCache,
+ Provider<GroupIndexer> indexer,
+ GroupAuditService auditService,
+ AccountCache accountCache,
+ RenameGroupOp.Factory renameGroupOpFactory,
+ @GerritServerId String serverId,
+ @GerritPersonIdent PersonIdent serverIdent,
+ MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory,
+ GitReferenceUpdated gitRefUpdated,
RetryHelper retryHelper,
- @Assisted @Nullable IdentifiedUser currentUser) {
+ Optional<IdentifiedUser> currentUser) {
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.groupCache = groupCache;
@@ -140,7 +219,7 @@ public class GroupsUpdate {
private static MetaDataUpdateFactory getMetaDataUpdateFactory(
MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory,
- @Nullable IdentifiedUser currentUser,
+ Optional<IdentifiedUser> currentUser,
PersonIdent serverIdent,
AuditLogFormatter auditLogFormatter) {
return (projectName, repository, batchRefUpdate) -> {
@@ -148,10 +227,10 @@ public class GroupsUpdate {
metaDataUpdateInternalFactory.create(projectName, repository, batchRefUpdate);
metaDataUpdate.getCommitBuilder().setCommitter(serverIdent);
PersonIdent authorIdent;
- if (currentUser != null) {
- metaDataUpdate.setAuthor(currentUser);
+ if (currentUser.isPresent()) {
+ metaDataUpdate.setAuthor(currentUser.get());
authorIdent =
- auditLogFormatter.getParsableAuthorIdent(currentUser.getAccount(), serverIdent);
+ auditLogFormatter.getParsableAuthorIdent(currentUser.get().getAccount(), serverIdent);
} else {
authorIdent = serverIdent;
}
@@ -161,8 +240,8 @@ public class GroupsUpdate {
}
private static PersonIdent getAuthorIdent(
- PersonIdent serverIdent, @Nullable IdentifiedUser currentUser) {
- return currentUser != null ? createPersonIdent(serverIdent, currentUser) : serverIdent;
+ PersonIdent serverIdent, Optional<IdentifiedUser> currentUser) {
+ return currentUser.map(user -> createPersonIdent(serverIdent, user)).orElse(serverIdent);
}
private static PersonIdent createPersonIdent(PersonIdent ident, IdentifiedUser user) {
@@ -177,13 +256,13 @@ public class GroupsUpdate {
* @param groupUpdate an {@code InternalGroupUpdate} which specifies optional properties of the
* group. If this {@code InternalGroupUpdate} updates a property which was already specified
* by the {@code InternalGroupCreation}, the value of this {@code InternalGroupUpdate} wins.
- * @throws OrmDuplicateKeyException if a group with the chosen name already exists
+ * @throws DuplicateKeyException if a group with the chosen name already exists
* @throws IOException if indexing fails, or an error occurs while reading/writing from/to NoteDb
* @return the created {@code InternalGroup}
*/
public InternalGroup createGroup(
InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws OrmDuplicateKeyException, IOException, ConfigInvalidException {
+ throws DuplicateKeyException, IOException, ConfigInvalidException {
try (TraceTimer timer =
TraceContext.newTimer(
"Creating group '%s'", groupUpdate.getName().orElseGet(groupCreation::getNameKey))) {
@@ -200,12 +279,12 @@ public class GroupsUpdate {
* @param groupUuid the UUID of the group to update
* @param groupUpdate an {@code InternalGroupUpdate} which indicates the desired updates on the
* group
- * @throws OrmDuplicateKeyException if the new name of the group is used by another group
+ * @throws DuplicateKeyException if the new name of the group is used by another group
* @throws IOException if indexing fails, or an error occurs while reading/writing from/to NoteDb
* @throws NoSuchGroupException if the specified group doesn't exist
*/
public void updateGroup(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws OrmDuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
+ throws DuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
try (TraceTimer timer = TraceContext.newTimer("Updating group %s", groupUuid)) {
Optional<Timestamp> updatedOn = groupUpdate.getUpdatedOn();
if (!updatedOn.isPresent()) {
@@ -222,7 +301,7 @@ public class GroupsUpdate {
private InternalGroup createGroupInNoteDbWithRetry(
InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException {
try {
return retryHelper.execute(
RetryHelper.ActionType.GROUP_UPDATE,
@@ -232,7 +311,7 @@ public class GroupsUpdate {
Throwables.throwIfUnchecked(e);
Throwables.throwIfInstanceOf(e, IOException.class);
Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
- Throwables.throwIfInstanceOf(e, OrmDuplicateKeyException.class);
+ Throwables.throwIfInstanceOf(e, DuplicateKeyException.class);
throw new IOException(e);
}
}
@@ -240,7 +319,7 @@ public class GroupsUpdate {
@VisibleForTesting
public InternalGroup createGroupInNoteDb(
InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException {
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
AccountGroup.NameKey groupName = groupUpdate.getName().orElseGet(groupCreation::getNameKey);
GroupNameNotes groupNameNotes =
@@ -262,7 +341,7 @@ public class GroupsUpdate {
private UpdateResult updateGroupInNoteDbWithRetry(
AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException, NoSuchGroupException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException, NoSuchGroupException {
try {
return retryHelper.execute(
RetryHelper.ActionType.GROUP_UPDATE,
@@ -272,7 +351,7 @@ public class GroupsUpdate {
Throwables.throwIfUnchecked(e);
Throwables.throwIfInstanceOf(e, IOException.class);
Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
- Throwables.throwIfInstanceOf(e, OrmDuplicateKeyException.class);
+ Throwables.throwIfInstanceOf(e, DuplicateKeyException.class);
Throwables.throwIfInstanceOf(e, NoSuchGroupException.class);
throw new IOException(e);
}
@@ -281,7 +360,7 @@ public class GroupsUpdate {
@VisibleForTesting
public UpdateResult updateGroupInNoteDb(
AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException, NoSuchGroupException {
+ throws IOException, ConfigInvalidException, DuplicateKeyException, NoSuchGroupException {
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersName, allUsersRepo, groupUuid);
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
@@ -353,10 +432,10 @@ public class GroupsUpdate {
RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
gitRefUpdated.fire(
- allUsersName, batchRefUpdate, currentUser != null ? currentUser.state() : null);
+ allUsersName, batchRefUpdate, currentUser.map(user -> user.state()).orElse(null));
}
- private void evictCachesOnGroupCreation(InternalGroup createdGroup) throws IOException {
+ private void evictCachesOnGroupCreation(InternalGroup createdGroup) {
logger.atFine().log("evict caches on creation of group %s", createdGroup.getGroupUUID());
// By UUID is used for the index and hence should be evicted before refreshing the index.
groupCache.evict(createdGroup.getGroupUUID());
@@ -369,7 +448,7 @@ public class GroupsUpdate {
createdGroup.getSubgroups().forEach(groupIncludeCache::evictParentGroupsOf);
}
- private void evictCachesOnGroupUpdate(UpdateResult result) throws IOException {
+ private void evictCachesOnGroupUpdate(UpdateResult result) {
logger.atFine().log("evict caches on update of group %s", result.getGroupUuid());
// By UUID is used for the index and hence should be evicted before refreshing the index.
groupCache.evict(result.getGroupUuid());
@@ -403,20 +482,20 @@ public class GroupsUpdate {
}
private void dispatchAuditEventsOnGroupCreation(InternalGroup createdGroup) {
- if (currentUser == null) {
+ if (!currentUser.isPresent()) {
return;
}
if (!createdGroup.getMembers().isEmpty()) {
groupAuditService.dispatchAddMembers(
- currentUser.getAccountId(),
+ currentUser.get().getAccountId(),
createdGroup.getGroupUUID(),
createdGroup.getMembers(),
createdGroup.getCreatedOn());
}
if (!createdGroup.getSubgroups().isEmpty()) {
groupAuditService.dispatchAddSubgroups(
- currentUser.getAccountId(),
+ currentUser.get().getAccountId(),
createdGroup.getGroupUUID(),
createdGroup.getSubgroups(),
createdGroup.getCreatedOn());
@@ -424,25 +503,34 @@ public class GroupsUpdate {
}
private void dispatchAuditEventsOnGroupUpdate(UpdateResult result, Timestamp updatedOn) {
- if (currentUser == null) {
+ if (!currentUser.isPresent()) {
return;
}
if (!result.getAddedMembers().isEmpty()) {
groupAuditService.dispatchAddMembers(
- currentUser.getAccountId(), result.getGroupUuid(), result.getAddedMembers(), updatedOn);
+ currentUser.get().getAccountId(),
+ result.getGroupUuid(),
+ result.getAddedMembers(),
+ updatedOn);
}
if (!result.getDeletedMembers().isEmpty()) {
groupAuditService.dispatchDeleteMembers(
- currentUser.getAccountId(), result.getGroupUuid(), result.getDeletedMembers(), updatedOn);
+ currentUser.get().getAccountId(),
+ result.getGroupUuid(),
+ result.getDeletedMembers(),
+ updatedOn);
}
if (!result.getAddedSubgroups().isEmpty()) {
groupAuditService.dispatchAddSubgroups(
- currentUser.getAccountId(), result.getGroupUuid(), result.getAddedSubgroups(), updatedOn);
+ currentUser.get().getAccountId(),
+ result.getGroupUuid(),
+ result.getAddedSubgroups(),
+ updatedOn);
}
if (!result.getDeletedSubgroups().isEmpty()) {
groupAuditService.dispatchDeleteSubgroups(
- currentUser.getAccountId(),
+ currentUser.get().getAccountId(),
result.getGroupUuid(),
result.getDeletedSubgroups(),
updatedOn);
diff --git a/java/com/google/gerrit/server/group/db/RenameGroupOp.java b/java/com/google/gerrit/server/group/db/RenameGroupOp.java
index eada57dee2..e002192b50 100644
--- a/java/com/google/gerrit/server/group/db/RenameGroupOp.java
+++ b/java/com/google/gerrit/server/group/db/RenameGroupOp.java
@@ -49,6 +49,7 @@ class RenameGroupOp extends DefaultQueueOp {
private final ProjectCache projectCache;
private final MetaDataUpdate.Server metaDataUpdateFactory;
+ private final ProjectConfig.Factory projectConfigFactory;
private final PersonIdent author;
private final AccountGroup.UUID uuid;
@@ -63,6 +64,7 @@ class RenameGroupOp extends DefaultQueueOp {
WorkQueue workQueue,
ProjectCache projectCache,
MetaDataUpdate.Server metaDataUpdateFactory,
+ ProjectConfig.Factory projectConfigFactory,
@Assisted("author") PersonIdent author,
@Assisted AccountGroup.UUID uuid,
@Assisted("oldName") String oldName,
@@ -70,6 +72,7 @@ class RenameGroupOp extends DefaultQueueOp {
super(workQueue);
this.projectCache = projectCache;
this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.projectConfigFactory = projectConfigFactory;
this.author = author;
this.uuid = uuid;
@@ -109,7 +112,7 @@ class RenameGroupOp extends DefaultQueueOp {
private void rename(MetaDataUpdate md) throws IOException, ConfigInvalidException {
boolean success = false;
for (int attempts = 0; !success && attempts < MAX_TRIES; attempts++) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
// The group isn't referenced, or its name has been fixed already.
//
diff --git a/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java b/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java
index 5a0d28cb8e..fa0628138c 100644
--- a/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java
+++ b/java/com/google/gerrit/server/group/db/testing/GroupTestUtil.java
@@ -45,8 +45,8 @@ public class GroupTestUtil {
String fileName,
String contents)
throws Exception {
- try (RevWalk rw = new RevWalk(allUsersRepo)) {
- TestRepository<Repository> testRepository = new TestRepository<>(allUsersRepo, rw);
+ try (RevWalk rw = new RevWalk(allUsersRepo);
+ TestRepository<Repository> testRepository = new TestRepository<>(allUsersRepo, rw)) {
TestRepository<Repository>.CommitBuilder builder =
testRepository
.branch(refName)
diff --git a/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java b/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
index f0ab638255..2f91394dee 100644
--- a/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
+++ b/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
@@ -23,7 +23,6 @@ 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.common.truth.Truth;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.group.InternalGroup;
import java.sql.Timestamp;
@@ -32,7 +31,11 @@ import org.eclipse.jgit.lib.ObjectId;
public class InternalGroupSubject extends Subject<InternalGroupSubject, InternalGroup> {
public static InternalGroupSubject assertThat(InternalGroup group) {
- return assertAbout(InternalGroupSubject::new).that(group);
+ return assertAbout(internalGroups()).that(group);
+ }
+
+ public static Subject.Factory<InternalGroupSubject, InternalGroup> internalGroups() {
+ return InternalGroupSubject::new;
}
private InternalGroupSubject(FailureMetadata metadata, InternalGroup actual) {
@@ -42,66 +45,66 @@ public class InternalGroupSubject extends Subject<InternalGroupSubject, Internal
public ComparableSubject<?, AccountGroup.UUID> groupUuid() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getGroupUUID()).named("groupUuid");
+ return check("groupUuid()").that(group.getGroupUUID());
}
public ComparableSubject<?, AccountGroup.NameKey> nameKey() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getNameKey()).named("nameKey");
+ return check("nameKey()").that(group.getNameKey());
}
public StringSubject name() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getName()).named("name");
+ return check("name()").that(group.getName());
}
- public DefaultSubject id() {
+ public Subject<DefaultSubject, Object> id() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getId()).named("id");
+ return check("id()").that(group.getId());
}
public StringSubject description() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getDescription()).named("description");
+ return check("description()").that(group.getDescription());
}
public ComparableSubject<?, AccountGroup.UUID> ownerGroupUuid() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getOwnerGroupUUID()).named("ownerGroupUuid");
+ return check("ownerGroupUuid()").that(group.getOwnerGroupUUID());
}
public BooleanSubject visibleToAll() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.isVisibleToAll()).named("visibleToAll");
+ return check("visibleToAll()").that(group.isVisibleToAll());
}
public ComparableSubject<?, Timestamp> createdOn() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getCreatedOn()).named("createdOn");
+ return check("createdOn()").that(group.getCreatedOn());
}
public IterableSubject members() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getMembers()).named("members");
+ return check("members()").that(group.getMembers());
}
public IterableSubject subgroups() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getSubgroups()).named("subgroups");
+ return check("subgroups()").that(group.getSubgroups());
}
public ComparableSubject<?, ObjectId> refState() {
isNotNull();
InternalGroup group = actual();
- return Truth.assertThat(group.getRefState()).named("refState");
+ return check("refState()").that(group.getRefState());
}
}
diff --git a/java/com/google/gerrit/server/index/GerritIndexStatus.java b/java/com/google/gerrit/server/index/GerritIndexStatus.java
index d835227057..0941c6c028 100644
--- a/java/com/google/gerrit/server/index/GerritIndexStatus.java
+++ b/java/com/google/gerrit/server/index/GerritIndexStatus.java
@@ -44,6 +44,10 @@ public class GerritIndexStatus {
return cfg.getBoolean(SECTION, indexDirName(indexName, version), KEY_READY, false);
}
+ public boolean exists(String indexName) {
+ return cfg.getSubsections(SECTION).stream().anyMatch(n -> n.startsWith(indexName));
+ }
+
public void save() throws IOException {
cfg.save();
}
diff --git a/java/com/google/gerrit/server/index/IndexModule.java b/java/com/google/gerrit/server/index/IndexModule.java
index 3c9538c2d9..e7b892d611 100644
--- a/java/com/google/gerrit/server/index/IndexModule.java
+++ b/java/com/google/gerrit/server/index/IndexModule.java
@@ -22,6 +22,7 @@ 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;
@@ -76,7 +77,7 @@ public class IndexModule extends LifecycleModule {
}
public static final ImmutableCollection<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
- ImmutableList.<SchemaDefinitions<?>>of(
+ ImmutableList.of(
AccountSchemaDefinitions.INSTANCE,
ChangeSchemaDefinitions.INSTANCE,
GroupSchemaDefinitions.INSTANCE,
@@ -85,7 +86,19 @@ 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));
- return cfg.getEnum("index", null, "type", IndexType.LUCENE);
+ 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();
}
private final int threads;
@@ -125,7 +138,7 @@ public class IndexModule extends LifecycleModule {
factory(ChangeIndexer.Factory.class);
bind(GroupIndexRewriter.class);
- bind(GroupIndexCollection.class);
+ // GroupIndexCollection is already bound very high up in SchemaModule.
listener().to(GroupIndexCollection.class);
factory(GroupIndexerImpl.Factory.class);
@@ -160,7 +173,7 @@ public class IndexModule extends LifecycleModule {
}
Collection<IndexDefinition<?, ?, ?>> result =
- ImmutableList.<IndexDefinition<?, ?, ?>>of(accounts, groups, changes, projects);
+ ImmutableList.of(accounts, groups, changes, projects);
Set<String> expected =
FluentIterable.from(ALL_SCHEMA_DEFS).transform(SchemaDefinitions::getName).toSet();
Set<String> actual = FluentIterable.from(result).transform(IndexDefinition::getName).toSet();
diff --git a/java/com/google/gerrit/server/index/IndexUtils.java b/java/com/google/gerrit/server/index/IndexUtils.java
index 9836e82b8b..4b5cd4946c 100644
--- a/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/java/com/google/gerrit/server/index/IndexUtils.java
@@ -18,10 +18,10 @@ 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.PROJECT;
-import com.google.common.base.Function;
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.QueryOptions;
import com.google.gerrit.index.project.ProjectField;
import com.google.gerrit.server.CurrentUser;
@@ -31,44 +31,28 @@ import com.google.gerrit.server.index.group.GroupField;
import com.google.gerrit.server.query.change.SingleGroupUser;
import java.io.IOException;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
import org.eclipse.jgit.errors.ConfigInvalidException;
public final class IndexUtils {
public static final ImmutableMap<String, String> CUSTOM_CHAR_MAPPING =
ImmutableMap.of("_", " ", ".", " ");
- public static final Function<Exception, IOException> MAPPER =
- new Function<Exception, IOException>() {
- @Override
- public IOException apply(Exception in) {
- if (in instanceof IOException) {
- return (IOException) in;
- } else if (in instanceof ExecutionException && in.getCause() instanceof IOException) {
- return (IOException) in.getCause();
- } else {
- return new IOException(in);
- }
- }
- };
-
- public static void setReady(SitePaths sitePaths, String name, int version, boolean ready)
- throws IOException {
+ public static void setReady(SitePaths sitePaths, String name, int version, boolean ready) {
try {
GerritIndexStatus cfg = new GerritIndexStatus(sitePaths);
cfg.setReady(name, version, ready);
cfg.save();
- } catch (ConfigInvalidException e) {
- throw new IOException(e);
+ } catch (ConfigInvalidException | IOException e) {
+ throw new StorageException(e);
}
}
- public static boolean getReady(SitePaths sitePaths, String name, int version) throws IOException {
+ public static boolean getReady(SitePaths sitePaths, String name, int version) {
try {
GerritIndexStatus cfg = new GerritIndexStatus(sitePaths);
return cfg.getReady(name, version);
- } catch (ConfigInvalidException e) {
- throw new IOException(e);
+ } catch (ConfigInvalidException | IOException e) {
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/index/OnlineReindexer.java b/java/com/google/gerrit/server/index/OnlineReindexer.java
index ec0e1d46b6..37677bdd03 100644
--- a/java/com/google/gerrit/server/index/OnlineReindexer.java
+++ b/java/com/google/gerrit/server/index/OnlineReindexer.java
@@ -18,12 +18,11 @@ import static java.util.Objects.requireNonNull;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexCollection;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.SiteIndexer;
-import java.io.IOException;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -35,7 +34,7 @@ public class OnlineReindexer<K, V, I extends Index<K, V>> {
private final SiteIndexer<K, V, I> batchIndexer;
private final int oldVersion;
private final int newVersion;
- private final DynamicSet<OnlineUpgradeListener> listeners;
+ private final PluginSetContext<OnlineUpgradeListener> listeners;
private I index;
private final AtomicBoolean running = new AtomicBoolean();
@@ -43,7 +42,7 @@ public class OnlineReindexer<K, V, I extends Index<K, V>> {
IndexDefinition<K, V, I> def,
int oldVersion,
int newVersion,
- DynamicSet<OnlineUpgradeListener> listeners) {
+ PluginSetContext<OnlineUpgradeListener> listeners) {
this.name = def.getName();
this.indexes = def.getIndexCollection();
this.batchIndexer = def.getSiteIndexer();
@@ -62,15 +61,13 @@ public class OnlineReindexer<K, V, I extends Index<K, V>> {
try {
reindex();
ok = true;
- } catch (IOException e) {
+ } catch (RuntimeException e) {
logger.atSevere().withCause(e).log(
"Online reindex of %s schema version %s failed", name, version(index));
} finally {
running.set(false);
if (!ok) {
- for (OnlineUpgradeListener listener : listeners) {
- listener.onFailure(name, oldVersion, newVersion);
- }
+ listeners.runEach(listener -> listener.onFailure(name, oldVersion, newVersion));
}
}
}
@@ -93,10 +90,8 @@ public class OnlineReindexer<K, V, I extends Index<K, V>> {
return i.getSchema().getVersion();
}
- private void reindex() throws IOException {
- for (OnlineUpgradeListener listener : listeners) {
- listener.onStart(name, oldVersion, newVersion);
- }
+ private void reindex() {
+ listeners.runEach(listener -> listener.onStart(name, oldVersion, newVersion));
index =
requireNonNull(
indexes.getWriteIndex(newVersion),
@@ -118,19 +113,13 @@ public class OnlineReindexer<K, V, I extends Index<K, V>> {
}
logger.atInfo().log("Reindex %s to version %s complete", name, version(index));
activateIndex();
- for (OnlineUpgradeListener listener : listeners) {
- listener.onSuccess(name, oldVersion, newVersion);
- }
+ listeners.runEach(listener -> listener.onSuccess(name, oldVersion, newVersion));
}
public void activateIndex() {
indexes.setSearchIndex(index);
logger.atInfo().log("Using %s schema version %s", name, version(index));
- try {
- index.markReady(true);
- } catch (IOException e) {
- logger.atWarning().log("Error activating new %s schema version %s", name, version(index));
- }
+ index.markReady(true);
List<I> toRemove = Lists.newArrayListWithExpectedSize(1);
for (I i : indexes.getWriteIndexes()) {
@@ -139,12 +128,8 @@ public class OnlineReindexer<K, V, I extends Index<K, V>> {
}
}
for (I i : toRemove) {
- try {
- i.markReady(false);
- indexes.removeWriteIndex(version(i));
- } catch (IOException e) {
- logger.atWarning().log("Error deactivating old %s schema version %s", name, version(i));
- }
+ i.markReady(false);
+ indexes.removeWriteIndex(version(i));
}
}
}
diff --git a/java/com/google/gerrit/server/index/VersionManager.java b/java/com/google/gerrit/server/index/VersionManager.java
index f37472c804..3417379b62 100644
--- a/java/com/google/gerrit/server/index/VersionManager.java
+++ b/java/com/google/gerrit/server/index/VersionManager.java
@@ -21,13 +21,13 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexCollection;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.IndexDefinition.IndexFactory;
import com.google.gerrit.index.Schema;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.ProvisionException;
import java.io.IOException;
import java.util.Collection;
@@ -61,7 +61,7 @@ public abstract class VersionManager implements LifecycleListener {
protected final String runReindexMsg;
protected final SitePaths sitePaths;
- private final DynamicSet<OnlineUpgradeListener> listeners;
+ private final PluginSetContext<OnlineUpgradeListener> listeners;
// The following fields must be accessed synchronized on this.
protected final Map<String, IndexDefinition<?, ?, ?>> defs;
@@ -69,7 +69,7 @@ public abstract class VersionManager implements LifecycleListener {
protected VersionManager(
SitePaths sitePaths,
- DynamicSet<OnlineUpgradeListener> listeners,
+ PluginSetContext<OnlineUpgradeListener> listeners,
Collection<IndexDefinition<?, ?, ?>> defs,
boolean onlineUpgrade) {
this.sitePaths = sitePaths;
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexer.java b/java/com/google/gerrit/server/index/account/AccountIndexer.java
index 91fa1d977c..7f4f295a1e 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexer.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexer.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.index.account;
import com.google.gerrit.reviewdb.client.Account;
-import java.io.IOException;
public interface AccountIndexer {
@@ -24,7 +23,7 @@ public interface AccountIndexer {
*
* @param id account id to index.
*/
- void index(Account.Id id) throws IOException;
+ void index(Account.Id id);
/**
* Synchronously reindex an account if it is stale.
@@ -32,5 +31,5 @@ public interface AccountIndexer {
* @param id account id to index.
* @return whether the account was reindexed
*/
- boolean reindexIfStale(Account.Id id) throws IOException;
+ boolean reindexIfStale(Account.Id id);
}
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
index 44bb340455..932e2c30aa 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
@@ -17,6 +17,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.index.Index;
import com.google.gerrit.reviewdb.client.Account;
@@ -74,7 +75,7 @@ public class AccountIndexerImpl implements AccountIndexer {
}
@Override
- public void index(Account.Id id) throws IOException {
+ public void index(Account.Id id) {
byIdCache.evict(id);
Optional<AccountState> accountState = byIdCache.get(id);
@@ -104,10 +105,14 @@ public class AccountIndexerImpl implements AccountIndexer {
}
@Override
- public boolean reindexIfStale(Account.Id id) throws IOException {
- if (stalenessChecker.isStale(id)) {
- index(id);
- return true;
+ public boolean reindexIfStale(Account.Id id) {
+ try {
+ if (stalenessChecker.isStale(id)) {
+ index(id);
+ return true;
+ }
+ } catch (IOException e) {
+ throw new StorageException(e);
}
return false;
}
@@ -121,6 +126,6 @@ public class AccountIndexerImpl implements AccountIndexer {
return indexes.getWriteIndexes();
}
- return index != null ? Collections.singleton(index) : ImmutableSet.<AccountIndex>of();
+ return index != null ? Collections.singleton(index) : ImmutableSet.of();
}
}
diff --git a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
index 644f1ebae3..8b9fa27754 100644
--- a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
+++ b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
@@ -17,15 +17,14 @@ package com.google.gerrit.server.index.account;
import static com.google.common.base.Preconditions.checkState;
import com.google.gerrit.index.Index;
-import com.google.gerrit.index.IndexedQuery;
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.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;
-import com.google.gwtorm.server.OrmException;
public class IndexedAccountQuery extends IndexedQuery<Account.Id, AccountState>
implements DataSource<AccountState>, Matchable<AccountState> {
@@ -37,7 +36,7 @@ public class IndexedAccountQuery extends IndexedQuery<Account.Id, AccountState>
}
@Override
- public boolean match(AccountState accountState) throws OrmException {
+ public boolean match(AccountState accountState) {
Predicate<AccountState> pred = getChild(0);
checkState(
pred.isMatchable(),
diff --git a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
index 7267ae2916..a9980ef583 100644
--- a/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
+++ b/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
@@ -29,7 +29,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MultiProgressMonitor;
import com.google.gerrit.server.git.MultiProgressMonitor.Task;
@@ -39,7 +38,6 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -59,7 +57,6 @@ public class AllChangesIndexer extends SiteIndexer<Change.Id, ChangeData, Change
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final int PROJECT_SLICE_MAX_REFS = 1000;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ChangeData.Factory changeDataFactory;
private final GitRepositoryManager repoManager;
private final ListeningExecutorService executor;
@@ -69,14 +66,12 @@ public class AllChangesIndexer extends SiteIndexer<Change.Id, ChangeData, Change
@Inject
AllChangesIndexer(
- SchemaFactory<ReviewDb> schemaFactory,
ChangeData.Factory changeDataFactory,
GitRepositoryManager repoManager,
@IndexExecutor(BATCH) ListeningExecutorService executor,
ChangeIndexer.Factory indexerFactory,
ChangeNotes.Factory notesFactory,
ProjectCache projectCache) {
- this.schemaFactory = schemaFactory;
this.changeDataFactory = changeDataFactory;
this.repoManager = repoManager;
this.executor = executor;
@@ -276,8 +271,7 @@ public class AllChangesIndexer extends SiteIndexer<Change.Id, ChangeData, Change
@Override
public Void call() throws Exception {
- try (Repository repo = repoManager.openRepository(project);
- ReviewDb db = schemaFactory.open()) {
+ try (Repository repo = repoManager.openRepository(project)) {
OnlineReindexMode.begin();
// Order of scanning changes is undefined. This is ok if we assume that packfile locality is
@@ -285,9 +279,7 @@ public class AllChangesIndexer extends SiteIndexer<Change.Id, ChangeData, Change
// It does mean that reindexing after invalidating the DiffSummary cache will be expensive,
// but the goal is to invalidate that cache as infrequently as we possibly can. And besides,
// we don't have concrete proof that improving packfile locality would help.
- notesFactory
- .scan(repo, db, project, id -> (id.get() % slices) == slice)
- .forEach(r -> index(db, r));
+ notesFactory.scan(repo, project, id -> (id.get() % slices) == slice).forEach(r -> index(r));
} catch (RepositoryNotFoundException rnfe) {
logger.atSevere().log(rnfe.getMessage());
} finally {
@@ -296,13 +288,13 @@ public class AllChangesIndexer extends SiteIndexer<Change.Id, ChangeData, Change
return null;
}
- private void index(ReviewDb db, ChangeNotesResult r) {
+ private void index(ChangeNotesResult r) {
if (r.error().isPresent()) {
fail("Failed to read change " + r.id() + " for indexing", true, r.error().get());
return;
}
try {
- indexer.index(changeDataFactory.create(db, r.notes()));
+ indexer.index(changeDataFactory.create(r.notes()));
done.update(1);
verboseWriter.format(
"Reindexed change %d (project: %s)\n", r.id().get(), r.notes().getProjectName().get());
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index da807e79ba..394761bb27 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -15,6 +15,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.gerrit.index.FieldDef.exact;
import static com.google.gerrit.index.FieldDef.fullText;
import static com.google.gerrit.index.FieldDef.intRange;
@@ -22,10 +23,8 @@ import static com.google.gerrit.index.FieldDef.integer;
import static com.google.gerrit.index.FieldDef.prefix;
import static com.google.gerrit.index.FieldDef.storedOnly;
import static com.google.gerrit.index.FieldDef.timestamp;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.APPROVAL_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.CHANGE_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.PATCH_SET_CODEC;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
@@ -39,13 +38,16 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.google.common.flogger.FluentLogger;
+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.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;
@@ -53,14 +55,16 @@ 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.OutputFormat;
+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;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.notedb.RobotCommentNotes;
import com.google.gerrit.server.project.SubmitRuleOptions;
@@ -68,11 +72,6 @@ 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.gson.Gson;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import com.google.gwtorm.server.OrmException;
-import com.google.protobuf.CodedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
@@ -153,13 +152,8 @@ public class ChangeField {
exact(ChangeQueryBuilder.FIELD_FILE)
.buildRepeatable(cd -> firstNonNull(cd.currentFilePaths(), ImmutableList.of()));
- public static Set<String> getFileParts(ChangeData cd) throws OrmException {
- List<String> paths;
- try {
- paths = cd.currentFilePaths();
- } catch (IOException e) {
- throw new OrmException(e);
- }
+ public static Set<String> getFileParts(ChangeData cd) {
+ List<String> paths = cd.currentFilePaths();
Splitter s = Splitter.on('/').omitEmptyStrings();
Set<String> r = new HashSet<>();
@@ -186,6 +180,89 @@ public class ChangeField {
public static final FieldDef<ChangeData, Iterable<String>> FILE_PART =
exact(ChangeQueryBuilder.FIELD_FILEPART).buildRepeatable(ChangeField::getFileParts);
+ /** File extensions of each file modified in the current patch set. */
+ public static final FieldDef<ChangeData, Iterable<String>> EXTENSION =
+ exact(ChangeQueryBuilder.FIELD_EXTENSION).buildRepeatable(ChangeField::getExtensions);
+
+ public static Set<String> getExtensions(ChangeData cd) {
+ return extensions(cd).collect(toSet());
+ }
+
+ /**
+ * File extensions of each file modified in the current patch set as a sorted list. The purpose of
+ * this field is to allow matching changes that only touch files with certain file extensions.
+ */
+ public static final FieldDef<ChangeData, String> ONLY_EXTENSIONS =
+ exact(ChangeQueryBuilder.FIELD_ONLY_EXTENSIONS).build(ChangeField::getAllExtensionsAsList);
+
+ public static String getAllExtensionsAsList(ChangeData cd) {
+ return extensions(cd).distinct().sorted().collect(joining(","));
+ }
+
+ /**
+ * Returns a stream with all file extensions that are used by files in the given change. A file
+ * extension is defined as the portion of the filename following the final `.`. Files with no `.`
+ * in their name have no extension. For them an empty string is returned as part of the stream.
+ *
+ * <p>If the change contains multiple files with the same extension the extension is returned
+ * multiple times in the stream (once per file).
+ */
+ private static Stream<String> extensions(ChangeData cd) {
+ return cd.currentFilePaths().stream()
+ // Use case-insensitive file extensions even though other file fields are case-sensitive.
+ // If we want to find "all Java files", we want to match both .java and .JAVA, even if we
+ // normally care about case sensitivity. (Whether we should change the existing file/path
+ // predicates to be case insensitive is a separate question.)
+ .map(f -> Files.getFileExtension(f).toLowerCase(Locale.US));
+ }
+
+ /** Footers from the commit message of the current patch set. */
+ public static final FieldDef<ChangeData, Iterable<String>> FOOTER =
+ exact(ChangeQueryBuilder.FIELD_FOOTER).buildRepeatable(ChangeField::getFooters);
+
+ public static Set<String> getFooters(ChangeData cd) {
+ return cd.commitFooters().stream()
+ .map(f -> f.toString().toLowerCase(Locale.US))
+ .collect(toSet());
+ }
+
+ /** Folders that are touched by the current patch set. */
+ public static final FieldDef<ChangeData, Iterable<String>> DIRECTORY =
+ exact(ChangeQueryBuilder.FIELD_DIRECTORY).buildRepeatable(ChangeField::getDirectories);
+
+ public static Set<String> getDirectories(ChangeData cd) {
+ List<String> paths = cd.currentFilePaths();
+
+ Splitter s = Splitter.on('/').omitEmptyStrings();
+ 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)) {
+ if (nextPart != null) {
+ r.add(nextPart);
+
+ if (directory.length() > 0) {
+ directory.append("/");
+ }
+ directory.append(nextPart);
+
+ String intermediateDir = directory.toString();
+ int i = intermediateDir.indexOf('/');
+ while (i >= 0) {
+ r.add(intermediateDir);
+ intermediateDir = intermediateDir.substring(i + 1);
+ i = intermediateDir.indexOf('/');
+ }
+ }
+ nextPart = part;
+ }
+ }
+ return r;
+ }
+
/** Owner/creator of the change. */
public static final FieldDef<ChangeData, Integer> OWNER =
integer(ChangeQueryBuilder.FIELD_OWNER).build(changeGetter(c -> c.getOwner().get()));
@@ -298,7 +375,7 @@ public class ChangeField {
continue;
}
- Long l = Longs.tryParse(v.substring(i2 + 1, v.length()));
+ Long l = Longs.tryParse(v.substring(i2 + 1));
if (l == null) {
logger.atWarning().log(
"Failed to parse timestamp of reviewer field from change %s: %s", changeId.get(), v);
@@ -351,7 +428,7 @@ public class ChangeField {
continue;
}
- Long l = Longs.tryParse(v.substring(i2 + 1, v.length()));
+ Long l = Longs.tryParse(v.substring(i2 + 1));
if (l == null) {
logger.atWarning().log(
"Failed to parse timestamp of reviewer by email field from change %s: %s",
@@ -373,7 +450,7 @@ 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) throws OrmException {
+ private static Set<String> getRevisions(ChangeData cd) {
Set<String> revisions = new HashSet<>();
for (PatchSet ps : cd.patchSets()) {
if (ps.getRevision() != null) {
@@ -392,7 +469,7 @@ public class ChangeField {
public static final FieldDef<ChangeData, Iterable<String>> LABEL =
exact("label2").buildRepeatable(cd -> getLabels(cd, true));
- private static Iterable<String> getLabels(ChangeData cd, boolean owners) throws OrmException {
+ private static Iterable<String> getLabels(ChangeData cd, boolean owners) {
Set<String> allApprovals = new HashSet<>();
Set<String> distinctApprovals = new HashSet<>();
for (PatchSetApproval a : cd.currentApprovals()) {
@@ -409,20 +486,19 @@ public class ChangeField {
return allApprovals;
}
- public static Set<String> getAuthorParts(ChangeData cd) throws OrmException, IOException {
+ public static Set<String> getAuthorParts(ChangeData cd) {
return SchemaUtil.getPersonParts(cd.getAuthor());
}
- public static Set<String> getAuthorNameAndEmail(ChangeData cd) throws OrmException, IOException {
+ public static Set<String> getAuthorNameAndEmail(ChangeData cd) {
return getNameAndEmail(cd.getAuthor());
}
- public static Set<String> getCommitterParts(ChangeData cd) throws OrmException, IOException {
+ public static Set<String> getCommitterParts(ChangeData cd) {
return SchemaUtil.getPersonParts(cd.getCommitter());
}
- public static Set<String> getCommitterNameAndEmail(ChangeData cd)
- throws OrmException, IOException {
+ public static Set<String> getCommitterNameAndEmail(ChangeData cd) {
return getNameAndEmail(cd.getCommitter());
}
@@ -469,12 +545,14 @@ public class ChangeField {
/** Serialized change object, used for pre-populating results. */
public static final FieldDef<ChangeData, byte[]> CHANGE =
- storedOnly("_change").build(changeGetter(CHANGE_CODEC::encodeToByteArray));
+ storedOnly("_change")
+ .build(changeGetter(change -> toProto(ChangeProtoConverter.INSTANCE, change)));
/** Serialized approvals for the current patch set, used for pre-populating results. */
public static final FieldDef<ChangeData, Iterable<byte[]>> APPROVAL =
storedOnly("_approval")
- .buildRepeatable(cd -> toProtos(APPROVAL_CODEC, cd.currentApprovals()));
+ .buildRepeatable(
+ cd -> toProtos(PatchSetApprovalProtoConverter.INSTANCE, cd.currentApprovals()));
public static String formatLabel(String label, int value) {
return formatLabel(label, value, null);
@@ -508,11 +586,15 @@ public class ChangeField {
cd.messages().stream().map(ChangeMessage::getMessage))
.collect(toSet()));
- /** Number of unresolved comments of the change. */
+ /** Number of unresolved comment threads of the change, including robot comments. */
public static final FieldDef<ChangeData, Integer> UNRESOLVED_COMMENT_COUNT =
intRange(ChangeQueryBuilder.FIELD_UNRESOLVED_COMMENT_COUNT)
.build(ChangeData::unresolvedCommentCount);
+ /** Total number of published inline comments of the change, including robot comments. */
+ public static final FieldDef<ChangeData, Integer> TOTAL_COMMENT_COUNT =
+ intRange("total_comments").build(ChangeData::totalCommentCount);
+
/** Whether the change is mergeable. */
public static final FieldDef<ChangeData, String> MERGEABLE =
exact(ChangeQueryBuilder.FIELD_MERGEABLE)
@@ -592,7 +674,8 @@ public class ChangeField {
/** Serialized patch set object, used for pre-populating results. */
public static final FieldDef<ChangeData, Iterable<byte[]>> PATCH_SET =
- storedOnly("_patch_set").buildRepeatable(cd -> toProtos(PATCH_SET_CODEC, cd.patchSets()));
+ storedOnly("_patch_set")
+ .buildRepeatable(cd -> toProtos(PatchSetProtoConverter.INSTANCE, cd.patchSets()));
/** Users who have edits on this change. */
public static final FieldDef<ChangeData, Iterable<Integer>> EDITBY =
@@ -751,7 +834,7 @@ public class ChangeField {
return storedSubmitRecords(cd.submitRecords(opts));
}
- public static List<String> formatSubmitRecordValues(ChangeData cd) throws OrmException {
+ public static List<String> formatSubmitRecordValues(ChangeData cd) {
return formatSubmitRecordValues(
cd.submitRecords(SUBMIT_RULE_OPTIONS_STRICT), cd.change().getOwner());
}
@@ -798,19 +881,17 @@ public class ChangeField {
.values()
.forEach(r -> result.add(RefState.of(r.ref()).toByteArray(allUsers(cd))));
- if (PrimaryStorage.of(cd.change()) == PrimaryStorage.NOTE_DB) {
- ChangeNotes notes = cd.notes();
- result.add(
- RefState.create(notes.getRefName(), notes.getMetaId()).toByteArray(project));
- notes.getRobotComments(); // Force loading robot comments.
- RobotCommentNotes robotNotes = notes.getRobotCommentNotes();
- result.add(
- RefState.create(robotNotes.getRefName(), robotNotes.getMetaId())
- .toByteArray(project));
- cd.draftRefs()
- .values()
- .forEach(r -> result.add(RefState.of(r).toByteArray(allUsers(cd))));
- }
+ ChangeNotes notes = cd.notes();
+ result.add(
+ RefState.create(notes.getRefName(), notes.getMetaId()).toByteArray(project));
+ notes.getRobotComments(); // Force loading robot comments.
+ RobotCommentNotes robotNotes = notes.getRobotCommentNotes();
+ result.add(
+ RefState.create(robotNotes.getRefName(), robotNotes.getMetaId())
+ .toByteArray(project));
+ cd.draftRefs()
+ .values()
+ .forEach(r -> result.add(RefState.of(r).toByteArray(allUsers(cd))));
return result;
});
@@ -835,15 +916,13 @@ public class ChangeField {
result.add(
RefStatePattern.create(RefNames.refsStarredChangesPrefix(id) + "*")
.toByteArray(allUsers(cd)));
- if (PrimaryStorage.of(cd.change()) == PrimaryStorage.NOTE_DB) {
- result.add(
- RefStatePattern.create(RefNames.refsDraftCommentsPrefix(id) + "*")
- .toByteArray(allUsers(cd)));
- }
+ result.add(
+ RefStatePattern.create(RefNames.refsDraftCommentsPrefix(id) + "*")
+ .toByteArray(allUsers(cd)));
return result;
});
- private static String getTopic(ChangeData cd) throws OrmException {
+ private static String getTopic(ChangeData cd) {
Change c = cd.change();
if (c == null) {
return null;
@@ -851,22 +930,12 @@ public class ChangeField {
return firstNonNull(c.getTopic(), "");
}
- private static <T> List<byte[]> toProtos(ProtobufCodec<T> codec, Collection<T> objs)
- throws OrmException {
- List<byte[]> result = Lists.newArrayListWithCapacity(objs.size());
- ByteArrayOutputStream out = new ByteArrayOutputStream(256);
- try {
- for (T obj : objs) {
- out.reset();
- CodedOutputStream cos = CodedOutputStream.newInstance(out);
- codec.encode(obj, cos);
- cos.flush();
- result.add(out.toByteArray());
- }
- } catch (IOException e) {
- throw new OrmException(e);
- }
- return result;
+ private static <T> List<byte[]> toProtos(ProtoConverter<?, T> converter, Collection<T> objects) {
+ return objects.stream().map(object -> toProto(converter, object)).collect(toImmutableList());
+ }
+
+ private static <T> byte[] toProto(ProtoConverter<?, T> converter, T object) {
+ return Protos.toByteArray(converter.toProto(object));
}
private static <T> FieldDef.Getter<ChangeData, T> changeGetter(Function<Change, T> func) {
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 51a68b392f..4597cbdd8d 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -18,7 +18,6 @@ import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
import com.google.common.base.Objects;
import com.google.common.flogger.FluentLogger;
-import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -27,29 +26,17 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.IndexExecutor;
-import com.google.gerrit.server.index.IndexUtils;
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.NotesMigration;
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;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
-import com.google.inject.util.Providers;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -58,7 +45,6 @@ import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
@@ -77,21 +63,8 @@ public class ChangeIndexer {
ChangeIndexer create(ListeningExecutorService executor, ChangeIndexCollection indexes);
}
- @SuppressWarnings("deprecation")
- public static com.google.common.util.concurrent.CheckedFuture<?, IOException> allAsList(
- List<? extends ListenableFuture<?>> futures) {
- // allAsList propagates the first seen exception, wrapped in
- // ExecutionException, so we can reuse the same mapper as for a single
- // future. Assume the actual contents of the exception are not useful to
- // callers. All exceptions are already logged by IndexTask.
- return Futures.makeChecked(Futures.allAsList(futures), IndexUtils.MAPPER);
- }
-
@Nullable private final ChangeIndexCollection indexes;
@Nullable private final ChangeIndex index;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final NotesMigration notesMigration;
- private final ChangeNotes.Factory changeNotesFactory;
private final ChangeData.Factory changeDataFactory;
private final ThreadLocalRequestContext context;
private final ListeningExecutorService batchExecutor;
@@ -108,9 +81,6 @@ public class ChangeIndexer {
@AssistedInject
ChangeIndexer(
@GerritServerConfig Config cfg,
- SchemaFactory<ReviewDb> schemaFactory,
- NotesMigration notesMigration,
- ChangeNotes.Factory changeNotesFactory,
ChangeData.Factory changeDataFactory,
ThreadLocalRequestContext context,
PluginSetContext<ChangeIndexedListener> indexedListeners,
@@ -119,9 +89,6 @@ public class ChangeIndexer {
@Assisted ListeningExecutorService executor,
@Assisted ChangeIndex index) {
this.executor = executor;
- this.schemaFactory = schemaFactory;
- this.notesMigration = notesMigration;
- this.changeNotesFactory = changeNotesFactory;
this.changeDataFactory = changeDataFactory;
this.context = context;
this.indexedListeners = indexedListeners;
@@ -134,10 +101,7 @@ public class ChangeIndexer {
@AssistedInject
ChangeIndexer(
- SchemaFactory<ReviewDb> schemaFactory,
@GerritServerConfig Config cfg,
- NotesMigration notesMigration,
- ChangeNotes.Factory changeNotesFactory,
ChangeData.Factory changeDataFactory,
ThreadLocalRequestContext context,
PluginSetContext<ChangeIndexedListener> indexedListeners,
@@ -146,9 +110,6 @@ public class ChangeIndexer {
@Assisted ListeningExecutorService executor,
@Assisted ChangeIndexCollection indexes) {
this.executor = executor;
- this.schemaFactory = schemaFactory;
- this.notesMigration = notesMigration;
- this.changeNotesFactory = changeNotesFactory;
this.changeDataFactory = changeDataFactory;
this.context = context;
this.indexedListeners = indexedListeners;
@@ -169,14 +130,12 @@ public class ChangeIndexer {
* @param id change to index.
* @return future for the indexing task.
*/
- @SuppressWarnings("deprecation")
- public com.google.common.util.concurrent.CheckedFuture<?, IOException> indexAsync(
- Project.NameKey project, Change.Id id) {
+ public ListenableFuture<?> indexAsync(Project.NameKey project, Change.Id id) {
IndexTask task = new IndexTask(project, id);
if (queuedIndexTasks.add(task)) {
return submit(task);
}
- return Futures.immediateCheckedFuture(null);
+ return Futures.immediateFuture(null);
}
/**
@@ -185,14 +144,12 @@ public class ChangeIndexer {
* @param ids changes to index.
* @return future for completing indexing of all changes.
*/
- @SuppressWarnings("deprecation")
- public com.google.common.util.concurrent.CheckedFuture<?, IOException> indexAsync(
- Project.NameKey project, Collection<Change.Id> ids) {
+ public ListenableFuture<?> indexAsync(Project.NameKey project, Collection<Change.Id> ids) {
List<ListenableFuture<?>> futures = new ArrayList<>(ids.size());
for (Change.Id id : ids) {
futures.add(indexAsync(project, id));
}
- return allAsList(futures);
+ return Futures.allAsList(futures);
}
/**
@@ -200,7 +157,7 @@ public class ChangeIndexer {
*
* @param cd change to index.
*/
- public void index(ChangeData cd) throws IOException {
+ public void index(ChangeData cd) {
indexImpl(cd);
// Always double-check whether the change might be stale immediately after
@@ -224,7 +181,7 @@ public class ChangeIndexer {
autoReindexIfStale(cd);
}
- private void indexImpl(ChangeData cd) throws IOException {
+ private void indexImpl(ChangeData cd) {
logger.atFine().log("Replace change %d in index.", cd.getId().get());
for (Index<?, ChangeData> i : getWriteIndexes()) {
try (TraceTimer traceTimer =
@@ -248,23 +205,20 @@ public class ChangeIndexer {
/**
* Synchronously index a change.
*
- * @param db review database.
* @param change change to index.
*/
- public void index(ReviewDb db, Change change) throws IOException, OrmException {
- index(newChangeData(db, change));
+ public void index(Change change) {
+ index(changeDataFactory.create(change));
}
/**
* Synchronously index a change.
*
- * @param db review database.
* @param project the project to which the change belongs.
* @param changeId ID of the change to index.
*/
- public void index(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws IOException, OrmException {
- index(newChangeData(db, project, changeId));
+ public void index(Project.NameKey project, Change.Id changeId) {
+ index(changeDataFactory.create(project, changeId));
}
/**
@@ -273,8 +227,7 @@ public class ChangeIndexer {
* @param id change to delete.
* @return future for the deleting task.
*/
- @SuppressWarnings("deprecation")
- public com.google.common.util.concurrent.CheckedFuture<?, IOException> deleteAsync(Change.Id id) {
+ public ListenableFuture<?> deleteAsync(Change.Id id) {
return submit(new DeleteTask(id));
}
@@ -283,7 +236,7 @@ public class ChangeIndexer {
*
* @param id change ID to delete.
*/
- public void delete(Change.Id id) throws IOException {
+ public void delete(Change.Id id) {
new DeleteTask(id).call();
}
@@ -297,14 +250,12 @@ public class ChangeIndexer {
* @param id ID of the change to index.
* @return future for reindexing the change; returns true if the change was stale.
*/
- @SuppressWarnings("deprecation")
- public com.google.common.util.concurrent.CheckedFuture<Boolean, IOException> reindexIfStale(
- Project.NameKey project, Change.Id id) {
+ public ListenableFuture<Boolean> reindexIfStale(Project.NameKey project, Change.Id id) {
ReindexIfStaleTask task = new ReindexIfStaleTask(project, id);
if (queuedReindexIfStaleTasks.add(task)) {
return submit(task, batchExecutor);
}
- return Futures.immediateCheckedFuture(false);
+ return Futures.immediateFuture(false);
}
private void autoReindexIfStale(ChangeData cd) {
@@ -323,17 +274,13 @@ public class ChangeIndexer {
return indexes != null ? indexes.getWriteIndexes() : Collections.singleton(index);
}
- @SuppressWarnings("deprecation")
- private <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
- Callable<T> task) {
+ private <T> ListenableFuture<T> submit(Callable<T> task) {
return submit(task, executor);
}
- @SuppressWarnings("deprecation")
- private static <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
+ private static <T> ListenableFuture<T> submit(
Callable<T> task, ListeningExecutorService executor) {
- return Futures.makeChecked(
- Futures.nonCancellationPropagating(executor.submit(task)), IndexUtils.MAPPER);
+ return Futures.nonCancellationPropagating(executor.submit(task));
}
private abstract class AbstractIndexTask<T> implements Callable<T> {
@@ -345,7 +292,7 @@ public class ChangeIndexer {
this.id = id;
}
- protected abstract T callImpl(Provider<ReviewDb> db) throws Exception;
+ protected abstract T callImpl() throws Exception;
protected abstract void remove();
@@ -355,37 +302,15 @@ public class ChangeIndexer {
@Override
public final T call() throws Exception {
try {
- final AtomicReference<Provider<ReviewDb>> dbRef = Atomics.newReference();
RequestContext newCtx =
- new RequestContext() {
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- Provider<ReviewDb> db = dbRef.get();
- if (db == null) {
- try {
- db = Providers.of(schemaFactory.open());
- } catch (OrmException e) {
- throw new ProvisionException("error opening ReviewDb", e);
- }
- dbRef.set(db);
- }
- return db;
- }
-
- @Override
- public CurrentUser getUser() {
- throw new OutOfScopeException("No user during ChangeIndexer");
- }
+ () -> {
+ throw new OutOfScopeException("No user during ChangeIndexer");
};
RequestContext oldCtx = context.setContext(newCtx);
try {
- return callImpl(newCtx.getReviewDbProvider());
+ return callImpl();
} finally {
context.setContext(oldCtx);
- Provider<ReviewDb> db = dbRef.get();
- if (db != null) {
- db.get().close();
- }
}
} catch (Exception e) {
logger.atSevere().withCause(e).log("Failed to execute %s", this);
@@ -400,9 +325,9 @@ public class ChangeIndexer {
}
@Override
- public Void callImpl(Provider<ReviewDb> db) throws Exception {
+ public Void callImpl() throws Exception {
remove();
- ChangeData cd = newChangeData(db.get(), project, id);
+ ChangeData cd = changeDataFactory.create(project, id);
index(cd);
return null;
}
@@ -432,7 +357,7 @@ public class ChangeIndexer {
}
}
- // Not AbstractIndexTask as it doesn't need ReviewDb.
+ // Not AbstractIndexTask as it doesn't need a request context.
private class DeleteTask implements Callable<Void> {
private final Change.Id id;
@@ -441,7 +366,7 @@ public class ChangeIndexer {
}
@Override
- public Void call() throws IOException {
+ public Void call() {
logger.atFine().log("Delete change %d from index.", id.get());
// Don't bother setting a RequestContext to provide the DB.
// Implementations should not need to access the DB in order to delete a
@@ -464,15 +389,13 @@ public class ChangeIndexer {
}
@Override
- public Boolean callImpl(Provider<ReviewDb> db) throws Exception {
+ public Boolean callImpl() throws Exception {
remove();
try {
if (stalenessChecker.isStale(id)) {
- indexImpl(newChangeData(db.get(), project, id));
+ indexImpl(changeDataFactory.create(project, id));
return true;
}
- } catch (NoSuchChangeException nsce) {
- logger.atFine().log("Change %s was deleted, aborting reindexing the change.", id.get());
} catch (Exception e) {
if (!isCausedByRepositoryNotFoundException(e)) {
throw e;
@@ -518,26 +441,4 @@ public class ChangeIndexer {
}
return false;
}
-
- // Avoid auto-rebuilding when reindexing if reading is disabled. This just
- // increases contention on the meta ref from a background indexing thread
- // with little benefit. The next actual write to the entity may still incur a
- // less-contentious rebuild.
- private ChangeData newChangeData(ReviewDb db, Change change) throws OrmException {
- if (!notesMigration.readChanges()) {
- ChangeNotes notes = changeNotesFactory.createWithAutoRebuildingDisabled(change, null);
- return changeDataFactory.create(db, notes);
- }
- return changeDataFactory.create(db, change);
- }
-
- private ChangeData newChangeData(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException {
- if (!notesMigration.readChanges()) {
- ChangeNotes notes =
- changeNotesFactory.createWithAutoRebuildingDisabled(db, project, changeId);
- return changeDataFactory.create(db, notes);
- }
- return changeDataFactory.create(db, project, changeId);
- }
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
index 2000cd176d..cde6a6433d 100644
--- a/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
+++ b/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java
@@ -99,7 +99,20 @@ public class ChangeSchemaDefinitions extends SchemaDefinitions<ChangeData> {
@Deprecated static final Schema<ChangeData> V49 = schema(V48);
// Bump Lucene version requires reindexing
- static final Schema<ChangeData> V50 = schema(V49);
+ @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);
+
+ // The computation of the 'extension' field is changed, hence reindexing is required.
+ static final Schema<ChangeData> V56 = schema(V55);
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 f6cee6dac4..9be93f77bb 100644
--- a/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
+++ b/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
@@ -20,7 +20,6 @@ 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;
-import java.io.IOException;
public class DummyChangeIndex implements ChangeIndex {
@Override
@@ -32,13 +31,13 @@ public class DummyChangeIndex implements ChangeIndex {
public void close() {}
@Override
- public void replace(ChangeData cd) throws IOException {}
+ public void replace(ChangeData cd) {}
@Override
- public void delete(Change.Id id) throws IOException {}
+ public void delete(Change.Id id) {}
@Override
- public void deleteAll() throws IOException {}
+ public void deleteAll() {}
@Override
public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts) {
@@ -46,7 +45,7 @@ public class DummyChangeIndex implements ChangeIndex {
}
@Override
- public void markReady(boolean ready) throws IOException {}
+ public void markReady(boolean ready) {}
public int getMaxLimit() {
return Integer.MAX_VALUE;
diff --git a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
index 66f8df2534..ed09eed2e9 100644
--- a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
+++ b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
@@ -19,25 +19,24 @@ import static com.google.gerrit.server.index.change.ChangeField.CHANGE;
import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
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.index.IndexConfig;
-import com.google.gerrit.index.IndexedQuery;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.IndexPredicate;
+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.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 com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -52,7 +51,7 @@ import java.util.Set;
public class IndexedChangeQuery extends IndexedQuery<Change.Id, ChangeData>
implements ChangeDataSource, Matchable<ChangeData> {
public static QueryOptions oneResult() {
- return createOptions(IndexConfig.createDefault(), 0, 1, ImmutableSet.<String>of());
+ return createOptions(IndexConfig.createDefault(), 0, 1, ImmutableSet.of());
}
public static QueryOptions createOptions(
@@ -81,7 +80,7 @@ public class IndexedChangeQuery extends IndexedQuery<Change.Id, ChangeData>
}
@Override
- public ResultSet<ChangeData> read() throws OrmException {
+ public ResultSet<ChangeData> read() {
final DataSource<ChangeData> currSource = source;
final ResultSet<ChangeData> rs = currSource.read();
@@ -98,8 +97,8 @@ public class IndexedChangeQuery extends IndexedQuery<Change.Id, ChangeData>
}
@Override
- public List<ChangeData> toList() {
- List<ChangeData> r = rs.toList();
+ public ImmutableList<ChangeData> toList() {
+ ImmutableList<ChangeData> r = rs.toList();
for (ChangeData cd : r) {
fromSource.put(cd, currSource);
}
@@ -114,7 +113,7 @@ public class IndexedChangeQuery extends IndexedQuery<Change.Id, ChangeData>
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
if (source != null && fromSource.get(cd) == source) {
return true;
}
diff --git a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
index be4f38c599..fd12345e16 100644
--- a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
+++ b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
@@ -28,7 +28,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -41,7 +40,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -98,12 +96,8 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
if (allUsersName.get().equals(event.getProjectName())) {
Account.Id accountId = Account.Id.fromRef(event.getRefName());
if (accountId != null && !event.getRefName().startsWith(RefNames.REFS_STARRED_CHANGES)) {
- try {
- accountCache.evict(accountId);
- indexer.get().index(accountId);
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Reindex account %s failed.", accountId);
- }
+ accountCache.evict(accountId);
+ indexer.get().index(accountId);
}
}
@@ -164,7 +158,7 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
}
@Override
- protected List<Change> impl(RequestContext ctx) throws OrmException {
+ protected List<Change> impl(RequestContext ctx) {
String ref = event.getRefName();
Project.NameKey project = new Project.NameKey(event.getProjectName());
if (ref.equals(RefNames.REFS_CONFIG)) {
@@ -194,16 +188,13 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
}
@Override
- protected Void impl(RequestContext ctx) throws OrmException, IOException {
+ protected Void impl(RequestContext ctx) throws IOException {
// Reload change, as some time may have passed since GetChanges.
- ReviewDb db = ctx.getReviewDbProvider().get();
remove();
try {
Change c =
- notesFactory
- .createChecked(db, new Project.NameKey(event.getProjectName()), id)
- .getChange();
- indexerFactory.create(executor, indexes).index(db, 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);
}
diff --git a/java/com/google/gerrit/server/index/change/StalenessChecker.java b/java/com/google/gerrit/server/index/change/StalenessChecker.java
index cf7db6f876..338cf3dd5f 100644
--- a/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.index.change;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import com.google.auto.value.AutoValue;
@@ -29,21 +28,15 @@ import com.google.common.collect.SetMultimap;
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.common.UsedAt;
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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.List;
@@ -66,21 +59,16 @@ public class StalenessChecker {
private final ChangeIndexCollection indexes;
private final GitRepositoryManager repoManager;
private final IndexConfig indexConfig;
- private final Provider<ReviewDb> db;
@Inject
StalenessChecker(
- ChangeIndexCollection indexes,
- GitRepositoryManager repoManager,
- IndexConfig indexConfig,
- Provider<ReviewDb> db) {
+ ChangeIndexCollection indexes, GitRepositoryManager repoManager, IndexConfig indexConfig) {
this.indexes = indexes;
this.repoManager = repoManager;
this.indexConfig = indexConfig;
- this.db = db;
}
- public boolean isStale(Change.Id id) throws IOException, OrmException {
+ public boolean isStale(Change.Id id) {
ChangeIndex i = indexes.getSearchIndex();
if (i == null) {
return false; // No index; caller couldn't do anything if it is stale.
@@ -96,25 +84,16 @@ public class StalenessChecker {
return true; // Not in index, but caller wants it to be.
}
ChangeData cd = result.get();
- return isStale(
- repoManager,
- id,
- cd.change(),
- ChangeNotes.readOneReviewDbChange(db.get(), id),
- parseStates(cd),
- parsePatterns(cd));
+ return isStale(repoManager, id, parseStates(cd), parsePatterns(cd));
}
@UsedAt(UsedAt.Project.GOOGLE)
public static boolean isStale(
GitRepositoryManager repoManager,
Change.Id id,
- Change indexChange,
- @Nullable Change reviewDbChange,
SetMultimap<Project.NameKey, RefState> states,
ListMultimap<Project.NameKey, RefStatePattern> patterns) {
- return reviewDbChangeIsStale(indexChange, reviewDbChange)
- || refsAreStale(repoManager, id, states, patterns);
+ return refsAreStale(repoManager, id, states, patterns);
}
@VisibleForTesting
@@ -134,31 +113,6 @@ public class StalenessChecker {
return false;
}
- @VisibleForTesting
- static boolean reviewDbChangeIsStale(Change indexChange, @Nullable Change reviewDbChange) {
- requireNonNull(indexChange);
- PrimaryStorage storageFromIndex = PrimaryStorage.of(indexChange);
- PrimaryStorage storageFromReviewDb = PrimaryStorage.of(reviewDbChange);
- if (reviewDbChange == null) {
- if (storageFromIndex == PrimaryStorage.REVIEW_DB) {
- return true; // Index says it should have been in ReviewDb, but it wasn't.
- }
- return false; // Not in ReviewDb, but that's ok.
- }
- checkArgument(
- indexChange.getId().equals(reviewDbChange.getId()),
- "mismatched change ID: %s != %s",
- indexChange.getId(),
- reviewDbChange.getId());
- if (storageFromIndex != storageFromReviewDb) {
- return true; // Primary storage differs, definitely stale.
- }
- if (storageFromReviewDb != PrimaryStorage.REVIEW_DB) {
- return false; // Not a ReviewDb change, don't check rowVersion.
- }
- return reviewDbChange.getRowVersion() != indexChange.getRowVersion();
- }
-
private SetMultimap<Project.NameKey, RefState> parseStates(ChangeData cd) {
return RefState.parseStates(cd.getRefStates());
}
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexer.java b/java/com/google/gerrit/server/index/group/GroupIndexer.java
index 503fd6b5fd..5d9232efa7 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexer.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexer.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.index.group;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import java.io.IOException;
public interface GroupIndexer {
@@ -24,7 +23,7 @@ public interface GroupIndexer {
*
* @param uuid group UUID to index.
*/
- void index(AccountGroup.UUID uuid) throws IOException;
+ void index(AccountGroup.UUID uuid);
/**
* Synchronously reindex a group if it is stale.
@@ -32,5 +31,5 @@ public interface GroupIndexer {
* @param uuid group UUID to index.
* @return whether the group was reindexed
*/
- boolean reindexIfStale(AccountGroup.UUID uuid) throws IOException;
+ boolean reindexIfStale(AccountGroup.UUID uuid);
}
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
index a9124e1676..5982de781c 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
@@ -17,6 +17,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.events.GroupIndexedListener;
import com.google.gerrit.index.Index;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -74,7 +75,7 @@ public class GroupIndexerImpl implements GroupIndexer {
}
@Override
- public void index(AccountGroup.UUID uuid) throws IOException {
+ public void index(AccountGroup.UUID uuid) {
// Evict the cache to get an up-to-date value for sure.
groupCache.evict(uuid);
Optional<InternalGroup> internalGroup = groupCache.get(uuid);
@@ -104,10 +105,14 @@ public class GroupIndexerImpl implements GroupIndexer {
}
@Override
- public boolean reindexIfStale(AccountGroup.UUID uuid) throws IOException {
- if (stalenessChecker.isStale(uuid)) {
- index(uuid);
- return true;
+ public boolean reindexIfStale(AccountGroup.UUID uuid) {
+ try {
+ if (stalenessChecker.isStale(uuid)) {
+ index(uuid);
+ return true;
+ }
+ } catch (IOException e) {
+ throw new StorageException(e);
}
return false;
}
diff --git a/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java b/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
index 79f25c0861..32393b0687 100644
--- a/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
+++ b/java/com/google/gerrit/server/index/group/IndexedGroupQuery.java
@@ -16,9 +16,9 @@ package com.google.gerrit.server.index.group;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.index.IndexedQuery;
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.AccountGroup;
diff --git a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
index 1a74eca6c8..6d6f78d91d 100644
--- a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -30,7 +30,6 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
-import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
@@ -71,7 +70,7 @@ public class ProjectIndexerImpl implements ProjectIndexer {
}
@Override
- public void index(Project.NameKey nameKey) throws IOException {
+ public void index(Project.NameKey nameKey) {
ProjectState projectState = projectCache.get(nameKey);
if (projectState != null) {
logger.atFine().log("Replace project %s in index", nameKey.get());
diff --git a/java/com/google/gerrit/server/index/project/StalenessChecker.java b/java/com/google/gerrit/server/index/project/StalenessChecker.java
index 733e48657f..dc5ebc6ea9 100644
--- a/java/com/google/gerrit/server/index/project/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/project/StalenessChecker.java
@@ -29,7 +29,6 @@ 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.io.IOException;
import java.util.Optional;
public class StalenessChecker {
@@ -48,7 +47,7 @@ public class StalenessChecker {
this.indexConfig = indexConfig;
}
- public boolean isStale(Project.NameKey project) throws IOException {
+ public boolean isStale(Project.NameKey project) {
ProjectData projectData = projectCache.get(project).toProjectData();
ProjectIndex i = indexes.getSearchIndex();
if (i == null) {
diff --git a/java/com/google/gerrit/server/ioutil/BUILD b/java/com/google/gerrit/server/ioutil/BUILD
index 5fcf0bf05b..ed58d5bd40 100644
--- a/java/com/google/gerrit/server/ioutil/BUILD
+++ b/java/com/google/gerrit/server/ioutil/BUILD
@@ -5,7 +5,7 @@ java_library(
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
- "//java/com/google/gerrit/reviewdb:client",
+ "//java/com/google/gerrit/reviewdb:server",
"//lib:automaton",
"//lib:guava",
"//lib/jgit/org.eclipse.jgit.archive:jgit-archive",
diff --git a/java/com/google/gerrit/server/ioutil/HostPlatform.java b/java/com/google/gerrit/server/ioutil/HostPlatform.java
index 5afda3c612..39e9c07a0d 100644
--- a/java/com/google/gerrit/server/ioutil/HostPlatform.java
+++ b/java/com/google/gerrit/server/ioutil/HostPlatform.java
@@ -33,12 +33,7 @@ public final class HostPlatform {
private static boolean compute(String platform) {
final String osDotName =
AccessController.doPrivileged(
- new PrivilegedAction<String>() {
- @Override
- public String run() {
- return System.getProperty("os.name");
- }
- });
+ (PrivilegedAction<String>) () -> System.getProperty("os.name"));
return osDotName != null && osDotName.toLowerCase().contains(platform);
}
diff --git a/java/com/google/gerrit/server/mail/CheckTokenException.java b/java/com/google/gerrit/server/mail/CheckTokenException.java
new file mode 100644
index 0000000000..bc6105af2b
--- /dev/null
+++ b/java/com/google/gerrit/server/mail/CheckTokenException.java
@@ -0,0 +1,28 @@
+// Copyright 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.mail;
+
+/** Indicates the SignedToken is invalid */
+public class CheckTokenException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ CheckTokenException(final String message) {
+ super(message);
+ }
+
+ CheckTokenException(final String message, final Throwable why) {
+ super(message, why);
+ }
+}
diff --git a/java/com/google/gerrit/server/mail/MailUtil.java b/java/com/google/gerrit/server/mail/MailUtil.java
index 507b53f170..4afcc7b2a2 100644
--- a/java/com/google/gerrit/server/mail/MailUtil.java
+++ b/java/com/google/gerrit/server/mail/MailUtil.java
@@ -18,17 +18,17 @@ 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.common.errors.NoSuchAccountException;
+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 com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.revwalk.FooterKey;
import org.eclipse.jgit.revwalk.FooterLine;
@@ -36,7 +36,7 @@ public class MailUtil {
public static MailRecipients getRecipientsFromFooters(
AccountResolver accountResolver, List<FooterLine> footerLines)
- throws OrmException, IOException {
+ throws IOException, ConfigInvalidException {
MailRecipients recipients = new MailRecipients();
for (FooterLine footerLine : footerLines) {
try {
@@ -45,7 +45,7 @@ public class MailUtil {
} else if (footerLine.matches(FooterKey.CC)) {
recipients.cc.add(toAccountId(accountResolver, footerLine.getValue().trim()));
}
- } catch (NoSuchAccountException e) {
+ } catch (UnprocessableEntityException e) {
continue;
}
}
@@ -59,13 +59,10 @@ public class MailUtil {
return recipients;
}
+ @SuppressWarnings("deprecation")
private static Account.Id toAccountId(AccountResolver accountResolver, String nameOrEmail)
- throws OrmException, NoSuchAccountException, IOException {
- Account a = accountResolver.findByNameOrEmail(nameOrEmail);
- if (a == null) {
- throw new NoSuchAccountException("\"" + nameOrEmail + "\" is not registered");
- }
- return a.getId();
+ throws UnprocessableEntityException, IOException, ConfigInvalidException {
+ return accountResolver.resolveByNameOrEmail(nameOrEmail).asUnique().getAccount().getId();
}
private static boolean isReviewer(FooterLine candidateFooterLine) {
@@ -124,7 +121,7 @@ public class MailUtil {
return Pattern.compile(".*");
}
- StringBuilder sb = new StringBuilder("");
+ StringBuilder sb = new StringBuilder();
for (String domain : domains) {
String quoted = "\\Q" + domain.replace("\\E", "\\E\\\\E\\Q") + "\\E|";
sb.append(quoted.replace("*", "\\E.*\\Q"));
diff --git a/java/com/google/gerrit/server/mail/SignedToken.java b/java/com/google/gerrit/server/mail/SignedToken.java
new file mode 100644
index 0000000000..7dcac1a996
--- /dev/null
+++ b/java/com/google/gerrit/server/mail/SignedToken.java
@@ -0,0 +1,228 @@
+// 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.gerrit.server.mail;
+
+import com.google.common.io.BaseEncoding;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import javax.crypto.Mac;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * Utility function to compute and verify XSRF tokens.
+ *
+ * <p>{@link SignedTokenEmailTokenVerifier} uses this class to verify tokens appearing in the custom
+ * <code>xsrfKey
+ * </code> JSON request property. The tokens protect against cross-site request forgery by depending
+ * upon the browser's security model. The classic browser security model prohibits a script from
+ * site A from reading any data received from site B. By sending unforgeable tokens from the server
+ * and asking the client to return them to us, the client script must have had read access to the
+ * token at some point and is therefore also from our server.
+ */
+public class SignedToken {
+ private static final int INT_SZ = 4;
+ private static final String MAC_ALG = "HmacSHA1";
+
+ /**
+ * Generate a random key for use with the XSRF library.
+ *
+ * @return a new private key, base 64 encoded.
+ */
+ public static String generateRandomKey() {
+ final byte[] r = new byte[26];
+ new SecureRandom().nextBytes(r);
+ return encodeBase64PrivateKey(r);
+ }
+
+ private final int maxAge;
+ private final SecretKeySpec key;
+ private final SecureRandom rng;
+ private final int tokenLength;
+
+ /**
+ * Create a new utility, using the specific key.
+ *
+ * @param age the number of seconds a token may remain valid.
+ * @param keyBase64 base 64 encoded representation of the key.
+ * @throws XsrfException the JVM doesn't support the necessary algorithms.
+ */
+ public SignedToken(final int age, final String keyBase64) throws XsrfException {
+ maxAge = age > 5 ? age / 5 : age;
+ key = new SecretKeySpec(decodeBase64PrivateKey(keyBase64), MAC_ALG);
+ rng = new SecureRandom();
+ tokenLength = 2 * INT_SZ + newMac().getMacLength();
+ }
+
+ /**
+ * Generate a new signed token.
+ *
+ * @param text the text string to sign. Typically this should be some user-specific string, to
+ * prevent replay attacks. The text must be safe to appear in whatever context the token
+ * itself will appear, as the text is included on the end of the token.
+ * @return the signed token. The text passed in <code>text</code> will appear after the first ','
+ * in the returned token string.
+ * @throws XsrfException the JVM doesn't support the necessary algorithms.
+ */
+ String newToken(final String text) throws XsrfException {
+ final int q = rng.nextInt();
+ final byte[] buf = new byte[tokenLength];
+ encodeInt(buf, 0, q);
+ encodeInt(buf, INT_SZ, now() ^ q);
+ computeToken(buf, text);
+ return encodeBase64(buf) + '$' + text;
+ }
+
+ /**
+ * Validate a returned token. If the token is valid then return a {@link ValidToken}, else will
+ * throw {@link XsrfException} when it's an unexpected token overflow or {@link
+ * CheckTokenException} when it's an illegal token string format.
+ *
+ * @param tokenString a token string previously created by this class.
+ * @param text text that must have been used during {@link #newToken(String)} in order for the
+ * token to be valid. If null the text will be taken from the token string itself.
+ * @return the token which is valid.
+ * @throws XsrfException the JVM doesn't support the necessary algorithms to generate a token.
+ * XSRF services are simply not available.
+ * @throws CheckTokenException throws when token is null, the empty string, has expired, does not
+ * match the text supplied, or is a forged token.
+ */
+ public ValidToken checkToken(final String tokenString, final String text)
+ throws XsrfException, CheckTokenException {
+
+ if (tokenString == null || tokenString.length() == 0) {
+ throw new CheckTokenException("Empty token");
+ }
+
+ final int s = tokenString.indexOf('$');
+ if (s <= 0) {
+ throw new CheckTokenException("Token does not contain character '$'");
+ }
+
+ final String recvText = tokenString.substring(s + 1);
+ final byte[] in;
+ try {
+ in = decodeBase64(tokenString.substring(0, s));
+ } catch (RuntimeException e) {
+ throw new CheckTokenException("Base64 decoding failed", e);
+ }
+
+ if (in.length != tokenLength) {
+ throw new CheckTokenException("Token length mismatch");
+ }
+
+ final int q = decodeInt(in, 0);
+ final int c = decodeInt(in, INT_SZ) ^ q;
+ final int n = now();
+ if (maxAge > 0 && Math.abs(c - n) > maxAge) {
+ throw new CheckTokenException("Token is expired");
+ }
+
+ final byte[] gen = new byte[tokenLength];
+ System.arraycopy(in, 0, gen, 0, 2 * INT_SZ);
+ computeToken(gen, text != null ? text : recvText);
+ if (!Arrays.equals(gen, in)) {
+ throw new CheckTokenException("Token text mismatch");
+ }
+
+ return new ValidToken(maxAge > 0 && c + (maxAge >> 1) <= n, recvText);
+ }
+
+ private void computeToken(final byte[] buf, final String text) throws XsrfException {
+ final Mac m = newMac();
+ m.update(buf, 0, 2 * INT_SZ);
+ m.update(toBytes(text));
+ try {
+ m.doFinal(buf, 2 * INT_SZ);
+ } catch (ShortBufferException e) {
+ throw new XsrfException("Unexpected token overflow", e);
+ }
+ }
+
+ private Mac newMac() throws XsrfException {
+ try {
+ final Mac m = Mac.getInstance(MAC_ALG);
+ m.init(key);
+ return m;
+ } catch (NoSuchAlgorithmException e) {
+ throw new XsrfException(MAC_ALG + " not supported", e);
+ } catch (InvalidKeyException e) {
+ throw new XsrfException("Invalid private key", e);
+ }
+ }
+
+ private static int now() {
+ return (int) (System.currentTimeMillis() / 5000L);
+ }
+
+ private static byte[] decodeBase64PrivateKey(final String privateKeyBase64String) {
+ return Base64.decodeBase64(toBytes(privateKeyBase64String));
+ }
+
+ private static String encodeBase64PrivateKey(final byte[] buf) {
+ return toString(Base64.encodeBase64(buf));
+ }
+
+ private static byte[] decodeBase64(final String s) {
+ return BaseEncoding.base64Url().decode(s);
+ }
+
+ private static String encodeBase64(final byte[] buf) {
+ return BaseEncoding.base64Url().encode(buf);
+ }
+
+ private static void encodeInt(final byte[] buf, final int o, final int v) {
+ int _v = v;
+ buf[o + 3] = (byte) _v;
+ _v >>>= 8;
+
+ buf[o + 2] = (byte) _v;
+ _v >>>= 8;
+
+ buf[o + 1] = (byte) _v;
+ _v >>>= 8;
+
+ buf[o] = (byte) _v;
+ }
+
+ private static int decodeInt(final byte[] buf, final int o) {
+ int r = buf[o] << 8;
+
+ r |= buf[o + 1] & 0xff;
+ r <<= 8;
+
+ r |= buf[o + 2] & 0xff;
+ return (r << 8) | (buf[o + 3] & 0xff);
+ }
+
+ private static byte[] toBytes(final String s) {
+ final byte[] r = new byte[s.length()];
+ for (int k = r.length - 1; k >= 0; k--) {
+ r[k] = (byte) s.charAt(k);
+ }
+ return r;
+ }
+
+ private static String toString(final byte[] b) {
+ final StringBuilder r = new StringBuilder(b.length);
+ for (int i = 0; i < b.length; i++) {
+ r.append((char) b[i]);
+ }
+ return r.toString();
+ }
+}
diff --git a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
index ba9bff8520..897a5f21ac 100644
--- a/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
+++ b/java/com/google/gerrit/server/mail/SignedTokenEmailTokenVerifier.java
@@ -21,10 +21,6 @@ import com.google.common.io.BaseEncoding;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.mail.send.RegisterNewEmailSender;
-import com.google.gwtjsonrpc.common.CheckTokenException;
-import com.google.gwtjsonrpc.server.SignedToken;
-import com.google.gwtjsonrpc.server.ValidToken;
-import com.google.gwtjsonrpc.server.XsrfException;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Singleton;
diff --git a/java/com/google/gerrit/server/mail/ValidToken.java b/java/com/google/gerrit/server/mail/ValidToken.java
new file mode 100644
index 0000000000..19d6cb0c57
--- /dev/null
+++ b/java/com/google/gerrit/server/mail/ValidToken.java
@@ -0,0 +1,36 @@
+// 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.gerrit.server.mail;
+
+/** A validated token from {@link SignedToken#checkToken(String, String)} */
+class ValidToken {
+ private final boolean refresh;
+ private final String data;
+
+ ValidToken(final boolean ref, final String d) {
+ refresh = ref;
+ data = d;
+ }
+
+ /** The text protected by the token's encryption key. */
+ String getData() {
+ return data;
+ }
+
+ /** True if the token's life span is almost half-over and should be renewed. */
+ boolean needsRefresh() {
+ return refresh;
+ }
+}
diff --git a/java/com/google/gerrit/server/mail/XsrfException.java b/java/com/google/gerrit/server/mail/XsrfException.java
new file mode 100644
index 0000000000..eda17c34b7
--- /dev/null
+++ b/java/com/google/gerrit/server/mail/XsrfException.java
@@ -0,0 +1,24 @@
+// 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.gerrit.server.mail;
+
+/** Indicates the requested method is not known. */
+public class XsrfException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ XsrfException(final String message, final Throwable why) {
+ super(message, why);
+ }
+}
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 15a71e0f3d..b1acab9961 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -17,10 +17,9 @@ package com.google.gerrit.server.mail.receive;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
-import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -66,7 +65,6 @@ import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -149,7 +147,7 @@ public class MailProcessor {
}
private void processImpl(BatchUpdate.Factory buf, MailMessage message)
- throws OrmException, UpdateException, RestApiException, IOException {
+ throws UpdateException, RestApiException, IOException {
for (Extension<MailFilter> filter : mailFilters) {
if (!filter.getProvider().get().shouldProcessMessage(message)) {
logger.atWarning().log(
@@ -210,7 +208,7 @@ public class MailProcessor {
private void persistComments(
BatchUpdate.Factory buf, MailMessage message, MailMetadata metadata, Account.Id sender)
- throws OrmException, UpdateException, RestApiException {
+ throws UpdateException, RestApiException {
try (ManualRequestContext ctx = oneOffRequestContext.openAs(sender)) {
List<ChangeData> changeDataList =
queryProvider.get().byLegacyChangeId(new Change.Id(metadata.changeNumber));
@@ -262,7 +260,7 @@ public class MailProcessor {
}
Op o = new Op(new PatchSet.Id(cd.getId(), metadata.patchSet), parsedComments, message.id());
- BatchUpdate batchUpdate = buf.create(cd.db(), project, ctx.getUser(), TimeUtil.nowTs());
+ BatchUpdate batchUpdate = buf.create(project, ctx.getUser(), TimeUtil.nowTs());
batchUpdate.addOp(cd.getId(), o);
batchUpdate.execute();
}
@@ -285,15 +283,15 @@ public class MailProcessor {
@Override
public boolean updateChange(ChangeContext ctx)
- throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
- patchSet = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ throws UnprocessableEntityException, PatchListNotAvailableException {
+ patchSet = psUtil.get(ctx.getNotes(), psId);
notes = ctx.getNotes();
if (patchSet == null) {
- throw new OrmException("patch set not found: " + psId);
+ throw new StorageException("patch set not found: " + psId);
}
changeMessage = generateChangeMessage(ctx);
- changeMessagesUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), changeMessage);
+ changeMessagesUtil.addChangeMessage(ctx.getUpdate(psId), changeMessage);
comments = new ArrayList<>();
for (MailComment c : parsedComments) {
@@ -304,10 +302,7 @@ public class MailProcessor {
persistentCommentFromMailComment(ctx, c, targetPatchSetForComment(ctx, c, patchSet)));
}
commentsUtil.putComments(
- ctx.getDb(),
- ctx.getUpdate(ctx.getChange().currentPatchSetId()),
- Status.PUBLISHED,
- comments);
+ ctx.getUpdate(ctx.getChange().currentPatchSetId()), Status.PUBLISHED, comments);
return true;
}
@@ -321,8 +316,7 @@ public class MailProcessor {
// Send email notifications
outgoingMailFactory
.create(
- NotifyHandling.ALL,
- ArrayListMultimap.create(),
+ ctx.getNotify(notes.getChangeId()),
notes,
patchSet,
ctx.getUser().asIdentifiedUser(),
@@ -335,12 +329,7 @@ public class MailProcessor {
Map<String, Short> approvals = new HashMap<>();
approvalsUtil
.byPatchSetUser(
- ctx.getDb(),
- notes,
- psId,
- ctx.getAccountId(),
- ctx.getRevWalk(),
- ctx.getRepoView().getConfig())
+ notes, psId, ctx.getAccountId(), ctx.getRevWalk(), ctx.getRepoView().getConfig())
.forEach(a -> approvals.put(a.getLabel(), a.getValue()));
// Fire Gerrit event. Note that approvals can't be granted via email, so old and new approvals
// are always the same here.
@@ -369,10 +358,9 @@ public class MailProcessor {
}
private PatchSet targetPatchSetForComment(
- ChangeContext ctx, MailComment mailComment, PatchSet current) throws OrmException {
+ ChangeContext ctx, MailComment mailComment, PatchSet current) {
if (mailComment.getInReplyTo() != null) {
return psUtil.get(
- ctx.getDb(),
ctx.getNotes(),
new PatchSet.Id(ctx.getChange().getId(), mailComment.getInReplyTo().key.patchSetId));
}
@@ -381,7 +369,7 @@ public class MailProcessor {
private Comment persistentCommentFromMailComment(
ChangeContext ctx, MailComment mailComment, PatchSet patchSetForComment)
- throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
+ throws UnprocessableEntityException, PatchListNotAvailableException {
String fileName;
// The patch set that this comment is based on is different if this
// comment was sent in reply to a comment on a previous patch set.
@@ -424,7 +412,7 @@ public class MailProcessor {
return "(" + numComments + (numComments > 1 ? " comments)" : " comment)");
}
- private Set<String> existingMessageIds(ChangeData cd) throws OrmException {
+ private Set<String> existingMessageIds(ChangeData cd) {
Set<String> existingMessageIds = new HashSet<>();
cd.messages().stream()
.forEach(
diff --git a/java/com/google/gerrit/server/mail/send/AbandonedSender.java b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
index 05dd54250c..1017965d2e 100644
--- a/java/com/google/gerrit/server/mail/send/AbandonedSender.java
+++ b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
@@ -14,11 +14,10 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -31,8 +30,7 @@ public class AbandonedSender extends ReplyToChangeSender {
@Inject
public AbandonedSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "abandon", ChangeEmail.newChangeData(ea, project, id));
}
diff --git a/java/com/google/gerrit/server/mail/send/AddKeySender.java b/java/com/google/gerrit/server/mail/send/AddKeySender.java
index 7701c1857f..6a4918cf36 100644
--- a/java/com/google/gerrit/server/mail/send/AddKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/AddKeySender.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.mail.send;
import com.google.common.base.Joiner;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/mail/send/AddReviewerSender.java b/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
index cb70106094..22abd9c38e 100644
--- a/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
@@ -14,10 +14,9 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -29,8 +28,7 @@ public class AddReviewerSender extends NewChangeSender {
@Inject
public AddReviewerSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, newChangeData(ea, project, id));
}
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 924ab2def9..55e2fd483d 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -15,10 +15,13 @@
package com.google.gerrit.server.mail.send;
import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+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.common.errors.EmailException;
+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;
@@ -43,9 +46,6 @@ import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
-import com.google.template.soy.data.SoyListData;
-import com.google.template.soy.data.SoyMapData;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.MessageFormat;
@@ -69,7 +69,7 @@ public abstract class ChangeEmail extends NotificationEmail {
protected static ChangeData newChangeData(
EmailArguments ea, Project.NameKey project, Change.Id id) {
- return ea.changeDataFactory.create(ea.db.get(), project, id);
+ return ea.changeDataFactory.create(project, id);
}
protected final Change change;
@@ -84,7 +84,7 @@ public abstract class ChangeEmail extends NotificationEmail {
protected Set<Account.Id> authors;
protected boolean emailOnlyAuthors;
- protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) throws OrmException {
+ protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) {
super(ea, mc, cd.change().getDest());
changeData = cd;
change = cd.change();
@@ -150,7 +150,7 @@ public abstract class ChangeEmail extends NotificationEmail {
if (patchSet == null) {
try {
patchSet = changeData.currentPatchSet();
- } catch (OrmException err) {
+ } catch (StorageException err) {
patchSet = null;
}
}
@@ -159,9 +159,8 @@ public abstract class ChangeEmail extends NotificationEmail {
setHeader(MailHeader.PATCH_SET.fieldName(), patchSet.getPatchSetId() + "");
if (patchSetInfo == null) {
try {
- patchSetInfo =
- args.patchSetInfoFactory.get(args.db.get(), changeData.notes(), patchSet.getId());
- } catch (PatchSetInfoNotAvailableException | OrmException err) {
+ patchSetInfo = args.patchSetInfoFactory.get(changeData.notes(), patchSet.getId());
+ } catch (PatchSetInfoNotAvailableException | StorageException err) {
patchSetInfo = null;
}
}
@@ -170,7 +169,7 @@ public abstract class ChangeEmail extends NotificationEmail {
try {
stars = changeData.stars();
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new EmailException("Failed to load stars for change " + change.getChangeId(), e);
}
@@ -185,14 +184,14 @@ public abstract class ChangeEmail extends NotificationEmail {
setChangeUrlHeader();
setCommitIdHeader();
- if (notify.ordinal() >= NotifyHandling.OWNER_REVIEWERS.ordinal()) {
+ if (notify.handling().compareTo(NotifyHandling.OWNER_REVIEWERS) >= 0) {
try {
addByEmail(
RecipientType.CC, changeData.reviewersByEmail().byState(ReviewerStateInternal.CC));
addByEmail(
RecipientType.CC,
changeData.reviewersByEmail().byState(ReviewerStateInternal.REVIEWER));
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new EmailException("Failed to add unregistered CCs " + change.getChangeId(), e);
}
}
@@ -295,10 +294,8 @@ public abstract class ChangeEmail extends NotificationEmail {
ps = patchSet;
} else {
try {
- ps =
- args.patchSetUtil.get(
- changeData.db(), changeData.notes(), new PatchSet.Id(change.getId(), patchSetId));
- } catch (OrmException e) {
+ ps = args.patchSetUtil.get(changeData.notes(), new PatchSet.Id(change.getId(), patchSetId));
+ } catch (StorageException e) {
throw new PatchListNotAvailableException("Failed to get patchSet", e);
}
}
@@ -327,7 +324,7 @@ public abstract class ChangeEmail extends NotificationEmail {
/** BCC any user who has starred this change. */
protected void bccStarredBy() {
- if (!NotifyHandling.ALL.equals(notify)) {
+ if (!NotifyHandling.ALL.equals(notify.handling())) {
return;
}
@@ -347,9 +344,8 @@ public abstract class ChangeEmail extends NotificationEmail {
}
@Override
- protected final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig)
- throws OrmException {
- if (!NotifyHandling.ALL.equals(notify)) {
+ protected final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig) {
+ if (!NotifyHandling.ALL.equals(notify.handling())) {
return new Watchers();
}
@@ -359,7 +355,8 @@ public abstract class ChangeEmail extends NotificationEmail {
/** Any user who has published comments on this change. */
protected void ccAllApprovals() {
- if (!NotifyHandling.ALL.equals(notify) && !NotifyHandling.OWNER_REVIEWERS.equals(notify)) {
+ if (!NotifyHandling.ALL.equals(notify.handling())
+ && !NotifyHandling.OWNER_REVIEWERS.equals(notify.handling())) {
return;
}
@@ -367,14 +364,15 @@ public abstract class ChangeEmail extends NotificationEmail {
for (Account.Id id : changeData.reviewers().all()) {
add(RecipientType.CC, id);
}
- } catch (OrmException err) {
+ } catch (StorageException err) {
logger.atWarning().withCause(err).log("Cannot CC users that reviewed updated change");
}
}
/** Users who were added as reviewers to this change. */
protected void ccExistingReviewers() {
- if (!NotifyHandling.ALL.equals(notify) && !NotifyHandling.OWNER_REVIEWERS.equals(notify)) {
+ if (!NotifyHandling.ALL.equals(notify.handling())
+ && !NotifyHandling.OWNER_REVIEWERS.equals(notify.handling())) {
return;
}
@@ -382,7 +380,7 @@ public abstract class ChangeEmail extends NotificationEmail {
for (Account.Id id : changeData.reviewers().byState(ReviewerStateInternal.REVIEWER)) {
add(RecipientType.CC, id);
}
- } catch (OrmException err) {
+ } catch (StorageException err) {
logger.atWarning().withCause(err).log("Cannot CC users that commented on updated change");
}
}
@@ -400,11 +398,7 @@ public abstract class ChangeEmail extends NotificationEmail {
return false;
}
try {
- args.permissionBackend
- .absentUser(to)
- .change(changeData)
- .database(args.db)
- .check(ChangePermission.READ);
+ args.permissionBackend.absentUser(to).change(changeData).check(ChangePermission.READ);
return true;
} catch (AuthException e) {
return false;
@@ -415,7 +409,7 @@ public abstract class ChangeEmail extends NotificationEmail {
protected Set<Account.Id> getAuthors() {
Set<Account.Id> authors = new HashSet<>();
- switch (notify) {
+ switch (notify.handling()) {
case NONE:
break;
case ALL:
@@ -512,7 +506,7 @@ public abstract class ChangeEmail extends NotificationEmail {
for (Account.Id who : changeData.reviewers().byState(state)) {
reviewers.add(getNameEmailFor(who));
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atWarning().withCause(e).log("Cannot get change reviewers");
}
return reviewers;
@@ -566,15 +560,15 @@ public abstract class ChangeEmail extends NotificationEmail {
}
/**
- * Generate a Soy list of maps representing each line of the unified diff. The line maps will have
- * a 'type' key which maps to one of 'common', 'add' or 'remove' and a 'text' key which maps to
- * the line's content.
+ * Generate a list of maps representing each line of the unified diff. The line maps will have a
+ * 'type' key which maps to one of 'common', 'add' or 'remove' and a 'text' key which maps to the
+ * line's content.
*/
- private SoyListData getDiffTemplateData() {
- SoyListData result = new SoyListData();
+ private ImmutableList<ImmutableMap<String, String>> getDiffTemplateData() {
+ ImmutableList.Builder<ImmutableMap<String, String>> result = ImmutableList.builder();
Splitter lineSplitter = Splitter.on(System.getProperty("line.separator"));
for (String diffLine : lineSplitter.split(getUnifiedDiff())) {
- SoyMapData lineData = new SoyMapData();
+ ImmutableMap.Builder<String, String> lineData = ImmutableMap.builder();
lineData.put("text", diffLine);
// Skip empty lines and lines that look like diff headers.
@@ -593,8 +587,8 @@ public abstract class ChangeEmail extends NotificationEmail {
break;
}
}
- result.add(lineData);
+ result.add(lineData.build());
}
- return result;
+ return result.build();
}
}
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index 90f43d8a69..7b11ce65a0 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -20,8 +20,9 @@ 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.common.errors.EmailException;
-import com.google.gerrit.common.errors.NoSuchEntityException;
+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;
@@ -41,7 +42,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -117,8 +117,7 @@ public class CommentSender extends ReplyToChangeSender {
CommentsUtil commentsUtil,
@GerritServerConfig Config cfg,
@Assisted Project.NameKey project,
- @Assisted Change.Id id)
- throws OrmException {
+ @Assisted Change.Id id) {
super(ea, "comment", newChangeData(ea, project, id));
this.commentsUtil = commentsUtil;
this.incomingEmailEnabled =
@@ -127,7 +126,7 @@ public class CommentSender extends ReplyToChangeSender {
this.replyToAddress = cfg.getString("sendemail", null, "replyToAddress");
}
- public void setComments(List<Comment> comments) throws OrmException {
+ public void setComments(List<Comment> comments) {
inlineComments = comments;
Set<String> paths = new HashSet<>();
@@ -151,10 +150,10 @@ public class CommentSender extends ReplyToChangeSender {
protected void init() throws EmailException {
super.init();
- if (notify.compareTo(NotifyHandling.OWNER_REVIEWERS) >= 0) {
+ if (notify.handling().compareTo(NotifyHandling.OWNER_REVIEWERS) >= 0) {
ccAllApprovals();
}
- if (notify.compareTo(NotifyHandling.ALL) >= 0) {
+ if (notify.handling().compareTo(NotifyHandling.ALL) >= 0) {
bccStarredBy();
includeWatchers(NotifyType.ALL_COMMENTS, !change.isWorkInProgress() && !change.isPrivate());
}
@@ -313,8 +312,8 @@ public class CommentSender extends ReplyToChangeSender {
Comment.Key key = new Comment.Key(child.parentUuid, child.key.filename, child.key.patchSetId);
try {
- return commentsUtil.getPublished(args.db.get(), changeData.notes(), key);
- } catch (OrmException e) {
+ return commentsUtil.getPublished(changeData.notes(), key);
+ } catch (StorageException e) {
logger.atWarning().log("Could not find the parent of this comment: %s", child);
return Optional.empty();
}
diff --git a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index fc9c14acdb..9895e07069 100644
--- a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -15,7 +15,8 @@
package com.google.gerrit.server.mail.send;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+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;
@@ -24,7 +25,6 @@ import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.stream.StreamSupport;
@@ -44,8 +44,7 @@ public class CreateChangeSender extends NewChangeSender {
EmailArguments ea,
PermissionBackend permissionBackend,
@Assisted Project.NameKey project,
- @Assisted Change.Id id)
- throws OrmException {
+ @Assisted Change.Id id) {
super(ea, newChangeData(ea, project, id));
this.permissionBackend = permissionBackend;
}
@@ -66,7 +65,7 @@ public class CreateChangeSender extends NewChangeSender {
add(RecipientType.TO, matching.to);
add(RecipientType.CC, matching.cc);
add(RecipientType.BCC, matching.bcc);
- } catch (OrmException err) {
+ } catch (StorageException err) {
// Just don't CC everyone. Better to send a partial message to those
// we already have queued up then to fail deliver entirely to people
// who have a lower interest in the change.
diff --git a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
index 31e4e77f82..39e21a52a2 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.mail.send;
import com.google.common.base.Joiner;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
index 576d506d2c..f941acc582 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
@@ -42,8 +41,7 @@ public class DeleteReviewerSender extends ReplyToChangeSender {
@Inject
public DeleteReviewerSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "deleteReviewer", newChangeData(ea, project, id));
}
diff --git a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
index 0c81293ba6..195d53df1e 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
@@ -14,11 +14,10 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -31,8 +30,7 @@ public class DeleteVoteSender extends ReplyToChangeSender {
@Inject
protected DeleteVoteSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "deleteVote", newChangeData(ea, project, id));
}
diff --git a/java/com/google/gerrit/server/mail/send/EmailArguments.java b/java/com/google/gerrit/server/mail/send/EmailArguments.java
index 41a464e3da..fe2f74b2da 100644
--- a/java/com/google/gerrit/server/mail/send/EmailArguments.java
+++ b/java/com/google/gerrit/server/mail/send/EmailArguments.java
@@ -14,16 +14,15 @@
package com.google.gerrit.server.mail.send;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.IdentifiedUser.GenericFactory;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.AllProjectsName;
@@ -75,7 +74,6 @@ public class EmailArguments {
final SitePaths site;
final ChangeQueryBuilder queryBuilder;
- final Provider<ReviewDb> db;
final ChangeData.Factory changeDataFactory;
final SoyTofu soyTofu;
final EmailSettings settings;
@@ -106,7 +104,6 @@ public class EmailArguments {
DynamicItem<UrlFormatter> urlFormatter,
AllProjectsName allProjectsName,
ChangeQueryBuilder queryBuilder,
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
@MailTemplates SoyTofu soyTofu,
EmailSettings settings,
@@ -136,7 +133,6 @@ public class EmailArguments {
this.urlFormatter = urlFormatter;
this.allProjectsName = allProjectsName;
this.queryBuilder = queryBuilder;
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.soyTofu = soyTofu;
this.settings = settings;
diff --git a/java/com/google/gerrit/server/mail/send/EmailSender.java b/java/com/google/gerrit/server/mail/send/EmailSender.java
index ce4964d3cd..9b3a1f737c 100644
--- a/java/com/google/gerrit/server/mail/send/EmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/EmailSender.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.mail.send;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.EmailHeader;
import java.util.Collection;
diff --git a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
index 07e165c95a..ca332ff275 100644
--- a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
+++ b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
index 99edc0402c..b5d384d730 100644
--- a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
+++ b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.mail.send;
import static java.util.Objects.requireNonNull;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.MailHeader;
diff --git a/java/com/google/gerrit/server/mail/send/MergedSender.java b/java/com/google/gerrit/server/mail/send/MergedSender.java
index 34f959c817..cbc4117e26 100644
--- a/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -19,13 +19,13 @@ 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.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -38,8 +38,8 @@ public class MergedSender extends ReplyToChangeSender {
private final LabelTypes labelTypes;
@Inject
- public MergedSender(EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ public MergedSender(
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "merged", newChangeData(ea, project, id));
labelTypes = changeData.getLabelTypes();
}
@@ -69,8 +69,7 @@ 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(
- args.db.get(), changeData.notes(), patchSet.getId(), null, null)) {
+ args.approvalsUtil.byPatchSet(changeData.notes(), patchSet.getId(), null, null)) {
LabelType lt = labelTypes.byLabel(ca.getLabelId());
if (lt == null) {
continue;
@@ -83,7 +82,7 @@ public class MergedSender extends ReplyToChangeSender {
}
return format("Approvals", pos) + format("Objections", neg);
- } catch (OrmException err) {
+ } catch (StorageException err) {
// Don't list the approvals
}
return "";
diff --git a/java/com/google/gerrit/server/mail/send/NewChangeSender.java b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
index 917094ef5d..a31596bdb0 100644
--- a/java/com/google/gerrit/server/mail/send/NewChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
@@ -14,12 +14,11 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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 com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -33,7 +32,7 @@ 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) throws OrmException {
+ protected NewChangeSender(EmailArguments ea, ChangeData cd) {
super(ea, "newchange", cd);
}
@@ -61,7 +60,7 @@ public abstract class NewChangeSender extends ChangeEmail {
setHeader("Message-ID", threadId);
setHeader("References", threadId);
- switch (notify) {
+ switch (notify.handling()) {
case NONE:
case OWNER:
break;
diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index be593ff9f2..9952ba6ff6 100644
--- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -17,7 +17,8 @@ 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.common.errors.EmailException;
+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;
@@ -25,7 +26,6 @@ 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 com.google.gwtorm.server.OrmException;
import java.util.HashMap;
import java.util.Map;
@@ -68,7 +68,7 @@ public abstract class NotificationEmail extends OutgoingEmail {
add(RecipientType.TO, matching.to);
add(RecipientType.CC, matching.cc);
add(RecipientType.BCC, matching.bcc);
- } catch (OrmException err) {
+ } catch (StorageException err) {
// Just don't CC everyone. Better to send a partial message to those
// we already have queued up then to fail deliver entirely to people
// who have a lower interest in the change.
@@ -77,8 +77,7 @@ public abstract class NotificationEmail extends OutgoingEmail {
}
/** Returns all watchers that are relevant */
- protected abstract Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig)
- throws OrmException;
+ protected abstract Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig);
/** Add users or email addresses to the TO, CC, or BCC list. */
protected void add(RecipientType type, Watchers.List list) {
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 664c1bf8a5..3bb710bb02 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -18,12 +18,9 @@ import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailSt
import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy.DISABLED;
import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
@@ -34,6 +31,7 @@ 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;
@@ -66,13 +64,12 @@ public abstract class OutgoingEmail {
private Address smtpFromAddress;
private StringBuilder textBody;
private StringBuilder htmlBody;
- private ListMultimap<RecipientType, Account.Id> accountsToNotify = ImmutableListMultimap.of();
protected Map<String, Object> soyContext;
protected Map<String, Object> soyContextEmailData;
protected List<String> footers;
protected final EmailArguments args;
protected Account.Id fromId;
- protected NotifyHandling notify = NotifyHandling.ALL;
+ protected NotifyResolver.Result notify = NotifyResolver.Result.all();
protected OutgoingEmail(EmailArguments ea, String mc) {
args = ea;
@@ -84,14 +81,10 @@ public abstract class OutgoingEmail {
fromId = id;
}
- public void setNotify(NotifyHandling notify) {
+ public void setNotify(NotifyResolver.Result notify) {
this.notify = requireNonNull(notify);
}
- public void setAccountsToNotify(ListMultimap<RecipientType, Account.Id> accountsToNotify) {
- this.accountsToNotify = requireNonNull(accountsToNotify);
- }
-
/**
* Format and enqueue the message for delivery.
*
@@ -106,7 +99,7 @@ public abstract class OutgoingEmail {
return;
}
- if (NotifyHandling.NONE.equals(notify) && accountsToNotify.isEmpty()) {
+ if (!notify.shouldNotify()) {
logger.atFine().log("Not sending '%s': Notify handling is NONE", messageClass);
return;
}
@@ -133,7 +126,7 @@ public abstract class OutgoingEmail {
// on their behalf to others.
//
add(RecipientType.CC, fromId);
- } else if (!accountsToNotify.containsValue(fromId) && rcptTo.remove(fromId)) {
+ } else if (!notify.accounts().containsValue(fromId) && rcptTo.remove(fromId)) {
// If they don't want a copy, but we queued one up anyway,
// drop them from the recipient lists.
//
@@ -253,8 +246,8 @@ public abstract class OutgoingEmail {
setHeader(FieldName.MESSAGE_ID, "");
setHeader(MailHeader.AUTO_SUBMITTED.fieldName(), "auto-generated");
- for (RecipientType recipientType : accountsToNotify.keySet()) {
- add(recipientType, accountsToNotify.get(recipientType));
+ for (RecipientType recipientType : notify.accounts().keySet()) {
+ add(recipientType, notify.accounts().get(recipientType));
}
setHeader(MailHeader.MESSAGE_TYPE.fieldName(), messageClass);
@@ -423,7 +416,7 @@ public abstract class OutgoingEmail {
return false;
}
- if ((accountsToNotify == null || accountsToNotify.isEmpty())
+ if (notify.accounts().isEmpty()
&& smtpRcptTo.size() == 1
&& rcptTo.size() == 1
&& rcptTo.contains(fromId)) {
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index 327deec98a..0a468b411c 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -35,7 +35,6 @@ import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.SingleGroupUser;
-import com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -62,8 +61,7 @@ public class ProjectWatch {
}
/** Returns all watchers that are relevant */
- public final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig)
- throws OrmException {
+ public final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig) {
Watchers matching = new Watchers();
Set<Account.Id> projectWatchers = new HashSet<>();
@@ -148,7 +146,7 @@ public class ProjectWatch {
}
}
- private void add(Watchers matching, NotifyConfig nc) throws OrmException, QueryParseException {
+ private void add(Watchers matching, NotifyConfig nc) throws QueryParseException {
for (GroupReference ref : nc.getGroups()) {
CurrentUser user = new SingleGroupUser(ref.getUUID());
if (filterMatch(user, nc.getFilter())) {
@@ -202,8 +200,7 @@ public class ProjectWatch {
Account.Id accountId,
ProjectWatchKey key,
Set<NotifyType> watchedTypes,
- NotifyType type)
- throws OrmException {
+ NotifyType type) {
IdentifiedUser user = args.identifiedUserFactory.create(accountId);
try {
@@ -221,8 +218,7 @@ public class ProjectWatch {
return false;
}
- private boolean filterMatch(CurrentUser user, String filter)
- throws OrmException, QueryParseException {
+ private boolean filterMatch(CurrentUser user, String filter) throws QueryParseException {
ChangeQueryBuilder qb;
Predicate<ChangeData> p = null;
diff --git a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
index 3bca00cf75..436736b560 100644
--- a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.mail.send;
import static java.util.Objects.requireNonNull;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.mail.Address;
import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
index 2398b82b3b..30bcdeb1d4 100644
--- a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
@@ -41,8 +40,7 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
@Inject
public ReplacePatchSetSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "newpatchset", newChangeData(ea, project, id));
}
@@ -63,7 +61,8 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
//
reviewers.remove(fromId);
}
- if (notify == NotifyHandling.ALL || notify == NotifyHandling.OWNER_REVIEWERS) {
+ if (notify.handling() == NotifyHandling.ALL
+ || notify.handling() == NotifyHandling.OWNER_REVIEWERS) {
add(RecipientType.TO, reviewers);
add(RecipientType.CC, extraCC);
}
diff --git a/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java b/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
index 61e9d1d1fa..960c3a84d9 100644
--- a/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
@@ -14,12 +14,11 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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;
-import com.google.gwtorm.server.OrmException;
/** Alert a user to a reply to a change, usually commentary made during review. */
public abstract class ReplyToChangeSender extends ChangeEmail {
@@ -27,7 +26,7 @@ public abstract class ReplyToChangeSender extends ChangeEmail {
T create(Project.NameKey project, Change.Id id);
}
- protected ReplyToChangeSender(EmailArguments ea, String mc, ChangeData cd) throws OrmException {
+ protected ReplyToChangeSender(EmailArguments ea, String mc, ChangeData cd) {
super(ea, mc, cd);
}
diff --git a/java/com/google/gerrit/server/mail/send/RestoredSender.java b/java/com/google/gerrit/server/mail/send/RestoredSender.java
index d7f8eb5293..0d998aa9b9 100644
--- a/java/com/google/gerrit/server/mail/send/RestoredSender.java
+++ b/java/com/google/gerrit/server/mail/send/RestoredSender.java
@@ -14,11 +14,10 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -31,8 +30,7 @@ public class RestoredSender extends ReplyToChangeSender {
@Inject
public RestoredSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "restore", ChangeEmail.newChangeData(ea, project, id));
}
diff --git a/java/com/google/gerrit/server/mail/send/RevertedSender.java b/java/com/google/gerrit/server/mail/send/RevertedSender.java
index 21703a33cf..48b5d9954f 100644
--- a/java/com/google/gerrit/server/mail/send/RevertedSender.java
+++ b/java/com/google/gerrit/server/mail/send/RevertedSender.java
@@ -14,11 +14,10 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -30,8 +29,7 @@ public class RevertedSender extends ReplyToChangeSender {
@Inject
public RevertedSender(
- EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
- throws OrmException {
+ EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
super(ea, "revert", ChangeEmail.newChangeData(ea, project, id));
}
diff --git a/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java b/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
index 9708b1b0b5..a120769f61 100644
--- a/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
+++ b/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
@@ -14,12 +14,11 @@
package com.google.gerrit.server.mail.send;
-import com.google.gerrit.common.errors.EmailException;
+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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -35,8 +34,7 @@ public class SetAssigneeSender extends ChangeEmail {
EmailArguments ea,
@Assisted Project.NameKey project,
@Assisted Change.Id id,
- @Assisted Account.Id assignee)
- throws OrmException {
+ @Assisted Account.Id assignee) {
super(ea, "setassignee", newChangeData(ea, project, id));
this.assignee = assignee;
}
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index a407cabe90..7207c0099c 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -21,7 +21,7 @@ import com.google.common.io.BaseEncoding;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.Version;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.EmailHeader;
import com.google.gerrit.server.config.ConfigUtil;
@@ -202,7 +202,7 @@ public class SmtpEmailSender implements EmailSender {
throw new EmailException("Sending email is disabled");
}
- StringBuffer rejected = new StringBuffer();
+ StringBuilder rejected = new StringBuilder();
try {
final SMTPClient client = open();
try {
@@ -239,10 +239,11 @@ public class SmtpEmailSender implements EmailSender {
*/
throw new EmailException(
rejected
- + "Server "
- + smtpHost
- + " rejected DATA command: "
- + client.getReplyString());
+ .append("Server ")
+ .append(smtpHost)
+ .append(" rejected DATA command: ")
+ .append(client.getReplyString())
+ .toString());
}
render(messageDataWriter, callerHeaders, textBody, htmlBody);
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 72f1ba6329..1c6057d6ff 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -17,24 +17,22 @@ package com.google.gerrit.server.notedb;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import static java.util.Objects.requireNonNull;
-import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
+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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -44,92 +42,84 @@ import org.eclipse.jgit.lib.Repository;
public abstract class AbstractChangeNotes<T> {
@VisibleForTesting
@Singleton
+ @UsedAt(UsedAt.Project.PLUGIN_CHECKS)
public static class Args {
- final GitRepositoryManager repoManager;
- final NotesMigration migration;
- final AllUsersName allUsers;
- final ChangeNoteJson changeNoteJson;
- final LegacyChangeNoteRead legacyChangeNoteRead;
- final NoteDbMetrics metrics;
- final Provider<ReviewDb> db;
+ // TODO(dborowitz): Some less smelly way of disabling NoteDb in tests.
+ public final AtomicBoolean failOnLoadForTest;
+ public final ChangeNoteJson changeNoteJson;
+ public final GitRepositoryManager repoManager;
+ public final AllUsersName allUsers;
+ public final LegacyChangeNoteRead legacyChangeNoteRead;
+ public final NoteDbMetrics metrics;
// Providers required to avoid dependency cycles.
- // ChangeRebuilder -> ChangeNotes.Factory -> Args
- final Provider<ChangeRebuilder> rebuilder;
-
// ChangeNoteCache -> Args
- final Provider<ChangeNotesCache> cache;
+ public final Provider<ChangeNotesCache> cache;
@Inject
Args(
GitRepositoryManager repoManager,
- NotesMigration migration,
AllUsersName allUsers,
ChangeNoteJson changeNoteJson,
LegacyChangeNoteRead legacyChangeNoteRead,
NoteDbMetrics metrics,
- Provider<ReviewDb> db,
- Provider<ChangeRebuilder> rebuilder,
Provider<ChangeNotesCache> cache) {
+ this.failOnLoadForTest = new AtomicBoolean();
this.repoManager = repoManager;
- this.migration = migration;
this.allUsers = allUsers;
this.legacyChangeNoteRead = legacyChangeNoteRead;
this.changeNoteJson = changeNoteJson;
this.metrics = metrics;
- this.db = db;
- this.rebuilder = rebuilder;
this.cache = cache;
}
}
- @AutoValue
- public abstract static class LoadHandle implements AutoCloseable {
- public static LoadHandle create(ChangeNotesRevWalk walk, ObjectId id) {
+ public static class LoadHandle implements AutoCloseable {
+ private final Repository repo;
+ private final ObjectId id;
+ private ChangeNotesRevWalk rw;
+
+ private LoadHandle(Repository repo, @Nullable ObjectId id) {
+ this.repo = requireNonNull(repo);
+
if (ObjectId.zeroId().equals(id)) {
id = null;
} else if (id != null) {
id = id.copy();
}
- return new AutoValue_AbstractChangeNotes_LoadHandle(requireNonNull(walk), id);
+ this.id = id;
}
- public static LoadHandle missing() {
- return new AutoValue_AbstractChangeNotes_LoadHandle(null, null);
+ public ChangeNotesRevWalk walk() {
+ if (rw == null) {
+ rw = ChangeNotesCommit.newRevWalk(repo);
+ }
+ return rw;
}
@Nullable
- public abstract ChangeNotesRevWalk walk();
-
- @Nullable
- public abstract ObjectId id();
+ public ObjectId id() {
+ return id;
+ }
@Override
public void close() {
- if (walk() != null) {
- walk().close();
+ if (rw != null) {
+ rw.close();
}
}
}
protected final Args args;
- protected final PrimaryStorage primaryStorage;
- protected final boolean autoRebuild;
private final Change.Id changeId;
private ObjectId revision;
private boolean loaded;
- AbstractChangeNotes(
- Args args, Change.Id changeId, @Nullable PrimaryStorage primaryStorage, boolean autoRebuild) {
+ protected AbstractChangeNotes(Args args, Change.Id changeId) {
this.args = requireNonNull(args);
this.changeId = requireNonNull(changeId);
- this.primaryStorage = primaryStorage;
- this.autoRebuild =
- primaryStorage == PrimaryStorage.REVIEW_DB
- && !args.migration.disableChangeReviewDb()
- && autoRebuild;
}
public Change.Id getChangeId() {
@@ -141,40 +131,24 @@ public abstract class AbstractChangeNotes<T> {
return revision;
}
- public T load() throws OrmException {
+ public T load() {
if (loaded) {
return self();
}
- boolean read = args.migration.readChanges();
- if (!read && primaryStorage == PrimaryStorage.NOTE_DB) {
- throw new OrmException("NoteDb is required to read change " + changeId);
- }
- boolean readOrWrite = read || args.migration.rawWriteChangesSetting();
- if (!readOrWrite) {
- // Don't even open the repo if we neither write to nor read from NoteDb. It's possible that
- // there is some garbage in the noteDbState field and/or the repo, but at this point NoteDb is
- // completely off so it's none of our business.
- loadDefaults();
- return self();
- }
- if (args.migration.failOnLoadForTest()) {
- throw new OrmException("Reading from NoteDb is disabled");
+ if (args.failOnLoadForTest.get()) {
+ throw new StorageException("Reading from NoteDb is disabled");
}
try (Timer1.Context 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.
LoadHandle handle = openHandle(repo)) {
- if (read) {
- revision = handle.id();
- onLoad(handle);
- } else {
- loadDefaults();
- }
+ revision = handle.id();
+ onLoad(handle);
loaded = true;
} catch (ConfigInvalidException | IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
return self();
}
@@ -199,25 +173,23 @@ public abstract class AbstractChangeNotes<T> {
}
protected LoadHandle openHandle(Repository repo, ObjectId id) {
- return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), id);
+ return new LoadHandle(repo, id);
}
- public T reload() throws NoSuchChangeException, OrmException {
+ public T reload() {
loaded = false;
return load();
}
- public ObjectId loadRevision() throws OrmException {
+ public ObjectId loadRevision() {
if (loaded) {
return getRevision();
- } else if (!args.migration.readChanges()) {
- return null;
}
try (Repository repo = args.repoManager.openRepository(getProjectName())) {
Ref ref = repo.getRefDatabase().exactRef(getRefName());
return ref != null ? ref.getObjectId() : null;
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
index 3b98938156..daef7e7a3f 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
@@ -27,12 +27,9 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
-import java.sql.Timestamp;
import java.util.Date;
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;
@@ -44,13 +41,11 @@ import org.eclipse.jgit.revwalk.RevWalk;
public abstract class AbstractChangeUpdate {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- protected final NotesMigration migration;
protected final ChangeNoteUtil noteUtil;
protected final Account.Id accountId;
protected final Account.Id realAccountId;
protected final PersonIdent authorIdent;
protected final Date when;
- private final long readOnlySkewMs;
@Nullable private final ChangeNotes notes;
private final Change change;
@@ -61,14 +56,11 @@ public abstract class AbstractChangeUpdate {
protected boolean rootOnly;
protected AbstractChangeUpdate(
- Config cfg,
- NotesMigration migration,
ChangeNotes notes,
CurrentUser user,
PersonIdent serverIdent,
ChangeNoteUtil noteUtil,
Date when) {
- this.migration = migration;
this.noteUtil = noteUtil;
this.serverIdent = new PersonIdent(serverIdent, when);
this.notes = notes;
@@ -78,12 +70,9 @@ public abstract class AbstractChangeUpdate {
this.realAccountId = realAccountId != null ? realAccountId : accountId;
this.authorIdent = ident(noteUtil, serverIdent, user, when);
this.when = when;
- this.readOnlySkewMs = NoteDbChangeState.getReadOnlySkew(cfg);
}
protected AbstractChangeUpdate(
- Config cfg,
- NotesMigration migration,
ChangeNoteUtil noteUtil,
PersonIdent serverIdent,
@Nullable ChangeNotes notes,
@@ -95,7 +84,6 @@ public abstract class AbstractChangeUpdate {
checkArgument(
(notes != null && change == null) || (notes == null && change != null),
"exactly one of notes or change required");
- this.migration = migration;
this.noteUtil = noteUtil;
this.serverIdent = new PersonIdent(serverIdent, when);
this.notes = notes;
@@ -104,7 +92,6 @@ public abstract class AbstractChangeUpdate {
this.realAccountId = realAccountId;
this.authorIdent = authorIdent;
this.when = when;
- this.readOnlySkewMs = NoteDbChangeState.getReadOnlySkew(cfg);
}
private static void checkUserType(CurrentUser user) {
@@ -214,21 +201,14 @@ public abstract class AbstractChangeUpdate {
* @return commit ID produced by inserting this update's commit, or null if this update is a no-op
* and should be skipped. The zero ID is a valid return value, and indicates the ref should be
* deleted.
- * @throws OrmException if a Gerrit-level error occurred.
* @throws IOException if a lower-level error occurred.
*/
- final ObjectId apply(RevWalk rw, ObjectInserter ins, ObjectId curr)
- throws OrmException, IOException {
+ final ObjectId apply(RevWalk rw, ObjectInserter ins, ObjectId curr) throws IOException {
if (isEmpty()) {
return null;
}
- // Allow this method to proceed even if migration.failChangeWrites() = true.
- // This may be used by an auto-rebuilding step that the caller does not plan
- // to actually store.
-
checkArgument(rw.getObjectReader().getCreatedFromInserter() == ins);
- checkNotReadOnly();
logger.atFinest().log(
"%s for change %s of project %s in %s (NoteDb)",
@@ -257,18 +237,6 @@ public abstract class AbstractChangeUpdate {
return result;
}
- protected void checkNotReadOnly() throws OrmException {
- ChangeNotes notes = getNotes();
- if (notes == null) {
- // Can only happen during ChangeRebuilder, which will never include a read-only lease.
- return;
- }
- Timestamp until = notes.getReadOnlyUntil();
- if (until != null && NoteDbChangeState.timeForReadOnlyCheck(readOnlySkewMs).before(until)) {
- throw new OrmException("change " + notes.getChangeId() + " is read-only until " + until);
- }
- }
-
/**
* Create a commit containing the contents of this update.
*
@@ -279,11 +247,10 @@ public abstract class AbstractChangeUpdate {
* indicates to the caller that it should be copied from the parent commit. To indicate that
* this update is a no-op (but this could not be determined by {@link #isEmpty()}), return the
* sentinel {@link #NO_OP_UPDATE}.
- * @throws OrmException if a Gerrit-level error occurred.
* @throws IOException if a lower-level error occurred.
*/
protected abstract CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
- throws OrmException, IOException;
+ throws IOException;
protected static final CommitBuilder NO_OP_UPDATE = new CommitBuilder();
diff --git a/java/com/google/gerrit/server/notedb/ChangeBundle.java b/java/com/google/gerrit/server/notedb/ChangeBundle.java
deleted file mode 100644
index 058e5e5785..0000000000
--- a/java/com/google/gerrit/server/notedb/ChangeBundle.java
+++ /dev/null
@@ -1,973 +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.server.notedb;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB;
-import static com.google.gerrit.server.util.time.TimeUtil.truncateToSecond;
-import static java.util.Comparator.comparing;
-import static java.util.Comparator.naturalOrder;
-import static java.util.Comparator.nullsFirst;
-import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Strings;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Ordering;
-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.Change;
-import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSet.Id;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.server.OrmException;
-import java.lang.reflect.Field;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * A bundle of all entities rooted at a single {@link Change} entity.
- *
- * <p>See the {@link Change} Javadoc for a depiction of this tree. Bundles may be compared using
- * {@link #differencesFrom(ChangeBundle)}, which normalizes out the minor implementation differences
- * between ReviewDb and NoteDb.
- */
-public class ChangeBundle {
- public enum Source {
- REVIEW_DB,
- NOTE_DB;
- }
-
- public static ChangeBundle fromNotes(CommentsUtil commentsUtil, ChangeNotes notes)
- throws OrmException {
- return new ChangeBundle(
- notes.getChange(),
- notes.getChangeMessages(),
- notes.getPatchSets().values(),
- notes.getApprovals().values(),
- Iterables.concat(
- CommentsUtil.toPatchLineComments(
- notes.getChangeId(),
- PatchLineComment.Status.DRAFT,
- commentsUtil.draftByChange(null, notes)),
- CommentsUtil.toPatchLineComments(
- notes.getChangeId(),
- PatchLineComment.Status.PUBLISHED,
- commentsUtil.publishedByChange(null, notes))),
- notes.getReviewers(),
- Source.NOTE_DB);
- }
-
- private static ImmutableSortedMap<ChangeMessage.Key, ChangeMessage> changeMessageMap(
- Collection<ChangeMessage> in) {
- return in.stream()
- .collect(
- toImmutableSortedMap(
- comparing((ChangeMessage.Key k) -> k.getParentKey().get())
- .thenComparing(k -> k.get()),
- cm -> cm.getKey(),
- cm -> cm));
- }
-
- // Unlike the *Map comparators, which are intended to make key lists diffable,
- // this comparator sorts first on timestamp, then on every other field.
- private static final Comparator<ChangeMessage> CHANGE_MESSAGE_COMPARATOR =
- comparing(ChangeMessage::getWrittenOn)
- .thenComparing(m -> m.getKey().getParentKey().get())
- .thenComparing(
- m -> m.getPatchSetId() != null ? m.getPatchSetId().get() : null,
- nullsFirst(naturalOrder()))
- .thenComparing(ChangeMessage::getAuthor, intKeyOrdering())
- .thenComparing(ChangeMessage::getMessage, nullsFirst(naturalOrder()));
-
- private static ImmutableList<ChangeMessage> changeMessageList(Iterable<ChangeMessage> in) {
- return Streams.stream(in).sorted(CHANGE_MESSAGE_COMPARATOR).collect(toImmutableList());
- }
-
- private static ImmutableSortedMap<Id, PatchSet> patchSetMap(Iterable<PatchSet> in) {
- return Streams.stream(in)
- .collect(toImmutableSortedMap(patchSetIdComparator(), PatchSet::getId, ps -> ps));
- }
-
- private static ImmutableSortedMap<PatchSetApproval.Key, PatchSetApproval> patchSetApprovalMap(
- Iterable<PatchSetApproval> in) {
- return Streams.stream(in)
- .collect(
- toImmutableSortedMap(
- comparing(PatchSetApproval.Key::getParentKey, patchSetIdComparator())
- .thenComparing(PatchSetApproval.Key::getAccountId, intKeyOrdering())
- .thenComparing(PatchSetApproval.Key::getLabelId),
- PatchSetApproval::getKey,
- a -> a));
- }
-
- private static ImmutableSortedMap<PatchLineComment.Key, PatchLineComment> patchLineCommentMap(
- Iterable<PatchLineComment> in) {
- return Streams.stream(in)
- .collect(
- toImmutableSortedMap(
- comparing(
- (PatchLineComment.Key k) -> k.getParentKey().getParentKey(),
- patchSetIdComparator())
- .thenComparing(PatchLineComment.Key::getParentKey)
- .thenComparing(PatchLineComment.Key::get),
- PatchLineComment::getKey,
- c -> c));
- }
-
- private static Comparator<PatchSet.Id> patchSetIdComparator() {
- return comparing((PatchSet.Id id) -> id.getParentKey().get()).thenComparing(id -> id.get());
- }
-
- static {
- // Initialization-time checks that the column set hasn't changed since the
- // last time this file was updated.
- checkColumns(Change.Id.class, 1);
-
- checkColumns(
- Change.class, 1, 2, 3, 4, 5, 7, 8, 10, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 101);
- checkColumns(ChangeMessage.Key.class, 1, 2);
- checkColumns(ChangeMessage.class, 1, 2, 3, 4, 5, 6, 7);
- checkColumns(PatchSet.Id.class, 1, 2);
- checkColumns(PatchSet.class, 1, 2, 3, 4, 6, 8, 9);
- checkColumns(PatchSetApproval.Key.class, 1, 2, 3);
- checkColumns(PatchSetApproval.class, 1, 2, 3, 6, 7, 8);
- checkColumns(PatchLineComment.Key.class, 1, 2);
- checkColumns(PatchLineComment.class, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
- }
-
- private final Change change;
- private final ImmutableList<ChangeMessage> changeMessages;
- private final ImmutableSortedMap<PatchSet.Id, PatchSet> patchSets;
- private final ImmutableMap<PatchSetApproval.Key, PatchSetApproval> patchSetApprovals;
- private final ImmutableMap<PatchLineComment.Key, PatchLineComment> patchLineComments;
- private final ReviewerSet reviewers;
- private final Source source;
-
- public ChangeBundle(
- Change change,
- Iterable<ChangeMessage> changeMessages,
- Iterable<PatchSet> patchSets,
- Iterable<PatchSetApproval> patchSetApprovals,
- Iterable<PatchLineComment> patchLineComments,
- ReviewerSet reviewers,
- Source source) {
- this.change = requireNonNull(change);
- this.changeMessages = changeMessageList(changeMessages);
- this.patchSets = ImmutableSortedMap.copyOfSorted(patchSetMap(patchSets));
- this.patchSetApprovals = ImmutableMap.copyOf(patchSetApprovalMap(patchSetApprovals));
- this.patchLineComments = ImmutableMap.copyOf(patchLineCommentMap(patchLineComments));
- this.reviewers = requireNonNull(reviewers);
- this.source = requireNonNull(source);
-
- for (ChangeMessage m : this.changeMessages) {
- checkArgument(m.getKey().getParentKey().equals(change.getId()));
- }
- for (PatchSet.Id id : this.patchSets.keySet()) {
- checkArgument(id.getParentKey().equals(change.getId()));
- }
- for (PatchSetApproval.Key k : this.patchSetApprovals.keySet()) {
- checkArgument(k.getParentKey().getParentKey().equals(change.getId()));
- }
- for (PatchLineComment.Key k : this.patchLineComments.keySet()) {
- checkArgument(k.getParentKey().getParentKey().getParentKey().equals(change.getId()));
- }
- }
-
- public Change getChange() {
- return change;
- }
-
- public ImmutableCollection<ChangeMessage> getChangeMessages() {
- return changeMessages;
- }
-
- public ImmutableCollection<PatchSet> getPatchSets() {
- return patchSets.values();
- }
-
- public ImmutableCollection<PatchSetApproval> getPatchSetApprovals() {
- return patchSetApprovals.values();
- }
-
- public ImmutableCollection<PatchLineComment> getPatchLineComments() {
- return patchLineComments.values();
- }
-
- public ReviewerSet getReviewers() {
- return reviewers;
- }
-
- public Source getSource() {
- return source;
- }
-
- public ImmutableList<String> differencesFrom(ChangeBundle o) {
- List<String> diffs = new ArrayList<>();
- diffChanges(diffs, this, o);
- diffChangeMessages(diffs, this, o);
- diffPatchSets(diffs, this, o);
- diffPatchSetApprovals(diffs, this, o);
- diffReviewers(diffs, this, o);
- diffPatchLineComments(diffs, this, o);
- return ImmutableList.copyOf(diffs);
- }
-
- private Timestamp getFirstPatchSetTime() {
- if (patchSets.isEmpty()) {
- return change.getCreatedOn();
- }
- return patchSets.firstEntry().getValue().getCreatedOn();
- }
-
- private Timestamp getLatestTimestamp() {
- Ordering<Timestamp> o = Ordering.natural().nullsFirst();
- Timestamp ts = null;
- for (ChangeMessage cm : filterChangeMessages()) {
- ts = o.max(ts, cm.getWrittenOn());
- }
- for (PatchSet ps : getPatchSets()) {
- ts = o.max(ts, ps.getCreatedOn());
- }
- for (PatchSetApproval psa : filterPatchSetApprovals().values()) {
- ts = o.max(ts, psa.getGranted());
- }
- for (PatchLineComment plc : filterPatchLineComments().values()) {
- // Ignore draft comments, as they do not show up in the change meta graph.
- if (plc.getStatus() != PatchLineComment.Status.DRAFT) {
- ts = o.max(ts, plc.getWrittenOn());
- }
- }
- return firstNonNull(ts, change.getLastUpdatedOn());
- }
-
- private Map<PatchSetApproval.Key, PatchSetApproval> filterPatchSetApprovals() {
- return limitToValidPatchSets(patchSetApprovals, PatchSetApproval.Key::getParentKey);
- }
-
- private Map<PatchLineComment.Key, PatchLineComment> filterPatchLineComments() {
- return limitToValidPatchSets(patchLineComments, k -> k.getParentKey().getParentKey());
- }
-
- private <K, V> Map<K, V> limitToValidPatchSets(Map<K, V> in, Function<K, PatchSet.Id> func) {
- return Maps.filterKeys(in, Predicates.compose(validPatchSetPredicate(), func));
- }
-
- private Predicate<PatchSet.Id> validPatchSetPredicate() {
- return patchSets::containsKey;
- }
-
- private Collection<ChangeMessage> filterChangeMessages() {
- final Predicate<PatchSet.Id> validPatchSet = validPatchSetPredicate();
- return Collections2.filter(
- changeMessages,
- m -> {
- PatchSet.Id psId = m.getPatchSetId();
- if (psId == null) {
- return true;
- }
- return validPatchSet.apply(psId);
- });
- }
-
- private static void diffChanges(List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
- Change a = bundleA.change;
- Change b = bundleB.change;
- String desc = a.getId().equals(b.getId()) ? describe(a.getId()) : "Changes";
-
- boolean excludeCreatedOn = false;
- boolean excludeCurrentPatchSetId = false;
- boolean excludeTopic = false;
- Timestamp aCreated = a.getCreatedOn();
- Timestamp bCreated = b.getCreatedOn();
- Timestamp aUpdated = a.getLastUpdatedOn();
- Timestamp bUpdated = b.getLastUpdatedOn();
-
- boolean excludeSubject = false;
- boolean excludeOrigSubj = false;
- // Subject is not technically a nullable field, but we observed some null
- // subjects in the wild on googlesource.com, so treat null as empty.
- String aSubj = Strings.nullToEmpty(a.getSubject());
- String bSubj = Strings.nullToEmpty(b.getSubject());
-
- // Allow created timestamp in NoteDb to be any of:
- // - The created timestamp of the change.
- // - The timestamp of the first remaining patch set.
- // - The last updated timestamp, if it is less than the created timestamp.
- //
- // Ignore subject if the NoteDb subject starts with the ReviewDb subject.
- // The NoteDb subject is read directly from the commit, whereas the ReviewDb
- // subject historically may have been truncated to fit in a SQL varchar
- // column.
- //
- // Ignore original subject on the ReviewDb side when comparing to NoteDb.
- // This field may have any number of values:
- // - It may be null, if the change has had no new patch sets pushed since
- // migrating to schema 103.
- // - It may match the first patch set subject, if the change was created
- // after migrating to schema 103.
- // - It may match the subject of the first patch set that was pushed after
- // the migration to schema 103, even though that is neither the subject
- // of the first patch set nor the subject of the last patch set. (See
- // Change#setCurrentPatchSet as of 43b10f86 for this behavior.) This
- // subject of an intermediate patch set is not available to the
- // ChangeBundle; we would have to get the subject from the repo, which is
- // inconvenient at this point.
- //
- // Ignore original subject on the ReviewDb side if it equals the subject of
- // the current patch set.
- //
- // For all of the above subject comparisons, first trim any leading spaces
- // from the NoteDb strings. (We actually do represent the leading spaces
- // faithfully during conversion, but JGit's FooterLine parser trims them
- // when reading.)
- //
- // Ignore empty topic on the ReviewDb side if it is null on the NoteDb side.
- //
- // Ignore currentPatchSetId on NoteDb side if ReviewDb does not point to a
- // valid patch set.
- //
- // Use max timestamp of all ReviewDb entities when comparing with NoteDb.
- if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
- boolean createdOnMatchesFirstPs =
- !timestampsDiffer(bundleA, bundleA.getFirstPatchSetTime(), bundleB, bCreated);
- boolean createdOnMatchesLastUpdatedOn =
- !timestampsDiffer(bundleA, aUpdated, bundleB, bCreated);
- boolean createdAfterUpdated = aCreated.compareTo(aUpdated) > 0;
- excludeCreatedOn =
- createdOnMatchesFirstPs || (createdAfterUpdated && createdOnMatchesLastUpdatedOn);
-
- aSubj = cleanReviewDbSubject(aSubj);
- bSubj = cleanNoteDbSubject(bSubj);
- excludeCurrentPatchSetId = !bundleA.validPatchSetPredicate().apply(a.currentPatchSetId());
- excludeSubject = bSubj.startsWith(aSubj) || excludeCurrentPatchSetId;
- excludeOrigSubj = true;
- String aTopic = trimOrNull(a.getTopic());
- excludeTopic =
- Objects.equals(aTopic, b.getTopic()) || ("".equals(aTopic) && b.getTopic() == null);
- aUpdated = bundleA.getLatestTimestamp();
- } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
- boolean createdOnMatchesFirstPs =
- !timestampsDiffer(bundleA, aCreated, bundleB, bundleB.getFirstPatchSetTime());
- boolean createdOnMatchesLastUpdatedOn =
- !timestampsDiffer(bundleA, aCreated, bundleB, bUpdated);
- boolean createdAfterUpdated = bCreated.compareTo(bUpdated) > 0;
- excludeCreatedOn =
- createdOnMatchesFirstPs || (createdAfterUpdated && createdOnMatchesLastUpdatedOn);
-
- aSubj = cleanNoteDbSubject(aSubj);
- bSubj = cleanReviewDbSubject(bSubj);
- excludeCurrentPatchSetId = !bundleB.validPatchSetPredicate().apply(b.currentPatchSetId());
- excludeSubject = aSubj.startsWith(bSubj) || excludeCurrentPatchSetId;
- excludeOrigSubj = true;
- String bTopic = trimOrNull(b.getTopic());
- excludeTopic =
- Objects.equals(bTopic, a.getTopic()) || (a.getTopic() == null && "".equals(bTopic));
- bUpdated = bundleB.getLatestTimestamp();
- }
-
- String subjectField = "subject";
- String updatedField = "lastUpdatedOn";
- List<String> exclude =
- Lists.newArrayList(subjectField, updatedField, "noteDbState", "rowVersion");
- if (excludeCreatedOn) {
- exclude.add("createdOn");
- }
- if (excludeCurrentPatchSetId) {
- exclude.add("currentPatchSetId");
- }
- if (excludeOrigSubj) {
- exclude.add("originalSubject");
- }
- if (excludeTopic) {
- exclude.add("topic");
- }
- diffColumnsExcluding(diffs, Change.class, desc, bundleA, a, bundleB, b, exclude);
-
- // Allow last updated timestamps to either be exactly equal (within slop),
- // or the NoteDb timestamp to be equal to the latest entity timestamp in the
- // whole ReviewDb bundle (within slop).
- if (timestampsDiffer(bundleA, a.getLastUpdatedOn(), bundleB, b.getLastUpdatedOn())) {
- diffTimestamps(
- diffs, desc, bundleA, aUpdated, bundleB, bUpdated, "effective last updated time");
- }
- if (!excludeSubject) {
- diffValues(diffs, desc, aSubj, bSubj, subjectField);
- }
- }
-
- private static String trimOrNull(String s) {
- return s != null ? CharMatcher.whitespace().trimFrom(s) : null;
- }
-
- private static String cleanReviewDbSubject(String s) {
- s = CharMatcher.is(' ').trimLeadingFrom(s);
-
- // An old JGit bug failed to extract subjects from commits with "\r\n"
- // terminators: https://bugs.eclipse.org/bugs/show_bug.cgi?id=400707
- // Changes created with this bug may have "\r\n" converted to "\r " and the
- // entire commit in the subject. The version of JGit used to read NoteDb
- // changes parses these subjects correctly, so we need to clean up old
- // ReviewDb subjects before comparing.
- int rn = s.indexOf("\r \r ");
- if (rn >= 0) {
- s = s.substring(0, rn);
- }
- return NoteDbUtil.sanitizeFooter(s);
- }
-
- private static String cleanNoteDbSubject(String s) {
- return NoteDbUtil.sanitizeFooter(s);
- }
-
- /**
- * Set of fields that must always exactly match between ReviewDb and NoteDb.
- *
- * <p>Used to limit the worst-case quadratic search when pairing off matching messages below.
- */
- @AutoValue
- abstract static class ChangeMessageCandidate {
- static ChangeMessageCandidate create(ChangeMessage cm) {
- return new AutoValue_ChangeBundle_ChangeMessageCandidate(
- cm.getAuthor(), cm.getMessage(), cm.getTag());
- }
-
- @Nullable
- abstract Account.Id author();
-
- @Nullable
- abstract String message();
-
- @Nullable
- abstract String tag();
-
- // Exclude:
- // - patch set, which may be null on ReviewDb side but not NoteDb
- // - UUID, which is always different between ReviewDb and NoteDb
- // - writtenOn, which is fuzzy
- }
-
- private static void diffChangeMessages(
- List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
- if (bundleA.source == REVIEW_DB && bundleB.source == REVIEW_DB) {
- // Both came from ReviewDb: check all fields exactly.
- Map<ChangeMessage.Key, ChangeMessage> as = changeMessageMap(bundleA.filterChangeMessages());
- Map<ChangeMessage.Key, ChangeMessage> bs = changeMessageMap(bundleB.filterChangeMessages());
-
- for (ChangeMessage.Key k : diffKeySets(diffs, as, bs)) {
- ChangeMessage a = as.get(k);
- ChangeMessage b = bs.get(k);
- String desc = describe(k);
- diffColumns(diffs, ChangeMessage.class, desc, bundleA, a, bundleB, b);
- }
- return;
- }
- Change.Id id = bundleA.getChange().getId();
- checkArgument(id.equals(bundleB.getChange().getId()));
-
- // Try to pair up matching ChangeMessages from each side, and succeed only
- // if both collections are empty at the end. Quadratic in the worst case,
- // but easy to reason about.
- List<ChangeMessage> as = new LinkedList<>(bundleA.filterChangeMessages());
-
- ListMultimap<ChangeMessageCandidate, ChangeMessage> bs = LinkedListMultimap.create();
- for (ChangeMessage b : bundleB.filterChangeMessages()) {
- bs.put(ChangeMessageCandidate.create(b), b);
- }
-
- Iterator<ChangeMessage> ait = as.iterator();
- A:
- while (ait.hasNext()) {
- ChangeMessage a = ait.next();
- Iterator<ChangeMessage> bit = bs.get(ChangeMessageCandidate.create(a)).iterator();
- while (bit.hasNext()) {
- ChangeMessage b = bit.next();
- if (changeMessagesMatch(bundleA, a, bundleB, b)) {
- ait.remove();
- bit.remove();
- continue A;
- }
- }
- }
-
- if (as.isEmpty() && bs.isEmpty()) {
- return;
- }
- StringBuilder sb =
- new StringBuilder("ChangeMessages differ for Change.Id ").append(id).append('\n');
- if (!as.isEmpty()) {
- sb.append("Only in A:");
- for (ChangeMessage cm : as) {
- sb.append("\n ").append(cm);
- }
- if (!bs.isEmpty()) {
- sb.append('\n');
- }
- }
- if (!bs.isEmpty()) {
- sb.append("Only in B:");
- bs.values().stream()
- .sorted(CHANGE_MESSAGE_COMPARATOR)
- .forEach(cm -> sb.append("\n ").append(cm));
- }
- diffs.add(sb.toString());
- }
-
- private static boolean changeMessagesMatch(
- ChangeBundle bundleA, ChangeMessage a, ChangeBundle bundleB, ChangeMessage b) {
- List<String> tempDiffs = new ArrayList<>();
- String temp = "temp";
-
- // ReviewDb allows timestamps before patch set was created, but NoteDb
- // truncates this to the patch set creation timestamp.
- Timestamp ta = a.getWrittenOn();
- Timestamp tb = b.getWrittenOn();
- PatchSet psa = bundleA.patchSets.get(a.getPatchSetId());
- PatchSet psb = bundleB.patchSets.get(b.getPatchSetId());
- boolean excludePatchSet = false;
- boolean excludeWrittenOn = false;
- if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
- excludePatchSet = a.getPatchSetId() == null;
- excludeWrittenOn =
- psa != null
- && psb != null
- && ta.before(psa.getCreatedOn())
- && tb.equals(psb.getCreatedOn());
- } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
- excludePatchSet = b.getPatchSetId() == null;
- excludeWrittenOn =
- psa != null
- && psb != null
- && tb.before(psb.getCreatedOn())
- && ta.equals(psa.getCreatedOn());
- }
-
- List<String> exclude = Lists.newArrayList("key");
- if (excludePatchSet) {
- exclude.add("patchset");
- }
- if (excludeWrittenOn) {
- exclude.add("writtenOn");
- }
-
- diffColumnsExcluding(tempDiffs, ChangeMessage.class, temp, bundleA, a, bundleB, b, exclude);
- return tempDiffs.isEmpty();
- }
-
- private static void diffPatchSets(
- List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
- Map<PatchSet.Id, PatchSet> as = bundleA.patchSets;
- Map<PatchSet.Id, PatchSet> bs = bundleB.patchSets;
- Optional<PatchSet.Id> minA = as.keySet().stream().min(intKeyOrdering());
- Optional<PatchSet.Id> minB = bs.keySet().stream().min(intKeyOrdering());
- Set<PatchSet.Id> ids = diffKeySets(diffs, as, bs);
-
- // Old versions of Gerrit had a bug that created patch sets during
- // rebase or submission with a createdOn timestamp earlier than the patch
- // set it was replacing. (In the cases I examined, it was equal to createdOn
- // for the change, but we're not counting on this exact behavior.)
- //
- // ChangeRebuilder ensures patch set events come out in order, but it's hard
- // to predict what the resulting timestamps would look like. So, completely
- // ignore the createdOn timestamps if both:
- // * ReviewDb timestamps are non-monotonic.
- // * NoteDb timestamps are monotonic.
- //
- // Allow the timestamp of the first patch set to match the creation time of
- // the change.
- boolean excludeAllCreatedOn = false;
- if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
- excludeAllCreatedOn = !createdOnIsMonotonic(as, ids) && createdOnIsMonotonic(bs, ids);
- } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
- excludeAllCreatedOn = createdOnIsMonotonic(as, ids) && !createdOnIsMonotonic(bs, ids);
- }
-
- for (PatchSet.Id id : ids) {
- PatchSet a = as.get(id);
- PatchSet b = bs.get(id);
- String desc = describe(id);
- String pushCertField = "pushCertificate";
-
- boolean excludeCreatedOn = excludeAllCreatedOn;
- boolean excludeDesc = false;
- if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
- excludeDesc = Objects.equals(trimOrNull(a.getDescription()), b.getDescription());
- excludeCreatedOn |=
- Optional.of(id).equals(minB) && b.getCreatedOn().equals(bundleB.change.getCreatedOn());
- } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
- excludeDesc = Objects.equals(a.getDescription(), trimOrNull(b.getDescription()));
- excludeCreatedOn |=
- Optional.of(id).equals(minA) && a.getCreatedOn().equals(bundleA.change.getCreatedOn());
- }
-
- List<String> exclude = Lists.newArrayList(pushCertField);
- if (excludeCreatedOn) {
- exclude.add("createdOn");
- }
- if (excludeDesc) {
- exclude.add("description");
- }
-
- diffColumnsExcluding(diffs, PatchSet.class, desc, bundleA, a, bundleB, b, exclude);
- diffValues(diffs, desc, trimPushCert(a), trimPushCert(b), pushCertField);
- }
- }
-
- private static String trimPushCert(PatchSet ps) {
- if (ps.getPushCertificate() == null) {
- return null;
- }
- return CharMatcher.is('\n').trimTrailingFrom(ps.getPushCertificate());
- }
-
- private static boolean createdOnIsMonotonic(
- Map<?, PatchSet> patchSets, Set<PatchSet.Id> limitToIds) {
- List<PatchSet> orderedById =
- patchSets.values().stream()
- .filter(ps -> limitToIds.contains(ps.getId()))
- .sorted(ChangeUtil.PS_ID_ORDER)
- .collect(toList());
- return Ordering.natural().onResultOf(PatchSet::getCreatedOn).isOrdered(orderedById);
- }
-
- private static void diffPatchSetApprovals(
- List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
- Map<PatchSetApproval.Key, PatchSetApproval> as = bundleA.filterPatchSetApprovals();
- Map<PatchSetApproval.Key, PatchSetApproval> bs = bundleB.filterPatchSetApprovals();
- for (PatchSetApproval.Key k : diffKeySets(diffs, as, bs)) {
- PatchSetApproval a = as.get(k);
- PatchSetApproval b = bs.get(k);
- String desc = describe(k);
-
- // ReviewDb allows timestamps before patch set was created, but NoteDb
- // truncates this to the patch set creation timestamp.
- //
- // ChangeRebuilder ensures all post-submit approvals happen after the
- // actual submit, so the timestamps may not line up. This shouldn't really
- // happen, because postSubmit shouldn't be set in ReviewDb until after the
- // change is submitted in ReviewDb, but you never know.
- //
- // Due to a quirk of PostReview, post-submit 0 votes might not have the
- // postSubmit bit set in ReviewDb. As these are only used for tombstone
- // purposes, ignore the postSubmit bit in NoteDb in this case.
- Timestamp ta = a.getGranted();
- Timestamp tb = b.getGranted();
- PatchSet psa = requireNonNull(bundleA.patchSets.get(a.getPatchSetId()));
- PatchSet psb = requireNonNull(bundleB.patchSets.get(b.getPatchSetId()));
- boolean excludeGranted = false;
- boolean excludePostSubmit = false;
- List<String> exclude = new ArrayList<>(1);
- if (bundleA.source == REVIEW_DB && bundleB.source == NOTE_DB) {
- excludeGranted =
- (ta.before(psa.getCreatedOn()) && tb.equals(psb.getCreatedOn()))
- || ta.compareTo(tb) < 0;
- excludePostSubmit = a.getValue() == 0 && b.isPostSubmit();
- } else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
- excludeGranted =
- (tb.before(psb.getCreatedOn()) && ta.equals(psa.getCreatedOn()))
- || (tb.compareTo(ta) < 0);
- excludePostSubmit = b.getValue() == 0 && a.isPostSubmit();
- }
-
- // Legacy submit approvals may or may not have tags associated with them,
- // depending on whether ChangeRebuilder happened to group them with the
- // status change.
- boolean excludeTag =
- bundleA.source != bundleB.source && a.isLegacySubmit() && b.isLegacySubmit();
-
- if (excludeGranted) {
- exclude.add("granted");
- }
- if (excludePostSubmit) {
- exclude.add("postSubmit");
- }
- if (excludeTag) {
- exclude.add("tag");
- }
-
- diffColumnsExcluding(diffs, PatchSetApproval.class, desc, bundleA, a, bundleB, b, exclude);
- }
- }
-
- private static void diffReviewers(
- List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
- diffSets(diffs, bundleA.reviewers.all(), bundleB.reviewers.all(), "reviewer");
- }
-
- private static void diffPatchLineComments(
- List<String> diffs, ChangeBundle bundleA, ChangeBundle bundleB) {
- Map<PatchLineComment.Key, PatchLineComment> as = bundleA.filterPatchLineComments();
- Map<PatchLineComment.Key, PatchLineComment> bs = bundleB.filterPatchLineComments();
- for (PatchLineComment.Key k : diffKeySets(diffs, as, bs)) {
- PatchLineComment a = as.get(k);
- PatchLineComment b = bs.get(k);
- String desc = describe(k);
- diffColumns(diffs, PatchLineComment.class, desc, bundleA, a, bundleB, b);
- }
- }
-
- private static <T> Set<T> diffKeySets(List<String> diffs, Map<T, ?> a, Map<T, ?> b) {
- if (a.isEmpty() && b.isEmpty()) {
- return a.keySet();
- }
- String clazz = keyClass((!a.isEmpty() ? a.keySet() : b.keySet()).iterator().next());
- return diffSets(diffs, a.keySet(), b.keySet(), clazz);
- }
-
- private static <T> Set<T> diffSets(List<String> diffs, Set<T> as, Set<T> bs, String desc) {
- if (as.isEmpty() && bs.isEmpty()) {
- return as;
- }
-
- Set<T> aNotB = Sets.difference(as, bs);
- Set<T> bNotA = Sets.difference(bs, as);
- if (aNotB.isEmpty() && bNotA.isEmpty()) {
- return as;
- }
- diffs.add(desc + " sets differ: " + aNotB + " only in A; " + bNotA + " only in B");
- return Sets.intersection(as, bs);
- }
-
- private static <T> void diffColumns(
- List<String> diffs,
- Class<T> clazz,
- String desc,
- ChangeBundle bundleA,
- T a,
- ChangeBundle bundleB,
- T b) {
- diffColumnsExcluding(diffs, clazz, desc, bundleA, a, bundleB, b);
- }
-
- private static <T> void diffColumnsExcluding(
- List<String> diffs,
- Class<T> clazz,
- String desc,
- ChangeBundle bundleA,
- T a,
- ChangeBundle bundleB,
- T b,
- String... exclude) {
- diffColumnsExcluding(diffs, clazz, desc, bundleA, a, bundleB, b, Arrays.asList(exclude));
- }
-
- private static <T> void diffColumnsExcluding(
- List<String> diffs,
- Class<T> clazz,
- String desc,
- ChangeBundle bundleA,
- T a,
- ChangeBundle bundleB,
- T b,
- Iterable<String> exclude) {
- Set<String> toExclude = Sets.newLinkedHashSet(exclude);
- for (Field f : clazz.getDeclaredFields()) {
- Column col = f.getAnnotation(Column.class);
- if (col == null) {
- continue;
- } else if (toExclude.remove(f.getName())) {
- continue;
- }
- f.setAccessible(true);
- try {
- if (Timestamp.class.isAssignableFrom(f.getType())) {
- diffTimestamps(diffs, desc, bundleA, a, bundleB, b, f.getName());
- } else {
- diffValues(diffs, desc, f.get(a), f.get(b), f.getName());
- }
- } catch (IllegalAccessException e) {
- throw new IllegalArgumentException(e);
- }
- }
- checkArgument(
- toExclude.isEmpty(),
- "requested columns to exclude not present in %s: %s",
- clazz.getSimpleName(),
- toExclude);
- }
-
- private static void diffTimestamps(
- List<String> diffs,
- String desc,
- ChangeBundle bundleA,
- Object a,
- ChangeBundle bundleB,
- Object b,
- String field) {
- checkArgument(a.getClass() == b.getClass());
- Class<?> clazz = a.getClass();
-
- Timestamp ta;
- Timestamp tb;
- try {
- Field f = clazz.getDeclaredField(field);
- checkArgument(f.getAnnotation(Column.class) != null);
- f.setAccessible(true);
- ta = (Timestamp) f.get(a);
- tb = (Timestamp) f.get(b);
- } catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
- throw new IllegalArgumentException(e);
- }
- diffTimestamps(diffs, desc, bundleA, ta, bundleB, tb, field);
- }
-
- private static void diffTimestamps(
- List<String> diffs,
- String desc,
- ChangeBundle bundleA,
- Timestamp ta,
- ChangeBundle bundleB,
- Timestamp tb,
- String fieldDesc) {
- if (bundleA.source == bundleB.source || ta == null || tb == null) {
- diffValues(diffs, desc, ta, tb, fieldDesc);
- } else if (bundleA.source == NOTE_DB) {
- diffTimestamps(diffs, desc, bundleA.getChange(), ta, bundleB.getChange(), tb, fieldDesc);
- } else {
- diffTimestamps(diffs, desc, bundleB.getChange(), tb, bundleA.getChange(), ta, fieldDesc);
- }
- }
-
- private static boolean timestampsDiffer(
- ChangeBundle bundleA, Timestamp ta, ChangeBundle bundleB, Timestamp tb) {
- List<String> tempDiffs = new ArrayList<>(1);
- diffTimestamps(tempDiffs, "temp", bundleA, ta, bundleB, tb, "temp");
- return !tempDiffs.isEmpty();
- }
-
- private static void diffTimestamps(
- List<String> diffs,
- String desc,
- Change changeFromNoteDb,
- Timestamp tsFromNoteDb,
- Change changeFromReviewDb,
- Timestamp tsFromReviewDb,
- String field) {
- // Because ChangeRebuilder may batch events together that are several
- // seconds apart, the timestamp in NoteDb may actually be several seconds
- // *earlier* than the timestamp in ReviewDb that it was converted from.
- checkArgument(
- tsFromNoteDb.equals(truncateToSecond(tsFromNoteDb)),
- "%s from NoteDb has non-rounded %s timestamp: %s",
- desc,
- field,
- tsFromNoteDb);
-
- if (tsFromReviewDb.before(changeFromReviewDb.getCreatedOn())
- && tsFromNoteDb.equals(changeFromNoteDb.getCreatedOn())) {
- // Timestamp predates change creation. These are truncated to change
- // creation time during NoteDb conversion, so allow this if the timestamp
- // in NoteDb matches the createdOn time in NoteDb.
- return;
- }
-
- long delta = tsFromReviewDb.getTime() - tsFromNoteDb.getTime();
- long max = ChangeRebuilderImpl.MAX_WINDOW_MS;
- if (delta < 0 || delta > max) {
- diffs.add(
- field
- + " differs for "
- + desc
- + " in NoteDb vs. ReviewDb:"
- + " {"
- + tsFromNoteDb
- + "} != {"
- + tsFromReviewDb
- + "}");
- }
- }
-
- private static void diffValues(
- List<String> diffs, String desc, Object va, Object vb, String name) {
- if (!Objects.equals(va, vb)) {
- diffs.add(name + " differs for " + desc + ": {" + va + "} != {" + vb + "}");
- }
- }
-
- private static String describe(Object key) {
- return keyClass(key) + " " + key;
- }
-
- private static String keyClass(Object obj) {
- Class<?> clazz = obj.getClass();
- String name = clazz.getSimpleName();
- checkArgument(name.endsWith("Key") || name.endsWith("Id"), "not an Id/Key class: %s", name);
- if (name.equals("Key") || name.equals("Id")) {
- return clazz.getEnclosingClass().getSimpleName() + "." + name;
- } else if (name.startsWith("AutoValue_")) {
- return name.substring(name.lastIndexOf('_') + 1);
- }
- return name;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName()
- + "{id="
- + change.getId()
- + ", ChangeMessage["
- + changeMessages.size()
- + "]"
- + ", PatchSet["
- + patchSets.size()
- + "]"
- + ", PatchSetApproval["
- + patchSetApprovals.size()
- + "]"
- + ", PatchLineComment["
- + patchLineComments.size()
- + "]"
- + "}";
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/ChangeBundleReader.java b/java/com/google/gerrit/server/notedb/ChangeBundleReader.java
deleted file mode 100644
index 3207c3bba8..0000000000
--- a/java/com/google/gerrit/server/notedb/ChangeBundleReader.java
+++ /dev/null
@@ -1,25 +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.server.notedb;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-
-public interface ChangeBundleReader {
- @Nullable
- ChangeBundle fromReviewDb(ReviewDb db, Change.Id id) throws OrmException;
-}
diff --git a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
index 06a0449ddc..8773d9b4f4 100644
--- a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -19,6 +19,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Comment;
@@ -28,8 +29,6 @@ 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.gerrit.server.config.GerritServerConfig;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -42,7 +41,6 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
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;
@@ -92,9 +90,7 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
@AssistedInject
private ChangeDraftUpdate(
- @GerritServerConfig Config cfg,
@GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
AllUsersName allUsers,
ChangeNoteUtil noteUtil,
@Assisted ChangeNotes notes,
@@ -102,25 +98,13 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
@Assisted("real") Account.Id realAccountId,
@Assisted PersonIdent authorIdent,
@Assisted Date when) {
- super(
- cfg,
- migration,
- noteUtil,
- serverIdent,
- notes,
- null,
- accountId,
- realAccountId,
- authorIdent,
- when);
+ super(noteUtil, serverIdent, notes, null, accountId, realAccountId, authorIdent, when);
this.draftsProject = allUsers;
}
@AssistedInject
private ChangeDraftUpdate(
- @GerritServerConfig Config cfg,
@GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
AllUsersName allUsers,
ChangeNoteUtil noteUtil,
@Assisted Change change,
@@ -128,17 +112,7 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
@Assisted("real") Account.Id realAccountId,
@Assisted PersonIdent authorIdent,
@Assisted Date when) {
- super(
- cfg,
- migration,
- noteUtil,
- serverIdent,
- null,
- change,
- accountId,
- realAccountId,
- authorIdent,
- when);
+ super(noteUtil, serverIdent, null, change, accountId, realAccountId, authorIdent, when);
this.draftsProject = allUsers;
}
@@ -158,7 +132,7 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
private CommitBuilder storeCommentsInNotes(
RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
- throws ConfigInvalidException, OrmException, IOException {
+ throws ConfigInvalidException, IOException {
RevisionNoteMap<ChangeRevisionNote> rnm = getRevisionNoteMap(rw, curr);
Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
@@ -207,20 +181,17 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
}
private RevisionNoteMap<ChangeRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
- throws ConfigInvalidException, OrmException, IOException {
- if (migration.readChanges()) {
- // If reading from changes is enabled, then the old DraftCommentNotes
- // already parsed the revision notes. We can reuse them as long as the ref
- // hasn't advanced.
- ChangeNotes changeNotes = getNotes();
- if (changeNotes != null) {
- DraftCommentNotes draftNotes = changeNotes.load().getDraftCommentNotes();
- if (draftNotes != null) {
- ObjectId idFromNotes = firstNonNull(draftNotes.getRevision(), ObjectId.zeroId());
- RevisionNoteMap<ChangeRevisionNote> rnm = draftNotes.getRevisionNoteMap();
- if (idFromNotes.equals(curr) && rnm != null) {
- return rnm;
- }
+ throws ConfigInvalidException, IOException {
+ // The old DraftCommentNotes already parsed the revision notes. We can reuse them as long as
+ // the ref hasn't advanced.
+ ChangeNotes changeNotes = getNotes();
+ if (changeNotes != null) {
+ DraftCommentNotes draftNotes = changeNotes.load().getDraftCommentNotes();
+ if (draftNotes != null) {
+ ObjectId idFromNotes = firstNonNull(draftNotes.getRevision(), ObjectId.zeroId());
+ RevisionNoteMap<ChangeRevisionNote> rnm = draftNotes.getRevisionNoteMap();
+ if (idFromNotes.equals(curr) && rnm != null) {
+ return rnm;
}
}
}
@@ -243,13 +214,13 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
@Override
protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
- throws OrmException, IOException {
+ throws IOException {
CommitBuilder cb = new CommitBuilder();
cb.setMessage("Update draft comments");
try {
return storeCommentsInNotes(rw, ins, curr, cb);
} catch (ConfigInvalidException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 8a0cabe894..628dfd2a73 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -39,7 +39,6 @@ public class ChangeNoteUtil {
public static final FooterKey FOOTER_PATCH_SET_DESCRIPTION =
new FooterKey("Patch-set-description");
public static final FooterKey FOOTER_PRIVATE = new FooterKey("Private");
- public static final FooterKey FOOTER_READ_ONLY_UNTIL = new FooterKey("Read-only-until");
public static final FooterKey FOOTER_REAL_USER = new FooterKey("Real-user");
public static final FooterKey FOOTER_STATUS = new FooterKey("Status");
public static final FooterKey FOOTER_SUBJECT = new FooterKey("Subject");
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index d548bf3fa9..a4021cb28c 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -17,7 +17,6 @@ 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.gerrit.server.notedb.NoteDbTable.CHANGES;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
@@ -28,18 +27,16 @@ 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.Iterators;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
-import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.metrics.Timer1;
+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;
@@ -51,33 +48,24 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
import com.google.gerrit.server.git.RefCache;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
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;
-import com.google.gwtorm.server.OrmException;
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;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -100,11 +88,6 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return new ConfigInvalidException("Change " + changeId + ": " + String.format(fmt, args));
}
- @Nullable
- public static Change readOneReviewDbChange(ReviewDb db, Change.Id id) throws OrmException {
- return ReviewDbUtil.unwrapDb(db).changes().get(id);
- }
-
@Singleton
public static class Factory {
private final Args args;
@@ -120,27 +103,16 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
this.projectCache = projectCache;
}
- public ChangeNotes createChecked(ReviewDb db, Change c) throws OrmException {
- return createChecked(db, c.getProject(), c.getId());
+ public ChangeNotes createChecked(Change c) {
+ return createChecked(c.getProject(), c.getId());
}
- public ChangeNotes createChecked(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException {
- Change change = readOneReviewDbChange(db, changeId);
- if (change == null) {
- if (!args.migration.readChanges()) {
- throw new NoSuchChangeException(changeId);
- }
- // Change isn't in ReviewDb, but its primary storage might be in NoteDb.
- // Prepopulate the change exists with proper noteDbState field.
- change = newNoteDbOnlyChange(project, changeId);
- } else if (!change.getProject().equals(project)) {
- throw new NoSuchChangeException(changeId);
- }
- return new ChangeNotes(args, change).load();
+ public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+ Change change = newChange(project, changeId);
+ return new ChangeNotes(args, change, true, null).load();
}
- public ChangeNotes createChecked(Change.Id changeId) throws OrmException {
+ public ChangeNotes createChecked(Change.Id changeId) {
InternalChangeQuery query = queryProvider.get().noFields();
List<ChangeData> changes = query.byLegacyChangeId(changeId);
if (changes.isEmpty()) {
@@ -153,43 +125,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return changes.get(0).notes();
}
- public static Change newNoteDbOnlyChange(Project.NameKey project, Change.Id changeId) {
- Change change =
- new Change(
- null, changeId, null, new Branch.NameKey(project, "INVALID_NOTE_DB_ONLY"), null);
- change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
- return change;
+ 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);
}
- private Change loadChangeFromDb(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException {
+ public ChangeNotes create(Project.NameKey project, Change.Id changeId) {
checkArgument(project != null, "project is required");
- Change change = readOneReviewDbChange(db, changeId);
-
- if (change == null) {
- if (args.migration.readChanges()) {
- return newNoteDbOnlyChange(project, changeId);
- }
- throw new NoSuchChangeException(changeId);
- }
- checkArgument(
- change.getProject().equals(project),
- "passed project %s when creating ChangeNotes for %s, but actual project is %s",
- project,
- changeId,
- change.getProject());
- return change;
- }
-
- public ChangeNotes create(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException {
- return new ChangeNotes(args, loadChangeFromDb(db, project, changeId)).load();
- }
-
- public ChangeNotes createWithAutoRebuildingDisabled(
- ReviewDb db, Project.NameKey project, Change.Id changeId) throws OrmException {
- return new ChangeNotes(args, loadChangeFromDb(db, project, changeId), true, false, null)
- .load();
+ return new ChangeNotes(args, newChange(project, changeId), true, null).load();
}
/**
@@ -200,229 +143,110 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
* @return change notes
*/
public ChangeNotes createFromIndexedChange(Change change) {
- return new ChangeNotes(args, change);
+ return new ChangeNotes(args, change, true, null);
}
- public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist)
- throws OrmException {
- return new ChangeNotes(args, change, shouldExist, false, null).load();
+ public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist) {
+ return new ChangeNotes(args, change, shouldExist, null).load();
}
- public ChangeNotes createWithAutoRebuildingDisabled(Change change, RefCache refs)
- throws OrmException {
- return new ChangeNotes(args, change, true, false, refs).load();
+ public ChangeNotes create(Change change, RefCache refs) {
+ return new ChangeNotes(args, change, true, refs).load();
}
- // TODO(ekempin): Remove when database backend is deleted
- /**
- * Instantiate ChangeNotes for a change that has been loaded by a batch read from the database.
- */
- private ChangeNotes createFromChangeOnlyWhenNoteDbDisabled(Change change) throws OrmException {
- checkState(
- !args.migration.readChanges(),
- "do not call createFromChangeWhenNoteDbDisabled when NoteDb is enabled");
- return new ChangeNotes(args, change).load();
- }
-
- public List<ChangeNotes> create(ReviewDb db, Collection<Change.Id> changeIds)
- throws OrmException {
+ public List<ChangeNotes> create(Collection<Change.Id> changeIds) {
List<ChangeNotes> notes = new ArrayList<>();
- if (args.migration.readChanges()) {
- for (Change.Id changeId : changeIds) {
- try {
- notes.add(createChecked(changeId));
- } catch (NoSuchChangeException e) {
- // Ignore missing changes to match Access#get(Iterable) behavior.
- }
+ for (Change.Id changeId : changeIds) {
+ try {
+ notes.add(createChecked(changeId));
+ } catch (NoSuchChangeException e) {
+ // Ignore missing changes to match Access#get(Iterable) behavior.
}
- return notes;
- }
-
- for (Change c : ReviewDbUtil.unwrapDb(db).changes().get(changeIds)) {
- notes.add(createFromChangeOnlyWhenNoteDbDisabled(c));
}
return notes;
}
public List<ChangeNotes> create(
- ReviewDb db,
Project.NameKey project,
Collection<Change.Id> changeIds,
- Predicate<ChangeNotes> predicate)
- throws OrmException {
+ Predicate<ChangeNotes> predicate) {
List<ChangeNotes> notes = new ArrayList<>();
- if (args.migration.readChanges()) {
- for (Change.Id cid : changeIds) {
- try {
- ChangeNotes cn = create(db, project, cid);
- if (cn.getChange() != null && predicate.test(cn)) {
- notes.add(cn);
- }
- } catch (NoSuchChangeException e) {
- // Match ReviewDb behavior, returning not found; maybe the caller learned about it from
- // a dangling patch set ref or something.
- continue;
- }
- }
- return notes;
- }
-
- for (Change c : ReviewDbUtil.unwrapDb(db).changes().get(changeIds)) {
- if (c != null && project.equals(c.getDest().getParentKey())) {
- ChangeNotes cn = createFromChangeOnlyWhenNoteDbDisabled(c);
- if (predicate.test(cn)) {
+ for (Change.Id cid : changeIds) {
+ try {
+ ChangeNotes cn = create(project, cid);
+ if (cn.getChange() != null && predicate.test(cn)) {
notes.add(cn);
}
+ } catch (NoSuchChangeException e) {
+ // Match ReviewDb behavior, returning not found; maybe the caller learned about it from
+ // a dangling patch set ref or something.
+ continue;
}
}
return notes;
}
- public ListMultimap<Project.NameKey, ChangeNotes> create(
- ReviewDb db, Predicate<ChangeNotes> predicate) throws IOException, OrmException {
+ public ListMultimap<Project.NameKey, ChangeNotes> create(Predicate<ChangeNotes> predicate)
+ throws IOException {
ListMultimap<Project.NameKey, ChangeNotes> m =
MultimapBuilder.hashKeys().arrayListValues().build();
- if (args.migration.readChanges()) {
- for (Project.NameKey project : projectCache.all()) {
- try (Repository repo = args.repoManager.openRepository(project)) {
- scanNoteDb(repo, db, project)
- .filter(r -> !r.error().isPresent())
- .map(ChangeNotesResult::notes)
- .filter(predicate)
- .forEach(n -> m.put(n.getProjectName(), n));
- }
- }
- } else {
- for (Change change : ReviewDbUtil.unwrapDb(db).changes().all()) {
- ChangeNotes notes = createFromChangeOnlyWhenNoteDbDisabled(change);
- if (predicate.test(notes)) {
- m.put(change.getProject(), notes);
- }
+ for (Project.NameKey project : projectCache.all()) {
+ try (Repository repo = args.repoManager.openRepository(project)) {
+ scan(repo, project)
+ .filter(r -> !r.error().isPresent())
+ .map(ChangeNotesResult::notes)
+ .filter(predicate)
+ .forEach(n -> m.put(n.getProjectName(), n));
}
}
return ImmutableListMultimap.copyOf(m);
}
- public Stream<ChangeNotesResult> scan(Repository repo, ReviewDb db, Project.NameKey project)
+ public Stream<ChangeNotesResult> scan(Repository repo, Project.NameKey project)
throws IOException {
- return scan(repo, db, project, null);
+ return scan(repo, project, null);
}
public Stream<ChangeNotesResult> scan(
- Repository repo,
- ReviewDb db,
- Project.NameKey project,
- Predicate<Change.Id> changeIdPredicate)
- throws IOException {
- return args.migration.readChanges()
- ? scanNoteDb(repo, db, project, changeIdPredicate)
- : scanReviewDb(repo, db, changeIdPredicate);
- }
-
- private Stream<ChangeNotesResult> scanReviewDb(
- Repository repo, ReviewDb db, Predicate<Change.Id> changeIdPredicate) throws IOException {
- // Scan IDs that might exist in ReviewDb, assuming that each change has at least one patch set
- // ref. Not all changes might exist: some patch set refs might have been written where the
- // corresponding ReviewDb write failed. These will be silently filtered out by the batch get
- // call below, which is intended.
- Set<Change.Id> ids = scanChangeIds(repo).fromPatchSetRefs();
-
- Stream<Change.Id> idStream = ids.stream();
- if (changeIdPredicate != null) {
- idStream = idStream.filter(changeIdPredicate);
- }
-
- // A batch size of N may overload get(Iterable), so use something smaller, but still >1.
- return Streams.stream(Iterators.partition(idStream.iterator(), 30))
- .flatMap(
- batch -> {
- try {
- return Streams.stream(ReviewDbUtil.unwrapDb(db).changes().get(batch))
- .map(this::toResult)
- .filter(Objects::nonNull);
- } catch (OrmException e) {
- // Return this error for each Id in the input batch.
- return batch.stream().map(id -> ChangeNotesResult.error(id, e));
- }
- });
- }
-
- private Stream<ChangeNotesResult> scanNoteDb(
- Repository repo, ReviewDb db, Project.NameKey project) throws IOException {
- return scanNoteDb(repo, db, project, null);
- }
-
- private Stream<ChangeNotesResult> scanNoteDb(
- Repository repo,
- ReviewDb db,
- Project.NameKey project,
- Predicate<Change.Id> changeIdPredicate)
+ Repository repo, Project.NameKey project, Predicate<Change.Id> changeIdPredicate)
throws IOException {
ScanResult sr = scanChangeIds(repo);
- PrimaryStorage defaultStorage = args.migration.changePrimaryStorage();
-
Stream<Change.Id> idStream = sr.all().stream();
if (changeIdPredicate != null) {
idStream = idStream.filter(changeIdPredicate);
}
- return idStream
- .map(id -> scanOneNoteDbChange(db, project, sr, defaultStorage, id))
- .filter(Objects::nonNull);
+ return idStream.map(id -> scanOneChange(project, sr, id)).filter(Objects::nonNull);
}
- private ChangeNotesResult scanOneNoteDbChange(
- ReviewDb db,
- Project.NameKey project,
- ScanResult sr,
- PrimaryStorage defaultStorage,
- Change.Id id) {
- Change change;
- try {
- change = readOneReviewDbChange(db, id);
- } catch (OrmException e) {
- return ChangeNotesResult.error(id, e);
- }
-
- if (change == null) {
- if (!sr.fromMetaRefs().contains(id)) {
- // Stray patch set refs can happen due to normal error conditions, e.g. failed
- // push processing, so aren't worth even a warning.
- return null;
- }
- if (defaultStorage == PrimaryStorage.REVIEW_DB) {
- // If changes should exist in ReviewDb, it's worth warning about a meta ref with
- // no corresponding ReviewDb data.
- logger.atWarning().log(
- "skipping change %s found in project %s but not in ReviewDb", id, project);
- return null;
- }
- // TODO(dborowitz): See discussion in NoteDbBatchUpdate#newChangeContext.
- change = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
- } else if (!change.getProject().equals(project)) {
- logger.atSevere().log(
- "skipping change %s found in project %s because ReviewDb change has project %s",
- id, project, change.getProject());
+ 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
+ // push processing, so aren't worth even a warning.
return null;
}
+
+ // 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);
}
@Nullable
- private ChangeNotesResult toResult(Change rawChangeFromReviewDbOrNoteDb) {
- ChangeNotes n = new ChangeNotes(args, rawChangeFromReviewDbOrNoteDb);
+ private ChangeNotesResult toResult(Change rawChangeFromNoteDb) {
+ ChangeNotes n = new ChangeNotes(args, rawChangeFromNoteDb, true, null);
try {
n.load();
- } catch (OrmException e) {
+ } catch (StorageException e) {
return ChangeNotesResult.error(n.getChangeId(), e);
}
return ChangeNotesResult.notes(n);
}
- /** Result of {@link #scan(Repository, ReviewDb, Project.NameKey)}. */
+ /** Result of {@link #scan(Repository,Project.NameKey)}. */
@AutoValue
public abstract static class ChangeNotesResult {
- static ChangeNotesResult error(Change.Id id, OrmException e) {
+ static ChangeNotesResult error(Change.Id id, StorageException e) {
return new AutoValue_ChangeNotes_Factory_ChangeNotesResult(id, Optional.of(e), null);
}
@@ -435,7 +259,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
public abstract Change.Id id();
/** Error encountered while loading this change, if any. */
- public abstract Optional<OrmException> error();
+ public abstract Optional<StorageException> error();
/**
* Notes loaded for this change.
@@ -487,7 +311,6 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
// notes easier.
RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
- private NoteDbUpdateManager.Result rebuildResult;
private DraftCommentNotes draftCommentNotes;
private RobotCommentNotes robotCommentNotes;
@@ -498,13 +321,8 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
private ImmutableSet<Comment.Key> commentKeys;
@VisibleForTesting
- public ChangeNotes(Args args, Change change) {
- this(args, change, true, true, null);
- }
-
- private ChangeNotes(
- Args args, Change change, boolean shouldExist, boolean autoRebuild, @Nullable RefCache refs) {
- super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
+ public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
+ super(args, change.getId());
this.change = new Change(change);
this.shouldExist = shouldExist;
this.refs = refs;
@@ -609,13 +427,12 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return commentKeys;
}
- public ImmutableListMultimap<RevId, Comment> getDraftComments(Account.Id author)
- throws OrmException {
+ public ImmutableListMultimap<RevId, Comment> getDraftComments(Account.Id author) {
return getDraftComments(author, null);
}
public ImmutableListMultimap<RevId, Comment> getDraftComments(
- Account.Id author, @Nullable Ref ref) throws OrmException {
+ Account.Id author, @Nullable Ref ref) {
loadDraftComments(author, ref);
// Filter out any zombie draft comments. These are drafts that are also in
// the published map, and arise when the update to All-Users to delete them
@@ -625,7 +442,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
draftCommentNotes.getComments(), e -> !getCommentKeys().contains(e.getValue().key)));
}
- public ImmutableListMultimap<RevId, RobotComment> getRobotComments() throws OrmException {
+ public ImmutableListMultimap<RevId, RobotComment> getRobotComments() {
loadRobotComments();
return robotCommentNotes.getComments();
}
@@ -635,15 +452,14 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
* However, this method will load the comments if no draft comments have been loaded or if the
* caller would like the drafts for another author.
*/
- private void loadDraftComments(Account.Id author, @Nullable Ref ref) throws OrmException {
+ private void loadDraftComments(Account.Id author, @Nullable Ref ref) {
if (draftCommentNotes == null || !author.equals(draftCommentNotes.getAuthor()) || ref != null) {
- draftCommentNotes =
- new DraftCommentNotes(args, change, author, autoRebuild, rebuildResult, ref);
+ draftCommentNotes = new DraftCommentNotes(args, getChangeId(), author, ref);
draftCommentNotes.load();
}
}
- private void loadRobotComments() throws OrmException {
+ private void loadRobotComments() {
if (robotCommentNotes == null) {
robotCommentNotes = new RobotCommentNotes(args, change);
robotCommentNotes.load();
@@ -659,7 +475,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return robotCommentNotes;
}
- public boolean containsComment(Comment c) throws OrmException {
+ public boolean containsComment(Comment c) {
if (containsCommentPublished(c)) {
return true;
}
@@ -687,19 +503,11 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
getPatchSets().get(psId), () -> String.format("missing current patch set %s", psId.get()));
}
- @VisibleForTesting
- public Timestamp getReadOnlyUntil() {
- return state.readOnlyUntil();
- }
-
@Override
- protected void onLoad(LoadHandle handle)
- throws NoSuchChangeException, IOException, ConfigInvalidException {
+ protected void onLoad(LoadHandle handle) throws NoSuchChangeException, IOException {
ObjectId rev = handle.id();
if (rev == null) {
- if (args.migration.readChanges()
- && PrimaryStorage.of(change) == PrimaryStorage.NOTE_DB
- && shouldExist) {
+ if (shouldExist) {
throw new NoSuchChangeException(getChangeId());
}
loadDefaults();
@@ -707,7 +515,7 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
}
ChangeNotesCache.Value v =
- args.cache.get().get(getProjectName(), getChangeId(), rev, handle.walk());
+ args.cache.get().get(getProjectName(), getChangeId(), rev, handle::walk);
state = v.state();
state.copyColumnsTo(change);
revisionNoteMap = v.revisionNoteMap();
@@ -727,89 +535,4 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
protected ObjectId readRef(Repository repo) throws IOException {
return refs != null ? refs.get(getRefName()).orElse(null) : super.readRef(repo);
}
-
- @Override
- protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
- if (autoRebuild) {
- NoteDbChangeState state = NoteDbChangeState.parse(change);
- if (args.migration.disableChangeReviewDb()) {
- checkState(
- state != null,
- "shouldn't have null NoteDbChangeState when ReviewDb disabled: %s",
- change);
- }
- ObjectId id = readRef(repo);
- if (id == null) {
- // Meta ref doesn't exist in NoteDb.
-
- if (state == null) {
- // Either ReviewDb change is being newly created, or it exists in ReviewDb but has not yet
- // been rebuilt for the first time, e.g. because we just turned on write-only mode. In
- // both cases, we don't want to auto-rebuild, just proceed with an empty ChangeNotes.
- return super.openHandle(repo, id);
- } else if (shouldExist && state.getPrimaryStorage() == PrimaryStorage.NOTE_DB) {
- throw new NoSuchChangeException(getChangeId());
- }
-
- // ReviewDb claims NoteDb state exists, but meta ref isn't present: fall through and
- // auto-rebuild if necessary.
- }
- RefCache refs = this.refs != null ? this.refs : new RepoRefCache(repo);
- if (!NoteDbChangeState.isChangeUpToDate(state, refs, getChangeId())) {
- return rebuildAndOpen(repo, id);
- }
- }
- return super.openHandle(repo);
- }
-
- private LoadHandle rebuildAndOpen(Repository repo, ObjectId oldId) throws IOException {
- Timer1.Context timer = args.metrics.autoRebuildLatency.start(CHANGES);
- try {
- Change.Id cid = getChangeId();
- ReviewDb db = args.db.get();
- ChangeRebuilder rebuilder = args.rebuilder.get();
- NoteDbUpdateManager.Result r;
- try (NoteDbUpdateManager manager = rebuilder.stage(db, cid)) {
- if (manager == null) {
- return super.openHandle(repo, oldId); // May be null in tests.
- }
- manager.setRefLogMessage("Auto-rebuilding change");
- r = manager.stageAndApplyDelta(change);
- try {
- rebuilder.execute(db, cid, manager);
- repo.scanForRepoChanges();
- } catch (OrmException | IOException e) {
- // Rebuilding failed. Most likely cause is contention on one or more
- // change refs; there are other types of errors that can happen during
- // rebuilding, but generally speaking they should happen during stage(),
- // not execute(). Assume that some other worker is going to successfully
- // store the rebuilt state, which is deterministic given an input
- // ChangeBundle.
- //
- // Parse notes from the staged result so we can return something useful
- // to the caller instead of throwing.
- logger.atFine().log("Rebuilding change %s failed: %s", getChangeId(), e.getMessage());
- args.metrics.autoRebuildFailureCount.increment(CHANGES);
- rebuildResult = requireNonNull(r);
- requireNonNull(r.newState());
- requireNonNull(r.staged());
- requireNonNull(r.staged().changeObjects());
- return LoadHandle.create(
- ChangeNotesCommit.newStagedRevWalk(repo, r.staged().changeObjects()),
- r.newState().getChangeMetaId());
- }
- }
- return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), r.newState().getChangeMetaId());
- } catch (NoSuchChangeException e) {
- return super.openHandle(repo, oldId);
- } catch (OrmException e) {
- throw new IOException(e);
- } finally {
- logger.atFine().log(
- "Rebuilt change %s in project %s in %s ms",
- getChangeId(),
- getProjectName(),
- TimeUnit.MILLISECONDS.convert(timer.stop(), TimeUnit.NANOSECONDS));
- }
- }
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesCache.java b/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
index cc316e5813..e2af855f45 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesCache.java
@@ -20,6 +20,7 @@ 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.proto.Protos;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
@@ -28,8 +29,7 @@ import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesKeyProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.gerrit.server.notedb.AbstractChangeNotes.Args;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
import com.google.inject.Inject;
@@ -42,6 +42,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import java.util.function.Supplier;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
@@ -85,7 +86,7 @@ public class ChangeNotesCache {
@Override
public byte[] serialize(Key object) {
- return ProtoCacheSerializers.toByteArray(
+ return Protos.toByteArray(
ChangeNotesKeyProto.newBuilder()
.setProject(object.project().get())
.setChangeId(object.changeId().get())
@@ -95,8 +96,7 @@ public class ChangeNotesCache {
@Override
public Key deserialize(byte[] in) {
- ChangeNotesKeyProto proto =
- ProtoCacheSerializers.parseUnchecked(ChangeNotesKeyProto.parser(), in);
+ ChangeNotesKeyProto proto = Protos.parseUnchecked(ChangeNotesKeyProto.parser(), in);
return Key.create(
new Project.NameKey(proto.getProject()),
new Change.Id(proto.getChangeId()),
@@ -171,7 +171,6 @@ public class ChangeNotesCache {
+ list(state.changeMessages(), changeMessage())
+ P
+ map(state.publishedComments().asMap(), comment())
- + T // readOnlyUntil
+ 1 // isPrivate
+ 1 // workInProgress
+ 1; // reviewStarted
@@ -337,13 +336,13 @@ public class ChangeNotesCache {
private class Loader implements Callable<ChangeNotesState> {
private final Key key;
- private final ChangeNotesRevWalk rw;
+ private final Supplier<ChangeNotesRevWalk> walkSupplier;
private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
- private Loader(Key key, ChangeNotesRevWalk rw) {
+ private Loader(Key key, Supplier<ChangeNotesRevWalk> walkSupplier) {
this.key = key;
- this.rw = rw;
+ this.walkSupplier = walkSupplier;
}
@Override
@@ -354,7 +353,7 @@ public class ChangeNotesCache {
new ChangeNotesParser(
key.changeId(),
key.id(),
- rw,
+ walkSupplier.get(),
args.changeNoteJson,
args.legacyChangeNoteRead,
args.metrics);
@@ -375,11 +374,15 @@ public class ChangeNotesCache {
this.args = args;
}
- Value get(Project.NameKey project, Change.Id changeId, ObjectId metaId, ChangeNotesRevWalk rw)
+ Value get(
+ Project.NameKey project,
+ Change.Id changeId,
+ ObjectId metaId,
+ Supplier<ChangeNotesRevWalk> walkSupplier)
throws IOException {
try {
Key key = Key.create(project, changeId, metaId);
- Loader loader = new Loader(key, rw);
+ Loader loader = new Loader(key, walkSupplier);
ChangeNotesState s = cache.get(key, loader);
return new AutoValue_ChangeNotesCache_Value(s, loader.revisionNoteMap);
} catch (ExecutionException e) {
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 98cbd02994..d3b34b9d60 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -26,7 +26,6 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET_DESCRIPTION;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PRIVATE;
-import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_READ_ONLY_UNTIL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_REAL_USER;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_REVERT_OF;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
@@ -38,6 +37,7 @@ 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.ChangeNoteUtil.parseCommitMessageRange;
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;
@@ -68,7 +68,6 @@ 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.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -77,7 +76,6 @@ import com.google.gerrit.server.util.LabelVote;
import java.io.IOException;
import java.nio.charset.Charset;
import java.sql.Timestamp;
-import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -85,7 +83,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -99,7 +96,6 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.FooterKey;
-import org.eclipse.jgit.util.GitDateParser;
import org.eclipse.jgit.util.RawParseUtils;
class ChangeNotesParser {
@@ -163,7 +159,6 @@ class ChangeNotesParser {
private String submissionId;
private String tag;
private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
- private Timestamp readOnlyUntil;
private Boolean isPrivate;
private Boolean workInProgress;
private Boolean previousWorkInProgressFooter;
@@ -266,7 +261,6 @@ class ChangeNotesParser {
submitRecords,
buildAllMessages(),
comments,
- readOnlyUntil,
firstNonNull(isPrivate, false),
firstNonNull(workInProgress, false),
firstNonNull(hasReviewStarted, true),
@@ -401,10 +395,6 @@ class ChangeNotesParser {
// behavior.
}
- if (readOnlyUntil == null) {
- parseReadOnlyUntil(commit);
- }
-
if (isPrivate == null) {
parseIsPrivate(commit);
}
@@ -469,7 +459,7 @@ class ChangeNotesParser {
throws ConfigInvalidException {
String line = parseOneFooter(commit, footerKey);
if (line == null) {
- throw expectedOneFooter(footerKey, Collections.<String>emptyList());
+ throw expectedOneFooter(footerKey, Collections.emptyList());
}
return line;
}
@@ -732,7 +722,7 @@ class ChangeNotesParser {
Map<RevId, ChangeRevisionNote> rns = revisionNoteMap.revisionNotes;
for (Map.Entry<RevId, ChangeRevisionNote> e : rns.entrySet()) {
- for (Comment c : e.getValue().getComments()) {
+ for (Comment c : e.getValue().getEntities()) {
comments.put(e.getKey(), c);
}
}
@@ -838,12 +828,8 @@ class ChangeNotesParser {
throw pe;
}
- // Store an actual 0-vote approval in the map for a removed approval, for
- // several reasons:
- // - This is closer to the ReviewDb representation, which leads to less
- // confusion and special-casing of NoteDb.
- // - More importantly, ApprovalCopier needs an actual approval in order to
- // block copying an earlier approval over a later delete.
+ // 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);
@@ -935,20 +921,6 @@ class ChangeNotesParser {
}
}
- private void parseReadOnlyUntil(ChangeNotesCommit commit) throws ConfigInvalidException {
- String raw = parseOneFooter(commit, FOOTER_READ_ONLY_UNTIL);
- if (raw == null) {
- return;
- }
- try {
- readOnlyUntil = new Timestamp(GitDateParser.parse(raw, null, Locale.US).getTime());
- } catch (ParseException e) {
- ConfigInvalidException cie = invalidFooter(FOOTER_READ_ONLY_UNTIL, raw);
- cie.initCause(e);
- throw cie;
- }
- }
-
private void parseIsPrivate(ChangeNotesCommit commit) throws ConfigInvalidException {
String raw = parseOneFooter(commit, FOOTER_PRIVATE);
if (raw == null) {
@@ -1030,7 +1002,7 @@ class ChangeNotesParser {
}
private void updatePatchSetStates() {
- Set<PatchSet.Id> missing = new TreeSet<>(ReviewDbUtil.intKeyOrdering());
+ 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)) {
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 02fe4167a6..fd260e7449 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -19,10 +19,6 @@ 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 com.google.gerrit.reviewdb.server.ReviewDbCodecs.APPROVAL_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.MESSAGE_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.PATCH_SET_CODEC;
-import static com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.toByteString;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
@@ -39,7 +35,9 @@ 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.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;
@@ -49,7 +47,10 @@ 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.OutputFormat;
+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.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -59,12 +60,11 @@ import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.Reviewer
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerStatusUpdateProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import com.google.gerrit.server.index.change.ChangeField.StoredSubmitRecord;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gson.Gson;
-import java.io.IOException;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.MessageLite;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
@@ -118,7 +118,6 @@ public abstract class ChangeNotesState {
List<SubmitRecord> submitRecords,
List<ChangeMessage> changeMessages,
ListMultimap<RevId, Comment> publishedComments,
- @Nullable Timestamp readOnlyUntil,
boolean isPrivate,
boolean workInProgress,
boolean reviewStarted,
@@ -166,16 +165,12 @@ public abstract class ChangeNotesState {
.submitRecords(submitRecords)
.changeMessages(changeMessages)
.publishedComments(publishedComments)
- .readOnlyUntil(readOnlyUntil)
.build();
}
/**
* Subset of Change columns that can be represented in NoteDb.
*
- * <p>Notable exceptions include rowVersion and noteDbState, which are only make sense when read
- * from NoteDb, so they cannot be cached.
- *
* <p>Fields should match the column names in {@link Change}, and are in listed column order.
*/
@AutoValue
@@ -302,9 +297,6 @@ public abstract class ChangeNotesState {
abstract ImmutableListMultimap<RevId, Comment> publishedComments();
- @Nullable
- abstract Timestamp readOnlyUntil();
-
Change newChange(Project.NameKey project) {
ChangeColumns c = requireNonNull(columns(), "columns are required");
Change change =
@@ -315,17 +307,15 @@ public abstract class ChangeNotesState {
new Branch.NameKey(project, c.branch()),
c.createdOn());
copyNonConstructorColumnsTo(change);
- change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
return change;
}
- void copyColumnsTo(Change change) throws IOException {
+ void copyColumnsTo(Change change) {
ChangeColumns c = columns();
checkState(
c != null && metaId() != null,
"missing columns or metaId in ChangeNotesState; is NoteDb enabled? %s",
this);
- checkMetaId(change);
change.setKey(c.changeKey());
change.setOwner(c.owner());
change.setDest(new Branch.NameKey(change.getProject(), c.branch()));
@@ -333,26 +323,6 @@ public abstract class ChangeNotesState {
copyNonConstructorColumnsTo(change);
}
- private void checkMetaId(Change change) throws IOException {
- NoteDbChangeState state = NoteDbChangeState.parse(change);
- if (state == null) {
- return; // Can happen during small NoteDb tests.
- } else if (state.getPrimaryStorage() == PrimaryStorage.NOTE_DB) {
- return;
- }
- checkState(state.getRefState().isPresent(), "expected RefState: %s", state);
- ObjectId idFromState = state.getRefState().get().changeMetaId();
- if (!idFromState.equals(metaId())) {
- throw new IOException(
- "cannot copy ChangeNotesState into Change "
- + changeId()
- + "; this ChangeNotesState was created from "
- + metaId()
- + ", but change requires state "
- + idFromState);
- }
- }
-
private void copyNonConstructorColumnsTo(Change change) {
ChangeColumns c = requireNonNull(columns(), "columns are required");
if (c.status() != null) {
@@ -428,8 +398,6 @@ public abstract class ChangeNotesState {
abstract Builder publishedComments(ListMultimap<RevId, Comment> publishedComments);
- abstract Builder readOnlyUntil(@Nullable Timestamp readOnlyUntil);
-
abstract ChangeNotesState build();
}
@@ -455,8 +423,15 @@ public abstract class ChangeNotesState {
object.pastAssignees().forEach(a -> b.addPastAssignee(a.get()));
object.hashtags().forEach(b::addHashtag);
- object.patchSets().forEach(e -> b.addPatchSet(toByteString(e.getValue(), PATCH_SET_CODEC)));
- object.approvals().forEach(e -> b.addApproval(toByteString(e.getValue(), APPROVAL_CODEC)));
+ object
+ .patchSets()
+ .forEach(e -> b.addPatchSet(toByteString(e.getValue(), PatchSetProtoConverter.INSTANCE)));
+ object
+ .approvals()
+ .forEach(
+ e ->
+ b.addApproval(
+ toByteString(e.getValue(), PatchSetApprovalProtoConverter.INSTANCE)));
object.reviewers().asTable().cellSet().forEach(c -> b.addReviewer(toReviewerSetEntry(c)));
object
@@ -480,14 +455,18 @@ public abstract class ChangeNotesState {
object
.submitRecords()
.forEach(r -> b.addSubmitRecord(GSON.toJson(new StoredSubmitRecord(r))));
- object.changeMessages().forEach(m -> b.addChangeMessage(toByteString(m, MESSAGE_CODEC)));
+ object
+ .changeMessages()
+ .forEach(m -> b.addChangeMessage(toByteString(m, ChangeMessageProtoConverter.INSTANCE)));
object.publishedComments().values().forEach(c -> b.addPublishedComment(GSON.toJson(c)));
- if (object.readOnlyUntil() != null) {
- b.setReadOnlyUntil(object.readOnlyUntil().getTime()).setHasReadOnlyUntil(true);
- }
+ return Protos.toByteArray(b.build());
+ }
- return ProtoCacheSerializers.toByteArray(b.build());
+ @VisibleForTesting
+ static <T> ByteString toByteString(T object, ProtoConverter<?, T> converter) {
+ MessageLite message = converter.toProto(object);
+ return Protos.toByteString(message);
}
private static ChangeColumnsProto toChangeColumnsProto(ChangeColumns cols) {
@@ -555,8 +534,7 @@ public abstract class ChangeNotesState {
@Override
public ChangeNotesState deserialize(byte[] in) {
- ChangeNotesStateProto proto =
- ProtoCacheSerializers.parseUnchecked(ChangeNotesStateProto.parser(), in);
+ ChangeNotesStateProto proto = Protos.parseUnchecked(ChangeNotesStateProto.parser(), in);
Change.Id changeId = new Change.Id(proto.getChangeId());
ChangeNotesState.Builder b =
@@ -571,12 +549,12 @@ public abstract class ChangeNotesState {
.hashtags(proto.getHashtagList())
.patchSets(
proto.getPatchSetList().stream()
- .map(PATCH_SET_CODEC::decode)
+ .map(bytes -> parseProtoFrom(PatchSetProtoConverter.INSTANCE, bytes))
.map(ps -> Maps.immutableEntry(ps.getId(), ps))
.collect(toImmutableList()))
.approvals(
proto.getApprovalList().stream()
- .map(APPROVAL_CODEC::decode)
+ .map(bytes -> parseProtoFrom(PatchSetApprovalProtoConverter.INSTANCE, bytes))
.map(a -> Maps.immutableEntry(a.getPatchSetId(), a))
.collect(toImmutableList()))
.reviewers(toReviewerSet(proto.getReviewerList()))
@@ -594,18 +572,21 @@ public abstract class ChangeNotesState {
.collect(toImmutableList()))
.changeMessages(
proto.getChangeMessageList().stream()
- .map(MESSAGE_CODEC::decode)
+ .map(bytes -> parseProtoFrom(ChangeMessageProtoConverter.INSTANCE, bytes))
.collect(toImmutableList()))
.publishedComments(
proto.getPublishedCommentList().stream()
.map(r -> GSON.fromJson(r, Comment.class))
.collect(toImmutableListMultimap(c -> new RevId(c.revId), c -> c)));
- if (proto.getHasReadOnlyUntil()) {
- b.readOnlyUntil(new Timestamp(proto.getReadOnlyUntil()));
- }
return b.build();
}
+ private static <P extends MessageLite, T> T parseProtoFrom(
+ ProtoConverter<P, T> converter, ByteString byteString) {
+ P message = Protos.parseUnchecked(converter.getParser(), byteString);
+ return converter.fromProto(message);
+ }
+
private static ChangeColumns toChangeColumns(Change.Id changeId, ChangeColumnsProto proto) {
ChangeColumns.Builder b =
ChangeColumns.builder()
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index bfb12caf6c..0cde36350b 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -29,7 +29,6 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_LABEL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET_DESCRIPTION;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PRIVATE;
-import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_READ_ONLY_UNTIL;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_REAL_USER;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_REVERT_OF;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_STATUS;
@@ -50,8 +49,8 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord;
+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;
@@ -62,16 +61,13 @@ 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.config.GerritServerConfig;
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.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
-import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
@@ -84,7 +80,6 @@ import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
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;
@@ -106,18 +101,8 @@ import org.eclipse.jgit.revwalk.RevWalk;
*/
public class ChangeUpdate extends AbstractChangeUpdate {
public interface Factory {
- ChangeUpdate create(ChangeNotes notes, CurrentUser user);
-
ChangeUpdate create(ChangeNotes notes, CurrentUser user, Date when);
- ChangeUpdate create(
- Change change,
- @Assisted("effective") @Nullable Account.Id accountId,
- @Assisted("real") @Nullable Account.Id realAccountId,
- PersonIdent authorIdent,
- Date when,
- Comparator<String> labelNameComparator);
-
@VisibleForTesting
ChangeUpdate create(
ChangeNotes notes, CurrentUser user, Date when, Comparator<String> labelNameComparator);
@@ -152,7 +137,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private boolean isAllowWriteToNewtRef;
private String psDescription;
private boolean currentPatchSet;
- private Timestamp readOnlyUntil;
private Boolean isPrivate;
private Boolean workInProgress;
private Integer revertOf;
@@ -164,37 +148,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@AssistedInject
private ChangeUpdate(
- @GerritServerConfig Config cfg,
- @GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
- NoteDbUpdateManager.Factory updateManagerFactory,
- ChangeDraftUpdate.Factory draftUpdateFactory,
- RobotCommentUpdate.Factory robotCommentUpdateFactory,
- DeleteCommentRewriter.Factory deleteCommentRewriterFactory,
- ProjectCache projectCache,
- @Assisted ChangeNotes notes,
- @Assisted CurrentUser user,
- ChangeNoteUtil noteUtil) {
- this(
- cfg,
- serverIdent,
- migration,
- updateManagerFactory,
- draftUpdateFactory,
- robotCommentUpdateFactory,
- deleteCommentRewriterFactory,
- projectCache,
- notes,
- user,
- serverIdent.getWhen(),
- noteUtil);
- }
-
- @AssistedInject
- private ChangeUpdate(
- @GerritServerConfig Config cfg,
@GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
NoteDbUpdateManager.Factory updateManagerFactory,
ChangeDraftUpdate.Factory draftUpdateFactory,
RobotCommentUpdate.Factory robotCommentUpdateFactory,
@@ -205,9 +159,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Assisted Date when,
ChangeNoteUtil noteUtil) {
this(
- cfg,
serverIdent,
- migration,
updateManagerFactory,
draftUpdateFactory,
robotCommentUpdateFactory,
@@ -226,9 +178,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@AssistedInject
private ChangeUpdate(
- @GerritServerConfig Config cfg,
@GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
NoteDbUpdateManager.Factory updateManagerFactory,
ChangeDraftUpdate.Factory draftUpdateFactory,
RobotCommentUpdate.Factory robotCommentUpdateFactory,
@@ -238,7 +188,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Assisted Date when,
@Assisted Comparator<String> labelNameComparator,
ChangeNoteUtil noteUtil) {
- super(cfg, migration, notes, user, serverIdent, noteUtil, when);
+ super(notes, user, serverIdent, noteUtil, when);
this.updateManagerFactory = updateManagerFactory;
this.draftUpdateFactory = draftUpdateFactory;
this.robotCommentUpdateFactory = robotCommentUpdateFactory;
@@ -246,44 +196,9 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.approvals = approvals(labelNameComparator);
}
- @AssistedInject
- private ChangeUpdate(
- @GerritServerConfig Config cfg,
- @GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
- NoteDbUpdateManager.Factory updateManagerFactory,
- ChangeDraftUpdate.Factory draftUpdateFactory,
- RobotCommentUpdate.Factory robotCommentUpdateFactory,
- DeleteCommentRewriter.Factory deleteCommentRewriterFactory,
- ChangeNoteUtil noteUtil,
- @Assisted Change change,
- @Assisted("effective") @Nullable Account.Id accountId,
- @Assisted("real") @Nullable Account.Id realAccountId,
- @Assisted PersonIdent authorIdent,
- @Assisted Date when,
- @Assisted Comparator<String> labelNameComparator) {
- super(
- cfg,
- migration,
- noteUtil,
- serverIdent,
- null,
- change,
- accountId,
- realAccountId,
- authorIdent,
- when);
- this.draftUpdateFactory = draftUpdateFactory;
- this.robotCommentUpdateFactory = robotCommentUpdateFactory;
- this.updateManagerFactory = updateManagerFactory;
- this.deleteCommentRewriterFactory = deleteCommentRewriterFactory;
- this.approvals = approvals(labelNameComparator);
- }
-
- public ObjectId commit() throws IOException, OrmException {
+ public ObjectId commit() throws IOException {
try (NoteDbUpdateManager updateManager = updateManagerFactory.create(getProjectName())) {
updateManager.add(this);
- updateManager.stageAndApplyDelta(getChange());
updateManager.execute();
}
return getResult();
@@ -396,9 +311,9 @@ public class ChangeUpdate extends AbstractChangeUpdate {
deleteCommentRewriterFactory.create(getChange().getId(), uuid, newMessage);
}
- public void deleteChangeMessageByRewritingHistory(int targetMessageIdx, String newMessage) {
+ public void deleteChangeMessageByRewritingHistory(String targetMessageId, String newMessage) {
deleteChangeMessageRewriter =
- new DeleteChangeMessageRewriter(getChange().getId(), targetMessageIdx, newMessage);
+ new DeleteChangeMessageRewriter(getChange().getId(), targetMessageId, newMessage);
}
@VisibleForTesting
@@ -514,7 +429,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
/** @return the tree id for the updated tree */
private ObjectId storeRevisionNotes(RevWalk rw, ObjectInserter inserter, ObjectId curr)
- throws ConfigInvalidException, OrmException, IOException {
+ throws ConfigInvalidException, IOException {
if (comments.isEmpty() && pushCert == null) {
return null;
}
@@ -541,20 +456,17 @@ public class ChangeUpdate extends AbstractChangeUpdate {
}
private RevisionNoteMap<ChangeRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
- throws ConfigInvalidException, OrmException, IOException {
+ throws ConfigInvalidException, IOException {
if (curr.equals(ObjectId.zeroId())) {
return RevisionNoteMap.emptyMap();
}
- if (migration.readChanges()) {
- // If reading from changes is enabled, then the old ChangeNotes may have
- // already parsed the revision notes. We can reuse them as long as the ref
- // hasn't advanced.
- ChangeNotes notes = getNotes();
- if (notes != null && notes.revisionNoteMap != null) {
- ObjectId idFromNotes = firstNonNull(notes.load().getRevision(), ObjectId.zeroId());
- if (idFromNotes.equals(curr)) {
- return notes.revisionNoteMap;
- }
+ // The old ChangeNotes may have already parsed the revision notes. We can reuse them as long as
+ // the ref hasn't advanced.
+ ChangeNotes notes = getNotes();
+ if (notes != null && notes.revisionNoteMap != null) {
+ ObjectId idFromNotes = firstNonNull(notes.load().getRevision(), ObjectId.zeroId());
+ if (idFromNotes.equals(curr)) {
+ return notes.revisionNoteMap;
}
}
NoteMap noteMap = NoteMap.read(rw.getObjectReader(), rw.parseCommit(curr));
@@ -570,12 +482,11 @@ public class ChangeUpdate extends AbstractChangeUpdate {
}
private void checkComments(
- Map<RevId, ChangeRevisionNote> existingNotes, Map<RevId, RevisionNoteBuilder> toUpdate)
- throws OrmException {
+ Map<RevId, ChangeRevisionNote> existingNotes, Map<RevId, RevisionNoteBuilder> toUpdate) {
// Prohibit various kinds of illegal operations on comments.
Set<Comment.Key> existing = new HashSet<>();
for (ChangeRevisionNote rn : existingNotes.values()) {
- for (Comment c : rn.getComments()) {
+ for (Comment c : rn.getEntities()) {
existing.add(c.key);
if (draftUpdate != null) {
// Take advantage of an existing update on All-Users to prune any
@@ -601,7 +512,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
for (RevisionNoteBuilder b : toUpdate.values()) {
for (Comment c : b.put.values()) {
if (existing.contains(c.key)) {
- throw new OrmException("Cannot update existing published comment: " + c);
+ throw new StorageException("Cannot update existing published comment: " + c);
}
}
}
@@ -614,7 +525,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Override
protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
- throws OrmException, IOException {
+ throws IOException {
checkState(
deleteCommentRewriter == null && deleteChangeMessageRewriter == null,
"cannot update and rewrite ref in one BatchUpdate");
@@ -750,10 +661,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
addIdent(msg, realAccountId).append('\n');
}
- if (readOnlyUntil != null) {
- addFooter(msg, FOOTER_READ_ONLY_UNTIL, NoteDbUtil.formatTime(serverIdent, readOnlyUntil));
- }
-
if (isPrivate != null) {
addFooter(msg, FOOTER_PRIVATE, isPrivate);
}
@@ -773,7 +680,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
cb.setTreeId(treeId);
}
} catch (ConfigInvalidException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
return cb;
}
@@ -813,7 +720,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
&& tag == null
&& psDescription == null
&& !currentPatchSet
- && readOnlyUntil == null
&& isPrivate == null
&& workInProgress == null
&& revertOf == null;
@@ -852,10 +758,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.workInProgress = workInProgress;
}
- void setReadOnlyUntil(Timestamp readOnlyUntil) {
- this.readOnlyUntil = readOnlyUntil;
- }
-
private static StringBuilder addFooter(StringBuilder sb, FooterKey footer) {
return sb.append(footer.getName()).append(": ");
}
@@ -877,13 +779,4 @@ public class ChangeUpdate extends AbstractChangeUpdate {
sb.append('>');
return sb;
}
-
- @Override
- protected void checkNotReadOnly() throws OrmException {
- // Allow setting Read-only-until to 0 to release an existing lease.
- if (readOnlyUntil != null && readOnlyUntil.getTime() == 0) {
- return;
- }
- super.checkNotReadOnly();
- }
}
diff --git a/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java b/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
deleted file mode 100644
index 4a7f7fce59..0000000000
--- a/java/com/google/gerrit/server/notedb/CommentJsonMigrator.java
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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.notedb;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
-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.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.update.RefUpdateUtil;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.storage.file.PackInserter;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.notes.Note;
-import org.eclipse.jgit.notes.NoteMap;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.MutableInteger;
-
-@Singleton
-public class CommentJsonMigrator {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public static class ProjectMigrationResult {
- public int skipped;
- public boolean ok;
- public List<String> refsUpdated;
- }
-
- private final LegacyChangeNoteRead legacyChangeNoteRead;
- private final ChangeNoteJson changeNoteJson;
- private final AllUsersName allUsers;
-
- @Inject
- CommentJsonMigrator(
- ChangeNoteJson changeNoteJson,
- GerritServerIdProvider gerritServerIdProvider,
- AllUsersName allUsers) {
- this.changeNoteJson = changeNoteJson;
- this.allUsers = allUsers;
- this.legacyChangeNoteRead = new LegacyChangeNoteRead(gerritServerIdProvider.get());
- }
-
- CommentJsonMigrator(ChangeNoteJson changeNoteJson, String serverId, AllUsersName allUsers) {
- this.changeNoteJson = changeNoteJson;
- this.legacyChangeNoteRead = new LegacyChangeNoteRead(serverId);
- this.allUsers = allUsers;
- }
-
- public ProjectMigrationResult migrateProject(
- Project.NameKey project, Repository repo, boolean dryRun) {
- ProjectMigrationResult progress = new ProjectMigrationResult();
- progress.ok = true;
- progress.skipped = 0;
- progress.refsUpdated = ImmutableList.of();
- try (RevWalk rw = new RevWalk(repo);
- ObjectInserter ins = newPackInserter(repo)) {
- BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
- bru.setAllowNonFastForwards(true);
- progress.ok &= migrateChanges(project, repo, rw, ins, bru);
- if (project.equals(allUsers)) {
- progress.ok &= migrateDrafts(allUsers, repo, rw, ins, bru);
- }
-
- progress.refsUpdated =
- bru.getCommands().stream().map(c -> c.getRefName()).collect(toImmutableList());
- if (!bru.getCommands().isEmpty()) {
- if (!dryRun) {
- ins.flush();
- RefUpdateUtil.executeChecked(bru, rw);
- }
- } else {
- progress.skipped++;
- }
- } catch (IOException e) {
- progress.ok = false;
- }
-
- return progress;
- }
-
- private boolean migrateChanges(
- Project.NameKey project, Repository repo, RevWalk rw, ObjectInserter ins, BatchRefUpdate bru)
- throws IOException {
- boolean ok = true;
- for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) {
- Change.Id changeId = Change.Id.fromRef(ref.getName());
- if (changeId == null || !ref.getName().equals(RefNames.changeMetaRef(changeId))) {
- continue;
- }
- ok &= migrateOne(project, rw, ins, bru, Status.PUBLISHED, changeId, ref);
- }
- return ok;
- }
-
- private boolean migrateDrafts(
- Project.NameKey allUsers,
- Repository allUsersRepo,
- RevWalk rw,
- ObjectInserter ins,
- BatchRefUpdate bru)
- throws IOException {
- boolean ok = true;
- for (Ref ref : allUsersRepo.getRefDatabase().getRefsByPrefix(RefNames.REFS_DRAFT_COMMENTS)) {
- Change.Id changeId = Change.Id.fromAllUsersRef(ref.getName());
- if (changeId == null) {
- continue;
- }
- ok &= migrateOne(allUsers, rw, ins, bru, Status.DRAFT, changeId, ref);
- }
- return ok;
- }
-
- private boolean migrateOne(
- Project.NameKey project,
- RevWalk rw,
- ObjectInserter ins,
- BatchRefUpdate bru,
- Status status,
- Change.Id changeId,
- Ref ref) {
- ObjectId oldId = ref.getObjectId();
- try {
- if (!hasAnyLegacyComments(rw, oldId)) {
- return true;
- }
- } catch (IOException e) {
- logger.atInfo().withCause(e).log(
- "Error reading change %s in %s; attempting migration anyway", changeId, project);
- }
-
- try {
- reset(rw, oldId);
-
- ObjectReader reader = rw.getObjectReader();
- ObjectId newId = null;
- RevCommit c;
- while ((c = rw.next()) != null) {
- CommitBuilder cb = new CommitBuilder();
- cb.setAuthor(c.getAuthorIdent());
- cb.setCommitter(c.getCommitterIdent());
- cb.setMessage(c.getFullMessage());
- cb.setEncoding(c.getEncoding());
- if (newId != null) {
- cb.setParentId(newId);
- }
-
- // Read/write using the low-level RevisionNote API, which works regardless of NotesMigration
- // state.
- NoteMap noteMap = NoteMap.read(reader, c);
- RevisionNoteMap<ChangeRevisionNote> revNoteMap =
- RevisionNoteMap.parse(
- changeNoteJson, legacyChangeNoteRead, changeId, reader, noteMap, status);
- RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(revNoteMap);
-
- for (RevId revId : revNoteMap.revisionNotes.keySet()) {
- // Call cache.get on each known RevId to read the old note in whichever format, then write
- // the note in JSON format.
- byte[] data = cache.get(revId).build(changeNoteJson);
- noteMap.set(ObjectId.fromString(revId.get()), ins.insert(OBJ_BLOB, data));
- }
- cb.setTreeId(noteMap.writeTree(ins));
- newId = ins.insert(cb);
- }
-
- bru.addCommand(new ReceiveCommand(oldId, newId, ref.getName()));
- return true;
- } catch (ConfigInvalidException | IOException e) {
- logger.atInfo().withCause(e).log("Error migrating change %s in %s", changeId, project);
- return false;
- }
- }
-
- private static boolean hasAnyLegacyComments(RevWalk rw, ObjectId id) throws IOException {
- ObjectReader reader = rw.getObjectReader();
- reset(rw, id);
-
- // Check the note map at each commit, not just the tip. It's possible that the server switched
- // from legacy to JSON partway through its history, which would have mixed legacy/JSON comments
- // in its history. Although the tip commit would continue to parse once we remove the legacy
- // parser, our goal is really to expunge all vestiges of the old format, which implies rewriting
- // history (and thus returning true) in this case.
- RevCommit c;
- while ((c = rw.next()) != null) {
- NoteMap noteMap = NoteMap.read(reader, c);
- for (Note note : noteMap) {
- // Match pre-parsing logic in RevisionNote#parse().
- ObjectLoader objectLoader = reader.open(note.getData(), OBJ_BLOB);
- if (objectLoader.isLarge()) {
- throw new IOException(String.format("Comment note %s is too large", note.name()));
- }
- byte[] raw = objectLoader.getCachedBytes();
- MutableInteger p = new MutableInteger();
- RevisionNote.trimLeadingEmptyLines(raw, p);
- if (!ChangeRevisionNote.isJson(raw, p.value)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static void reset(RevWalk rw, ObjectId id) throws IOException {
- rw.reset();
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.REVERSE);
- rw.markStart(rw.parseCommit(id));
- }
-
- private static ObjectInserter newPackInserter(Repository repo) {
- if (!(repo instanceof FileRepository)) {
- return repo.newObjectInserter();
- }
- PackInserter ins = ((FileRepository) repo).getObjectDatabase().newPackInserter();
- ins.checkExisting(false);
- return ins;
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java b/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java
index 212aa3762e..8fb28e1bc9 100644
--- a/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteChangeMessageRewriter.java
@@ -15,6 +15,7 @@
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 org.eclipse.jgit.util.RawParseUtils.decode;
@@ -40,12 +41,12 @@ import org.eclipse.jgit.util.RawParseUtils;
public class DeleteChangeMessageRewriter implements NoteDbRewriter {
private final Change.Id changeId;
- private final int targetMessageIdx;
+ private final String targetMessageId;
private final String newChangeMessage;
- DeleteChangeMessageRewriter(Change.Id changeId, int targetMessageIdx, String newChangeMessage) {
+ DeleteChangeMessageRewriter(Change.Id changeId, String targetMessageId, String newChangeMessage) {
this.changeId = changeId;
- this.targetMessageIdx = targetMessageIdx;
+ this.targetMessageId = checkNotNull(targetMessageId);
this.newChangeMessage = newChangeMessage;
}
@@ -67,21 +68,18 @@ public class DeleteChangeMessageRewriter implements NoteDbRewriter {
ObjectId newTipId = null;
RevCommit originalCommit;
- int idx = 0;
+ boolean startRewrite = false;
while ((originalCommit = revWalk.next()) != null) {
- if (idx < targetMessageIdx) {
+ boolean isTargetCommit = originalCommit.getId().getName().equals(targetMessageId);
+ if (!startRewrite && !isTargetCommit) {
newTipId = originalCommit;
- idx++;
continue;
}
+ startRewrite = true;
String newCommitMessage =
- (idx == targetMessageIdx)
- ? createNewCommitMessage(originalCommit)
- : originalCommit.getFullMessage();
+ isTargetCommit ? createNewCommitMessage(originalCommit) : originalCommit.getFullMessage();
newTipId = rewriteOneCommit(originalCommit, newTipId, newCommitMessage, inserter);
-
- idx++;
}
return newTipId;
}
diff --git a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
index 656e9ec951..c100550f90 100644
--- a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
@@ -25,7 +25,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -86,7 +85,7 @@ public class DeleteCommentRewriter implements NoteDbRewriter {
@Override
public ObjectId rewriteCommitHistory(RevWalk revWalk, ObjectInserter inserter, ObjectId currTip)
- throws IOException, ConfigInvalidException, OrmException {
+ throws IOException, ConfigInvalidException {
checkArgument(!currTip.equals(ObjectId.zeroId()));
// Walk from the first commit of the branch.
@@ -142,7 +141,7 @@ public class DeleteCommentRewriter implements NoteDbRewriter {
return RevisionNoteMap.parse(
changeNoteJson, legacyChangeNoteRead, changeId, reader, noteMap, PUBLISHED)
.revisionNotes.values().stream()
- .flatMap(n -> n.getComments().stream())
+ .flatMap(n -> n.getEntities().stream())
.collect(toMap(c -> c.key.uuid, Function.identity()));
}
diff --git a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
index aea9555574..495ac659dd 100644
--- a/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
+++ b/java/com/google/gerrit/server/notedb/DeleteZombieCommentsRefs.java
@@ -18,10 +18,10 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
+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.gerrit.server.update.RefUpdateUtil;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 8acc8d4ff5..213613ec9f 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -16,7 +16,6 @@ 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.server.notedb.NoteDbTable.CHANGES;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
@@ -25,24 +24,15 @@ 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.metrics.Timer1;
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.server.ReviewDb;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.StagedResult;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
-import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
@@ -50,53 +40,29 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.ReceiveCommand;
/** View of the draft comments for a single {@link Change} based on the log of its drafts branch. */
public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public interface Factory {
- DraftCommentNotes create(Change change, Account.Id accountId);
-
- DraftCommentNotes createWithAutoRebuildingDisabled(Change.Id changeId, Account.Id accountId);
+ DraftCommentNotes create(Change.Id changeId, Account.Id accountId);
}
- private final Change change;
private final Account.Id author;
- private final NoteDbUpdateManager.Result rebuildResult;
private final Ref ref;
private ImmutableListMultimap<RevId, Comment> comments;
private RevisionNoteMap<ChangeRevisionNote> revisionNoteMap;
@AssistedInject
- DraftCommentNotes(Args args, @Assisted Change change, @Assisted Account.Id author) {
- this(args, change, author, true, null, null);
- }
-
- @AssistedInject
DraftCommentNotes(Args args, @Assisted Change.Id changeId, @Assisted Account.Id author) {
- // PrimaryStorage is unknown; this should only called by
- // PatchLineCommentsUtil#draftByAuthor, which can live with this.
- super(args, changeId, null, false);
- this.change = null;
- this.author = author;
- this.rebuildResult = null;
- this.ref = null;
+ this(args, changeId, author, null);
}
- DraftCommentNotes(
- Args args,
- Change change,
- Account.Id author,
- boolean autoRebuild,
- @Nullable NoteDbUpdateManager.Result rebuildResult,
- @Nullable Ref ref) {
- super(args, change.getId(), PrimaryStorage.of(change), autoRebuild);
- this.change = change;
- this.author = author;
- this.rebuildResult = rebuildResult;
+ DraftCommentNotes(Args args, Change.Id changeId, Account.Id author, @Nullable Ref ref) {
+ super(args, changeId);
+ this.author = requireNonNull(author);
this.ref = ref;
if (ref != null) {
checkArgument(
@@ -164,7 +130,7 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
PatchLineComment.Status.DRAFT);
ListMultimap<RevId, Comment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
for (ChangeRevisionNote rn : revisionNoteMap.revisionNotes.values()) {
- for (Comment c : rn.getComments()) {
+ for (Comment c : rn.getEntities()) {
cs.put(new RevId(c.revId), c);
}
}
@@ -181,79 +147,6 @@ public class DraftCommentNotes extends AbstractChangeNotes<DraftCommentNotes> {
return args.allUsers;
}
- @Override
- protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
- if (rebuildResult != null) {
- StagedResult sr = requireNonNull(rebuildResult.staged());
- return LoadHandle.create(
- ChangeNotesCommit.newStagedRevWalk(repo, sr.allUsersObjects()),
- findNewId(sr.allUsersCommands(), getRefName()));
- } else if (change != null && autoRebuild) {
- NoteDbChangeState state = NoteDbChangeState.parse(change);
- // Only check if this particular user's drafts are up to date, to avoid
- // reading unnecessary refs.
- if (!NoteDbChangeState.areDraftsUpToDate(
- state, new RepoRefCache(repo), getChangeId(), author)) {
- return rebuildAndOpen(repo);
- }
- }
- return super.openHandle(repo);
- }
-
- private static ObjectId findNewId(Iterable<ReceiveCommand> cmds, String refName) {
- for (ReceiveCommand cmd : cmds) {
- if (cmd.getRefName().equals(refName)) {
- return cmd.getNewId();
- }
- }
- return null;
- }
-
- private LoadHandle rebuildAndOpen(Repository repo) throws NoSuchChangeException, IOException {
- Timer1.Context timer = args.metrics.autoRebuildLatency.start(CHANGES);
- try {
- Change.Id cid = getChangeId();
- ReviewDb db = args.db.get();
- ChangeRebuilder rebuilder = args.rebuilder.get();
- NoteDbUpdateManager.Result r;
- try (NoteDbUpdateManager manager = rebuilder.stage(db, cid)) {
- if (manager == null) {
- return super.openHandle(repo); // May be null in tests.
- }
- r = manager.stageAndApplyDelta(change);
- try {
- rebuilder.execute(db, cid, manager);
- repo.scanForRepoChanges();
- } catch (OrmException | IOException e) {
- // See ChangeNotes#rebuildAndOpen.
- logger.atFine().log(
- "Rebuilding change %s via drafts failed: %s", getChangeId(), e.getMessage());
- args.metrics.autoRebuildFailureCount.increment(CHANGES);
- requireNonNull(r.staged());
- return LoadHandle.create(
- ChangeNotesCommit.newStagedRevWalk(repo, r.staged().allUsersObjects()), draftsId(r));
- }
- }
- return LoadHandle.create(ChangeNotesCommit.newRevWalk(repo), draftsId(r));
- } catch (NoSuchChangeException e) {
- return super.openHandle(repo);
- } catch (OrmException e) {
- throw new IOException(e);
- } finally {
- logger.atFine().log(
- "Rebuilt change %s in %s in %s ms via drafts",
- getChangeId(),
- change != null ? "project " + change.getProject() : "unknown project",
- TimeUnit.MILLISECONDS.convert(timer.stop(), TimeUnit.NANOSECONDS));
- }
- }
-
- private ObjectId draftsId(NoteDbUpdateManager.Result r) {
- requireNonNull(r);
- requireNonNull(r.newState());
- return r.newState().getDraftIds().get(author);
- }
-
@VisibleForTesting
NoteMap getNoteMap() {
return revisionNoteMap != null ? revisionNoteMap.noteMap : null;
diff --git a/java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java b/java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java
deleted file mode 100644
index 347ba4863a..0000000000
--- a/java/com/google/gerrit/server/notedb/GwtormChangeBundleReader.java
+++ /dev/null
@@ -1,52 +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.server.notedb;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.notedb.ChangeBundle.Source;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.util.List;
-
-@Singleton
-public class GwtormChangeBundleReader implements ChangeBundleReader {
- @Inject
- GwtormChangeBundleReader() {}
-
- @Override
- @Nullable
- public ChangeBundle fromReviewDb(ReviewDb db, Change.Id id) throws OrmException {
- Change reviewDbChange = db.changes().get(id);
- if (reviewDbChange == null) {
- return null;
- }
-
- // TODO(dborowitz): Figure out how to do this more consistently, e.g. hand-written inner joins.
- List<PatchSetApproval> approvals = db.patchSetApprovals().byChange(id).toList();
- return new ChangeBundle(
- reviewDbChange,
- db.changeMessages().byChange(id),
- db.patchSets().byChange(id),
- approvals,
- db.patchComments().byChange(id),
- ReviewerSet.fromApprovals(approvals),
- Source.REVIEW_DB);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/IntBlob.java b/java/com/google/gerrit/server/notedb/IntBlob.java
new file mode 100644
index 0000000000..cbc933cbc7
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/IntBlob.java
@@ -0,0 +1,136 @@
+// 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 java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
+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.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;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+@AutoValue
+public abstract class IntBlob {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ public static Optional<IntBlob> parse(Repository repo, String refName) throws IOException {
+ try (ObjectReader or = repo.newObjectReader()) {
+ return parse(repo, refName, or);
+ }
+ }
+
+ public static Optional<IntBlob> parse(Repository repo, String refName, RevWalk rw)
+ throws IOException {
+ return parse(repo, refName, rw.getObjectReader());
+ }
+
+ private static Optional<IntBlob> parse(Repository repo, String refName, ObjectReader or)
+ throws IOException {
+ Ref ref = repo.exactRef(refName);
+ if (ref == null) {
+ return Optional.empty();
+ }
+ ObjectId id = ref.getObjectId();
+ ObjectLoader ol = or.open(id, OBJ_BLOB);
+ if (ol.getType() != OBJ_BLOB) {
+ // In theory this should be thrown by open but not all implementations may do it properly
+ // (certainly InMemoryRepository doesn't).
+ throw new IncorrectObjectTypeException(id, OBJ_BLOB);
+ }
+ String str = CharMatcher.whitespace().trimFrom(new String(ol.getCachedBytes(), UTF_8));
+ Integer value = Ints.tryParse(str);
+ if (value == null) {
+ throw new StorageException("invalid value in " + refName + " blob at " + id.name());
+ }
+ return Optional.of(IntBlob.create(id, value));
+ }
+
+ public static RefUpdate tryStore(
+ Repository repo,
+ RevWalk rw,
+ Project.NameKey projectName,
+ String refName,
+ @Nullable ObjectId oldId,
+ int val,
+ GitReferenceUpdated gitRefUpdated)
+ throws IOException {
+ ObjectId newId;
+ try (ObjectInserter ins = repo.newObjectInserter()) {
+ logger.atFine().log(
+ "storing value %d on %s in %s (oldId: %s)",
+ val, refName, projectName, oldId == null ? "null" : oldId.name());
+ newId = ins.insert(OBJ_BLOB, Integer.toString(val).getBytes(UTF_8));
+ ins.flush();
+ logger.atFine().log(
+ "successfully stored %d on %s as %s in %s", val, refName, newId.name(), projectName);
+ }
+ RefUpdate ru = repo.updateRef(refName);
+ if (oldId != null) {
+ ru.setExpectedOldObjectId(oldId);
+ }
+ ru.disableRefLog();
+ ru.setNewObjectId(newId);
+ ru.setForceUpdate(true); // Required for non-commitish updates.
+ RefUpdate.Result result = ru.update(rw);
+ if (refUpdated(result)) {
+ gitRefUpdated.fire(projectName, ru, null);
+ }
+ return ru;
+ }
+
+ public static void store(
+ Repository repo,
+ RevWalk rw,
+ Project.NameKey projectName,
+ String refName,
+ @Nullable ObjectId oldId,
+ int val,
+ GitReferenceUpdated gitRefUpdated)
+ throws IOException {
+ RefUpdateUtil.checkResult(tryStore(repo, rw, projectName, refName, oldId, val, gitRefUpdated));
+ }
+
+ private static boolean refUpdated(RefUpdate.Result result) {
+ return result == RefUpdate.Result.NEW || result == RefUpdate.Result.FORCED;
+ }
+
+ @VisibleForTesting
+ static IntBlob create(AnyObjectId id, int value) {
+ return new AutoValue_IntBlob(id.copy(), value);
+ }
+
+ public abstract ObjectId id();
+
+ public abstract int value();
+}
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
index 819c8ac730..916cc16d79 100644
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
+++ b/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
@@ -21,7 +21,6 @@ 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.Comment.Key;
import com.google.gerrit.reviewdb.client.CommentRange;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RevId;
@@ -71,7 +70,7 @@ public class LegacyChangeNoteRead {
if (p.value >= note.length) {
return ImmutableList.of();
}
- Set<Key> seen = new HashSet<>();
+ Set<Comment.Key> seen = new HashSet<>();
List<Comment> result = new ArrayList<>();
int sizeOfNote = note.length;
byte[] psb = ChangeNoteUtil.PATCH_SET.getBytes(UTF_8);
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
index 291e825087..2d5293f0e2 100644
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
+++ b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
@@ -21,10 +21,10 @@ 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.UsedAt;
import com.google.gerrit.server.config.GerritServerId;
import com.google.inject.Inject;
import java.io.OutputStream;
diff --git a/java/com/google/gerrit/server/notedb/MutableNotesMigration.java b/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
deleted file mode 100644
index 7f4912b125..0000000000
--- a/java/com/google/gerrit/server/notedb/MutableNotesMigration.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// 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.notedb;
-
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.util.function.Function;
-import org.eclipse.jgit.lib.Config;
-
-/**
- * {@link NotesMigration} with additional methods for altering the migration state at runtime.
- *
- * <p>Almost all callers care only about inspecting the migration state, and for safety should not
- * have access to mutation methods, which must be used with extreme care. Those callers should
- * inject {@link NotesMigration}.
- *
- * <p>Some callers, namely the NoteDb migration pipeline and tests, do need to alter the migration
- * state at runtime, and those callers are expected to take the necessary precautions such as
- * keeping the in-memory and on-disk config state in sync. Those callers use this class.
- *
- * <p>Mutations to the {@link MutableNotesMigration} are guaranteed to be instantly visible to all
- * callers that use the non-mutable {@link NotesMigration}. The current implementation accomplishes
- * this by always binding {@link NotesMigration} to {@link MutableNotesMigration} in Guice, so there
- * is just one {@link NotesMigration} instance process-wide.
- */
-@Singleton
-public class MutableNotesMigration extends NotesMigration {
- public static MutableNotesMigration newDisabled() {
- return new MutableNotesMigration(new Config());
- }
-
- public static MutableNotesMigration fromConfig(Config cfg) {
- return new MutableNotesMigration(cfg);
- }
-
- @Inject
- MutableNotesMigration(@GerritServerConfig Config cfg) {
- super(Snapshot.create(cfg));
- }
-
- public MutableNotesMigration setReadChanges(boolean readChanges) {
- return set(b -> b.setReadChanges(readChanges));
- }
-
- public MutableNotesMigration setWriteChanges(boolean writeChanges) {
- return set(b -> b.setWriteChanges(writeChanges));
- }
-
- public MutableNotesMigration setReadChangeSequence(boolean readChangeSequence) {
- return set(b -> b.setReadChangeSequence(readChangeSequence));
- }
-
- public MutableNotesMigration setChangePrimaryStorage(PrimaryStorage changePrimaryStorage) {
- return set(b -> b.setChangePrimaryStorage(changePrimaryStorage));
- }
-
- public MutableNotesMigration setDisableChangeReviewDb(boolean disableChangeReviewDb) {
- return set(b -> b.setDisableChangeReviewDb(disableChangeReviewDb));
- }
-
- public MutableNotesMigration setFailOnLoadForTest(boolean failOnLoadForTest) {
- return set(b -> b.setFailOnLoadForTest(failOnLoadForTest));
- }
-
- /**
- * Set the in-memory values returned by this instance to match the given state.
- *
- * <p>This method is only intended for use by {@link
- * com.google.gerrit.server.notedb.rebuild.NoteDbMigrator}.
- *
- * <p>This <em>only</em> modifies the in-memory state; if this instance was initialized from a
- * file-based config, the underlying storage is not updated. Callers are responsible for managing
- * the underlying storage on their own.
- */
- public MutableNotesMigration setFrom(NotesMigrationState state) {
- snapshot.set(state.snapshot());
- return this;
- }
-
- /** @see #setFrom(NotesMigrationState) */
- public MutableNotesMigration setFrom(NotesMigration other) {
- snapshot.set(other.snapshot.get());
- return this;
- }
-
- private MutableNotesMigration set(Function<Snapshot.Builder, Snapshot.Builder> f) {
- snapshot.updateAndGet(s -> f.apply(s.toBuilder()).build());
- return this;
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbChangeState.java b/java/com/google/gerrit/server/notedb/NoteDbChangeState.java
deleted file mode 100644
index cda6cee298..0000000000
--- a/java/com/google/gerrit/server/notedb/NoteDbChangeState.java
+++ /dev/null
@@ -1,477 +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.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.gerrit.reviewdb.client.RefNames.refsDraftComments;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.NOTE_DB;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
-import static java.util.Objects.requireNonNull;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.primitives.Longs;
-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.server.ReviewDbUtil;
-import com.google.gerrit.server.git.RefCache;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmRuntimeException;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * The state of all relevant NoteDb refs across all repos corresponding to a given Change entity.
- *
- * <p>Stored serialized in the {@code Change#noteDbState} field, and used to determine whether the
- * state in NoteDb is out of date.
- *
- * <p>Serialized in one of the forms:
- *
- * <ul>
- * <li>[meta-sha],[account1]=[drafts-sha],[account2]=[drafts-sha]...
- * <li>R,[meta-sha],[account1]=[drafts-sha],[account2]=[drafts-sha]...
- * <li>R=[read-only-until],[meta-sha],[account1]=[drafts-sha],[account2]=[drafts-sha]...
- * <li>N
- * <li>N=[read-only-until]
- * </ul>
- *
- * in numeric account ID order, with hex SHA-1s for human readability.
- */
-public class NoteDbChangeState {
- public static final String NOTE_DB_PRIMARY_STATE = "N";
-
- public enum PrimaryStorage {
- REVIEW_DB('R'),
- NOTE_DB('N');
-
- private final char code;
-
- PrimaryStorage(char code) {
- this.code = code;
- }
-
- public static PrimaryStorage of(@Nullable Change c) {
- return of(NoteDbChangeState.parse(c));
- }
-
- public static PrimaryStorage of(@Nullable NoteDbChangeState s) {
- return s != null ? s.getPrimaryStorage() : REVIEW_DB;
- }
- }
-
- @AutoValue
- public abstract static class Delta {
- @VisibleForTesting
- public static Delta create(
- Change.Id changeId,
- Optional<ObjectId> newChangeMetaId,
- Map<Account.Id, ObjectId> newDraftIds) {
- if (newDraftIds == null) {
- newDraftIds = ImmutableMap.of();
- }
- return new AutoValue_NoteDbChangeState_Delta(
- changeId, newChangeMetaId, ImmutableMap.copyOf(newDraftIds));
- }
-
- abstract Change.Id changeId();
-
- abstract Optional<ObjectId> newChangeMetaId();
-
- abstract ImmutableMap<Account.Id, ObjectId> newDraftIds();
- }
-
- @AutoValue
- public abstract static class RefState {
- @VisibleForTesting
- public static RefState create(ObjectId changeMetaId, Map<Account.Id, ObjectId> draftIds) {
- return new AutoValue_NoteDbChangeState_RefState(
- changeMetaId.copy(),
- ImmutableMap.copyOf(Maps.filterValues(draftIds, id -> !ObjectId.zeroId().equals(id))));
- }
-
- private static Optional<RefState> parse(Change.Id changeId, List<String> parts) {
- checkArgument(!parts.isEmpty(), "missing state string for change %s", changeId);
- ObjectId changeMetaId = ObjectId.fromString(parts.get(0));
- Map<Account.Id, ObjectId> draftIds = Maps.newHashMapWithExpectedSize(parts.size() - 1);
- Splitter s = Splitter.on('=');
- for (int i = 1; i < parts.size(); i++) {
- String p = parts.get(i);
- List<String> draftParts = s.splitToList(p);
- checkArgument(
- draftParts.size() == 2, "invalid draft state part for change %s: %s", changeId, p);
- Optional<Account.Id> accountId = Account.Id.tryParse(draftParts.get(0));
- checkArgument(
- accountId.isPresent(),
- "invalid account ID in draft state part for change %s: %s",
- changeId,
- p);
- draftIds.put(accountId.get(), ObjectId.fromString(draftParts.get(1)));
- }
- return Optional.of(create(changeMetaId, draftIds));
- }
-
- abstract ObjectId changeMetaId();
-
- abstract ImmutableMap<Account.Id, ObjectId> draftIds();
-
- @Override
- public final String toString() {
- return appendTo(new StringBuilder()).toString();
- }
-
- StringBuilder appendTo(StringBuilder sb) {
- sb.append(changeMetaId().name());
- for (Account.Id id : ReviewDbUtil.intKeyOrdering().sortedCopy(draftIds().keySet())) {
- sb.append(',').append(id.get()).append('=').append(draftIds().get(id).name());
- }
- return sb;
- }
- }
-
- public static NoteDbChangeState parse(@Nullable Change c) {
- return c != null ? parse(c.getId(), c.getNoteDbState()) : null;
- }
-
- @VisibleForTesting
- public static NoteDbChangeState parse(Change.Id id, @Nullable String str) {
- if (Strings.isNullOrEmpty(str)) {
- // Return null rather than Optional as this is what goes in the field in
- // ReviewDb.
- return null;
- }
- List<String> parts = Splitter.on(',').splitToList(str);
- String first = parts.get(0);
- Optional<Timestamp> readOnlyUntil = parseReadOnlyUntil(id, str, first);
-
- // Only valid NOTE_DB state is "N".
- if (parts.size() == 1 && first.charAt(0) == NOTE_DB.code) {
- return new NoteDbChangeState(id, NOTE_DB, Optional.empty(), readOnlyUntil);
- }
-
- // Otherwise it must be REVIEW_DB, either "R,<RefState>" or just
- // "<RefState>". Allow length > 0 for forward compatibility.
- if (first.length() > 0) {
- Optional<RefState> refState;
- if (first.charAt(0) == REVIEW_DB.code) {
- refState = RefState.parse(id, parts.subList(1, parts.size()));
- } else {
- refState = RefState.parse(id, parts);
- }
- return new NoteDbChangeState(id, REVIEW_DB, refState, readOnlyUntil);
- }
- throw invalidState(id, str);
- }
-
- private static Optional<Timestamp> parseReadOnlyUntil(
- Change.Id id, String fullStr, String first) {
- if (first.length() > 2 && first.charAt(1) == '=') {
- Long ts = Longs.tryParse(first.substring(2));
- if (ts == null) {
- throw invalidState(id, fullStr);
- }
- return Optional.of(new Timestamp(ts));
- }
- return Optional.empty();
- }
-
- private static IllegalArgumentException invalidState(Change.Id id, String str) {
- return new IllegalArgumentException("invalid state string for change " + id + ": " + str);
- }
-
- /**
- * Apply a delta to the state stored in a change entity.
- *
- * <p>This method does not check whether the old state was read-only; it is up to the caller to
- * not violate read-only semantics when storing the change back in ReviewDb.
- *
- * @param change change entity. The delta is applied against this entity's {@code noteDbState} and
- * the new state is stored back in the entity as a side effect.
- * @param delta delta to apply.
- * @return new state, equivalent to what is stored in {@code change} as a side effect.
- */
- public static NoteDbChangeState applyDelta(Change change, Delta delta) {
- if (delta == null) {
- return null;
- }
- String oldStr = change.getNoteDbState();
- if (oldStr == null && !delta.newChangeMetaId().isPresent()) {
- // Neither an old nor a new meta ID was present, most likely because we
- // aren't writing a NoteDb graph at all for this change at this point. No
- // point in proceeding.
- return null;
- }
- NoteDbChangeState oldState = parse(change.getId(), oldStr);
- if (oldState != null && oldState.getPrimaryStorage() == NOTE_DB) {
- // NOTE_DB state doesn't include RefState, so applying a delta is a no-op.
- return oldState;
- }
-
- ObjectId changeMetaId;
- if (delta.newChangeMetaId().isPresent()) {
- changeMetaId = delta.newChangeMetaId().get();
- if (changeMetaId.equals(ObjectId.zeroId())) {
- change.setNoteDbState(null);
- return null;
- }
- } else {
- changeMetaId = oldState.getChangeMetaId();
- }
-
- Map<Account.Id, ObjectId> draftIds = new HashMap<>();
- if (oldState != null) {
- draftIds.putAll(oldState.getDraftIds());
- }
- for (Map.Entry<Account.Id, ObjectId> e : delta.newDraftIds().entrySet()) {
- if (e.getValue().equals(ObjectId.zeroId())) {
- draftIds.remove(e.getKey());
- } else {
- draftIds.put(e.getKey(), e.getValue());
- }
- }
-
- NoteDbChangeState state =
- new NoteDbChangeState(
- change.getId(),
- oldState != null ? oldState.getPrimaryStorage() : REVIEW_DB,
- Optional.of(RefState.create(changeMetaId, draftIds)),
- // Copy old read-only deadline rather than advancing it; the caller is
- // still responsible for finishing the rest of its work before the lease
- // runs out.
- oldState != null ? oldState.getReadOnlyUntil() : Optional.empty());
- change.setNoteDbState(state.toString());
- return state;
- }
-
- // TODO(dborowitz): Ugly. Refactor these static methods into a Checker class
- // or something. They do not belong in NoteDbChangeState itself because:
- // - need to inject Config but don't want a whole Factory
- // - can't be methods on NoteDbChangeState because state is nullable (though
- // we could also solve this by inventing an empty-but-non-null state)
- // Also we should clean up duplicated code between static/non-static methods.
- public static boolean isChangeUpToDate(
- @Nullable NoteDbChangeState state, RefCache changeRepoRefs, Change.Id changeId)
- throws IOException {
- if (PrimaryStorage.of(state) == NOTE_DB) {
- return true; // Primary storage is NoteDb, up to date by definition.
- }
- if (state == null) {
- return !changeRepoRefs.get(changeMetaRef(changeId)).isPresent();
- }
- return state.isChangeUpToDate(changeRepoRefs);
- }
-
- public static boolean areDraftsUpToDate(
- @Nullable NoteDbChangeState state,
- RefCache draftsRepoRefs,
- Change.Id changeId,
- Account.Id accountId)
- throws IOException {
- if (PrimaryStorage.of(state) == NOTE_DB) {
- return true; // Primary storage is NoteDb, up to date by definition.
- }
- if (state == null) {
- return !draftsRepoRefs.get(refsDraftComments(changeId, accountId)).isPresent();
- }
- return state.areDraftsUpToDate(draftsRepoRefs, accountId);
- }
-
- public static long getReadOnlySkew(Config cfg) {
- return cfg.getTimeUnit("notedb", null, "maxTimestampSkew", 1000, TimeUnit.MILLISECONDS);
- }
-
- static Timestamp timeForReadOnlyCheck(long skewMs) {
- // Subtract some slop in case the machine that set the change's read-only
- // lease has a clock behind ours.
- return new Timestamp(TimeUtil.nowMs() - skewMs);
- }
-
- public static void checkNotReadOnly(@Nullable Change change, long skewMs) {
- checkNotReadOnly(parse(change), skewMs);
- }
-
- public static void checkNotReadOnly(@Nullable NoteDbChangeState state, long skewMs) {
- if (state == null) {
- return; // No state means ReviewDb primary non-read-only.
- } else if (state.isReadOnly(timeForReadOnlyCheck(skewMs))) {
- throw new OrmRuntimeException(
- "change "
- + state.getChangeId()
- + " is read-only until "
- + state.getReadOnlyUntil().get());
- }
- }
-
- private final Change.Id changeId;
- private final PrimaryStorage primaryStorage;
- private final Optional<RefState> refState;
- private final Optional<Timestamp> readOnlyUntil;
-
- public NoteDbChangeState(
- Change.Id changeId,
- PrimaryStorage primaryStorage,
- Optional<RefState> refState,
- Optional<Timestamp> readOnlyUntil) {
- this.changeId = requireNonNull(changeId);
- this.primaryStorage = requireNonNull(primaryStorage);
- this.refState = requireNonNull(refState);
- this.readOnlyUntil = requireNonNull(readOnlyUntil);
-
- switch (primaryStorage) {
- case REVIEW_DB:
- checkArgument(
- refState.isPresent(),
- "expected RefState for change %s with primary storage %s",
- changeId,
- primaryStorage);
- break;
- case NOTE_DB:
- checkArgument(
- !refState.isPresent(),
- "expected no RefState for change %s with primary storage %s",
- changeId,
- primaryStorage);
- break;
- default:
- throw new IllegalStateException("invalid PrimaryStorage: " + primaryStorage);
- }
- }
-
- public PrimaryStorage getPrimaryStorage() {
- return primaryStorage;
- }
-
- public boolean isChangeUpToDate(RefCache changeRepoRefs) throws IOException {
- if (primaryStorage == NOTE_DB) {
- return true; // Primary storage is NoteDb, up to date by definition.
- }
- Optional<ObjectId> id = changeRepoRefs.get(changeMetaRef(changeId));
- if (!id.isPresent()) {
- return getChangeMetaId().equals(ObjectId.zeroId());
- }
- return id.get().equals(getChangeMetaId());
- }
-
- public boolean areDraftsUpToDate(RefCache draftsRepoRefs, Account.Id accountId)
- throws IOException {
- if (primaryStorage == NOTE_DB) {
- return true; // Primary storage is NoteDb, up to date by definition.
- }
- Optional<ObjectId> id = draftsRepoRefs.get(refsDraftComments(changeId, accountId));
- if (!id.isPresent()) {
- return !getDraftIds().containsKey(accountId);
- }
- return id.get().equals(getDraftIds().get(accountId));
- }
-
- public boolean isUpToDate(RefCache changeRepoRefs, RefCache draftsRepoRefs) throws IOException {
- if (primaryStorage == NOTE_DB) {
- return true; // Primary storage is NoteDb, up to date by definition.
- }
- if (!isChangeUpToDate(changeRepoRefs)) {
- return false;
- }
- for (Account.Id accountId : getDraftIds().keySet()) {
- if (!areDraftsUpToDate(draftsRepoRefs, accountId)) {
- return false;
- }
- }
- return true;
- }
-
- public boolean isReadOnly(Timestamp now) {
- return readOnlyUntil.isPresent() && now.before(readOnlyUntil.get());
- }
-
- public Optional<Timestamp> getReadOnlyUntil() {
- return readOnlyUntil;
- }
-
- public NoteDbChangeState withReadOnlyUntil(Timestamp ts) {
- return new NoteDbChangeState(changeId, primaryStorage, refState, Optional.of(ts));
- }
-
- public Change.Id getChangeId() {
- return changeId;
- }
-
- public ObjectId getChangeMetaId() {
- return refState().changeMetaId();
- }
-
- public ImmutableMap<Account.Id, ObjectId> getDraftIds() {
- return refState().draftIds();
- }
-
- public Optional<RefState> getRefState() {
- return refState;
- }
-
- private RefState refState() {
- checkState(refState.isPresent(), "state for %s has no RefState: %s", changeId, this);
- return refState.get();
- }
-
- @Override
- public String toString() {
- switch (primaryStorage) {
- case REVIEW_DB:
- if (!readOnlyUntil.isPresent()) {
- // Don't include enum field, just IDs (though parse would accept it).
- return refState().toString();
- }
- return primaryStorage.code + "=" + readOnlyUntil.get().getTime() + "," + refState.get();
- case NOTE_DB:
- if (!readOnlyUntil.isPresent()) {
- return NOTE_DB_PRIMARY_STATE;
- }
- return primaryStorage.code + "=" + readOnlyUntil.get().getTime();
- default:
- throw new IllegalArgumentException("Unsupported PrimaryStorage: " + primaryStorage);
- }
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(changeId, primaryStorage, refState, readOnlyUntil);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof NoteDbChangeState)) {
- return false;
- }
- NoteDbChangeState s = (NoteDbChangeState) o;
- return changeId.equals(s.changeId)
- && primaryStorage.equals(s.primaryStorage)
- && refState.equals(s.refState)
- && readOnlyUntil.equals(s.readOnlyUntil);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbMetrics.java b/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
index be06d1197d..61f475f3a9 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbMetrics.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.notedb;
-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;
@@ -44,16 +43,6 @@ class NoteDbMetrics {
*/
final Timer1<NoteDbTable> parseLatency;
- /**
- * Latency due to auto-rebuilding entities when out of date.
- *
- * <p>Excludes latency from reading ref to check whether the entity is up to date.
- */
- final Timer1<NoteDbTable> autoRebuildLatency;
-
- /** Count of auto-rebuild attempts that failed. */
- final Counter1<NoteDbTable> autoRebuildFailureCount;
-
@Inject
NoteDbMetrics(MetricMaker metrics) {
Field<NoteDbTable> view = Field.ofEnum(NoteDbTable.class, "table");
@@ -89,19 +78,5 @@ class NoteDbMetrics {
.setCumulative()
.setUnit(Units.MICROSECONDS),
view);
-
- autoRebuildLatency =
- metrics.newTimer(
- "notedb/auto_rebuild_latency",
- new Description("NoteDb auto-rebuilding latency by table")
- .setCumulative()
- .setUnit(Units.MILLISECONDS),
- view);
-
- autoRebuildFailureCount =
- metrics.newCounter(
- "notedb/auto_rebuild_failure_count",
- new Description("NoteDb auto-rebuilding attempts that failed by table").setCumulative(),
- view);
}
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbModule.java b/java/com/google/gerrit/server/notedb/NoteDbModule.java
index c76c39b818..d8a5fd57e6 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbModule.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbModule.java
@@ -17,33 +17,21 @@ package com.google.gerrit.server.notedb;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Id;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gerrit.server.notedb.rebuild.NotesMigrationStateListener;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
-import org.eclipse.jgit.lib.Config;
public class NoteDbModule extends FactoryModule {
- private final Config cfg;
private final boolean useTestBindings;
- static NoteDbModule forTest(Config cfg) {
- return new NoteDbModule(cfg, true);
+ static NoteDbModule forTest() {
+ return new NoteDbModule(true);
}
- public NoteDbModule(Config cfg) {
- this(cfg, false);
+ public NoteDbModule() {
+ this(false);
}
- private NoteDbModule(Config cfg, boolean useTestBindings) {
- this.cfg = cfg;
+ private NoteDbModule(boolean useTestBindings) {
this.useTestBindings = useTestBindings;
}
@@ -56,60 +44,13 @@ public class NoteDbModule extends FactoryModule {
factory(NoteDbUpdateManager.Factory.class);
factory(RobotCommentNotes.Factory.class);
factory(RobotCommentUpdate.Factory.class);
- DynamicSet.setOf(binder(), NotesMigrationStateListener.class);
if (!useTestBindings) {
install(ChangeNotesCache.module());
- if (cfg.getBoolean("noteDb", null, "testRebuilderWrapper", false)) {
- // Yes, another variety of test bindings with a different way of
- // configuring it.
- bind(ChangeRebuilder.class).to(TestChangeRebuilderWrapper.class);
- } else {
- bind(ChangeRebuilder.class).to(ChangeRebuilderImpl.class);
- }
} else {
- bind(ChangeRebuilder.class)
- .toInstance(
- new ChangeRebuilder(null) {
- @Override
- public Result rebuild(ReviewDb db, Change.Id changeId) {
- return null;
- }
-
- @Override
- public Result rebuildEvenIfReadOnly(ReviewDb db, Id changeId) {
- return null;
- }
-
- @Override
- public Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle) {
- return null;
- }
-
- @Override
- public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId) {
- return null;
- }
-
- @Override
- public Result execute(
- ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager) {
- return null;
- }
-
- @Override
- public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle) {
- // Do nothing.
- }
-
- @Override
- public void rebuildReviewDb(ReviewDb db, Project.NameKey project, Id changeId) {
- // Do nothing.
- }
- });
bind(new TypeLiteral<Cache<ChangeNotesCache.Key, ChangeNotesState>>() {})
.annotatedWith(Names.named(ChangeNotesCache.CACHE_NAME))
- .toInstance(CacheBuilder.newBuilder().<ChangeNotesCache.Key, ChangeNotesState>build());
+ .toInstance(CacheBuilder.newBuilder().build());
}
}
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbRewriter.java b/java/com/google/gerrit/server/notedb/NoteDbRewriter.java
index 3c7b0a31fa..19754d1a6b 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbRewriter.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbRewriter.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.notedb;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
@@ -35,5 +34,5 @@ public interface NoteDbRewriter {
* @return the {@code ObjectId} of the ref's new tip commit.
*/
ObjectId rewriteCommitHistory(RevWalk revWalk, ObjectInserter inserter, ObjectId currTip)
- throws IOException, ConfigInvalidException, OrmException;
+ throws IOException, ConfigInvalidException;
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
index 12448fb95b..ea42a9dcd5 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
@@ -17,21 +17,18 @@ 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.REFS_DRAFT_COMMENTS;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
import static java.util.Objects.requireNonNull;
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.HashBasedTable;
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.common.collect.Table;
import com.google.gerrit.common.Nullable;
+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.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
@@ -40,18 +37,13 @@ import com.google.gerrit.server.config.AllUsersName;
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.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.update.ChainedReceiveCommands;
-import com.google.gerrit.server.update.RefUpdateUtil;
import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gwtorm.server.OrmConcurrencyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
@@ -78,8 +70,6 @@ import org.eclipse.jgit.transport.ReceiveCommand;
* {@link #stage()}.
*/
public class NoteDbUpdateManager implements AutoCloseable {
- public static final String CHANGES_READ_ONLY = "NoteDb changes are read-only";
-
private static final ImmutableList<String> PACKAGE_PREFIXES =
ImmutableList.of("com.google.gerrit.server.", "com.google.gerrit.");
private static final ImmutableSet<String> SERVLET_NAMES =
@@ -90,77 +80,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
NoteDbUpdateManager create(Project.NameKey projectName);
}
- @AutoValue
- public abstract static class StagedResult {
- private static StagedResult create(
- Change.Id id, NoteDbChangeState.Delta delta, OpenRepo changeRepo, OpenRepo allUsersRepo) {
- ImmutableList<ReceiveCommand> changeCommands = ImmutableList.of();
- ImmutableList<InsertedObject> changeObjects = ImmutableList.of();
- if (changeRepo != null) {
- changeCommands = changeRepo.getCommandsSnapshot();
- changeObjects = changeRepo.getInsertedObjects();
- }
- ImmutableList<ReceiveCommand> allUsersCommands = ImmutableList.of();
- ImmutableList<InsertedObject> allUsersObjects = ImmutableList.of();
- if (allUsersRepo != null) {
- allUsersCommands = allUsersRepo.getCommandsSnapshot();
- allUsersObjects = allUsersRepo.getInsertedObjects();
- }
- return new AutoValue_NoteDbUpdateManager_StagedResult(
- id, delta,
- changeCommands, changeObjects,
- allUsersCommands, allUsersObjects);
- }
-
- public abstract Change.Id id();
-
- @Nullable
- public abstract NoteDbChangeState.Delta delta();
-
- public abstract ImmutableList<ReceiveCommand> changeCommands();
-
- /**
- * Objects inserted into the change repo for this change.
- *
- * <p>Includes all objects inserted for any change in this repo that may have been processed by
- * the corresponding {@link NoteDbUpdateManager} instance, not just those objects that were
- * inserted to handle this specific change's updates.
- *
- * @return inserted objects, or null if the corresponding {@link NoteDbUpdateManager} was
- * configured not to {@link NoteDbUpdateManager#setSaveObjects(boolean) save objects}.
- */
- @Nullable
- public abstract ImmutableList<InsertedObject> changeObjects();
-
- public abstract ImmutableList<ReceiveCommand> allUsersCommands();
-
- /**
- * Objects inserted into the All-Users repo for this change.
- *
- * <p>Includes all objects inserted into All-Users for any change that may have been processed
- * by the corresponding {@link NoteDbUpdateManager} instance, not just those objects that were
- * inserted to handle this specific change's updates.
- *
- * @return inserted objects, or null if the corresponding {@link NoteDbUpdateManager} was
- * configured not to {@link NoteDbUpdateManager#setSaveObjects(boolean) save objects}.
- */
- @Nullable
- public abstract ImmutableList<InsertedObject> allUsersObjects();
- }
-
- @AutoValue
- public abstract static class Result {
- static Result create(NoteDbUpdateManager.StagedResult staged, NoteDbChangeState newState) {
- return new AutoValue_NoteDbUpdateManager_Result(newState, staged);
- }
-
- @Nullable
- public abstract NoteDbChangeState newState();
-
- @Nullable
- abstract NoteDbUpdateManager.StagedResult staged();
- }
-
public static class OpenRepo implements AutoCloseable {
public final Repository repo;
public final RevWalk rw;
@@ -171,15 +90,13 @@ public class NoteDbUpdateManager implements AutoCloseable {
@Nullable private final ObjectInserter finalIns;
private final boolean close;
- private final boolean saveObjects;
private OpenRepo(
Repository repo,
RevWalk rw,
@Nullable ObjectInserter ins,
ChainedReceiveCommands cmds,
- boolean close,
- boolean saveObjects) {
+ boolean close) {
ObjectReader reader = rw.getObjectReader();
checkArgument(
ins == null || reader.getCreatedFromInserter() == ins,
@@ -188,44 +105,25 @@ public class NoteDbUpdateManager implements AutoCloseable {
reader.getCreatedFromInserter());
this.repo = requireNonNull(repo);
- if (saveObjects) {
- this.inMemIns = new InMemoryInserter(rw.getObjectReader());
- this.tempIns = inMemIns;
- } else {
- checkArgument(ins != null);
- this.inMemIns = null;
- this.tempIns = ins;
- }
+ 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;
- this.saveObjects = saveObjects;
}
public Optional<ObjectId> getObjectId(String refName) throws IOException {
return cmds.get(refName);
}
- ImmutableList<ReceiveCommand> getCommandsSnapshot() {
- return ImmutableList.copyOf(cmds.getCommands().values());
- }
-
- @Nullable
- ImmutableList<InsertedObject> getInsertedObjects() {
- return saveObjects ? inMemIns.getInsertedObjects() : null;
- }
-
void flush() throws IOException {
flushToFinalInserter();
finalIns.flush();
}
void flushToFinalInserter() throws IOException {
- if (!saveObjects) {
- return;
- }
checkState(finalIns != null);
for (InsertedObject obj : inMemIns.getInsertedObjects()) {
finalIns.insert(obj.type(), obj.data().toByteArray());
@@ -248,7 +146,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
private final Provider<PersonIdent> serverIdent;
private final GitRepositoryManager repoManager;
- private final NotesMigration migration;
private final AllUsersName allUsersName;
private final NoteDbMetrics metrics;
private final Project.NameKey projectName;
@@ -260,10 +157,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
private OpenRepo changeRepo;
private OpenRepo allUsersRepo;
- private Map<Change.Id, StagedResult> staged;
- private boolean checkExpectedState = true;
- private boolean saveObjects = true;
- private boolean atomicRefUpdates = true;
+ private boolean executed;
private String refLogMessage;
private PersonIdent refLogIdent;
private PushCertificate pushCert;
@@ -272,13 +166,11 @@ public class NoteDbUpdateManager implements AutoCloseable {
NoteDbUpdateManager(
@GerritPersonIdent Provider<PersonIdent> serverIdent,
GitRepositoryManager repoManager,
- NotesMigration migration,
AllUsersName allUsersName,
NoteDbMetrics metrics,
@Assisted Project.NameKey projectName) {
this.serverIdent = serverIdent;
this.repoManager = repoManager;
- this.migration = migration;
this.allUsersName = allUsersName;
this.metrics = metrics;
this.projectName = projectName;
@@ -309,50 +201,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
public NoteDbUpdateManager setChangeRepo(
Repository repo, RevWalk rw, @Nullable ObjectInserter ins, ChainedReceiveCommands cmds) {
checkState(changeRepo == null, "change repo already initialized");
- changeRepo = new OpenRepo(repo, rw, ins, cmds, false, saveObjects);
- return this;
- }
-
- public NoteDbUpdateManager setAllUsersRepo(
- Repository repo, RevWalk rw, @Nullable ObjectInserter ins, ChainedReceiveCommands cmds) {
- checkState(allUsersRepo == null, "All-Users repo already initialized");
- allUsersRepo = new OpenRepo(repo, rw, ins, cmds, false, saveObjects);
- return this;
- }
-
- public NoteDbUpdateManager setCheckExpectedState(boolean checkExpectedState) {
- this.checkExpectedState = checkExpectedState;
- return this;
- }
-
- /**
- * Set whether to save objects and make them available in {@link StagedResult}s.
- *
- * <p>If set, all objects inserted into all repos managed by this instance will be buffered in
- * memory, and the {@link StagedResult}s will return non-null lists from {@link
- * StagedResult#changeObjects()} and {@link StagedResult#allUsersObjects()}.
- *
- * <p>Not recommended if modifying a large number of changes with a single manager.
- *
- * @param saveObjects whether to save objects; defaults to true.
- * @return this
- */
- public NoteDbUpdateManager setSaveObjects(boolean saveObjects) {
- this.saveObjects = saveObjects;
- return this;
- }
-
- /**
- * Set whether to use atomic ref updates.
- *
- * <p>Can be set to false when the change updates represented by this manager aren't logically
- * related, e.g. when the updater is only used to group objects together with a single inserter.
- *
- * @param atomicRefUpdates whether to use atomic ref updates; defaults to true.
- * @return this
- */
- public NoteDbUpdateManager setAtomicRefUpdates(boolean atomicRefUpdates) {
- this.atomicRefUpdates = atomicRefUpdates;
+ changeRepo = new OpenRepo(repo, rw, ins, cmds, false);
return this;
}
@@ -385,16 +234,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
return this;
}
- public OpenRepo getChangeRepo() throws IOException {
- initChangeRepo();
- return changeRepo;
- }
-
- public OpenRepo getAllUsersRepo() throws IOException {
- initAllUsersRepo();
- return allUsersRepo;
- }
-
private void initChangeRepo() throws IOException {
if (changeRepo == null) {
changeRepo = openRepo(projectName);
@@ -412,7 +251,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
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, saveObjects) {
+ return new OpenRepo(repo, rw, ins, new ChainedReceiveCommands(repo), true) {
@Override
public void close() {
reader.close();
@@ -423,9 +262,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
private boolean isEmpty() {
- if (!migration.commitChangeWrites()) {
- return true;
- }
return changeUpdates.isEmpty()
&& draftUpdates.isEmpty()
&& robotCommentUpdates.isEmpty()
@@ -448,12 +284,12 @@ public class NoteDbUpdateManager implements AutoCloseable {
* @param update the update to add.
*/
public void add(ChangeUpdate update) {
+ checkNotExecuted();
checkArgument(
update.getProjectName().equals(projectName),
"update for project %s cannot be added to manager for project %s",
update.getProjectName(),
projectName);
- checkState(staged == null, "cannot add new update after staging");
checkArgument(
!rewriters.containsKey(update.getRefName()),
"cannot update & rewrite ref %s in one BatchUpdate",
@@ -500,105 +336,36 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
public void add(ChangeDraftUpdate draftUpdate) {
- checkState(staged == null, "cannot add new update after staging");
+ checkNotExecuted();
draftUpdates.put(draftUpdate.getRefName(), draftUpdate);
}
public void deleteChange(Change.Id id) {
- checkState(staged == null, "cannot add new change to delete after staging");
+ checkNotExecuted();
toDelete.add(id);
}
/**
* Stage updates in the manager's internal list of commands.
*
- * @return map of the state that would get written to the applicable repo(s) for each affected
- * change.
- * @throws OrmException if a database layer error occurs.
* @throws IOException if a storage layer error occurs.
*/
- public Map<Change.Id, StagedResult> stage() throws OrmException, IOException {
- if (staged != null) {
- return staged;
- }
+ private void stage() throws IOException {
try (Timer1.Context timer = metrics.stageUpdateLatency.start(CHANGES)) {
- staged = new HashMap<>();
if (isEmpty()) {
- return staged;
+ return;
}
initChangeRepo();
if (!draftUpdates.isEmpty() || !toDelete.isEmpty()) {
initAllUsersRepo();
}
- checkExpectedState();
addCommands();
-
- Table<Change.Id, Account.Id, ObjectId> allDraftIds = getDraftIds();
- Set<Change.Id> changeIds = new HashSet<>();
- for (ReceiveCommand cmd : changeRepo.getCommandsSnapshot()) {
- Change.Id changeId = Change.Id.fromRef(cmd.getRefName());
- if (changeId == null || !cmd.getRefName().equals(RefNames.changeMetaRef(changeId))) {
- // Not a meta ref update, likely due to a repo update along with the change meta update.
- continue;
- }
- changeIds.add(changeId);
- Optional<ObjectId> metaId = Optional.of(cmd.getNewId());
- staged.put(
- changeId,
- StagedResult.create(
- changeId,
- NoteDbChangeState.Delta.create(
- changeId, metaId, allDraftIds.rowMap().remove(changeId)),
- changeRepo,
- allUsersRepo));
- }
-
- for (Map.Entry<Change.Id, Map<Account.Id, ObjectId>> e : allDraftIds.rowMap().entrySet()) {
- // If a change remains in the table at this point, it means we are
- // updating its drafts but not the change itself.
- StagedResult r =
- StagedResult.create(
- e.getKey(),
- NoteDbChangeState.Delta.create(e.getKey(), Optional.empty(), e.getValue()),
- changeRepo,
- allUsersRepo);
- checkState(
- r.changeCommands().isEmpty(),
- "should not have change commands when updating only drafts: %s",
- r);
- staged.put(r.id(), r);
- }
-
- return staged;
}
}
- public Result stageAndApplyDelta(Change change) throws OrmException, IOException {
- StagedResult sr = stage().get(change.getId());
- NoteDbChangeState newState =
- NoteDbChangeState.applyDelta(change, sr != null ? sr.delta() : null);
- return Result.create(sr, newState);
- }
-
- private Table<Change.Id, Account.Id, ObjectId> getDraftIds() {
- Table<Change.Id, Account.Id, ObjectId> draftIds = HashBasedTable.create();
- if (allUsersRepo == null) {
- return draftIds;
- }
- for (ReceiveCommand cmd : allUsersRepo.getCommandsSnapshot()) {
- String r = cmd.getRefName();
- if (r.startsWith(REFS_DRAFT_COMMENTS)) {
- Change.Id changeId = Change.Id.fromRefPart(r.substring(REFS_DRAFT_COMMENTS.length()));
- Account.Id accountId = Account.Id.fromRefSuffix(r);
- checkDraftRef(accountId != null && changeId != null, r);
- draftIds.put(changeId, accountId, cmd.getNewId());
- }
- }
- return draftIds;
- }
-
public void flush() throws IOException {
+ checkNotExecuted();
if (changeRepo != null) {
changeRepo.flush();
}
@@ -608,17 +375,15 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
@Nullable
- public BatchRefUpdate execute() throws OrmException, IOException {
+ public BatchRefUpdate execute() throws IOException {
return execute(false);
}
@Nullable
- public BatchRefUpdate execute(boolean dryrun) throws OrmException, IOException {
- // Check before even inspecting the list, as this is a programmer error.
- if (migration.failChangeWrites()) {
- throw new OrmException(CHANGES_READ_ONLY);
- }
+ public BatchRefUpdate execute(boolean dryrun) throws IOException {
+ checkNotExecuted();
if (isEmpty()) {
+ executed = true;
return null;
}
try (Timer1.Context timer = metrics.updateLatency.start(CHANGES)) {
@@ -633,6 +398,7 @@ 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);
+ executed = true;
return result;
} finally {
close();
@@ -660,7 +426,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
bru.setRefLogMessage(firstNonNull(guessRestApiHandler(), "Update NoteDb refs"), false);
}
bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent.get());
- bru.setAtomic(atomicRefUpdates);
+ bru.setAtomic(true);
or.cmds.addTo(bru);
bru.setAllowNonFastForwards(true);
@@ -709,7 +475,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
return -1;
}
- private void addCommands() throws OrmException, IOException {
+ private void addCommands() throws IOException {
if (isEmpty()) {
return;
}
@@ -731,7 +497,6 @@ public class NoteDbUpdateManager implements AutoCloseable {
for (Change.Id id : toDelete) {
doDelete(id);
}
- checkExpectedState();
}
private void doDelete(Change.Id id) throws IOException {
@@ -751,78 +516,12 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
}
- public static class MismatchedStateException extends OrmException {
- private static final long serialVersionUID = 1L;
-
- private MismatchedStateException(Change.Id id, NoteDbChangeState expectedState) {
- super(
- String.format(
- "cannot apply NoteDb updates for change %s; change meta ref does not match %s",
- id, expectedState.getChangeMetaId().name()));
- }
- }
-
- private void checkExpectedState() throws OrmException, IOException {
- if (!checkExpectedState) {
- return;
- }
-
- // Refuse to apply an update unless the state in NoteDb matches the state
- // claimed in the ref. This means we may have failed a NoteDb ref update,
- // and it would be incorrect to claim that the ref is up to date after this
- // pipeline.
- //
- // Generally speaking, this case should be rare; in most cases, we should
- // have detected and auto-fixed the stale state when creating ChangeNotes
- // that got passed into the ChangeUpdate.
- for (Collection<ChangeUpdate> us : changeUpdates.asMap().values()) {
- ChangeUpdate u = us.iterator().next();
- NoteDbChangeState expectedState = NoteDbChangeState.parse(u.getChange());
-
- if (expectedState == null) {
- // No previous state means we haven't previously written NoteDb graphs
- // for this change yet. This means either:
- // - The change is new, and we'll be creating its ref.
- // - We short-circuited before adding any commands that update this
- // ref, and we won't stage a delta for this change either.
- // Either way, it is safe to proceed here rather than throwing
- // MismatchedStateException.
- continue;
- }
-
- if (expectedState.getPrimaryStorage() == PrimaryStorage.NOTE_DB) {
- // NoteDb is primary, no need to compare state to ReviewDb.
- continue;
- }
-
- if (!expectedState.isChangeUpToDate(changeRepo.cmds.getRepoRefCache())) {
- throw new MismatchedStateException(u.getId(), expectedState);
- }
- }
-
- for (Collection<ChangeDraftUpdate> us : draftUpdates.asMap().values()) {
- ChangeDraftUpdate u = us.iterator().next();
- NoteDbChangeState expectedState = NoteDbChangeState.parse(u.getChange());
-
- if (expectedState == null || expectedState.getPrimaryStorage() == PrimaryStorage.NOTE_DB) {
- continue; // See above.
- }
-
- Account.Id accountId = u.getAccountId();
- if (!expectedState.areDraftsUpToDate(allUsersRepo.cmds.getRepoRefCache(), accountId)) {
- ObjectId expectedDraftId =
- firstNonNull(expectedState.getDraftIds().get(accountId), ObjectId.zeroId());
- throw new OrmConcurrencyException(
- String.format(
- "cannot apply NoteDb updates for change %s;"
- + " draft ref for account %s does not match %s",
- u.getId(), accountId, expectedDraftId.name()));
- }
- }
+ private void checkNotExecuted() {
+ checkState(!executed, "update has already been executed");
}
private static <U extends AbstractChangeUpdate> void addUpdates(
- ListMultimap<String, U> all, OpenRepo or) throws OrmException, IOException {
+ 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();
@@ -837,7 +536,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
ObjectId curr = old;
for (U u : updates) {
if (u.isRootOnly() && !old.equals(ObjectId.zeroId())) {
- throw new OrmException("Given ChangeUpdate is only allowed on initial commit");
+ throw new StorageException("Given ChangeUpdate is only allowed on initial commit");
}
ObjectId next = u.apply(or.rw, or.tempIns, curr);
if (next == null) {
@@ -852,13 +551,13 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
private static void addRewrites(ListMultimap<String, NoteDbRewriter> rewriters, OpenRepo openRepo)
- throws OrmException, IOException {
+ throws IOException {
for (Map.Entry<String, Collection<NoteDbRewriter>> entry : rewriters.asMap().entrySet()) {
String refName = entry.getKey();
ObjectId oldTip = openRepo.cmds.get(refName).orElse(ObjectId.zeroId());
if (oldTip.equals(ObjectId.zeroId())) {
- throw new OrmException(String.format("Ref %s is empty", refName));
+ throw new StorageException(String.format("Ref %s is empty", refName));
}
ObjectId currTip = oldTip;
@@ -871,7 +570,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
}
} catch (ConfigInvalidException e) {
- throw new OrmException("Cannot rewrite commit history", e);
+ throw new StorageException("Cannot rewrite commit history", e);
}
if (!oldTip.equals(currTip)) {
@@ -887,8 +586,4 @@ public class NoteDbUpdateManager implements AutoCloseable {
}
return updates.iterator().next().allowWriteToNewRef();
}
-
- private static void checkDraftRef(boolean condition, String refName) {
- checkState(condition, "invalid draft ref: %s", refName);
- }
}
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUtil.java b/java/com/google/gerrit/server/notedb/NoteDbUtil.java
index 21fada8173..667ceaba4d 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbUtil.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbUtil.java
@@ -33,7 +33,7 @@ public class NoteDbUtil {
String email = ident.getEmailAddress();
int at = email.indexOf('@');
if (at >= 0) {
- String host = email.substring(at + 1, email.length());
+ String host = email.substring(at + 1);
if (host.equals(serverId)) {
Integer id = Ints.tryParse(email.substring(0, at));
if (id != null) {
diff --git a/java/com/google/gerrit/server/notedb/NotesMigration.java b/java/com/google/gerrit/server/notedb/NotesMigration.java
deleted file mode 100644
index 9cee2cda9e..0000000000
--- a/java/com/google/gerrit/server/notedb/NotesMigration.java
+++ /dev/null
@@ -1,250 +0,0 @@
-// 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.notedb;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;
-
-import com.google.auto.value.AutoValue;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.inject.AbstractModule;
-import java.util.concurrent.atomic.AtomicReference;
-import org.eclipse.jgit.lib.Config;
-
-/**
- * Current low-level settings of the NoteDb migration for changes.
- *
- * <p>This class only describes the migration state of the {@link
- * com.google.gerrit.reviewdb.client.Change Change} entity group, since it is possible for a given
- * site to be in different states of the Change NoteDb migration process while staying at the same
- * ReviewDb schema version. It does <em>not</em> describe the migration state of non-Change tables;
- * those are automatically migrated using the ReviewDb schema migration process, so the NoteDb
- * migration state at a given ReviewDb schema cannot vary.
- *
- * <p>In many places, core Gerrit code should not directly care about the NoteDb migration state,
- * and should prefer high-level APIs like {@link com.google.gerrit.server.ApprovalsUtil
- * ApprovalsUtil} that don't require callers to inspect the migration state. The
- * <em>implementation</em> of those utilities does care about the state, and should query the {@code
- * NotesMigration} for the properties of the migration, for example, {@link #changePrimaryStorage()
- * where new changes should be stored}.
- *
- * <p>Core Gerrit code is mostly interested in one facet of the migration at a time (reading or
- * writing, say), but not all combinations of return values are supported or even make sense.
- *
- * <p>This class controls the state of the migration according to options in {@code gerrit.config}.
- * In general, any changes to these options should only be made by adventurous administrators, who
- * know what they're doing, on non-production data, for the purposes of testing the NoteDb
- * implementation. Changing options quite likely requires re-running {@code MigrateToNoteDb}. For
- * these reasons, the options remain undocumented.
- *
- * <p><strong>Note:</strong> Callers should not assume the values returned by {@code
- * NotesMigration}'s methods will not change in a running server.
- */
-public abstract class NotesMigration {
- public static final String SECTION_NOTE_DB = "noteDb";
- public static final String READ = "read";
- public static final String WRITE = "write";
- public static final String DISABLE_REVIEW_DB = "disableReviewDb";
-
- private static final String PRIMARY_STORAGE = "primaryStorage";
- private static final String SEQUENCE = "sequence";
-
- public static class Module extends AbstractModule {
- @Override
- public void configure() {
- bind(MutableNotesMigration.class);
- bind(NotesMigration.class).to(MutableNotesMigration.class);
- }
- }
-
- @AutoValue
- abstract static class Snapshot {
- static Builder builder() {
- // Default values are defined as what we would read from an empty config.
- return create(new Config()).toBuilder();
- }
-
- static Snapshot create(Config cfg) {
- return new AutoValue_NotesMigration_Snapshot.Builder()
- .setWriteChanges(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), WRITE, false))
- .setReadChanges(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), READ, false))
- .setReadChangeSequence(cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, false))
- .setChangePrimaryStorage(
- cfg.getEnum(
- SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, PrimaryStorage.REVIEW_DB))
- .setDisableChangeReviewDb(
- cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, false))
- .setFailOnLoadForTest(false) // Only set in tests, can't be set via config.
- .build();
- }
-
- abstract boolean writeChanges();
-
- abstract boolean readChanges();
-
- abstract boolean readChangeSequence();
-
- abstract PrimaryStorage changePrimaryStorage();
-
- abstract boolean disableChangeReviewDb();
-
- abstract boolean failOnLoadForTest();
-
- abstract Builder toBuilder();
-
- void setConfigValues(Config cfg) {
- cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), WRITE, writeChanges());
- cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), READ, readChanges());
- cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, readChangeSequence());
- cfg.setEnum(SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, changePrimaryStorage());
- cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, disableChangeReviewDb());
- }
-
- @AutoValue.Builder
- abstract static class Builder {
- abstract Builder setWriteChanges(boolean writeChanges);
-
- abstract Builder setReadChanges(boolean readChanges);
-
- abstract Builder setReadChangeSequence(boolean readChangeSequence);
-
- abstract Builder setChangePrimaryStorage(PrimaryStorage changePrimaryStorage);
-
- abstract Builder setDisableChangeReviewDb(boolean disableChangeReviewDb);
-
- abstract Builder setFailOnLoadForTest(boolean failOnLoadForTest);
-
- abstract Snapshot autoBuild();
-
- Snapshot build() {
- Snapshot s = autoBuild();
- checkArgument(
- !(s.disableChangeReviewDb() && s.changePrimaryStorage() != PrimaryStorage.NOTE_DB),
- "cannot disable ReviewDb for changes if default change primary storage is ReviewDb");
- return s;
- }
- }
- }
-
- protected final AtomicReference<Snapshot> snapshot;
-
- /**
- * Read changes from NoteDb.
- *
- * <p>Change data is read from NoteDb refs, but ReviewDb is still the source of truth. If the
- * loader determines NoteDb is out of date, the change data in NoteDb will be transparently
- * rebuilt. This means that some code paths that look read-only may in fact attempt to write.
- *
- * <p>If true and {@code writeChanges() = false}, changes can still be read from NoteDb, but any
- * attempts to write will generate an error.
- */
- public final boolean readChanges() {
- return snapshot.get().readChanges();
- }
-
- /**
- * Write changes to NoteDb.
- *
- * <p>This method is awkwardly named because you should be using either {@link
- * #commitChangeWrites()} or {@link #failChangeWrites()} instead.
- *
- * <p>Updates to change data are written to NoteDb refs, but ReviewDb is still the source of
- * truth. Change data will not be written unless the NoteDb refs are already up to date, and the
- * write path will attempt to rebuild the change if not.
- *
- * <p>If false, the behavior when attempting to write depends on {@code readChanges()}. If {@code
- * readChanges() = false}, writes to NoteDb are simply ignored; if {@code true}, any attempts to
- * write will generate an error.
- */
- public final boolean rawWriteChangesSetting() {
- return snapshot.get().writeChanges();
- }
-
- /**
- * Read sequential change ID numbers from NoteDb.
- *
- * <p>If true, change IDs are read from {@code refs/sequences/changes} in All-Projects. If false,
- * change IDs are read from ReviewDb's native sequences.
- */
- public final boolean readChangeSequence() {
- return snapshot.get().readChangeSequence();
- }
-
- /** @return default primary storage for new changes. */
- public final PrimaryStorage changePrimaryStorage() {
- return snapshot.get().changePrimaryStorage();
- }
-
- /**
- * Disable ReviewDb access for changes.
- *
- * <p>When set, ReviewDb operations involving the Changes table become no-ops. Lookups return no
- * results; updates do nothing, as does opening, committing, or rolling back a transaction on the
- * Changes table.
- */
- public final boolean disableChangeReviewDb() {
- return snapshot.get().disableChangeReviewDb();
- }
-
- /**
- * Whether to fail when reading any data from NoteDb.
- *
- * <p>Used in conjunction with {@link #readChanges()} for tests.
- */
- public boolean failOnLoadForTest() {
- return snapshot.get().failOnLoadForTest();
- }
-
- public final boolean commitChangeWrites() {
- // It may seem odd that readChanges() without writeChanges() means we should
- // attempt to commit writes. However, this method is used by callers to know
- // whether or not they should short-circuit and skip attempting to read or
- // write NoteDb refs.
- //
- // It is possible for commitChangeWrites() to return true and
- // failChangeWrites() to also return true, causing an error later in the
- // same codepath. This specific condition is used by the auto-rebuilding
- // path to rebuild a change and stage the results, but not commit them due
- // to failChangeWrites().
- return rawWriteChangesSetting() || readChanges();
- }
-
- public final boolean failChangeWrites() {
- return !rawWriteChangesSetting() && readChanges();
- }
-
- public final void setConfigValues(Config cfg) {
- snapshot.get().setConfigValues(cfg);
- }
-
- @Override
- public final boolean equals(Object o) {
- return o instanceof NotesMigration
- && snapshot.get().equals(((NotesMigration) o).snapshot.get());
- }
-
- @Override
- public final int hashCode() {
- return snapshot.get().hashCode();
- }
-
- protected NotesMigration(Snapshot snapshot) {
- this.snapshot = new AtomicReference<>(snapshot);
- }
-
- final Snapshot snapshot() {
- return snapshot.get();
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/NotesMigrationState.java b/java/com/google/gerrit/server/notedb/NotesMigrationState.java
deleted file mode 100644
index c682aed8c2..0000000000
--- a/java/com/google/gerrit/server/notedb/NotesMigrationState.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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.notedb;
-
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NotesMigration.Snapshot;
-import java.util.Optional;
-import java.util.stream.Stream;
-import org.eclipse.jgit.lib.Config;
-
-/**
- * Possible high-level states of the NoteDb migration for changes.
- *
- * <p>This class describes the series of states required to migrate a site from ReviewDb-only to
- * NoteDb-only. This process has several steps, and covers only a small subset of the theoretically
- * possible combinations of {@link NotesMigration} return values.
- *
- * <p>These states are ordered: a one-way migration from ReviewDb to NoteDb will pass through states
- * in the order in which they are defined.
- */
-public enum NotesMigrationState {
- REVIEW_DB(false, false, false, PrimaryStorage.REVIEW_DB, false),
-
- WRITE(false, true, false, PrimaryStorage.REVIEW_DB, false),
-
- READ_WRITE_NO_SEQUENCE(true, true, false, PrimaryStorage.REVIEW_DB, false),
-
- READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY(true, true, true, PrimaryStorage.REVIEW_DB, false),
-
- READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY(true, true, true, PrimaryStorage.NOTE_DB, false),
-
- NOTE_DB(true, true, true, PrimaryStorage.NOTE_DB, true);
-
- public static final NotesMigrationState FINAL = NOTE_DB;
-
- public static Optional<NotesMigrationState> forConfig(Config cfg) {
- return forSnapshot(Snapshot.create(cfg));
- }
-
- public static Optional<NotesMigrationState> forNotesMigration(NotesMigration migration) {
- return forSnapshot(migration.snapshot());
- }
-
- private static Optional<NotesMigrationState> forSnapshot(Snapshot s) {
- return Stream.of(values()).filter(v -> v.snapshot.equals(s)).findFirst();
- }
-
- private final Snapshot snapshot;
-
- NotesMigrationState(
- // Arguments match abstract methods in NotesMigration.
- boolean readChanges,
- boolean rawWriteChangesSetting,
- boolean readChangeSequence,
- PrimaryStorage changePrimaryStorage,
- boolean disableChangeReviewDb) {
- this.snapshot =
- Snapshot.builder()
- .setReadChanges(readChanges)
- .setWriteChanges(rawWriteChangesSetting)
- .setReadChangeSequence(readChangeSequence)
- .setChangePrimaryStorage(changePrimaryStorage)
- .setDisableChangeReviewDb(disableChangeReviewDb)
- .build();
- }
-
- public void setConfigValues(Config cfg) {
- snapshot.setConfigValues(cfg);
- }
-
- public String toText() {
- Config cfg = new Config();
- setConfigValues(cfg);
- return cfg.toText();
- }
-
- Snapshot snapshot() {
- return snapshot;
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java b/java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java
deleted file mode 100644
index 0fe3d68b68..0000000000
--- a/java/com/google/gerrit/server/notedb/PrimaryStorageMigrator.java
+++ /dev/null
@@ -1,577 +0,0 @@
-// 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.notedb;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.github.rholder.retry.RetryException;
-import com.github.rholder.retry.Retryer;
-import com.github.rholder.retry.RetryerBuilder;
-import com.github.rholder.retry.StopStrategies;
-import com.github.rholder.retry.WaitStrategies;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-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.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.InternalUser;
-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.RepoRefCache;
-import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.InternalChangeQuery;
-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.RetryHelper;
-import com.google.gerrit.server.update.UpdateException;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-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.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-
-/** Helper to migrate the {@link PrimaryStorage} of individual changes. */
-@Singleton
-public class PrimaryStorageMigrator {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- /**
- * Exception thrown during migration if the change has no {@code noteDbState} field at the
- * beginning of the migration.
- */
- public static class NoNoteDbStateException extends RuntimeException {
- private static final long serialVersionUID = 1L;
-
- private NoNoteDbStateException(Change.Id id) {
- super("change " + id + " has no note_db_state; rebuild it first");
- }
- }
-
- private final AllUsersName allUsers;
- private final ChangeNotes.Factory changeNotesFactory;
- private final ChangeRebuilder rebuilder;
- private final ChangeUpdate.Factory updateFactory;
- private final GitRepositoryManager repoManager;
- private final InternalUser.Factory internalUserFactory;
- private final Provider<InternalChangeQuery> queryProvider;
- private final Provider<ReviewDb> db;
- private final RetryHelper retryHelper;
-
- private final long skewMs;
- private final long timeoutMs;
- private final Retryer<NoteDbChangeState> testEnsureRebuiltRetryer;
- private ProjectCache projectCache;
-
- @Inject
- PrimaryStorageMigrator(
- @GerritServerConfig Config cfg,
- Provider<ReviewDb> db,
- GitRepositoryManager repoManager,
- AllUsersName allUsers,
- ChangeRebuilder rebuilder,
- ChangeNotes.Factory changeNotesFactory,
- Provider<InternalChangeQuery> queryProvider,
- ChangeUpdate.Factory updateFactory,
- InternalUser.Factory internalUserFactory,
- RetryHelper retryHelper,
- ProjectCache projectCache) {
- this(
- cfg,
- db,
- repoManager,
- allUsers,
- rebuilder,
- null,
- changeNotesFactory,
- queryProvider,
- updateFactory,
- internalUserFactory,
- retryHelper,
- projectCache);
- }
-
- @VisibleForTesting
- public PrimaryStorageMigrator(
- Config cfg,
- Provider<ReviewDb> db,
- GitRepositoryManager repoManager,
- AllUsersName allUsers,
- ChangeRebuilder rebuilder,
- @Nullable Retryer<NoteDbChangeState> testEnsureRebuiltRetryer,
- ChangeNotes.Factory changeNotesFactory,
- Provider<InternalChangeQuery> queryProvider,
- ChangeUpdate.Factory updateFactory,
- InternalUser.Factory internalUserFactory,
- RetryHelper retryHelper,
- ProjectCache projectCache) {
- this.db = db;
- this.repoManager = repoManager;
- this.allUsers = allUsers;
- this.rebuilder = rebuilder;
- this.testEnsureRebuiltRetryer = testEnsureRebuiltRetryer;
- this.changeNotesFactory = changeNotesFactory;
- this.queryProvider = queryProvider;
- this.updateFactory = updateFactory;
- this.internalUserFactory = internalUserFactory;
- this.retryHelper = retryHelper;
- this.projectCache = projectCache;
- skewMs = NoteDbChangeState.getReadOnlySkew(cfg);
-
- String s = "notedb";
- timeoutMs =
- cfg.getTimeUnit(
- s,
- null,
- "primaryStorageMigrationTimeout",
- MILLISECONDS.convert(60, SECONDS),
- MILLISECONDS);
- }
-
- public boolean migrateToNoteDbPrimary(Collection<Change.Id> changes) {
- boolean result = true;
- for (Change.Id id : changes) {
- try {
- try {
- migrateToNoteDbPrimary(id);
- } catch (NoNoteDbStateException e) {
- if (canSkipPrimaryStorageMigration(db(), id)) {
- logger.atWarning().withCause(e).log(
- "Change %s previously failed to rebuild;" + " skipping primary storage migration",
- id);
- } else {
- throw e;
- }
- }
- } catch (Exception e) {
- logger.atSevere().withCause(e).log("Error migrating primary storage for %s", id);
- result = false;
- }
- }
- return result;
- }
-
- /**
- * Checks whether a change is so corrupt that it can be completely skipped by the primary storage
- * migration step.
- *
- * <p>To get to the point where this method is called from {@link #setNoteDbPrimary}, it means we
- * attempted to rebuild it, and encountered an error that was then caught in {@link
- * #rebuildProjectSlice} and skipped. As a result, there is no {@code noteDbState} field in the
- * change by the time we get to {@link #setNoteDbPrimary}, so {@code migrateToNoteDbPrimary}
- * throws an exception.
- *
- * <p>We have to do this hacky double-checking because we don't have a way for the rebuilding
- * phase to communicate to the primary storage migration phase that the change is skippable. It
- * would be possible to store this info in some field in this class, but there is no guarantee
- * that the rebuild and primary storage migration phases are run in the same JVM invocation.
- *
- * <p>In an ideal world, we could do this through the {@link
- * com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage} enum, having a separate value
- * for errors. However, that would be an invasive change touching many non-migration-related parts
- * of the NoteDb migration code, which is too risky to attempt in the stable branch where this bug
- * had to be fixed.
- *
- * <p>As of this writing, there are only two cases where this happens: when a change has no patch
- * sets, or the project doesn't exist.
- */
- private boolean canSkipPrimaryStorageMigration(ReviewDb db, Change.Id id) {
- try {
- return Iterables.isEmpty(unwrapDb(db).patchSets().byChange(id))
- || projectCache.get(unwrapDb(db).changes().get(id).getProject()) == null;
- } catch (Exception e) {
- logger.atSevere().withCause(e).log(
- "Error checking if change %s can be skipped, assuming no", id);
- return false;
- }
- }
-
- /**
- * Migrate a change's primary storage from ReviewDb to NoteDb.
- *
- * <p>This method will return only if the primary storage of the change is NoteDb afterwards. (It
- * may return early if the primary storage was already NoteDb.)
- *
- * <p>If this method throws an exception, then the primary storage of the change is probably not
- * NoteDb. (It is possible that the primary storage of the change is NoteDb in this case, but
- * there was an error reading the state.) Moreover, after an exception, the change may be
- * read-only until a lease expires. If the caller chooses to retry, they should wait until the
- * read-only lease expires; this method will fail relatively quickly if called on a read-only
- * change.
- *
- * <p>Note that if the change is read-only after this method throws an exception, that does not
- * necessarily guarantee that the read-only lease was acquired during that particular method
- * invocation; this call may have in fact failed because another thread acquired the lease first.
- *
- * @param id change ID.
- * @throws OrmException if a ReviewDb-level error occurs.
- * @throws IOException if a repo-level error occurs.
- */
- public void migrateToNoteDbPrimary(Change.Id id) throws OrmException, IOException {
- // Since there are multiple non-atomic steps in this method, we need to
- // consider what happens when there is another writer concurrent with the
- // thread executing this method.
- //
- // Let:
- // * OR = other writer writes noteDbState & new data to ReviewDb (in one
- // transaction)
- // * ON = other writer writes to NoteDb
- // * MRO = migrator sets state to read-only
- // * MR = ensureRebuilt writes rebuilt noteDbState to ReviewDb (but does not
- // otherwise update ReviewDb in this transaction)
- // * MN = ensureRebuilt writes rebuilt state to NoteDb
- //
- // Consider all the interleavings of these operations.
- //
- // * OR,ON,MRO,...
- // Other writer completes before migrator begins; this is not a concurrent
- // write.
- // * MRO,...,OR,...
- // OR will fail, since it atomically checks that the noteDbState is not
- // read-only before proceeding. This results in an exception, but not a
- // concurrent write.
- //
- // Thus all the "interesting" interleavings start with OR,MRO, and differ on
- // where ON falls relative to MR/MN.
- //
- // * OR,MRO,ON,MR,MN
- // The other NoteDb write succeeds despite the noteDbState being
- // read-only. Because the read-only state from MRO includes the update
- // from OR, the change is up-to-date at this point. Thus MR,MN is a no-op.
- // The end result is an up-to-date, read-only change.
- //
- // * OR,MRO,MR,ON,MN
- // The change is out-of-date when ensureRebuilt begins, because OR
- // succeeded but the corresponding ON has not happened yet. ON will
- // succeed, because there have been no intervening NoteDb writes. MN will
- // fail, because ON updated the state in NoteDb to something other than
- // what MR claimed. This leaves the change in an out-of-date, read-only
- // state.
- //
- // If this method threw an exception in this case, the change would
- // eventually switch back to read-write when the read-only lease expires,
- // so this situation is recoverable. However, it would be inconvenient for
- // a change to be read-only for so long.
- //
- // Thus, as an optimization, we have a retry loop that attempts
- // ensureRebuilt while still holding the same read-only lease. This
- // effectively results in the interleaving OR,MR,ON,MR,MN; in contrast
- // with the previous case, here, MR/MN actually rebuilds the change. In
- // the case of a write failure, MR/MN might fail and get retried again. If
- // it exceeds the maximum number of retries, an exception is thrown.
- //
- // * OR,MRO,MR,MN,ON
- // The change is out-of-date when ensureRebuilt begins. The change is
- // rebuilt, leaving a new state in NoteDb. ON will fail, because the old
- // NoteDb state has changed since the ref state was read when the update
- // began (prior to OR). This results in an exception from ON, but the end
- // result is still an up-to-date, read-only change. The end user that
- // initiated the other write observes an error, but this is no different
- // from other errors that need retrying, e.g. due to a backend write
- // failure.
-
- Stopwatch sw = Stopwatch.createStarted();
- Change readOnlyChange = setReadOnlyInReviewDb(id); // MRO
- if (readOnlyChange == null) {
- return; // Already migrated.
- }
-
- NoteDbChangeState rebuiltState;
- try {
- // MR,MN
- rebuiltState =
- ensureRebuiltRetryer(sw)
- .call(
- () ->
- ensureRebuilt(
- readOnlyChange.getProject(),
- id,
- NoteDbChangeState.parse(readOnlyChange)));
- } catch (RetryException | ExecutionException e) {
- throw new OrmException(e);
- }
-
- // At this point, the noteDbState in ReviewDb is read-only, and it is
- // guaranteed to match the state actually in NoteDb. Now it is safe to set
- // the primary storage to NoteDb.
-
- setPrimaryStorageNoteDb(id, rebuiltState);
- logger.atFine().log(
- "Migrated change %s to NoteDb primary in %sms", id, sw.elapsed(MILLISECONDS));
- }
-
- private Change setReadOnlyInReviewDb(Change.Id id) throws OrmException {
- AtomicBoolean alreadyMigrated = new AtomicBoolean(false);
- Change result =
- db().changes()
- .atomicUpdate(
- id,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- NoteDbChangeState state = NoteDbChangeState.parse(change);
- if (state == null) {
- // Could rebuild the change here, but that's more complexity, and this
- // normally shouldn't happen.
- //
- // Known cases where this happens are described in and handled by
- // NoteDbMigrator#canSkipPrimaryStorageMigration.
- throw new NoNoteDbStateException(id);
- }
- // If the change is already read-only, then the lease is held by another
- // (likely failed) migrator thread. Fail early, as we can't take over
- // the lease.
- NoteDbChangeState.checkNotReadOnly(change, skewMs);
- if (state.getPrimaryStorage() != PrimaryStorage.NOTE_DB) {
- Timestamp now = TimeUtil.nowTs();
- Timestamp until = new Timestamp(now.getTime() + timeoutMs);
- change.setNoteDbState(state.withReadOnlyUntil(until).toString());
- } else {
- alreadyMigrated.set(true);
- }
- return change;
- }
- });
- return alreadyMigrated.get() ? null : result;
- }
-
- private Retryer<NoteDbChangeState> ensureRebuiltRetryer(Stopwatch sw) {
- if (testEnsureRebuiltRetryer != null) {
- return testEnsureRebuiltRetryer;
- }
- // Retry the ensureRebuilt step with backoff until half the timeout has
- // expired, leaving the remaining half for the rest of the steps.
- long remainingNanos = (MILLISECONDS.toNanos(timeoutMs) / 2) - sw.elapsed(NANOSECONDS);
- remainingNanos = Math.max(remainingNanos, 0);
- return RetryerBuilder.<NoteDbChangeState>newBuilder()
- .retryIfException(e -> (e instanceof IOException) || (e instanceof OrmException))
- .withWaitStrategy(
- WaitStrategies.join(
- WaitStrategies.exponentialWait(250, MILLISECONDS),
- WaitStrategies.randomWait(50, MILLISECONDS)))
- .withStopStrategy(StopStrategies.stopAfterDelay(remainingNanos, NANOSECONDS))
- .build();
- }
-
- private NoteDbChangeState ensureRebuilt(
- Project.NameKey project, Change.Id id, NoteDbChangeState readOnlyState)
- throws IOException, OrmException, RepositoryNotFoundException {
- try (Repository changeRepo = repoManager.openRepository(project);
- Repository allUsersRepo = repoManager.openRepository(allUsers)) {
- if (!readOnlyState.isUpToDate(new RepoRefCache(changeRepo), new RepoRefCache(allUsersRepo))) {
- NoteDbUpdateManager.Result r = rebuilder.rebuildEvenIfReadOnly(db(), id);
- checkState(
- r.newState().getReadOnlyUntil().equals(readOnlyState.getReadOnlyUntil()),
- "state after rebuilding has different read-only lease: %s != %s",
- r.newState(),
- readOnlyState);
- readOnlyState = r.newState();
- }
- }
- return readOnlyState;
- }
-
- private void setPrimaryStorageNoteDb(Change.Id id, NoteDbChangeState expectedState)
- throws OrmException {
- db().changes()
- .atomicUpdate(
- id,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- NoteDbChangeState state = NoteDbChangeState.parse(change);
- if (!Objects.equals(state, expectedState)) {
- throw new OrmRuntimeException(badState(state, expectedState));
- }
- Timestamp until = state.getReadOnlyUntil().get();
- if (TimeUtil.nowTs().after(until)) {
- throw new OrmRuntimeException(
- "read-only lease on change " + id + " expired at " + until);
- }
- change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
- return change;
- }
- });
- }
-
- private ReviewDb db() {
- return ReviewDbUtil.unwrapDb(db.get());
- }
-
- private String badState(NoteDbChangeState actual, NoteDbChangeState expected) {
- return "state changed unexpectedly: " + actual + " != " + expected;
- }
-
- public void migrateToReviewDbPrimary(Change.Id id, @Nullable Project.NameKey project)
- throws OrmException, IOException {
- // Migrating back to ReviewDb primary is much simpler than the original migration to NoteDb
- // primary, because when NoteDb is primary, each write only goes to one storage location rather
- // than both. We only need to consider whether a concurrent writer (OR) conflicts with the first
- // setReadOnlyInNoteDb step (MR) in this method.
- //
- // If OR wins, then either:
- // * MR will set read-only after OR is completed, which is not a concurrent write.
- // * MR will fail to set read-only with a lock failure. The caller will have to retry, but the
- // change is not in a read-only state, so behavior is not degraded in the meantime.
- //
- // If MR wins, then either:
- // * OR will fail with a read-only exception (via AbstractChangeNotes#apply).
- // * OR will fail with a lock failure.
- //
- // In all of these scenarios, the change is read-only if and only if MR succeeds.
- //
- // There will be no concurrent writes to ReviewDb for this change until
- // setPrimaryStorageReviewDb completes, because ReviewDb writes are not attempted when primary
- // storage is NoteDb. After the primary storage changes back, it is possible for subsequent
- // NoteDb writes to conflict with the releaseReadOnlyLeaseInNoteDb step, but at this point,
- // since ReviewDb is primary, we are back to ignoring them.
- Stopwatch sw = Stopwatch.createStarted();
- if (project == null) {
- project = getProject(id);
- }
- ObjectId newMetaId = setReadOnlyInNoteDb(project, id);
- rebuilder.rebuildReviewDb(db(), project, id);
- setPrimaryStorageReviewDb(id, newMetaId);
- releaseReadOnlyLeaseInNoteDb(project, id);
- logger.atFine().log(
- "Migrated change %s to ReviewDb primary in %sms", id, sw.elapsed(MILLISECONDS));
- }
-
- private ObjectId setReadOnlyInNoteDb(Project.NameKey project, Change.Id id)
- throws OrmException, IOException {
- Timestamp now = TimeUtil.nowTs();
- Timestamp until = new Timestamp(now.getTime() + timeoutMs);
- ChangeUpdate update =
- updateFactory.create(
- changeNotesFactory.createChecked(db.get(), project, id), internalUserFactory.create());
- update.setReadOnlyUntil(until);
- return update.commit();
- }
-
- private void setPrimaryStorageReviewDb(Change.Id id, ObjectId newMetaId)
- throws OrmException, IOException {
- ImmutableMap.Builder<Account.Id, ObjectId> draftIds = ImmutableMap.builder();
- try (Repository repo = repoManager.openRepository(allUsers)) {
- for (Ref draftRef :
- repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftCommentsPrefix(id))) {
- Account.Id accountId = Account.Id.fromRef(draftRef.getName());
- if (accountId != null) {
- draftIds.put(accountId, draftRef.getObjectId().copy());
- }
- }
- }
- NoteDbChangeState newState =
- new NoteDbChangeState(
- id,
- PrimaryStorage.REVIEW_DB,
- Optional.of(RefState.create(newMetaId, draftIds.build())),
- Optional.empty());
- db().changes()
- .atomicUpdate(
- id,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- if (PrimaryStorage.of(change) != PrimaryStorage.NOTE_DB) {
- throw new OrmRuntimeException(
- "change " + id + " is not NoteDb primary: " + change.getNoteDbState());
- }
- change.setNoteDbState(newState.toString());
- return change;
- }
- });
- }
-
- private void releaseReadOnlyLeaseInNoteDb(Project.NameKey project, Change.Id id)
- throws OrmException {
- // Use a BatchUpdate since ReviewDb is primary at this point, so it needs to reflect the update.
- // (In practice retrying won't happen, since we aren't using fused updates at this point.)
- try {
- retryHelper.execute(
- updateFactory -> {
- try (BatchUpdate bu =
- updateFactory.create(
- db.get(), project, internalUserFactory.create(), TimeUtil.nowTs())) {
- bu.addOp(
- id,
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) {
- ctx.getUpdate(ctx.getChange().currentPatchSetId())
- .setReadOnlyUntil(new Timestamp(0));
- return true;
- }
- });
- bu.execute();
- return null;
- }
- });
- } catch (RestApiException | UpdateException e) {
- throw new OrmException(e);
- }
- }
-
- private Project.NameKey getProject(Change.Id id) throws OrmException {
- List<ChangeData> cds =
- queryProvider.get().setRequestedFields(ChangeField.PROJECT).byLegacyChangeId(id);
- Set<Project.NameKey> projects = new TreeSet<>();
- for (ChangeData cd : cds) {
- projects.add(cd.project());
- }
- if (projects.size() != 1) {
- throw new OrmException(
- "zero or multiple projects found for change "
- + id
- + ", must specify project explicitly: "
- + projects);
- }
- return projects.iterator().next();
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/RepoSequence.java b/java/com/google/gerrit/server/notedb/RepoSequence.java
index 4c497ac4a4..68b2b95399 100644
--- a/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -27,33 +27,28 @@ import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
-import com.google.common.primitives.Ints;
+import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.Runnables;
-import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
+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 com.google.gwtorm.server.OrmException;
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;
import java.util.concurrent.locks.ReentrantLock;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
@@ -69,15 +64,17 @@ import org.eclipse.jgit.transport.ReceiveCommand;
* numbers.
*/
public class RepoSequence {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
@FunctionalInterface
public interface Seed {
- int get() throws OrmException;
+ int get();
}
@VisibleForTesting
- static RetryerBuilder<RefUpdate.Result> retryerBuilder() {
- return RetryerBuilder.<RefUpdate.Result>newBuilder()
- .retryIfResult(Predicates.equalTo(RefUpdate.Result.LOCK_FAILURE))
+ static RetryerBuilder<RefUpdate> retryerBuilder() {
+ return RetryerBuilder.<RefUpdate>newBuilder()
+ .retryIfResult(ru -> ru != null && RefUpdate.Result.LOCK_FAILURE.equals(ru.getResult()))
.withWaitStrategy(
WaitStrategies.join(
WaitStrategies.exponentialWait(5, TimeUnit.SECONDS),
@@ -85,7 +82,7 @@ public class RepoSequence {
.withStopStrategy(StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS));
}
- private static final Retryer<RefUpdate.Result> RETRYER = retryerBuilder().build();
+ private static final Retryer<RefUpdate> RETRYER = retryerBuilder().build();
private final GitRepositoryManager repoManager;
private final GitReferenceUpdated gitRefUpdated;
@@ -95,7 +92,7 @@ public class RepoSequence {
private final int floor;
private final int batchSize;
private final Runnable afterReadRef;
- private final Retryer<RefUpdate.Result> retryer;
+ private final Retryer<RefUpdate> retryer;
// Protects all non-final fields.
private final Lock counterLock;
@@ -153,7 +150,7 @@ public class RepoSequence {
Seed seed,
int batchSize,
Runnable afterReadRef,
- Retryer<RefUpdate.Result> retryer) {
+ Retryer<RefUpdate> retryer) {
this(repoManager, gitRefUpdated, projectName, name, seed, batchSize, afterReadRef, retryer, 0);
}
@@ -165,7 +162,7 @@ public class RepoSequence {
Seed seed,
int batchSize,
Runnable afterReadRef,
- Retryer<RefUpdate.Result> retryer,
+ Retryer<RefUpdate> retryer,
int floor) {
this.repoManager = requireNonNull(repoManager, "repoManager");
this.gitRefUpdated = requireNonNull(gitRefUpdated, "gitRefUpdated");
@@ -187,10 +184,11 @@ public class RepoSequence {
this.afterReadRef = requireNonNull(afterReadRef, "afterReadRef");
this.retryer = requireNonNull(retryer, "retryer");
+ logger.atFine().log("sequence batch size for %s is %s", name, batchSize);
counterLock = new ReentrantLock(true);
}
- public int next() throws OrmException {
+ public int next() {
counterLock.lock();
try {
if (counter >= limit) {
@@ -202,7 +200,7 @@ public class RepoSequence {
}
}
- public ImmutableList<Integer> next(int count) throws OrmException {
+ public ImmutableList<Integer> next(int count) {
if (count == 0) {
return ImmutableList.of();
}
@@ -226,74 +224,26 @@ public class RepoSequence {
}
}
- @VisibleForTesting
- public void set(int val) throws OrmException {
- // Don't bother spinning. This is only for tests, and a test that calls set
- // concurrently with other writes is doing it wrong.
- counterLock.lock();
- try {
- try (Repository repo = repoManager.openRepository(projectName);
- RevWalk rw = new RevWalk(repo)) {
- checkResult(store(repo, rw, null, val));
- counter = limit;
- } catch (IOException e) {
- throw new OrmException(e);
- }
- } finally {
- counterLock.unlock();
- }
- }
-
- public void increaseTo(int val) throws OrmException {
- counterLock.lock();
- try {
- try (Repository repo = repoManager.openRepository(projectName);
- RevWalk rw = new RevWalk(repo)) {
- TryIncreaseTo attempt = new TryIncreaseTo(repo, rw, val);
- checkResult(retryer.call(attempt));
- counter = limit;
- } catch (ExecutionException | RetryException e) {
- if (e.getCause() != null) {
- Throwables.throwIfInstanceOf(e.getCause(), OrmException.class);
- }
- throw new OrmException(e);
- } catch (IOException e) {
- throw new OrmException(e);
- }
- } finally {
- counterLock.unlock();
- }
- }
-
- private void acquire(int count) throws OrmException {
+ 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);
- checkResult(retryer.call(attempt));
+ RefUpdateUtil.checkResult(retryer.call(attempt));
counter = attempt.next;
limit = counter + count;
acquireCount++;
} catch (ExecutionException | RetryException e) {
if (e.getCause() != null) {
- Throwables.throwIfInstanceOf(e.getCause(), OrmException.class);
+ Throwables.throwIfInstanceOf(e.getCause(), StorageException.class);
}
- throw new OrmException(e);
+ throw new StorageException(e);
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
- private void checkResult(RefUpdate.Result result) throws OrmException {
- if (!refUpdated(result) && result != Result.NO_CHANGE) {
- throw new OrmException("failed to update " + refName + ": " + result);
- }
- }
-
- private boolean refUpdated(RefUpdate.Result result) {
- return result == RefUpdate.Result.NEW || result == RefUpdate.Result.FORCED;
- }
-
- private class TryAcquire implements Callable<RefUpdate.Result> {
+ private class TryAcquire implements Callable<RefUpdate> {
private final Repository repo;
private final RevWalk rw;
private final int count;
@@ -307,86 +257,20 @@ public class RepoSequence {
}
@Override
- public RefUpdate.Result call() throws Exception {
- Ref ref = repo.exactRef(refName);
+ public RefUpdate call() throws Exception {
+ Optional<IntBlob> blob = IntBlob.parse(repo, refName, rw);
afterReadRef.run();
ObjectId oldId;
- if (ref == null) {
+ if (!blob.isPresent()) {
oldId = ObjectId.zeroId();
next = seed.get();
} else {
- oldId = ref.getObjectId();
- next = parse(rw, oldId);
+ oldId = blob.get().id();
+ next = blob.get().value();
}
next = Math.max(floor, next);
- return store(repo, rw, oldId, next + count);
- }
- }
-
- private class TryIncreaseTo implements Callable<RefUpdate.Result> {
- private final Repository repo;
- private final RevWalk rw;
- private final int value;
-
- private TryIncreaseTo(Repository repo, RevWalk rw, int value) {
- this.repo = repo;
- this.rw = rw;
- this.value = value;
- }
-
- @Override
- public RefUpdate.Result call() throws Exception {
- Ref ref = repo.exactRef(refName);
- afterReadRef.run();
- ObjectId oldId;
- if (ref == null) {
- oldId = ObjectId.zeroId();
- } else {
- oldId = ref.getObjectId();
- int next = parse(rw, oldId);
- if (next >= value) {
- // a concurrent write updated the ref already to this or a higher value
- return RefUpdate.Result.NO_CHANGE;
- }
- }
- return store(repo, rw, oldId, value);
- }
- }
-
- private int parse(RevWalk rw, ObjectId id) throws IOException, OrmException {
- ObjectLoader ol = rw.getObjectReader().open(id, OBJ_BLOB);
- if (ol.getType() != OBJ_BLOB) {
- // In theory this should be thrown by open but not all implementations
- // may do it properly (certainly InMemoryRepository doesn't).
- throw new IncorrectObjectTypeException(id, OBJ_BLOB);
- }
- String str = CharMatcher.whitespace().trimFrom(new String(ol.getCachedBytes(), UTF_8));
- Integer val = Ints.tryParse(str);
- if (val == null) {
- throw new OrmException("invalid value in " + refName + " blob at " + id.name());
- }
- return val;
- }
-
- private RefUpdate.Result store(Repository repo, RevWalk rw, @Nullable ObjectId oldId, int val)
- throws IOException {
- ObjectId newId;
- try (ObjectInserter ins = repo.newObjectInserter()) {
- newId = ins.insert(OBJ_BLOB, Integer.toString(val).getBytes(UTF_8));
- ins.flush();
- }
- RefUpdate ru = repo.updateRef(refName);
- if (oldId != null) {
- ru.setExpectedOldObjectId(oldId);
- }
- ru.disableRefLog();
- ru.setNewObjectId(newId);
- ru.setForceUpdate(true); // Required for non-commitish updates.
- RefUpdate.Result result = ru.update(rw);
- if (refUpdated(result)) {
- gitRefUpdated.fire(projectName, ru, null);
+ return IntBlob.tryStore(repo, rw, projectName, refName, oldId, next + count, gitRefUpdated);
}
- return result;
}
public static ReceiveCommand storeNew(ObjectInserter ins, String name, int val)
diff --git a/java/com/google/gerrit/server/notedb/RevisionNote.java b/java/com/google/gerrit/server/notedb/RevisionNote.java
index deec7e924e..ff649a9c44 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNote.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNote.java
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkState;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.common.UsedAt;
import java.io.IOException;
import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -26,7 +26,8 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.util.MutableInteger;
-abstract class RevisionNote<T extends Comment> {
+@UsedAt(UsedAt.Project.PLUGIN_CHECKS)
+public abstract class RevisionNote<T> {
static final int MAX_NOTE_SZ = 25 << 20;
protected static void trimLeadingEmptyLines(byte[] bytes, MutableInteger p) {
@@ -39,9 +40,9 @@ abstract class RevisionNote<T extends Comment> {
private final ObjectId noteId;
private byte[] raw;
- private ImmutableList<T> comments;
+ private ImmutableList<T> entities;
- RevisionNote(ObjectReader reader, ObjectId noteId) {
+ public RevisionNote(ObjectReader reader, ObjectId noteId) {
this.reader = reader;
this.noteId = noteId;
}
@@ -51,9 +52,16 @@ abstract class RevisionNote<T extends Comment> {
return raw;
}
- public ImmutableList<T> getComments() {
+ @UsedAt(UsedAt.Project.PLUGIN_CHECKS)
+ public T getOnlyEntity() {
checkParsed();
- return comments;
+ checkState(entities.size() == 1, "expected exactly one entity");
+ return entities.get(0);
+ }
+
+ public ImmutableList<T> getEntities() {
+ checkParsed();
+ return entities;
}
public void parse() throws IOException, ConfigInvalidException {
@@ -61,11 +69,11 @@ abstract class RevisionNote<T extends Comment> {
MutableInteger p = new MutableInteger();
trimLeadingEmptyLines(raw, p);
if (p.value >= raw.length) {
- comments = ImmutableList.of();
+ entities = ImmutableList.of();
return;
}
- comments = ImmutableList.copyOf(parse(raw, p.value));
+ entities = ImmutableList.copyOf(parse(raw, p.value));
}
protected abstract List<T> parse(byte[] raw, int offset)
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
index b8c7d7dc85..ac7a89d904 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
@@ -68,7 +68,7 @@ class RevisionNoteBuilder {
RevisionNoteBuilder(RevisionNote<? extends Comment> base) {
if (base != null) {
baseRaw = base.getRaw();
- baseComments = base.getComments();
+ baseComments = base.getEntities();
put = Maps.newHashMapWithExpectedSize(baseComments.size());
if (base instanceof ChangeRevisionNote) {
pushCert = ((ChangeRevisionNote) base).getPushCert();
diff --git a/java/com/google/gerrit/server/notedb/RevisionNoteMap.java b/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
index 17a061a8f2..da790e248b 100644
--- a/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
+++ b/java/com/google/gerrit/server/notedb/RevisionNoteMap.java
@@ -64,7 +64,7 @@ class RevisionNoteMap<T extends RevisionNote<? extends Comment>> {
}
static <T extends RevisionNote<? extends Comment>> RevisionNoteMap<T> emptyMap() {
- return new RevisionNoteMap<>(NoteMap.newEmptyMap(), ImmutableMap.<RevId, T>of());
+ return new RevisionNoteMap<>(NoteMap.newEmptyMap(), ImmutableMap.of());
}
private RevisionNoteMap(NoteMap noteMap, ImmutableMap<RevId, T> revisionNotes) {
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
index a05e6a1aef..92dd7d8b7e 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
@@ -24,7 +24,6 @@ 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.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -49,7 +48,7 @@ public class RobotCommentNotes extends AbstractChangeNotes<RobotCommentNotes> {
@Inject
RobotCommentNotes(Args args, @Assisted Change change) {
- super(args, change.getId(), PrimaryStorage.of(change), false);
+ super(args, change.getId());
this.change = change;
}
@@ -98,7 +97,7 @@ public class RobotCommentNotes extends AbstractChangeNotes<RobotCommentNotes> {
args.changeNoteJson, reader, NoteMap.read(reader, tipCommit));
ListMultimap<RevId, RobotComment> cs = MultimapBuilder.hashKeys().arrayListValues().build();
for (RobotCommentsRevisionNote rn : revisionNoteMap.revisionNotes.values()) {
- for (RobotComment c : rn.getComments()) {
+ for (RobotComment c : rn.getEntities()) {
cs.put(new RevId(c.revId), c);
}
}
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
index e7ac5b783f..0304ab81d7 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
@@ -19,14 +19,13 @@ import static com.google.gerrit.reviewdb.client.RefNames.robotCommentsRef;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.collect.Sets;
+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.gerrit.server.config.GerritServerConfig;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -38,7 +37,6 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
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;
@@ -74,50 +72,26 @@ public class RobotCommentUpdate extends AbstractChangeUpdate {
@AssistedInject
private RobotCommentUpdate(
- @GerritServerConfig Config cfg,
@GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
ChangeNoteUtil noteUtil,
@Assisted ChangeNotes notes,
@Assisted("effective") Account.Id accountId,
@Assisted("real") Account.Id realAccountId,
@Assisted PersonIdent authorIdent,
@Assisted Date when) {
- super(
- cfg,
- migration,
- noteUtil,
- serverIdent,
- notes,
- null,
- accountId,
- realAccountId,
- authorIdent,
- when);
+ super(noteUtil, serverIdent, notes, null, accountId, realAccountId, authorIdent, when);
}
@AssistedInject
private RobotCommentUpdate(
- @GerritServerConfig Config cfg,
@GerritPersonIdent PersonIdent serverIdent,
- NotesMigration migration,
ChangeNoteUtil noteUtil,
@Assisted Change change,
@Assisted("effective") Account.Id accountId,
@Assisted("real") Account.Id realAccountId,
@Assisted PersonIdent authorIdent,
@Assisted Date when) {
- super(
- cfg,
- migration,
- noteUtil,
- serverIdent,
- null,
- change,
- accountId,
- realAccountId,
- authorIdent,
- when);
+ super(noteUtil, serverIdent, null, change, accountId, realAccountId, authorIdent, when);
}
public void putComment(RobotComment c) {
@@ -127,7 +101,7 @@ public class RobotCommentUpdate extends AbstractChangeUpdate {
private CommitBuilder storeCommentsInNotes(
RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
- throws ConfigInvalidException, OrmException, IOException {
+ throws ConfigInvalidException, IOException {
RevisionNoteMap<RobotCommentsRevisionNote> rnm = getRevisionNoteMap(rw, curr);
Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
@@ -174,23 +148,20 @@ public class RobotCommentUpdate extends AbstractChangeUpdate {
}
private RevisionNoteMap<RobotCommentsRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
- throws ConfigInvalidException, OrmException, IOException {
+ throws ConfigInvalidException, IOException {
if (curr.equals(ObjectId.zeroId())) {
return RevisionNoteMap.emptyMap();
}
- if (migration.readChanges()) {
- // If reading from changes is enabled, then the old RobotCommentNotes
- // already parsed the revision notes. We can reuse them as long as the ref
- // hasn't advanced.
- ChangeNotes changeNotes = getNotes();
- if (changeNotes != null) {
- RobotCommentNotes robotCommentNotes = changeNotes.load().getRobotCommentNotes();
- if (robotCommentNotes != null) {
- ObjectId idFromNotes = firstNonNull(robotCommentNotes.getRevision(), ObjectId.zeroId());
- RevisionNoteMap<RobotCommentsRevisionNote> rnm = robotCommentNotes.getRevisionNoteMap();
- if (idFromNotes.equals(curr) && rnm != null) {
- return rnm;
- }
+ // The old RobotCommentNotes already parsed the revision notes. We can reuse them as long as
+ // the ref hasn't advanced.
+ ChangeNotes changeNotes = getNotes();
+ if (changeNotes != null) {
+ RobotCommentNotes robotCommentNotes = changeNotes.load().getRobotCommentNotes();
+ if (robotCommentNotes != null) {
+ ObjectId idFromNotes = firstNonNull(robotCommentNotes.getRevision(), ObjectId.zeroId());
+ RevisionNoteMap<RobotCommentsRevisionNote> rnm = robotCommentNotes.getRevisionNoteMap();
+ if (idFromNotes.equals(curr) && rnm != null) {
+ return rnm;
}
}
}
@@ -208,13 +179,13 @@ public class RobotCommentUpdate extends AbstractChangeUpdate {
@Override
protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
- throws OrmException, IOException {
+ throws IOException {
CommitBuilder cb = new CommitBuilder();
cb.setMessage("Update robot comments");
try {
return storeCommentsInNotes(rw, ins, curr, cb);
} catch (ConfigInvalidException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/notedb/Sequences.java b/java/com/google/gerrit/server/notedb/Sequences.java
new file mode 100644
index 0000000000..d4144cea06
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/Sequences.java
@@ -0,0 +1,140 @@
+// 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.server.notedb;
+
+import com.google.common.collect.ImmutableList;
+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.Timer2;
+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.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
+
+@Singleton
+public class Sequences {
+ private static final String SECTION_NOTEDB = "noteDb";
+ private static final String KEY_SEQUENCE_BATCH_SIZE = "sequenceBatchSize";
+ private static final int DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE = 1;
+ private static final int DEFAULT_CHANGES_SEQUENCE_BATCH_SIZE = 20;
+
+ public static final String NAME_ACCOUNTS = "accounts";
+ public static final String NAME_GROUPS = "groups";
+ public static final String NAME_CHANGES = "changes";
+
+ public static final int FIRST_ACCOUNT_ID = 1000000;
+ public static final int FIRST_GROUP_ID = 1;
+ public static final int FIRST_CHANGE_ID = 1;
+
+ private enum SequenceType {
+ ACCOUNTS,
+ CHANGES,
+ GROUPS;
+ }
+
+ private final RepoSequence accountSeq;
+ private final RepoSequence changeSeq;
+ private final RepoSequence groupSeq;
+ private final Timer2<SequenceType, Boolean> nextIdLatency;
+
+ @Inject
+ public Sequences(
+ @GerritServerConfig Config cfg,
+ GitRepositoryManager repoManager,
+ GitReferenceUpdated gitRefUpdated,
+ AllProjectsName allProjects,
+ AllUsersName allUsers,
+ MetricMaker metrics) {
+
+ int accountBatchSize =
+ cfg.getInt(
+ SECTION_NOTEDB,
+ NAME_ACCOUNTS,
+ KEY_SEQUENCE_BATCH_SIZE,
+ DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE);
+ accountSeq =
+ new RepoSequence(
+ repoManager,
+ gitRefUpdated,
+ allUsers,
+ NAME_ACCOUNTS,
+ () -> FIRST_ACCOUNT_ID,
+ accountBatchSize);
+
+ int changeBatchSize =
+ cfg.getInt(
+ SECTION_NOTEDB,
+ NAME_CHANGES,
+ KEY_SEQUENCE_BATCH_SIZE,
+ DEFAULT_CHANGES_SEQUENCE_BATCH_SIZE);
+ changeSeq =
+ new RepoSequence(
+ repoManager,
+ gitRefUpdated,
+ allProjects,
+ NAME_CHANGES,
+ () -> FIRST_CHANGE_ID,
+ changeBatchSize);
+
+ int groupBatchSize = 1;
+ groupSeq =
+ new RepoSequence(
+ repoManager,
+ gitRefUpdated,
+ allUsers,
+ NAME_GROUPS,
+ () -> FIRST_GROUP_ID,
+ groupBatchSize);
+
+ nextIdLatency =
+ metrics.newTimer(
+ "sequence/next_id_latency",
+ new Description("Latency of requesting IDs from repo sequences")
+ .setCumulative()
+ .setUnit(Units.MILLISECONDS),
+ Field.ofEnum(SequenceType.class, "sequence"),
+ Field.ofBoolean("multiple"));
+ }
+
+ public int nextAccountId() {
+ try (Timer2.Context timer = nextIdLatency.start(SequenceType.ACCOUNTS, false)) {
+ return accountSeq.next();
+ }
+ }
+
+ public int nextChangeId() {
+ try (Timer2.Context 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)) {
+ return changeSeq.next(count);
+ }
+ }
+
+ public int nextGroupId() {
+ try (Timer2.Context timer = nextIdLatency.start(SequenceType.GROUPS, false)) {
+ return groupSeq.next();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java b/java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java
deleted file mode 100644
index 11fef24ec2..0000000000
--- a/java/com/google/gerrit/server/notedb/TestChangeRebuilderWrapper.java
+++ /dev/null
@@ -1,125 +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.server.notedb;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Id;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@VisibleForTesting
-@Singleton
-public class TestChangeRebuilderWrapper extends ChangeRebuilder {
- private final ChangeRebuilderImpl delegate;
- private final AtomicBoolean failNextUpdate;
- private final AtomicBoolean stealNextUpdate;
-
- @Inject
- TestChangeRebuilderWrapper(SchemaFactory<ReviewDb> schemaFactory, ChangeRebuilderImpl rebuilder) {
- super(schemaFactory);
- this.delegate = rebuilder;
- this.failNextUpdate = new AtomicBoolean();
- this.stealNextUpdate = new AtomicBoolean();
- }
-
- public void failNextUpdate() {
- failNextUpdate.set(true);
- }
-
- public void stealNextUpdate() {
- stealNextUpdate.set(true);
- }
-
- @Override
- public Result rebuild(ReviewDb db, Change.Id changeId) throws IOException, OrmException {
- return rebuild(db, changeId, true);
- }
-
- @Override
- public Result rebuildEvenIfReadOnly(ReviewDb db, Change.Id changeId)
- throws IOException, OrmException {
- return rebuild(db, changeId, false);
- }
-
- private Result rebuild(ReviewDb db, Change.Id changeId, boolean checkReadOnly)
- throws IOException, OrmException {
- if (failNextUpdate.getAndSet(false)) {
- throw new IOException("Update failed");
- }
- Result result =
- checkReadOnly
- ? delegate.rebuild(db, changeId)
- : delegate.rebuildEvenIfReadOnly(db, changeId);
- if (stealNextUpdate.getAndSet(false)) {
- throw new IOException("Update stolen");
- }
- return result;
- }
-
- @Override
- public Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle)
- throws IOException, OrmException {
- // stealNextUpdate doesn't really apply in this case because the IOException
- // would normally come from the manager.execute() method, which isn't called
- // here.
- return delegate.rebuild(manager, bundle);
- }
-
- @Override
- public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
- throws IOException, OrmException {
- // Don't inspect stealNextUpdate; that happens in execute() below.
- return delegate.stage(db, changeId);
- }
-
- @Override
- public Result execute(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
- throws OrmException, IOException {
- if (failNextUpdate.getAndSet(false)) {
- throw new IOException("Update failed");
- }
- Result result = delegate.execute(db, changeId, manager);
- if (stealNextUpdate.getAndSet(false)) {
- throw new IOException("Update stolen");
- }
- return result;
- }
-
- @Override
- public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
- throws IOException, OrmException {
- // Don't check for manual failure; that happens in execute().
- delegate.buildUpdates(manager, bundle);
- }
-
- @Override
- public void rebuildReviewDb(ReviewDb db, Project.NameKey project, Id changeId)
- throws OrmException {
- if (failNextUpdate.getAndSet(false)) {
- throw new OrmException("Update failed");
- }
- delegate.rebuildReviewDb(db, project, changeId);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java b/java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java
deleted file mode 100644
index 0e6d3e98a7..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/AbortUpdateException.java
+++ /dev/null
@@ -1,25 +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.server.notedb.rebuild;
-
-import com.google.gwtorm.server.OrmRuntimeException;
-
-class AbortUpdateException extends OrmRuntimeException {
- private static final long serialVersionUID = 1L;
-
- AbortUpdateException() {
- super("aborted");
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java b/java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java
deleted file mode 100644
index 9ecf476093..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ApprovalEvent.java
+++ /dev/null
@@ -1,64 +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.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import java.sql.Timestamp;
-
-class ApprovalEvent extends Event {
- private PatchSetApproval psa;
-
- ApprovalEvent(PatchSetApproval psa, Timestamp changeCreatedOn) {
- super(
- psa.getPatchSetId(),
- psa.getAccountId(),
- psa.getRealAccountId(),
- psa.getGranted(),
- changeCreatedOn,
- psa.getTag());
- this.psa = psa;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return false;
- }
-
- @Override
- protected boolean canHaveTag() {
- // Legacy SUBM approvals don't have a tag field set, but the corresponding
- // ChangeMessage for merging the change does. We need to let these be in the
- // same meta commit so the SUBM approval isn't counted as post-submit.
- return !psa.isLegacySubmit();
- }
-
- @Override
- void apply(ChangeUpdate update) {
- checkUpdate(update);
- update.putApproval(psa.getLabel(), psa.getValue());
- }
-
- @Override
- protected boolean isPostSubmitApproval() {
- return psa.isPostSubmit();
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- helper.add("approval", psa);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java
deleted file mode 100644
index 53c9dc41ee..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeMessageEvent.java
+++ /dev/null
@@ -1,185 +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.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-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.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.sql.Timestamp;
-import java.util.Map;
-import java.util.Optional;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class ChangeMessageEvent extends Event {
- private static final ImmutableMap<Change.Status, Pattern> STATUS_PATTERNS =
- ImmutableMap.of(
- Change.Status.ABANDONED, Pattern.compile("^Abandoned(\n.*)*$"),
- Change.Status.MERGED,
- Pattern.compile(
- "^Change has been successfully (merged|cherry-picked|rebased|pushed).*$"),
- Change.Status.NEW, Pattern.compile("^Restored(\n.*)*$"));
-
- private static final Pattern PRIVATE_SET_REGEXP = Pattern.compile("^Set private$");
- private static final Pattern PRIVATE_UNSET_REGEXP = Pattern.compile("^Unset private$");
-
- private static final Pattern TOPIC_SET_REGEXP = Pattern.compile("^Topic set to (.+)$");
- private static final Pattern TOPIC_CHANGED_REGEXP =
- Pattern.compile("^Topic changed from (.+) to (.+)$");
- private static final Pattern TOPIC_REMOVED_REGEXP = Pattern.compile("^Topic (.+) removed$");
-
- private static final Pattern WIP_SET_REGEXP = Pattern.compile("^Set Work In Progress$");
- private static final Pattern WIP_UNSET_REGEXP = Pattern.compile("^Set Ready For Review$");
-
- private final Change change;
- private final Change noteDbChange;
- private final Optional<Change.Status> status;
- private final ChangeMessage message;
-
- ChangeMessageEvent(
- Change change, Change noteDbChange, ChangeMessage message, Timestamp changeCreatedOn) {
- super(
- message.getPatchSetId(),
- message.getAuthor(),
- message.getRealAuthor(),
- message.getWrittenOn(),
- changeCreatedOn,
- message.getTag());
- this.change = change;
- this.noteDbChange = noteDbChange;
- this.message = message;
- this.status = parseStatus(message);
- }
-
- @Override
- boolean uniquePerUpdate() {
- return true;
- }
-
- @Override
- protected boolean isSubmit() {
- return status.isPresent() && status.get() == Change.Status.MERGED;
- }
-
- @Override
- protected boolean canHaveTag() {
- return true;
- }
-
- @SuppressWarnings("deprecation")
- @Override
- void apply(ChangeUpdate update) throws OrmException {
- checkUpdate(update);
- update.setChangeMessage(message.getMessage());
- setPrivate(update);
- setTopic(update);
- setWorkInProgress(update);
-
- if (status.isPresent()) {
- Change.Status s = status.get();
- update.fixStatus(s);
- noteDbChange.setStatus(s);
- if (s == Change.Status.MERGED) {
- update.setSubmissionId(change.getSubmissionId());
- noteDbChange.setSubmissionId(change.getSubmissionId());
- }
- }
- }
-
- private static Optional<Change.Status> parseStatus(ChangeMessage message) {
- String msg = message.getMessage();
- if (msg == null) {
- return Optional.empty();
- }
- for (Map.Entry<Change.Status, Pattern> e : STATUS_PATTERNS.entrySet()) {
- if (e.getValue().matcher(msg).matches()) {
- return Optional.of(e.getKey());
- }
- }
- return Optional.empty();
- }
-
- private void setPrivate(ChangeUpdate update) {
- String msg = message.getMessage();
- if (msg == null) {
- return;
- }
- Matcher m = PRIVATE_SET_REGEXP.matcher(msg);
- if (m.matches()) {
- update.setPrivate(true);
- noteDbChange.setPrivate(true);
- return;
- }
-
- m = PRIVATE_UNSET_REGEXP.matcher(msg);
- if (m.matches()) {
- update.setPrivate(false);
- noteDbChange.setPrivate(false);
- }
- }
-
- private void setTopic(ChangeUpdate update) {
- String msg = message.getMessage();
- if (msg == null) {
- return;
- }
- Matcher m = TOPIC_SET_REGEXP.matcher(msg);
- if (m.matches()) {
- String topic = m.group(1);
- update.setTopic(topic);
- noteDbChange.setTopic(topic);
- return;
- }
-
- m = TOPIC_CHANGED_REGEXP.matcher(msg);
- if (m.matches()) {
- String topic = m.group(2);
- update.setTopic(topic);
- noteDbChange.setTopic(topic);
- return;
- }
-
- if (TOPIC_REMOVED_REGEXP.matcher(msg).matches()) {
- update.setTopic(null);
- noteDbChange.setTopic(null);
- }
- }
-
- private void setWorkInProgress(ChangeUpdate update) {
- String msg = Strings.nullToEmpty(message.getMessage());
- String tag = message.getTag();
- if (ChangeMessagesUtil.TAG_SET_WIP.equals(tag)
- || ChangeMessagesUtil.TAG_UPLOADED_WIP_PATCH_SET.equals(tag)
- || WIP_SET_REGEXP.matcher(msg).matches()) {
- update.setWorkInProgress(true);
- noteDbChange.setWorkInProgress(true);
- } else if (ChangeMessagesUtil.TAG_SET_READY.equals(tag)
- || ChangeMessagesUtil.TAG_UPLOADED_PATCH_SET.equals(tag)
- || WIP_UNSET_REGEXP.matcher(msg).matches()) {
- update.setWorkInProgress(false);
- noteDbChange.setWorkInProgress(false);
- }
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- helper.add("message", message);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java
deleted file mode 100644
index 8ce9987568..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilder.java
+++ /dev/null
@@ -1,81 +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.server.notedb.rebuild;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import java.io.IOException;
-
-public abstract class ChangeRebuilder {
- public static class NoPatchSetsException extends OrmException {
- private static final long serialVersionUID = 1L;
-
- NoPatchSetsException(Change.Id changeId) {
- super("Change " + changeId + " cannot be rebuilt because it has no patch sets");
- }
- }
-
- private final SchemaFactory<ReviewDb> schemaFactory;
-
- protected ChangeRebuilder(SchemaFactory<ReviewDb> schemaFactory) {
- this.schemaFactory = schemaFactory;
- }
-
- public final ListenableFuture<Result> rebuildAsync(
- Change.Id id, ListeningExecutorService executor) {
- return executor.submit(
- () -> {
- try (ReviewDb db = schemaFactory.open()) {
- return rebuild(db, id);
- }
- });
- }
-
- /**
- * Rebuild ReviewDb contents by copying from NoteDb.
- *
- * <p>Requires NoteDb to be the primary storage for the change.
- */
- public abstract void rebuildReviewDb(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException;
-
- // In the following methods "rebuilding" always refers to copying the state
- // from ReviewDb to NoteDb, i.e. assuming ReviewDb is the primary storage.
-
- public abstract Result rebuild(ReviewDb db, Change.Id changeId) throws IOException, OrmException;
-
- public abstract Result rebuildEvenIfReadOnly(ReviewDb db, Change.Id changeId)
- throws IOException, OrmException;
-
- public abstract Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle)
- throws IOException, OrmException;
-
- public abstract void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
- throws IOException, OrmException;
-
- public abstract NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
- throws IOException, OrmException;
-
- public abstract Result execute(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
- throws OrmException, IOException;
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java b/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
deleted file mode 100644
index 0d516e5b88..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
+++ /dev/null
@@ -1,685 +0,0 @@
-// Copyright (C) 2014 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.rebuild;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
-import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_PATCH_SET;
-import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Splitter;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Table;
-import com.google.common.primitives.Ints;
-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.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-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.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeDraftUpdate;
-import com.google.gerrit.server.notedb.ChangeNoteUtil;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.OpenRepo;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.Result;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.update.ChainedReceiveCommands;
-import com.google.gwtorm.client.Key;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeMap;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-public class ChangeRebuilderImpl extends ChangeRebuilder {
- /**
- * The maximum amount of time between the ReviewDb timestamp of the first and last events batched
- * together into a single NoteDb update.
- *
- * <p>Used to account for the fact that different records with their own timestamps (e.g. {@link
- * PatchSetApproval} and {@link ChangeMessage}) historically didn't necessarily use the same
- * timestamp, and tended to call {@code System.currentTimeMillis()} independently.
- */
- public static final long MAX_WINDOW_MS = SECONDS.toMillis(3);
-
- /**
- * The maximum amount of time between two consecutive events to consider them to be in the same
- * batch.
- */
- static final long MAX_DELTA_MS = SECONDS.toMillis(1);
-
- private final ChangeBundleReader bundleReader;
- private final ChangeDraftUpdate.Factory draftUpdateFactory;
- private final ChangeNoteUtil changeNoteUtil;
- private final ChangeNotes.Factory notesFactory;
- private final ChangeUpdate.Factory updateFactory;
- private final CommentsUtil commentsUtil;
- private final NoteDbUpdateManager.Factory updateManagerFactory;
- private final NotesMigration migration;
- private final PatchListCache patchListCache;
- private final PersonIdent serverIdent;
- private final ProjectCache projectCache;
- private final String serverId;
- private final long skewMs;
-
- @Inject
- ChangeRebuilderImpl(
- @GerritServerConfig Config cfg,
- SchemaFactory<ReviewDb> schemaFactory,
- ChangeBundleReader bundleReader,
- ChangeDraftUpdate.Factory draftUpdateFactory,
- ChangeNoteUtil changeNoteUtil,
- ChangeNotes.Factory notesFactory,
- ChangeUpdate.Factory updateFactory,
- CommentsUtil commentsUtil,
- NoteDbUpdateManager.Factory updateManagerFactory,
- NotesMigration migration,
- PatchListCache patchListCache,
- @GerritPersonIdent PersonIdent serverIdent,
- @Nullable ProjectCache projectCache,
- @GerritServerId String serverId) {
- super(schemaFactory);
- this.bundleReader = bundleReader;
- this.draftUpdateFactory = draftUpdateFactory;
- this.changeNoteUtil = changeNoteUtil;
- this.notesFactory = notesFactory;
- this.updateFactory = updateFactory;
- this.commentsUtil = commentsUtil;
- this.updateManagerFactory = updateManagerFactory;
- this.migration = migration;
- this.patchListCache = patchListCache;
- this.serverIdent = serverIdent;
- this.projectCache = projectCache;
- this.serverId = serverId;
- this.skewMs = NoteDbChangeState.getReadOnlySkew(cfg);
- }
-
- @Override
- public Result rebuild(ReviewDb db, Change.Id changeId) throws IOException, OrmException {
- return rebuild(db, changeId, true);
- }
-
- @Override
- public Result rebuildEvenIfReadOnly(ReviewDb db, Change.Id changeId)
- throws IOException, OrmException {
- return rebuild(db, changeId, false);
- }
-
- private Result rebuild(ReviewDb db, Change.Id changeId, boolean checkReadOnly)
- throws IOException, OrmException {
- db = ReviewDbUtil.unwrapDb(db);
- // Read change just to get project; this instance is then discarded so we can read a consistent
- // ChangeBundle inside a transaction.
- Change change = db.changes().get(changeId);
- if (change == null) {
- throw new NoSuchChangeException(changeId);
- }
- try (NoteDbUpdateManager manager = updateManagerFactory.create(change.getProject())) {
- buildUpdates(manager, bundleReader.fromReviewDb(db, changeId));
- return execute(db, changeId, manager, checkReadOnly, true);
- }
- }
-
- @Override
- public Result rebuild(NoteDbUpdateManager manager, ChangeBundle bundle)
- throws NoSuchChangeException, IOException, OrmException {
- Change change = new Change(bundle.getChange());
- buildUpdates(manager, bundle);
- return manager.stageAndApplyDelta(change);
- }
-
- @Override
- public NoteDbUpdateManager stage(ReviewDb db, Change.Id changeId)
- throws IOException, OrmException {
- db = ReviewDbUtil.unwrapDb(db);
- Change change = checkNoteDbState(ChangeNotes.readOneReviewDbChange(db, changeId));
- if (change == null) {
- throw new NoSuchChangeException(changeId);
- }
- NoteDbUpdateManager manager = updateManagerFactory.create(change.getProject());
- buildUpdates(manager, bundleReader.fromReviewDb(db, changeId));
- manager.stage();
- return manager;
- }
-
- @Override
- public Result execute(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
- throws OrmException, IOException {
- return execute(db, changeId, manager, true, true);
- }
-
- public Result execute(
- ReviewDb db,
- Change.Id changeId,
- NoteDbUpdateManager manager,
- boolean checkReadOnly,
- boolean executeManager)
- throws OrmException, IOException {
- db = ReviewDbUtil.unwrapDb(db);
- Change change = checkNoteDbState(ChangeNotes.readOneReviewDbChange(db, changeId));
- if (change == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- String oldNoteDbStateStr = change.getNoteDbState();
- Result r = manager.stageAndApplyDelta(change);
- String newNoteDbStateStr = change.getNoteDbState();
- if (newNoteDbStateStr == null) {
- throw new OrmException(
- String.format(
- "Rebuilding change %s produced no writes to NoteDb: %s",
- changeId, bundleReader.fromReviewDb(db, changeId)));
- }
- NoteDbChangeState newNoteDbState =
- requireNonNull(NoteDbChangeState.parse(changeId, newNoteDbStateStr));
- try {
- db.changes()
- .atomicUpdate(
- changeId,
- new AtomicUpdate<Change>() {
- @Override
- public Change update(Change change) {
- if (checkReadOnly) {
- NoteDbChangeState.checkNotReadOnly(change, skewMs);
- }
- String currNoteDbStateStr = change.getNoteDbState();
- if (Objects.equals(currNoteDbStateStr, newNoteDbStateStr)) {
- // Another thread completed the same rebuild we were about to.
- throw new AbortUpdateException();
- } else if (!Objects.equals(oldNoteDbStateStr, currNoteDbStateStr)) {
- // Another thread updated the state to something else.
- throw new ConflictingUpdateRuntimeException(change, oldNoteDbStateStr);
- }
- change.setNoteDbState(newNoteDbStateStr);
- return change;
- }
- });
- } catch (ConflictingUpdateRuntimeException e) {
- // Rethrow as an OrmException so the caller knows to use staged results. Strictly speaking
- // they are not completely up to date, but result we send to the caller is the same as if this
- // rebuild had executed before the other thread.
- throw new ConflictingUpdateException(e);
- } catch (AbortUpdateException e) {
- if (newNoteDbState.isUpToDate(
- manager.getChangeRepo().cmds.getRepoRefCache(),
- manager.getAllUsersRepo().cmds.getRepoRefCache())) {
- // If the state in ReviewDb matches NoteDb at this point, it means another thread
- // successfully completed this rebuild. It's ok to not execute the update in this case,
- // since the object referenced in the Result was flushed to the repo by whatever thread won
- // the race.
- return r;
- }
- // If the state doesn't match, that means another thread attempted this rebuild, but
- // failed. Fall through and try to update the ref again.
- }
- if (migration.failChangeWrites()) {
- // Don't even attempt to execute if read-only, it would fail anyway. But do throw an exception
- // to the caller so they know to use the staged results instead of reading from the repo.
- throw new OrmException(NoteDbUpdateManager.CHANGES_READ_ONLY);
- }
- if (executeManager) {
- manager.execute();
- }
- return r;
- }
-
- static Change checkNoteDbState(Change c) throws OrmException {
- // Can only rebuild a change if its primary storage is ReviewDb.
- NoteDbChangeState s = NoteDbChangeState.parse(c);
- if (s != null && s.getPrimaryStorage() != PrimaryStorage.REVIEW_DB) {
- throw new OrmException(String.format("cannot rebuild change %s with state %s", c.getId(), s));
- }
- return c;
- }
-
- @Override
- public void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
- throws IOException, OrmException {
- manager.setCheckExpectedState(false).setRefLogMessage("Rebuilding change");
- Change change = new Change(bundle.getChange());
- if (bundle.getPatchSets().isEmpty()) {
- throw new NoPatchSetsException(change.getId());
- }
- if (change.getLastUpdatedOn().compareTo(change.getCreatedOn()) < 0) {
- // A bug in data migration might set created_on to the time of the migration. The
- // correct timestamps were lost, but we can at least set it so created_on is not after
- // last_updated_on.
- // See https://bugs.chromium.org/p/gerrit/issues/detail?id=7397
- change.setCreatedOn(change.getLastUpdatedOn());
- }
-
- // We will rebuild all events, except for draft comments, in buckets based on author and
- // timestamp.
- List<Event> events = new ArrayList<>();
- ListMultimap<Account.Id, DraftCommentEvent> draftCommentEvents =
- MultimapBuilder.hashKeys().arrayListValues().build();
-
- events.addAll(getHashtagsEvents(change, manager));
-
- // Delete ref only after hashtags have been read.
- deleteChangeMetaRef(change, manager.getChangeRepo().cmds);
- deleteDraftRefs(change, manager.getAllUsersRepo());
-
- Integer minPsNum = getMinPatchSetNum(bundle);
- TreeMap<PatchSet.Id, PatchSetEvent> patchSetEvents =
- new TreeMap<>(ReviewDbUtil.intKeyOrdering());
-
- for (PatchSet ps : bundle.getPatchSets()) {
- PatchSetEvent pse = new PatchSetEvent(change, ps, manager.getChangeRepo().rw);
- patchSetEvents.put(ps.getId(), pse);
- events.add(pse);
- for (Comment c : getComments(bundle, serverId, Status.PUBLISHED, ps)) {
- CommentEvent e = new CommentEvent(c, change, ps, patchListCache);
- events.add(e.addDep(pse));
- }
- for (Comment c : getComments(bundle, serverId, Status.DRAFT, ps)) {
- DraftCommentEvent e = new DraftCommentEvent(c, change, ps, patchListCache);
- draftCommentEvents.put(c.author.getId(), e);
- }
- }
- ensurePatchSetOrder(patchSetEvents);
-
- for (PatchSetApproval psa : bundle.getPatchSetApprovals()) {
- PatchSetEvent pse = patchSetEvents.get(psa.getPatchSetId());
- if (pse != null) {
- events.add(new ApprovalEvent(psa, change.getCreatedOn()).addDep(pse));
- }
- }
-
- for (Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> r :
- bundle.getReviewers().asTable().cellSet()) {
- events.add(new ReviewerEvent(r, change.getCreatedOn()));
- }
-
- Change noteDbChange = new Change(null, null, null, null, null);
- for (ChangeMessage msg : bundle.getChangeMessages()) {
- Event msgEvent = new ChangeMessageEvent(change, noteDbChange, msg, change.getCreatedOn());
- if (msg.getPatchSetId() != null) {
- PatchSetEvent pse = patchSetEvents.get(msg.getPatchSetId());
- if (pse == null) {
- continue; // Ignore events for missing patch sets.
- }
- msgEvent.addDep(pse);
- }
- events.add(msgEvent);
- }
-
- sortAndFillEvents(change, noteDbChange, bundle.getPatchSets(), events, minPsNum);
-
- EventList<Event> el = new EventList<>();
- for (Event e : events) {
- if (!el.canAdd(e)) {
- flushEventsToUpdate(manager, el, change);
- checkState(el.canAdd(e));
- }
- el.add(e);
- }
- flushEventsToUpdate(manager, el, change);
-
- EventList<DraftCommentEvent> plcel = new EventList<>();
- for (Account.Id author : draftCommentEvents.keys()) {
- for (DraftCommentEvent e : Ordering.natural().sortedCopy(draftCommentEvents.get(author))) {
- if (!plcel.canAdd(e)) {
- flushEventsToDraftUpdate(manager, plcel, change);
- checkState(plcel.canAdd(e));
- }
- plcel.add(e);
- }
- flushEventsToDraftUpdate(manager, plcel, change);
- }
- }
-
- private static Integer getMinPatchSetNum(ChangeBundle bundle) {
- Integer minPsNum = null;
- for (PatchSet ps : bundle.getPatchSets()) {
- int n = ps.getId().get();
- if (minPsNum == null || n < minPsNum) {
- minPsNum = n;
- }
- }
- return minPsNum;
- }
-
- private static void ensurePatchSetOrder(TreeMap<PatchSet.Id, PatchSetEvent> events) {
- if (events.isEmpty()) {
- return;
- }
- Iterator<PatchSetEvent> it = events.values().iterator();
- PatchSetEvent curr = it.next();
- while (it.hasNext()) {
- PatchSetEvent next = it.next();
- next.addDep(curr);
- curr = next;
- }
- }
-
- private static List<Comment> getComments(
- ChangeBundle bundle, String serverId, PatchLineComment.Status status, PatchSet ps) {
- return bundle.getPatchLineComments().stream()
- .filter(c -> c.getPatchSetId().equals(ps.getId()) && c.getStatus() == status)
- .map(plc -> plc.asComment(serverId))
- .sorted(CommentsUtil.COMMENT_ORDER)
- .collect(toList());
- }
-
- private void sortAndFillEvents(
- Change change,
- Change noteDbChange,
- ImmutableCollection<PatchSet> patchSets,
- List<Event> events,
- Integer minPsNum) {
- Event finalUpdates = new FinalUpdatesEvent(change, noteDbChange, patchSets);
- events.add(finalUpdates);
- setPostSubmitDeps(events);
- new EventSorter(events).sort();
-
- // Ensure the first event in the list creates the change, setting the author and any required
- // footers. Also force the creation time of the first patch set to match the creation time of
- // the change.
- Event first = events.get(0);
- if (first instanceof PatchSetEvent && change.getOwner().equals(first.user)) {
- first.when = change.getCreatedOn();
- ((PatchSetEvent) first).createChange = true;
- } else {
- events.add(0, new CreateChangeEvent(change, minPsNum));
- }
-
- // Final pass to correct some inconsistencies.
- //
- // First, fill in any missing patch set IDs using the latest patch set of the change at the time
- // of the event, because NoteDb can't represent actions with no associated patch set ID. This
- // workaround is as if a user added a ChangeMessage on the change by replying from the latest
- // patch set.
- //
- // Start with the first patch set that actually exists. If there are no patch sets at all,
- // minPsNum will be null, so just bail and use 1 as the patch set ID.
- //
- // Second, ensure timestamps are nondecreasing, by copying the previous timestamp if this
- // happens. This assumes that the only way this can happen is due to dependency constraints, and
- // it is ok to give an event the same timestamp as one of its dependencies.
- int ps = firstNonNull(minPsNum, 1);
- for (int i = 0; i < events.size(); i++) {
- Event e = events.get(i);
- if (e.psId == null) {
- e.psId = new PatchSet.Id(change.getId(), ps);
- } else {
- ps = Math.max(ps, e.psId.get());
- }
-
- if (i > 0) {
- Event p = events.get(i - 1);
- if (e.when.before(p.when)) {
- e.when = p.when;
- }
- }
- }
- }
-
- private void setPostSubmitDeps(List<Event> events) {
- Optional<Event> submitEvent =
- Lists.reverse(events).stream().filter(Event::isSubmit).findFirst();
- if (submitEvent.isPresent()) {
- events.stream().filter(Event::isPostSubmitApproval).forEach(e -> e.addDep(submitEvent.get()));
- }
- }
-
- private void flushEventsToUpdate(
- NoteDbUpdateManager manager, EventList<Event> events, Change change)
- throws OrmException, IOException {
- if (events.isEmpty()) {
- return;
- }
- Comparator<String> labelNameComparator;
- if (projectCache != null) {
- labelNameComparator = projectCache.get(change.getProject()).getLabelTypes().nameComparator();
- } else {
- // No project cache available, bail and use natural ordering; there's no semantic difference
- // anyway difference.
- labelNameComparator = Ordering.natural();
- }
- ChangeUpdate update =
- updateFactory.create(
- change,
- events.getAccountId(),
- events.getRealAccountId(),
- newAuthorIdent(events),
- events.getWhen(),
- labelNameComparator);
- update.setAllowWriteToNewRef(true);
- update.setPatchSetId(events.getPatchSetId());
- update.setTag(events.getTag());
- for (Event e : events) {
- e.apply(update);
- }
- manager.add(update);
- events.clear();
- }
-
- private void flushEventsToDraftUpdate(
- NoteDbUpdateManager manager, EventList<DraftCommentEvent> events, Change change) {
- if (events.isEmpty()) {
- return;
- }
- ChangeDraftUpdate update =
- draftUpdateFactory.create(
- change,
- events.getAccountId(),
- events.getRealAccountId(),
- newAuthorIdent(events),
- events.getWhen());
- update.setPatchSetId(events.getPatchSetId());
- for (DraftCommentEvent e : events) {
- e.applyDraft(update);
- }
- manager.add(update);
- events.clear();
- }
-
- private PersonIdent newAuthorIdent(EventList<?> events) {
- Account.Id id = events.getAccountId();
- if (id == null) {
- return new PersonIdent(serverIdent, events.getWhen());
- }
- return changeNoteUtil.newIdent(id, events.getWhen(), serverIdent);
- }
-
- private List<HashtagsEvent> getHashtagsEvents(Change change, NoteDbUpdateManager manager)
- throws IOException {
- String refName = changeMetaRef(change.getId());
- Optional<ObjectId> old = manager.getChangeRepo().getObjectId(refName);
- if (!old.isPresent()) {
- return Collections.emptyList();
- }
-
- RevWalk rw = manager.getChangeRepo().rw;
- List<HashtagsEvent> events = new ArrayList<>();
- rw.reset();
- rw.markStart(rw.parseCommit(old.get()));
- for (RevCommit commit : rw) {
- Account.Id authorId;
- try {
- authorId =
- changeNoteUtil
- .getLegacyChangeNoteRead()
- .parseIdent(commit.getAuthorIdent(), change.getId());
- } catch (ConfigInvalidException e) {
- continue; // Corrupt data, no valid hashtags in this commit.
- }
- PatchSet.Id psId = parsePatchSetId(change, commit);
- Set<String> hashtags = parseHashtags(commit);
- if (authorId == null || psId == null || hashtags == null) {
- continue;
- }
-
- Timestamp commitTime = new Timestamp(commit.getCommitterIdent().getWhen().getTime());
- events.add(new HashtagsEvent(psId, authorId, commitTime, hashtags, change.getCreatedOn()));
- }
- return events;
- }
-
- private Set<String> parseHashtags(RevCommit commit) {
- List<String> hashtagsLines = commit.getFooterLines(FOOTER_HASHTAGS);
- if (hashtagsLines.isEmpty() || hashtagsLines.size() > 1) {
- return null;
- }
-
- if (hashtagsLines.get(0).isEmpty()) {
- return ImmutableSet.of();
- }
- return Sets.newHashSet(Splitter.on(',').split(hashtagsLines.get(0)));
- }
-
- private PatchSet.Id parsePatchSetId(Change change, RevCommit commit) {
- List<String> psIdLines = commit.getFooterLines(FOOTER_PATCH_SET);
- if (psIdLines.size() != 1) {
- return null;
- }
- Integer psId = Ints.tryParse(psIdLines.get(0));
- if (psId == null) {
- return null;
- }
- return new PatchSet.Id(change.getId(), psId);
- }
-
- private void deleteChangeMetaRef(Change change, ChainedReceiveCommands cmds) throws IOException {
- String refName = changeMetaRef(change.getId());
- Optional<ObjectId> old = cmds.get(refName);
- if (old.isPresent()) {
- cmds.add(new ReceiveCommand(old.get(), ObjectId.zeroId(), refName));
- }
- }
-
- private void deleteDraftRefs(Change change, OpenRepo allUsersRepo) throws IOException {
- for (Ref r :
- allUsersRepo
- .repo
- .getRefDatabase()
- .getRefsByPrefix(RefNames.refsDraftCommentsPrefix(change.getId()))) {
- allUsersRepo.cmds.add(new ReceiveCommand(r.getObjectId(), ObjectId.zeroId(), r.getName()));
- }
- }
-
- static void createChange(ChangeUpdate update, Change change) {
- update.setSubjectForCommit("Create change");
- update.setChangeId(change.getKey().get());
- update.setBranch(change.getDest().get());
- update.setSubject(change.getOriginalSubject());
- if (change.getRevertOf() != null) {
- update.setRevertOf(change.getRevertOf().get());
- }
- }
-
- @Override
- public void rebuildReviewDb(ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException {
- // TODO(dborowitz): Fail fast if changes tables are disabled in ReviewDb.
- ChangeNotes notes = notesFactory.create(db, project, changeId);
- ChangeBundle bundle = ChangeBundle.fromNotes(commentsUtil, notes);
-
- db = ReviewDbUtil.unwrapDb(db);
- db.changes().beginTransaction(changeId);
- try {
- Change c = db.changes().get(changeId);
- if (c != null) {
- PrimaryStorage ps = PrimaryStorage.of(c);
- switch (ps) {
- case REVIEW_DB:
- return; // Nothing to do.
- case NOTE_DB:
- break; // Continue and rebuild.
- default:
- throw new OrmException("primary storage of " + changeId + " is " + ps);
- }
- } else {
- c = notes.getChange();
- }
- db.changes().upsert(Collections.singleton(c));
- putExactlyEntities(
- db.changeMessages(), db.changeMessages().byChange(c.getId()), bundle.getChangeMessages());
- putExactlyEntities(db.patchSets(), db.patchSets().byChange(c.getId()), bundle.getPatchSets());
- putExactlyEntities(
- db.patchSetApprovals(),
- db.patchSetApprovals().byChange(c.getId()),
- bundle.getPatchSetApprovals());
- putExactlyEntities(
- db.patchComments(),
- db.patchComments().byChange(c.getId()),
- bundle.getPatchLineComments());
- db.commit();
- } finally {
- db.rollback();
- }
- }
-
- private static <T, K extends Key<?>> void putExactlyEntities(
- Access<T, K> access, Iterable<T> existing, Collection<T> ents) throws OrmException {
- Set<K> toKeep = access.toMap(ents).keySet();
- access.delete(
- FluentIterable.from(existing).filter(e -> !toKeep.contains(access.primaryKey(e))));
- access.upsert(ents);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java b/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
deleted file mode 100644
index 8f7b387073..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
+++ /dev/null
@@ -1,82 +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.server.notedb.rebuild;
-
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.flogger.FluentLogger;
-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.PatchSet;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-
-class CommentEvent extends Event {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public final Comment c;
- private final Change change;
- private final PatchSet ps;
- private final PatchListCache cache;
-
- CommentEvent(Comment c, Change change, PatchSet ps, PatchListCache cache) {
- super(
- CommentsUtil.getCommentPsId(change.getId(), c),
- c.author.getId(),
- c.getRealAuthor().getId(),
- c.writtenOn,
- change.getCreatedOn(),
- c.tag);
- this.c = c;
- this.change = change;
- this.ps = ps;
- this.cache = cache;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return false;
- }
-
- @Override
- protected boolean canHaveTag() {
- return true;
- }
-
- @Override
- void apply(ChangeUpdate update) {
- checkUpdate(update);
- if (c.revId == null) {
- try {
- setCommentRevId(c, cache, change, ps);
- } catch (PatchListNotAvailableException e) {
- logger.atWarning().log(
- "Unable to determine parent commit of patch set %s (%s); omitting inline comment %s",
- ps.getId(), ps.getRevision(), c);
- return;
- }
- }
- update.putComment(PatchLineComment.Status.PUBLISHED, c);
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- helper.add("message", c.message);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java b/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java
deleted file mode 100644
index d8e7480fdf..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.notedb.rebuild;
-
-import com.google.gwtorm.server.OrmException;
-
-/**
- * {@link com.google.gwtorm.server.OrmException} thrown by {@link ChangeRebuilder} when rebuilding a
- * change failed because another operation modified its {@link
- * com.google.gerrit.server.notedb.NoteDbChangeState}.
- */
-public class ConflictingUpdateException extends OrmException {
- private static final long serialVersionUID = 1L;
-
- // Always created from a ConflictingUpdateRuntimeException because it originates from an
- // AtomicUpdate, which cannot throw checked exceptions.
- ConflictingUpdateException(ConflictingUpdateRuntimeException cause) {
- super(cause.getMessage(), cause);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java b/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java
deleted file mode 100644
index abfafa2041..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ConflictingUpdateRuntimeException.java
+++ /dev/null
@@ -1,29 +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.server.notedb.rebuild;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmRuntimeException;
-
-class ConflictingUpdateRuntimeException extends OrmRuntimeException {
- private static final long serialVersionUID = 1L;
-
- ConflictingUpdateRuntimeException(Change change, String expectedNoteDbState) {
- super(
- String.format(
- "Expected change %s to have noteDbState %s but was %s",
- change.getId(), expectedNoteDbState, change.getNoteDbState()));
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java b/java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java
deleted file mode 100644
index d01071ba02..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/CreateChangeEvent.java
+++ /dev/null
@@ -1,59 +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.server.notedb.rebuild;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-
-class CreateChangeEvent extends Event {
- private final Change change;
-
- private static PatchSet.Id psId(Change change, Integer minPsNum) {
- int n;
- if (minPsNum == null) {
- // There were no patch sets for the change at all, so something is very
- // wrong. Bail and use 1 as the patch set.
- n = 1;
- } else {
- n = minPsNum;
- }
- return new PatchSet.Id(change.getId(), n);
- }
-
- CreateChangeEvent(Change change, Integer minPsNum) {
- super(
- psId(change, minPsNum),
- change.getOwner(),
- change.getOwner(),
- change.getCreatedOn(),
- change.getCreatedOn(),
- null);
- this.change = change;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return true;
- }
-
- @Override
- void apply(ChangeUpdate update) throws IOException, OrmException {
- checkUpdate(update);
- ChangeRebuilderImpl.createChange(update, change);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java b/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
deleted file mode 100644
index 2a2795dc75..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
+++ /dev/null
@@ -1,81 +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.server.notedb.rebuild;
-
-import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.flogger.FluentLogger;
-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.CommentsUtil;
-import com.google.gerrit.server.notedb.ChangeDraftUpdate;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-
-class DraftCommentEvent extends Event {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public final Comment c;
- private final Change change;
- private final PatchSet ps;
- private final PatchListCache cache;
-
- DraftCommentEvent(Comment c, Change change, PatchSet ps, PatchListCache cache) {
- super(
- CommentsUtil.getCommentPsId(change.getId(), c),
- c.author.getId(),
- c.getRealAuthor().getId(),
- c.writtenOn,
- change.getCreatedOn(),
- c.tag);
- this.c = c;
- this.change = change;
- this.ps = ps;
- this.cache = cache;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return false;
- }
-
- @Override
- void apply(ChangeUpdate update) {
- throw new UnsupportedOperationException();
- }
-
- void applyDraft(ChangeDraftUpdate draftUpdate) {
- if (c.revId == null) {
- try {
- setCommentRevId(c, cache, change, ps);
- } catch (PatchListNotAvailableException e) {
- logger.atWarning().log(
- "Unable to determine parent commit of patch set %s (%s);"
- + " omitting draft inline comment %s",
- ps.getId(), ps.getRevision(), c);
- return;
- }
- }
- draftUpdate.putComment(c);
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- helper.add("message", c.message);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/Event.java b/java/com/google/gerrit/server/notedb/rebuild/Event.java
deleted file mode 100644
index 3957c5c748..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/Event.java
+++ /dev/null
@@ -1,146 +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.server.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl.MAX_WINDOW_MS;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.ComparisonChain;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.notedb.AbstractChangeUpdate;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-abstract class Event implements Comparable<Event> {
- // NOTE: EventList only supports direct subclasses, not an arbitrary
- // hierarchy.
-
- final Account.Id user;
- final Account.Id realUser;
- final String tag;
- final boolean predatesChange;
-
- /** Dependencies of this event; other events that must happen before this one. */
- final List<Event> deps;
-
- Timestamp when;
- PatchSet.Id psId;
-
- protected Event(
- PatchSet.Id psId,
- Account.Id effectiveUser,
- Account.Id realUser,
- Timestamp when,
- Timestamp changeCreatedOn,
- String tag) {
- this.psId = psId;
- this.user = effectiveUser;
- this.realUser = realUser != null ? realUser : effectiveUser;
- this.tag = tag;
- // Truncate timestamps at the change's createdOn timestamp.
- predatesChange = when.before(changeCreatedOn);
- this.when = predatesChange ? changeCreatedOn : when;
- deps = new ArrayList<>();
- }
-
- protected void checkUpdate(AbstractChangeUpdate update) {
- checkState(
- Objects.equals(update.getPatchSetId(), psId),
- "cannot apply event for %s to update for %s",
- update.getPatchSetId(),
- psId);
- checkState(
- when.getTime() - update.getWhen().getTime() <= MAX_WINDOW_MS,
- "event at %s outside update window starting at %s",
- when,
- update.getWhen());
- checkState(
- Objects.equals(update.getNullableAccountId(), user),
- "cannot apply event by %s to update by %s",
- user,
- update.getNullableAccountId());
- }
-
- Event addDep(Event e) {
- deps.add(e);
- return this;
- }
-
- /**
- * @return whether this event type must be unique per {@link ChangeUpdate}, i.e. there may be at
- * most one of this type.
- */
- abstract boolean uniquePerUpdate();
-
- abstract void apply(ChangeUpdate update) throws OrmException, IOException;
-
- protected boolean isPostSubmitApproval() {
- return false;
- }
-
- protected boolean isSubmit() {
- return false;
- }
-
- protected boolean canHaveTag() {
- return false;
- }
-
- @Override
- public String toString() {
- ToStringHelper helper =
- MoreObjects.toStringHelper(this)
- .add("psId", psId)
- .add("effectiveUser", user)
- .add("realUser", realUser)
- .add("when", when)
- .add("tag", tag);
- addToString(helper);
- return helper.toString();
- }
-
- /** @param helper toString helper to add fields to */
- protected void addToString(ToStringHelper helper) {}
-
- @Override
- public int compareTo(Event other) {
- return ComparisonChain.start()
- .compareFalseFirst(this.isFinalUpdates(), other.isFinalUpdates())
- .compare(this.when, other.when)
- .compareTrueFirst(isPatchSet(), isPatchSet())
- .compareTrueFirst(this.predatesChange, other.predatesChange)
- .compare(this.user, other.user, ReviewDbUtil.intKeyOrdering())
- .compare(this.realUser, other.realUser, ReviewDbUtil.intKeyOrdering())
- .compare(this.psId, other.psId, ReviewDbUtil.intKeyOrdering().nullsLast())
- .result();
- }
-
- private boolean isPatchSet() {
- return this instanceof PatchSetEvent;
- }
-
- private boolean isFinalUpdates() {
- return this instanceof FinalUpdatesEvent;
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/EventList.java b/java/com/google/gerrit/server/notedb/rebuild/EventList.java
deleted file mode 100644
index e83814df81..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/EventList.java
+++ /dev/null
@@ -1,170 +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.server.notedb.rebuild;
-
-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.Lists;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Objects;
-
-class EventList<E extends Event> implements Iterable<E> {
- private final ArrayList<E> list = new ArrayList<>();
- private boolean isSubmit;
-
- @Override
- public Iterator<E> iterator() {
- return list.iterator();
- }
-
- void add(E e) {
- list.add(e);
- if (e.isSubmit()) {
- isSubmit = true;
- }
- }
-
- void clear() {
- list.clear();
- isSubmit = false;
- }
-
- boolean isEmpty() {
- return list.isEmpty();
- }
-
- boolean canAdd(E e) {
- if (isEmpty()) {
- return true;
- }
- if (e instanceof FinalUpdatesEvent) {
- return false; // FinalUpdatesEvent always gets its own update.
- }
-
- Event last = getLast();
- if (!Objects.equals(e.user, last.user)
- || !Objects.equals(e.realUser, last.realUser)
- || !e.psId.equals(last.psId)) {
- return false; // Different patch set or author.
- }
- if (e.canHaveTag() && canHaveTag() && !Objects.equals(e.tag, getTag())) {
- // We should trust the tag field, and it doesn't match.
- return false;
- }
- if (e.isPostSubmitApproval() && isSubmit) {
- // Post-submit approvals must come after the update that submits.
- return false;
- }
-
- long t = e.when.getTime();
- long tFirst = getFirstTime();
- long tLast = getLastTime();
- checkArgument(t >= tLast, "event %s is before previous event in list %s", e, last);
- if (t - tLast > ChangeRebuilderImpl.MAX_DELTA_MS
- || t - tFirst > ChangeRebuilderImpl.MAX_WINDOW_MS) {
- return false; // Too much time elapsed.
- }
-
- if (!e.uniquePerUpdate()) {
- return true;
- }
- for (Event o : this) {
- if (e.getClass() == o.getClass()) {
- return false; // Only one event of this type allowed per update.
- }
- }
-
- // TODO(dborowitz): Additional heuristics, like keeping events separate if
- // they affect overlapping fields within a single entity.
-
- return true;
- }
-
- Timestamp getWhen() {
- return get(0).when;
- }
-
- PatchSet.Id getPatchSetId() {
- PatchSet.Id id = requireNonNull(get(0).psId);
- for (int i = 1; i < size(); i++) {
- checkState(
- get(i).psId.equals(id), "mismatched patch sets in EventList: %s != %s", id, get(i).psId);
- }
- return id;
- }
-
- Account.Id getAccountId() {
- Account.Id id = get(0).user;
- for (int i = 1; i < size(); i++) {
- checkState(
- Objects.equals(id, get(i).user),
- "mismatched users in EventList: %s != %s",
- id,
- get(i).user);
- }
- return id;
- }
-
- Account.Id getRealAccountId() {
- Account.Id id = get(0).realUser;
- for (int i = 1; i < size(); i++) {
- checkState(
- Objects.equals(id, get(i).realUser),
- "mismatched real users in EventList: %s != %s",
- id,
- get(i).realUser);
- }
- return id;
- }
-
- String getTag() {
- for (E e : Lists.reverse(list)) {
- if (e.tag != null) {
- return e.tag;
- }
- }
- return null;
- }
-
- private boolean canHaveTag() {
- return list.stream().anyMatch(Event::canHaveTag);
- }
-
- private E get(int i) {
- return list.get(i);
- }
-
- private int size() {
- return list.size();
- }
-
- private E getLast() {
- return list.get(list.size() - 1);
- }
-
- private long getLastTime() {
- return getLast().when.getTime();
- }
-
- private long getFirstTime() {
- return list.get(0).when.getTime();
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/EventSorter.java b/java/com/google/gerrit/server/notedb/rebuild/EventSorter.java
deleted file mode 100644
index 077a02787d..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/EventSorter.java
+++ /dev/null
@@ -1,112 +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.server.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * Helper to sort a list of events.
- *
- * <p>Events are sorted in two passes:
- *
- * <ol>
- * <li>Sort by natural order (timestamp, patch set, author, etc.)
- * <li>Postpone any events with dependencies to occur only after all of their dependencies, where
- * this violates natural order.
- * </ol>
- *
- * {@link #sort()} modifies the event list in place (similar to {@link Collections#sort(List)}), but
- * does not modify any event. In particular, events might end up out of order with respect to
- * timestamp; callers are responsible for adjusting timestamps later if they prefer monotonicity.
- */
-class EventSorter {
- private final List<Event> out;
- private final LinkedHashSet<Event> sorted;
- private ListMultimap<Event, Event> waiting;
- private SetMultimap<Event, Event> deps;
-
- EventSorter(List<Event> events) {
- LinkedHashSet<Event> all = new LinkedHashSet<>(events);
- out = events;
-
- for (Event e : events) {
- for (Event d : e.deps) {
- checkArgument(all.contains(d), "dep %s of %s not in input list", d, e);
- }
- }
-
- all.clear();
- sorted = all; // Presized.
- }
-
- void sort() {
- // First pass: sort by natural order.
- PriorityQueue<Event> todo = new PriorityQueue<>(out);
-
- // Populate waiting map after initial sort to preserve natural order.
- waiting = MultimapBuilder.hashKeys().arrayListValues().build();
- deps = MultimapBuilder.hashKeys().hashSetValues().build();
- for (Event e : todo) {
- for (Event d : e.deps) {
- deps.put(e, d);
- waiting.put(d, e);
- }
- }
-
- // Second pass: enforce dependencies.
- int size = out.size();
- while (!todo.isEmpty()) {
- process(todo.remove(), todo);
- }
- checkState(
- sorted.size() == size, "event sort expected %s elements, got %s", size, sorted.size());
-
- // Modify out in-place a la Collections#sort.
- out.clear();
- out.addAll(sorted);
- }
-
- void process(Event e, PriorityQueue<Event> todo) {
- if (sorted.contains(e)) {
- return; // Already emitted.
- }
- if (!deps.get(e).isEmpty()) {
- // Not all events that e depends on have been emitted yet. Ignore e for
- // now; it will get added back to the queue in the block below once its
- // last dependency is processed.
- return;
- }
-
- // All events that e depends on have been emitted, so e can be emitted.
- sorted.add(e);
-
- // Remove e from the dependency set of all events waiting on e, and add
- // those events back to the queue in the original priority order for
- // reconsideration.
- for (Event w : waiting.get(e)) {
- deps.get(w).remove(e);
- todo.add(w);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java b/java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java
deleted file mode 100644
index 55d5a313ef..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/FinalUpdatesEvent.java
+++ /dev/null
@@ -1,101 +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.server.notedb.rebuild;
-
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.ImmutableCollection;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.util.Objects;
-
-class FinalUpdatesEvent extends Event {
- private final Change change;
- private final Change noteDbChange;
- private final ImmutableCollection<PatchSet> patchSets;
-
- FinalUpdatesEvent(Change change, Change noteDbChange, ImmutableCollection<PatchSet> patchSets) {
- super(
- change.currentPatchSetId(),
- change.getOwner(),
- change.getOwner(),
- change.getLastUpdatedOn(),
- change.getCreatedOn(),
- null);
- this.change = change;
- this.noteDbChange = noteDbChange;
- this.patchSets = patchSets;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return true;
- }
-
- @SuppressWarnings("deprecation")
- @Override
- void apply(ChangeUpdate update) throws OrmException {
- if (!Objects.equals(change.getTopic(), noteDbChange.getTopic())) {
- update.setTopic(change.getTopic());
- }
- if (!statusMatches()) {
- // TODO(dborowitz): Stamp approximate approvals at this time.
- update.fixStatus(change.getStatus());
- }
- if (change.isPrivate() != noteDbChange.isPrivate()) {
- update.setPrivate(change.isPrivate());
- }
- if (change.isWorkInProgress() != noteDbChange.isWorkInProgress()) {
- update.setWorkInProgress(change.isWorkInProgress());
- }
- if (change.getSubmissionId() != null && noteDbChange.getSubmissionId() == null) {
- update.setSubmissionId(change.getSubmissionId());
- }
- if (!Objects.equals(change.getAssignee(), noteDbChange.getAssignee())) {
- // TODO(dborowitz): Parse intermediate values out from messages.
- update.setAssignee(change.getAssignee());
- }
- if (!patchSets.isEmpty() && !highestNumberedPatchSetIsCurrent()) {
- update.setCurrentPatchSet();
- }
- if (!update.isEmpty()) {
- update.setSubjectForCommit("Final NoteDb migration updates");
- }
- }
-
- private boolean statusMatches() {
- return Objects.equals(change.getStatus(), noteDbChange.getStatus());
- }
-
- private boolean highestNumberedPatchSetIsCurrent() {
- PatchSet.Id max = patchSets.stream().map(PatchSet::getId).max(intKeyOrdering()).get();
- return max.equals(change.currentPatchSetId());
- }
-
- @Override
- protected boolean isSubmit() {
- return change.getStatus() == Change.Status.MERGED;
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- if (!statusMatches()) {
- helper.add("status", change.getStatus());
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java b/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java
deleted file mode 100644
index f2a5cc6598..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java
+++ /dev/null
@@ -1,121 +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.rebuild;
-
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_GC_SECTION;
-import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_AUTO;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GarbageCollectionResult;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GarbageCollection;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.function.Consumer;
-import org.eclipse.jgit.lib.Repository;
-
-@Singleton
-public class GcAllUsers {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private final AllUsersName allUsers;
- private final GarbageCollection.Factory gcFactory;
- private final GitRepositoryManager repoManager;
-
- @Inject
- GcAllUsers(
- AllUsersName allUsers,
- GarbageCollection.Factory gcFactory,
- GitRepositoryManager repoManager) {
- this.allUsers = allUsers;
- this.gcFactory = gcFactory;
- this.repoManager = repoManager;
- }
-
- public void runWithLogger() {
- // Print log messages using logger, and skip progress.
- run(s -> logger.atInfo().log(s), null);
- }
-
- public void run(PrintWriter writer) {
- // Print both log messages and progress to given writer.
- run(requireNonNull(writer)::println, writer);
- }
-
- private void run(Consumer<String> logOneLine, @Nullable PrintWriter progressWriter) {
- if (!(repoManager instanceof LocalDiskRepositoryManager)) {
- logOneLine.accept("Skipping GC of " + allUsers + "; not a local disk repo");
- return;
- }
- if (!enableAutoGc(logOneLine)) {
- logOneLine.accept(
- "Skipping GC of "
- + allUsers
- + " due to disabling "
- + CONFIG_GC_SECTION
- + "."
- + CONFIG_KEY_AUTO);
- logOneLine.accept(
- "If loading accounts is slow after the NoteDb migration, run `git gc` on "
- + allUsers
- + " manually");
- return;
- }
-
- if (progressWriter == null) {
- // Mimic log line from GarbageCollection.
- logOneLine.accept("collecting garbage for \"" + allUsers + "\":\n");
- }
- GarbageCollectionResult result =
- gcFactory.create().run(ImmutableList.of(allUsers), progressWriter);
- if (!result.hasErrors()) {
- return;
- }
- for (GarbageCollectionResult.Error e : result.getErrors()) {
- switch (e.getType()) {
- case GC_ALREADY_SCHEDULED:
- logOneLine.accept("GC already scheduled for " + e.getProjectName());
- break;
- case GC_FAILED:
- logOneLine.accept("GC failed for " + e.getProjectName());
- break;
- case REPOSITORY_NOT_FOUND:
- logOneLine.accept(e.getProjectName() + " repo not found");
- break;
- default:
- logOneLine.accept("GC failed for " + e.getProjectName() + ": " + e.getType());
- break;
- }
- }
- }
-
- private boolean enableAutoGc(Consumer<String> logOneLine) {
- try (Repository repo = repoManager.openRepository(allUsers)) {
- return repo.getConfig().getInt(CONFIG_GC_SECTION, CONFIG_KEY_AUTO, -1) != 0;
- } catch (IOException e) {
- logOneLine.accept(
- "Error reading config for " + allUsers + ":\n" + Throwables.getStackTraceAsString(e));
- return false;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java b/java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java
deleted file mode 100644
index 4f6f6ad339..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/HashtagsEvent.java
+++ /dev/null
@@ -1,62 +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.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.sql.Timestamp;
-import java.util.Set;
-
-class HashtagsEvent extends Event {
- private final Set<String> hashtags;
-
- HashtagsEvent(
- PatchSet.Id psId,
- Account.Id who,
- Timestamp when,
- Set<String> hashtags,
- Timestamp changeCreatdOn) {
- super(
- psId,
- who,
- who,
- when,
- changeCreatdOn,
- // Somewhat confusingly, hashtags do not use the setTag method on
- // AbstractChangeUpdate, so pass null as the tag.
- null);
- this.hashtags = hashtags;
- }
-
- @Override
- boolean uniquePerUpdate() {
- // Since these are produced from existing commits in the old NoteDb graph,
- // we know that there must be one per commit in the rebuilt graph.
- return true;
- }
-
- @Override
- void apply(ChangeUpdate update) throws OrmException {
- update.setHashtags(hashtags);
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- helper.add("hashtags", hashtags);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/MigrationException.java b/java/com/google/gerrit/server/notedb/rebuild/MigrationException.java
deleted file mode 100644
index 0cf78be5f8..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/MigrationException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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.notedb.rebuild;
-
-import java.io.IOException;
-
-/** Exception thrown by {@link NoteDbMigrator} when migration fails. */
-public class MigrationException extends IOException {
- private static final long serialVersionUID = 1L;
-
- MigrationException(String message) {
- super(message);
- }
-
- MigrationException(String message, Throwable why) {
- super(message, why);
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
deleted file mode 100644
index 36b07e51ad..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ /dev/null
@@ -1,1183 +0,0 @@
-// 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.notedb.rebuild;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.WRITE;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Comparator.comparing;
-import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Streams;
-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.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.common.FormatUtil;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Change.Id;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.InternalUser;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfigProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LockFailureException;
-import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.NoteDbTable;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.gerrit.server.notedb.PrimaryStorageMigrator;
-import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.update.ChainedReceiveCommands;
-import com.google.gerrit.server.update.RefUpdateUtil;
-import com.google.gerrit.server.util.ManualRequestContext;
-import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.storage.file.GC;
-import org.eclipse.jgit.internal.storage.file.PackInserter;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.storage.pack.PackConfig;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.FS;
-
-/** One stop shop for migrating a site's change storage from ReviewDb to NoteDb. */
-public class NoteDbMigrator implements AutoCloseable {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final String AUTO_MIGRATE = "autoMigrate";
- private static final String TRIAL = "trial";
-
- private static final int PROJECT_SLICE_MAX_REFS = 1000;
- private static final int GC_INTERVAL = 10000;
-
- public static boolean getAutoMigrate(Config cfg) {
- return cfg.getBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), AUTO_MIGRATE, false);
- }
-
- private static void setAutoMigrate(Config cfg, boolean autoMigrate) {
- cfg.setBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), AUTO_MIGRATE, autoMigrate);
- }
-
- public static boolean getTrialMode(Config cfg) {
- return cfg.getBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), TRIAL, false);
- }
-
- public static void setTrialMode(Config cfg, boolean trial) {
- cfg.setBoolean(SECTION_NOTE_DB, NoteDbTable.CHANGES.key(), TRIAL, trial);
- }
-
- private static class NoteDbMigrationLoggerOut extends OutputStream {
- private StringBuilder outputBuffer = new StringBuilder();
-
- @Override
- public synchronized void write(int b) throws IOException {
- if (b == '\r' || b == '\n') {
- logger.atInfo().log(outputBuffer.toString());
- outputBuffer = new StringBuilder();
- } else {
- outputBuffer.append(Character.toChars(b));
- }
- }
- }
-
- public static class Builder {
- private final Config cfg;
- private final SitePaths sitePaths;
- private final Provider<PersonIdent> serverIdent;
- private final AllUsersName allUsers;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final GitRepositoryManager repoManager;
- private final NoteDbUpdateManager.Factory updateManagerFactory;
- private final ChangeBundleReader bundleReader;
- private final AllProjectsName allProjects;
- private final InternalUser.Factory userFactory;
- private final ThreadLocalRequestContext requestContext;
- private final ChangeRebuilderImpl rebuilder;
- private final WorkQueue workQueue;
- private final MutableNotesMigration globalNotesMigration;
- private final PrimaryStorageMigrator primaryStorageMigrator;
- private final DynamicSet<NotesMigrationStateListener> listeners;
- private final ProjectCache projectCache;
-
- private int threads;
- private ImmutableList<Project.NameKey> projects = ImmutableList.of();
- private ImmutableList<Project.NameKey> skipProjects = ImmutableList.of();
- private ImmutableList<Change.Id> changes = ImmutableList.of();
- private OutputStream progressOut = new NoteDbMigrationLoggerOut();
- private NotesMigrationState stopAtState;
- private boolean trial;
- private boolean forceRebuild;
- private boolean forceStateChangeWithSkip;
- private int sequenceGap = -1;
- private boolean autoMigrate;
- private boolean verbose;
-
- @Inject
- Builder(
- GerritServerConfigProvider configProvider,
- SitePaths sitePaths,
- @GerritPersonIdent Provider<PersonIdent> serverIdent,
- AllUsersName allUsers,
- SchemaFactory<ReviewDb> schemaFactory,
- GitRepositoryManager repoManager,
- NoteDbUpdateManager.Factory updateManagerFactory,
- ChangeBundleReader bundleReader,
- AllProjectsName allProjects,
- ThreadLocalRequestContext requestContext,
- InternalUser.Factory userFactory,
- ChangeRebuilderImpl rebuilder,
- WorkQueue workQueue,
- MutableNotesMigration globalNotesMigration,
- PrimaryStorageMigrator primaryStorageMigrator,
- DynamicSet<NotesMigrationStateListener> listeners,
- ProjectCache projectCache) {
- // Reload gerrit.config/notedb.config on each migrator invocation, in case a previous
- // migration in the same process modified the on-disk contents. This ensures the defaults for
- // trial/autoMigrate get set correctly below.
- this.cfg = configProvider.loadConfig();
- this.sitePaths = sitePaths;
- this.serverIdent = serverIdent;
- this.allUsers = allUsers;
- this.schemaFactory = schemaFactory;
- this.repoManager = repoManager;
- this.updateManagerFactory = updateManagerFactory;
- this.bundleReader = bundleReader;
- this.allProjects = allProjects;
- this.requestContext = requestContext;
- this.userFactory = userFactory;
- this.rebuilder = rebuilder;
- this.workQueue = workQueue;
- this.globalNotesMigration = globalNotesMigration;
- this.primaryStorageMigrator = primaryStorageMigrator;
- this.listeners = listeners;
- this.projectCache = projectCache;
- this.trial = getTrialMode(cfg);
- this.autoMigrate = getAutoMigrate(cfg);
- }
-
- /**
- * Set the number of threads used by parallelizable phases of the migration, such as rebuilding
- * all changes.
- *
- * <p>Not all phases are parallelizable, and calling {@link #rebuild()} directly will do
- * substantial work in the calling thread regardless of the number of threads configured.
- *
- * <p>By default, all work is done in the calling thread.
- *
- * @param threads thread count; if less than 2, all work happens in the calling thread.
- * @return this.
- */
- public Builder setThreads(int threads) {
- this.threads = threads;
- return this;
- }
-
- /**
- * Limit the set of projects that are processed.
- *
- * <p>Incompatible with {@link #setChanges(Collection)}.
- *
- * <p>By default, all projects will be processed.
- *
- * @param projects set of projects; if null or empty, all projects will be processed.
- * @return this.
- */
- public Builder setProjects(@Nullable Collection<Project.NameKey> projects) {
- this.projects = projects != null ? ImmutableList.copyOf(projects) : ImmutableList.of();
- return this;
- }
-
- /**
- * Process all projects except these
- *
- * <p>Incompatible with {@link #setProjects(Collection)} and {@link #setChanges(Collection)}
- *
- * <p>By default, all projects will be processed.
- *
- * @param skipProjects set of projects; if null or empty all project will be processed
- * @return this.
- */
- public Builder setSkipProjects(@Nullable Collection<Project.NameKey> skipProjects) {
- this.skipProjects =
- skipProjects != null ? ImmutableList.copyOf(skipProjects) : ImmutableList.of();
- return this;
- }
-
- /**
- * Limit the set of changes that are processed.
- *
- * <p>Incompatible with {@link #setProjects(Collection)}.
- *
- * <p>By default, all changes will be processed.
- *
- * @param changes set of changes; if null or empty, all changes will be processed.
- * @return this.
- */
- public Builder setChanges(@Nullable Collection<Change.Id> changes) {
- this.changes = changes != null ? ImmutableList.copyOf(changes) : ImmutableList.of();
- return this;
- }
-
- /**
- * Set output stream for progress monitors.
- *
- * <p>By default, there is no progress monitor output (although there may be other logs).
- *
- * @param progressOut output stream.
- * @return this.
- */
- public Builder setProgressOut(OutputStream progressOut) {
- this.progressOut = requireNonNull(progressOut);
- return this;
- }
-
- /**
- * Stop at a specific migration state, for testing only.
- *
- * @param stopAtState state to stop at.
- * @return this.
- */
- @VisibleForTesting
- public Builder setStopAtStateForTesting(NotesMigrationState stopAtState) {
- this.stopAtState = stopAtState;
- return this;
- }
-
- /**
- * Rebuild in "trial mode": configure Gerrit to write to and read from NoteDb, but leave
- * ReviewDb as the source of truth for all changes.
- *
- * <p>By default, trial mode is off, and NoteDb is the source of truth for all changes following
- * the migration.
- *
- * @param trial whether to rebuild in trial mode.
- * @return this.
- */
- public Builder setTrialMode(boolean trial) {
- this.trial = trial;
- return this;
- }
-
- /**
- * Rebuild all changes in NoteDb from ReviewDb, even if Gerrit is currently configured to read
- * from NoteDb.
- *
- * <p>Only supported if ReviewDb is still the source of truth for all changes.
- *
- * <p>By default, force rebuilding is off.
- *
- * @param forceRebuild whether to force rebuilding.
- * @return this.
- */
- public Builder setForceRebuild(boolean forceRebuild) {
- this.forceRebuild = forceRebuild;
- return this;
- }
-
- /**
- * Force state change to next migration state if some projects were skipped.
- *
- * <p>This makes sense when the skipped projects are migrated in a copy of the site and migrated
- * data will be transported using git fetch.
- *
- * @param forceStateChangeWithSkip whether state change to next migration state should be
- * enforced if some projects were skipped.
- * @return this.
- */
- public Builder setForceStateChangeWithSkip(boolean forceStateChangeWithSkip) {
- this.forceStateChangeWithSkip = forceStateChangeWithSkip;
- return this;
- }
-
- /**
- * Gap between ReviewDb change sequence numbers and NoteDb.
- *
- * <p>If NoteDb sequences are enabled in a running server, there is a race between the migration
- * step that calls {@code nextChangeId()} to seed the ref, and other threads that call {@code
- * nextChangeId()} to create new changes. In order to prevent these operations stepping on one
- * another, we use this value to skip some predefined sequence numbers. This is strongly
- * recommended in a running server.
- *
- * <p>If the migration takes place offline, there is no race with other threads, and this option
- * may be set to 0. However, admins may still choose to use a gap, for example to make it easier
- * to distinguish changes that were created before and after the NoteDb migration.
- *
- * <p>By default, uses the value from {@code noteDb.changes.initialSequenceGap} in {@code
- * gerrit.config}, which defaults to 1000.
- *
- * @param sequenceGap sequence gap size; if negative, use the default.
- * @return this.
- */
- public Builder setSequenceGap(int sequenceGap) {
- this.sequenceGap = sequenceGap;
- return this;
- }
-
- /**
- * Enable auto-migration on subsequent daemon launches.
- *
- * <p>If true, prior to running any migration steps, sets the necessary configuration in {@code
- * gerrit.config} to make {@code gerrit.war daemon} retry the migration on next startup, if it
- * fails.
- *
- * @param autoMigrate whether to set auto-migration config.
- * @return this.
- */
- public Builder setAutoMigrate(boolean autoMigrate) {
- this.autoMigrate = autoMigrate;
- return this;
- }
-
- /**
- * Enable verbose log output
- *
- * @param verbose enable verbose log output
- * @return this.
- */
- public Builder setVerbose(boolean verbose) {
- this.verbose = verbose;
- return this;
- }
-
- public NoteDbMigrator build() throws MigrationException {
- return new NoteDbMigrator(
- sitePaths,
- schemaFactory,
- serverIdent,
- allUsers,
- repoManager,
- updateManagerFactory,
- bundleReader,
- allProjects,
- requestContext,
- userFactory,
- rebuilder,
- globalNotesMigration,
- primaryStorageMigrator,
- listeners,
- threads > 1
- ? MoreExecutors.listeningDecorator(
- workQueue.createQueue(threads, "RebuildChange", true))
- : MoreExecutors.newDirectExecutorService(),
- projects,
- skipProjects,
- changes,
- progressOut,
- stopAtState,
- projectCache,
- trial,
- forceRebuild,
- forceStateChangeWithSkip,
- sequenceGap >= 0 ? sequenceGap : Sequences.getChangeSequenceGap(cfg),
- autoMigrate,
- verbose);
- }
- }
-
- private static class ProjectContext {
- final ReentrantLock gcLock;
- final Project.NameKey project;
- int sliceCount;
- int changeCount;
- final AtomicLong changesMigratedCount;
-
- ProjectContext(Project.NameKey project, int sliceCount) {
- this.gcLock = new ReentrantLock();
- this.project = project;
- this.sliceCount = sliceCount;
- this.changesMigratedCount = new AtomicLong();
- }
- }
-
- private static class ProjectSlice {
- final ProjectContext ctx;
- final List<Id> changes;
- int sliceNumber = 0;
-
- ProjectSlice(ProjectContext ctx, List<Id> changes, int sliceNumber) {
- this.ctx = ctx;
- this.changes = changes;
- this.sliceNumber = sliceNumber;
- }
-
- @Override
- public String toString() {
- return "Slice [project=" + ctx.project + "]";
- }
- }
-
- private final FileBasedConfig gerritConfig;
- private final FileBasedConfig noteDbConfig;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final Provider<PersonIdent> serverIdent;
- private final AllUsersName allUsers;
- private final GitRepositoryManager repoManager;
- private final NoteDbUpdateManager.Factory updateManagerFactory;
- private final ChangeBundleReader bundleReader;
- private final AllProjectsName allProjects;
- private final ThreadLocalRequestContext requestContext;
- private final InternalUser.Factory userFactory;
- private final ChangeRebuilderImpl rebuilder;
- private final MutableNotesMigration globalNotesMigration;
- private final PrimaryStorageMigrator primaryStorageMigrator;
- private final DynamicSet<NotesMigrationStateListener> listeners;
-
- private final ListeningExecutorService executor;
- private final ImmutableList<Project.NameKey> projects;
- private final ImmutableList<Project.NameKey> skipProjects;
- private final ImmutableList<Change.Id> changes;
- private final OutputStream progressOut;
- private final NotesMigrationState stopAtState;
- private final boolean trial;
- private final boolean forceRebuild;
- private final boolean forceStateChangeWithSkip;
- private final int sequenceGap;
- private final boolean autoMigrate;
- private final boolean verbose;
-
- private final AtomicLong globalChangeCounter = new AtomicLong();
- private long totalChangeCount;
-
- private NoteDbMigrator(
- SitePaths sitePaths,
- SchemaFactory<ReviewDb> schemaFactory,
- Provider<PersonIdent> serverIdent,
- AllUsersName allUsers,
- GitRepositoryManager repoManager,
- NoteDbUpdateManager.Factory updateManagerFactory,
- ChangeBundleReader bundleReader,
- AllProjectsName allProjects,
- ThreadLocalRequestContext requestContext,
- InternalUser.Factory userFactory,
- ChangeRebuilderImpl rebuilder,
- MutableNotesMigration globalNotesMigration,
- PrimaryStorageMigrator primaryStorageMigrator,
- DynamicSet<NotesMigrationStateListener> listeners,
- ListeningExecutorService executor,
- ImmutableList<Project.NameKey> projects,
- ImmutableList<Project.NameKey> skipProjects,
- ImmutableList<Change.Id> changes,
- OutputStream progressOut,
- NotesMigrationState stopAtState,
- ProjectCache projectCache,
- boolean trial,
- boolean forceRebuild,
- boolean forceStateChangeWithSkip,
- int sequenceGap,
- boolean autoMigrate,
- boolean verbose)
- throws MigrationException {
- if (ImmutableList.of(!changes.isEmpty(), !projects.isEmpty(), !skipProjects.isEmpty()).stream()
- .filter(e -> e)
- .count()
- > 1) {
- throw new MigrationException("Cannot combine changes, projects and skipProjects");
- }
- if (sequenceGap < 0) {
- throw new MigrationException("Sequence gap must be non-negative: " + sequenceGap);
- }
-
- this.schemaFactory = schemaFactory;
- this.serverIdent = serverIdent;
- this.allUsers = allUsers;
- this.rebuilder = rebuilder;
- this.repoManager = repoManager;
- this.updateManagerFactory = updateManagerFactory;
- this.bundleReader = bundleReader;
- this.allProjects = allProjects;
- this.requestContext = requestContext;
- this.userFactory = userFactory;
- this.globalNotesMigration = globalNotesMigration;
- this.primaryStorageMigrator = primaryStorageMigrator;
- this.listeners = listeners;
- this.executor = executor;
- this.projects = projects;
- this.skipProjects = skipProjects;
- this.changes = changes;
- this.progressOut = progressOut;
- this.stopAtState = stopAtState;
- this.trial = trial;
- this.forceRebuild = forceRebuild;
- this.forceStateChangeWithSkip = forceStateChangeWithSkip;
- this.sequenceGap = sequenceGap;
- this.autoMigrate = autoMigrate;
- this.verbose = verbose;
-
- // Stack notedb.config over gerrit.config, in the same way as GerritServerConfigProvider.
- this.gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
- this.noteDbConfig =
- new FileBasedConfig(gerritConfig, sitePaths.notedb_config.toFile(), FS.detect());
- }
-
- @Override
- public void close() {
- executor.shutdownNow();
- }
-
- public void migrate() throws OrmException, IOException {
- if (!changes.isEmpty()
- || !projects.isEmpty()
- || (!forceStateChangeWithSkip && !skipProjects.isEmpty())) {
- throw new MigrationException(
- "Cannot set changes or projects or skipProjects during full migration; call rebuild()"
- + " instead");
- }
- Optional<NotesMigrationState> maybeState = loadState();
- if (!maybeState.isPresent()) {
- throw new MigrationException("Could not determine initial migration state");
- }
-
- NotesMigrationState state = maybeState.get();
- if (trial && state.compareTo(READ_WRITE_NO_SEQUENCE) > 0) {
- throw new MigrationException(
- "Migration has already progressed past the endpoint of the \"trial mode\" state;"
- + " NoteDb is already the primary storage for some changes");
- }
- if (forceRebuild && state.compareTo(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY) > 0) {
- throw new MigrationException(
- "Cannot force rebuild changes; NoteDb is already the primary storage for some changes");
- }
- setControlFlags();
-
- boolean rebuilt = false;
- while (state.compareTo(NOTE_DB) < 0) {
- if (state.equals(stopAtState)) {
- return;
- }
- boolean stillNeedsRebuild = forceRebuild && !rebuilt;
- if (trial && state.compareTo(READ_WRITE_NO_SEQUENCE) >= 0) {
- if (stillNeedsRebuild && state == READ_WRITE_NO_SEQUENCE) {
- // We're at the end state of trial mode, but still need a rebuild due to forceRebuild. Let
- // the loop go one more time.
- } else {
- return;
- }
- }
- switch (state) {
- case REVIEW_DB:
- state = turnOnWrites(state);
- break;
- case WRITE:
- state = rebuildAndEnableReads(state);
- rebuilt = true;
- break;
- case READ_WRITE_NO_SEQUENCE:
- if (stillNeedsRebuild) {
- state = rebuildAndEnableReads(state);
- rebuilt = true;
- } else {
- state = enableSequences(state);
- }
- break;
- case READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY:
- if (stillNeedsRebuild) {
- state = rebuildAndEnableReads(state);
- rebuilt = true;
- } else {
- state = setNoteDbPrimary(state);
- }
- break;
- case READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY:
- // The only way we can get here is if there was a failure on a previous run of
- // setNoteDbPrimary, since that method moves to NOTE_DB if it completes
- // successfully. Assume that not all changes were converted and re-run the step.
- // migrateToNoteDbPrimary is a relatively fast no-op for already-migrated changes, so this
- // isn't actually repeating work.
- state = setNoteDbPrimary(state);
- break;
- case NOTE_DB:
- // Done!
- break;
- default:
- throw new MigrationException(
- "Migration out of the following state is not supported:\n" + state.toText());
- }
- }
- }
-
- private NotesMigrationState turnOnWrites(NotesMigrationState prev) throws IOException {
- return saveState(prev, WRITE);
- }
-
- private NotesMigrationState rebuildAndEnableReads(NotesMigrationState prev)
- throws OrmException, IOException {
- rebuild();
- return saveState(prev, READ_WRITE_NO_SEQUENCE);
- }
-
- private NotesMigrationState enableSequences(NotesMigrationState prev)
- throws OrmException, IOException {
- try (ReviewDb db = schemaFactory.open()) {
- @SuppressWarnings("deprecation")
- final int nextChangeId = db.nextChangeId();
-
- RepoSequence seq =
- new RepoSequence(
- repoManager,
- GitReferenceUpdated.DISABLED,
- allProjects,
- Sequences.NAME_CHANGES,
- // If sequenceGap is 0, this writes into the sequence ref the same ID that is returned
- // by the call to seq.next() below. If we actually used this as a change ID, that
- // would be a problem, but we just discard it, so this is safe.
- () -> nextChangeId + sequenceGap - 1,
- 1,
- nextChangeId);
- seq.next();
- }
- return saveState(prev, READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);
- }
-
- private NotesMigrationState setNoteDbPrimary(NotesMigrationState prev)
- throws MigrationException, OrmException, IOException {
- checkState(
- projects.isEmpty()
- && changes.isEmpty()
- && (forceStateChangeWithSkip || skipProjects.isEmpty()),
- "Should not have attempted setNoteDbPrimary with a subset of changes");
- checkState(
- prev == READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY
- || prev == READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY,
- "Unexpected start state for setNoteDbPrimary: %s",
- prev);
-
- // Before changing the primary storage of old changes, ensure new changes are created with
- // NoteDb primary.
- prev = saveState(prev, READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY);
-
- Stopwatch sw = Stopwatch.createStarted();
- logger.atInfo().log("Setting primary storage to NoteDb");
- List<Change.Id> allChanges;
- try (ReviewDb db = unwrapDb(schemaFactory.open())) {
- if (forceStateChangeWithSkip) {
- allChanges =
- Streams.stream(db.changes().all())
- .filter(c -> !skipProjects.contains(c.getProject()))
- .map(Change::getId)
- .collect(toList());
- } else {
- allChanges = Streams.stream(db.changes().all()).map(Change::getId).collect(toList());
- }
- }
-
- try (ContextHelper contextHelper = new ContextHelper()) {
- List<ListenableFuture<Boolean>> futures =
- Lists.partition(allChanges, PROJECT_SLICE_MAX_REFS).stream()
- .map(
- partition ->
- executor.submit(
- () -> {
- try (ManualRequestContext ctx = contextHelper.open()) {
- return primaryStorageMigrator.migrateToNoteDbPrimary(partition);
- }
- }))
- .collect(toList());
- boolean ok = futuresToBoolean(futures, "Error migrating primary storage");
- double t = sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
- logger.atInfo().log(
- "Migrated primary storage of %d changes in %.01fs (%.01f/s)\n",
- allChanges.size(), t, allChanges.size() / t);
- if (!ok) {
- throw new MigrationException("Migrating primary storage for some changes failed, see log");
- }
- }
-
- return disableReviewDb(prev);
- }
-
- private NotesMigrationState disableReviewDb(NotesMigrationState prev) throws IOException {
- return saveState(prev, NOTE_DB, c -> setAutoMigrate(c, false));
- }
-
- private Optional<NotesMigrationState> loadState() throws IOException {
- try {
- gerritConfig.load();
- noteDbConfig.load();
- return NotesMigrationState.forConfig(noteDbConfig);
- } catch (ConfigInvalidException | IllegalArgumentException e) {
- logger.atWarning().withCause(e).log(
- "error reading NoteDb migration options from %s", noteDbConfig.getFile());
- return Optional.empty();
- }
- }
-
- private NotesMigrationState saveState(
- NotesMigrationState expectedOldState, NotesMigrationState newState) throws IOException {
- return saveState(expectedOldState, newState, c -> {});
- }
-
- private NotesMigrationState saveState(
- NotesMigrationState expectedOldState,
- NotesMigrationState newState,
- Consumer<Config> additionalUpdates)
- throws IOException {
- synchronized (globalNotesMigration) {
- // This read-modify-write is racy. We're counting on the fact that no other Gerrit operation
- // modifies gerrit.config, and hoping that admins don't either.
- Optional<NotesMigrationState> actualOldState = loadState();
- if (!actualOldState.equals(Optional.of(expectedOldState))) {
- throw new MigrationException(
- "Cannot move to new state:\n"
- + newState.toText()
- + "\n\n"
- + "Expected this state in gerrit.config:\n"
- + expectedOldState.toText()
- + "\n\n"
- + (actualOldState.isPresent()
- ? "But found this state:\n" + actualOldState.get().toText()
- : "But could not parse the current state"));
- }
-
- preStateChange(expectedOldState, newState);
-
- newState.setConfigValues(noteDbConfig);
- additionalUpdates.accept(noteDbConfig);
- noteDbConfig.save();
-
- // Only set in-memory state once it's been persisted to storage.
- globalNotesMigration.setFrom(newState);
- logger.atInfo().log("Migration state: %s => %s", expectedOldState, newState);
-
- return newState;
- }
- }
-
- private void preStateChange(NotesMigrationState oldState, NotesMigrationState newState)
- throws IOException {
- for (NotesMigrationStateListener listener : listeners) {
- listener.preStateChange(oldState, newState);
- }
- }
-
- private void setControlFlags() throws MigrationException {
- synchronized (globalNotesMigration) {
- try {
- noteDbConfig.load();
- setAutoMigrate(noteDbConfig, autoMigrate);
- setTrialMode(noteDbConfig, trial);
- noteDbConfig.save();
- } catch (ConfigInvalidException | IOException e) {
- throw new MigrationException("Error saving auto-migration config", e);
- }
- }
- }
-
- private List<ProjectSlice> slice() throws OrmException {
- ImmutableListMultimap<Project.NameKey, Change.Id> changesByProject = getChangesByProject();
- List<Project.NameKey> projectNames =
- Ordering.usingToString().sortedCopy(changesByProject.keySet());
- List<ProjectSlice> slices = Lists.newArrayList();
- for (Project.NameKey project : projectNames) {
- int sliceNumber = 1;
- List<List<Id>> projectSlices =
- Lists.partition(changesByProject.get(project), PROJECT_SLICE_MAX_REFS);
- ProjectContext ctx = new ProjectContext(project, projectSlices.size());
- ctx.changeCount = changesByProject.get(project).size();
- for (List<Id> s : projectSlices) {
- ProjectSlice ps = new ProjectSlice(ctx, s, sliceNumber++);
- slices.add(ps);
- }
- }
- Collections.shuffle(slices);
- totalChangeCount = changesByProject.size();
- return slices;
- }
-
- public void rebuild() throws MigrationException, OrmException {
- if (!globalNotesMigration.commitChangeWrites()) {
- throw new MigrationException("Cannot rebuild without noteDb.changes.write=true");
- }
- Stopwatch sw = Stopwatch.createStarted();
- logger.atInfo().log("Rebuilding changes in NoteDb");
-
- List<ProjectSlice> slices = slice();
- List<ListenableFuture<Boolean>> futures = new ArrayList<>();
- for (ProjectSlice slice : slices) {
- ListenableFuture<Boolean> future =
- executor.submit(
- () -> {
- try (ContextHelper contextHelper = new ContextHelper()) {
- return rebuildProjectSlice(contextHelper.getReviewDb(), slice);
- } catch (Exception e) {
- logger.atSevere().withCause(e).log("Error rebuilding project %s", slice.ctx);
- return false;
- }
- });
- futures.add(future);
- }
- boolean ok = futuresToBoolean(futures, "Error rebuilding projects");
- double t = sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
- logger.atInfo().log(
- "Rebuilt %d changes in %.01fs (%.01f/s)\n", totalChangeCount, t, totalChangeCount / t);
- if (!ok) {
- throw new MigrationException("Rebuilding some changes failed, see log");
- }
- }
-
- private ImmutableListMultimap<Project.NameKey, Change.Id> getChangesByProject()
- throws OrmException {
- // Memoize all changes so we can close the db connection and allow other threads to use the full
- // connection pool.
- SetMultimap<Project.NameKey, Change.Id> out =
- MultimapBuilder.treeKeys(comparing(Project.NameKey::get))
- .treeSetValues(comparing(Change.Id::get))
- .build();
- try (ReviewDb db = unwrapDb(schemaFactory.open())) {
- if (!projects.isEmpty()) {
- return byProject(db.changes().all(), c -> projects.contains(c.getProject()), out);
- }
- if (!skipProjects.isEmpty()) {
- return byProject(db.changes().all(), c -> !skipProjects.contains(c.getProject()), out);
- }
- if (!changes.isEmpty()) {
- return byProject(db.changes().get(changes), c -> true, out);
- }
- return byProject(db.changes().all(), c -> true, out);
- }
- }
-
- private static ImmutableListMultimap<Project.NameKey, Change.Id> byProject(
- Iterable<Change> changes,
- Predicate<Change> pred,
- SetMultimap<Project.NameKey, Change.Id> out) {
- Streams.stream(changes).filter(pred).forEach(c -> out.put(c.getProject(), c.getId()));
- return ImmutableListMultimap.copyOf(out);
- }
-
- private static ObjectInserter newPackInserter(Repository repo) {
- if (!(repo instanceof FileRepository)) {
- return repo.newObjectInserter();
- }
- PackInserter ins = ((FileRepository) repo).getObjectDatabase().newPackInserter();
- ins.checkExisting(false);
- return ins;
- }
-
- private boolean rebuildProjectSlice(ReviewDb db, ProjectSlice slice) {
- ProjectContext ctx = slice.ctx;
- boolean ok = true;
- ProgressMonitor pm =
- new TextProgressMonitor(
- new PrintWriter(new BufferedWriter(new OutputStreamWriter(progressOut, UTF_8))));
- Project.NameKey project = ctx.project;
- String oldThreadName = Thread.currentThread().getName();
- Thread.currentThread()
- .setName(
- String.format(
- "Rebuild %s (slice %d/%d)", ctx.project, slice.sliceNumber, ctx.sliceCount));
- try (Repository changeRepo = repoManager.openRepository(project);
- // Only use a PackInserter for the change repo, not All-Users.
- //
- // It's not possible to share a single inserter for All-Users across all project tasks, and
- // we don't want to add one pack per project to All-Users. Adding many loose objects is
- // preferable to many packs.
- //
- // Anyway, the number of objects inserted into All-Users is proportional to the number
- // of pending draft comments, which should not be high (relative to the total number of
- // changes), so the number of loose objects shouldn't be too unreasonable.
- ObjectInserter changeIns = newPackInserter(changeRepo);
- ObjectReader changeReader = changeIns.newReader();
- RevWalk changeRw = new RevWalk(changeReader);
- Repository allUsersRepo = repoManager.openRepository(allUsers);
- ObjectInserter allUsersIns = allUsersRepo.newObjectInserter();
- ObjectReader allUsersReader = allUsersIns.newReader();
- RevWalk allUsersRw = new RevWalk(allUsersReader)) {
- ChainedReceiveCommands changeCmds = new ChainedReceiveCommands(changeRepo);
- ChainedReceiveCommands allUsersCmds = new ChainedReceiveCommands(allUsersRepo);
-
- pm.beginTask(
- FormatUtil.elide(
- String.format(
- "Rebuilding project %s slice %d/%d",
- project.get(), slice.sliceNumber, ctx.sliceCount),
- 60),
- slice.changes.size());
- int toSave = 0;
- try {
- logger.atInfo().log(
- "Starting to rebuild changes from project %s slice %d/%d",
- project.get(), slice.sliceNumber, ctx.sliceCount);
- long pc = 0;
- for (Change.Id changeId : slice.changes) {
- // NoteDbUpdateManager assumes that all commands in its OpenRepo were added by itself, so
- // we can't share the top-level ChainedReceiveCommands. Use a new set of commands sharing
- // the same underlying repo, and copy commands back to the top-level
- // ChainedReceiveCommands later. This also assumes that each ref in the final list of
- // commands was only modified by a single NoteDbUpdateManager; since we use one manager
- // per change, and each ref corresponds to exactly one change, this assumption should be
- // safe.
- ChainedReceiveCommands tmpChangeCmds =
- new ChainedReceiveCommands(changeCmds.getRepoRefCache());
- ChainedReceiveCommands tmpAllUsersCmds =
- new ChainedReceiveCommands(allUsersCmds.getRepoRefCache());
-
- try (NoteDbUpdateManager manager =
- updateManagerFactory
- .create(project)
- .setAtomicRefUpdates(false)
- .setSaveObjects(false)
- .setChangeRepo(changeRepo, changeRw, changeIns, tmpChangeCmds)
- .setAllUsersRepo(allUsersRepo, allUsersRw, allUsersIns, tmpAllUsersCmds)) {
- rebuild(db, changeId, manager);
-
- // Executing with dryRun=true writes all objects to the underlying inserters and adds
- // commands to the ChainedReceiveCommands. Afterwards, we can discard the manager, so we
- // don't keep using any memory beyond what may be buffered in the PackInserter.
- manager.execute(true);
-
- tmpChangeCmds.getCommands().values().forEach(c -> addCommand(changeCmds, c));
- tmpAllUsersCmds.getCommands().values().forEach(c -> addCommand(allUsersCmds, c));
-
- toSave++;
- } catch (NoPatchSetsException e) {
- logger.atWarning().log(e.getMessage());
- } catch (ConflictingUpdateException ex) {
- logger.atWarning().log(
- "Rebuilding detected a conflicting ReviewDb update for change %s;"
- + " will be auto-rebuilt at runtime",
- changeId);
- } catch (Throwable t) {
- logger.atSevere().withCause(t).log("Failed to rebuild change %s", changeId);
- ok = false;
- }
- if (verbose) {
- logger.atInfo().log("Rebuilt change %s", changeId.get());
- }
- long c = globalChangeCounter.incrementAndGet();
- if (c % 1000 == 0) {
- logger.atInfo().log(
- "Total number of rebuilt changes %d/%d (%.01f%%)",
- c, totalChangeCount, (100.0 * c) / totalChangeCount);
- }
- pc = ctx.changesMigratedCount.incrementAndGet();
- if (pc % GC_INTERVAL == 0) {
- gc(project, changeRepo, ctx.gcLock);
- }
- pm.update(1);
- }
- logger.atInfo().log(
- "Finished rebuilding changes of project %s, slice %d/%d, changes %d/%d)",
- project.get(), slice.sliceNumber, ctx.sliceCount, pc, ctx.changeCount);
- } finally {
- pm.endTask();
- }
-
- pm.beginTask(
- FormatUtil.elide("Saving noteDb refs for " + project.get(), 60), ProgressMonitor.UNKNOWN);
- try {
- save(changeRepo, changeRw, changeIns, changeCmds);
- save(allUsersRepo, allUsersRw, allUsersIns, allUsersCmds);
- // This isn't really useful progress. If we passed a real ProgressMonitor to
- // BatchRefUpdate#execute we might get something more incremental, but that doesn't allow us
- // to specify the repo name in the task text.
- pm.update(toSave);
- } catch (LockFailureException e) {
- logger.atWarning().log(
- "Rebuilding detected a conflicting NoteDb update for the following refs, which will"
- + " be auto-rebuilt at runtime: %s",
- e.getFailedRefs().stream().distinct().sorted().collect(joining(", ")));
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Failed to save NoteDb state for %s", project);
- } finally {
- pm.endTask();
- }
- } catch (RepositoryNotFoundException e) {
- logger.atWarning().log("Repository %s not found", project);
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Failed to rebuild project %s", project);
- } finally {
- Thread.currentThread().setName(oldThreadName);
- }
- return ok;
- }
-
- private void gc(Project.NameKey project, Repository repo, ReentrantLock gcLock) {
- if (repo instanceof FileRepository && gcLock.tryLock()) {
- try {
- FileRepository r = (FileRepository) repo;
- GC gc = new GC(r);
- // known limitation in jgit 5.1: bitmap index creation is slow due to bug 562740,
- // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=562740
- logger.atInfo().log("Running GC on project %s", project);
- PackConfig pconfig = new PackConfig(repo);
- pconfig.setBuildBitmaps(false);
- // let auto gc decide when gc needs to really do something
- gc.setAuto(true);
- gc.setPackConfig(pconfig);
- gc.gc();
- logger.atInfo().log("Finished GC on project %s", project);
- } catch (IOException | ParseException e) {
- logger.atSevere().withCause(e).log("GC of project %s failed", project);
- } finally {
- gcLock.unlock();
- logger.atFine().log("Released gc lock for project %s", project);
- }
- }
- }
-
- private void rebuild(ReviewDb db, Change.Id changeId, NoteDbUpdateManager manager)
- throws OrmException, IOException {
- // Match ChangeRebuilderImpl#stage, but without calling manager.stage(), since that can only be
- // called after building updates for all changes.
- Change change =
- ChangeRebuilderImpl.checkNoteDbState(ChangeNotes.readOneReviewDbChange(db, changeId));
- if (change == null) {
- // Could log here instead, but this matches the behavior of ChangeRebuilderImpl#stage.
- throw new NoSuchChangeException(changeId);
- }
- rebuilder.buildUpdates(manager, bundleReader.fromReviewDb(db, changeId));
-
- rebuilder.execute(db, changeId, manager, true, false);
- }
-
- private static void addCommand(ChainedReceiveCommands cmds, ReceiveCommand cmd) {
- // ChainedReceiveCommands doesn't allow no-ops, but these occur when rebuilding a
- // previously-rebuilt change.
- if (!cmd.getOldId().equals(cmd.getNewId())) {
- cmds.add(cmd);
- }
- }
-
- private void save(Repository repo, RevWalk rw, ObjectInserter ins, ChainedReceiveCommands cmds)
- throws IOException {
- if (cmds.isEmpty()) {
- return;
- }
- ins.flush();
- BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
- bru.setRefLogMessage("Migrate changes to NoteDb", false);
- bru.setRefLogIdent(serverIdent.get());
- bru.setAtomic(false);
- bru.setAllowNonFastForwards(true);
- cmds.addTo(bru);
- RefUpdateUtil.executeChecked(bru, rw);
- }
-
- private static boolean futuresToBoolean(List<ListenableFuture<Boolean>> futures, String errMsg) {
- try {
- return Futures.allAsList(futures).get().stream().allMatch(b -> b);
- } catch (InterruptedException | ExecutionException e) {
- logger.atSevere().withCause(e).log(errMsg);
- return false;
- }
- }
-
- private class ContextHelper implements AutoCloseable {
- private final Thread callingThread;
- private ReviewDb db;
- private Runnable closeDb;
-
- ContextHelper() {
- callingThread = Thread.currentThread();
- }
-
- ManualRequestContext open() throws OrmException {
- return new ManualRequestContext(
- userFactory.create(),
- // Reuse the same lazily-opened ReviewDb on the original calling thread, otherwise open
- // SchemaFactory in the normal way.
- Thread.currentThread().equals(callingThread) ? this::getReviewDb : schemaFactory,
- requestContext);
- }
-
- synchronized ReviewDb getReviewDb() throws OrmException {
- if (db == null) {
- ReviewDb actual = schemaFactory.open();
- closeDb = actual::close;
- db =
- new ReviewDbWrapper(unwrapDb(actual)) {
- @Override
- public void close() {
- // Closed by ContextHelper#close.
- }
- };
- }
- return db;
- }
-
- @Override
- public synchronized void close() {
- if (db != null) {
- closeDb.run();
- db = null;
- closeDb = null;
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java b/java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java
deleted file mode 100644
index aef30a2a51..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/NotesMigrationStateListener.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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.notedb.rebuild;
-
-import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import java.io.IOException;
-
-/** Listener for state changes performed by {@link OnlineNoteDbMigrator}. */
-@ExtensionPoint
-public interface NotesMigrationStateListener {
- /**
- * Invoked just before saving the new migration state.
- *
- * @param oldState state prior to this state change.
- * @param newState state after this state change.
- * @throws IOException if an error occurred, which will cause the migration to abort. Exceptions
- * that should be considered non-fatal must be caught (and ideally logged) by the
- * implementation rather than thrown.
- */
- void preStateChange(NotesMigrationState oldState, NotesMigrationState newState)
- throws IOException;
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
deleted file mode 100644
index 46f5b22e91..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.notedb.rebuild;
-
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.flogger.FluentLogger;
-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.index.OnlineUpgrader;
-import com.google.gerrit.server.index.VersionManager;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.Config;
-
-@Singleton
-public class OnlineNoteDbMigrator implements LifecycleListener {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final String TRIAL = "OnlineNoteDbMigrator/trial";
-
- private static final String ONLINE_MIGRATION_THREADS = "onlineMigrationThreads";
-
- public static class Module extends LifecycleModule {
- private final boolean trial;
-
- public Module(boolean trial) {
- this.trial = trial;
- }
-
- @Override
- public void configure() {
- listener().to(OnlineNoteDbMigrator.class);
- bindConstant().annotatedWith(Names.named(TRIAL)).to(trial);
- }
- }
-
- private final GcAllUsers gcAllUsers;
- private final OnlineUpgrader indexUpgrader;
- private final Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
- private final boolean upgradeIndex;
- private final boolean trial;
- private final int threads;
-
- @Inject
- OnlineNoteDbMigrator(
- @GerritServerConfig Config cfg,
- GcAllUsers gcAllUsers,
- OnlineUpgrader indexUpgrader,
- Provider<NoteDbMigrator.Builder> migratorBuilderProvider,
- @Named(TRIAL) boolean trial) {
- this.gcAllUsers = gcAllUsers;
- this.indexUpgrader = indexUpgrader;
- this.migratorBuilderProvider = migratorBuilderProvider;
- this.upgradeIndex = VersionManager.getOnlineUpgrade(cfg);
- this.trial = trial || NoteDbMigrator.getTrialMode(cfg);
- this.threads = cfg.getInt(SECTION_NOTE_DB, ONLINE_MIGRATION_THREADS, 1);
- }
-
- @Override
- public void start() {
- Thread t = new Thread(this::migrate);
- t.setDaemon(true);
- t.setName(getClass().getSimpleName());
- t.start();
- }
-
- private void migrate() {
- logger.atInfo().log("Starting online NoteDb migration");
- if (upgradeIndex) {
- logger.atInfo().log(
- "Online index schema upgrades will be deferred until NoteDb migration is complete");
- }
- Stopwatch sw = Stopwatch.createStarted();
- // TODO(dborowitz): maybe expose a progress monitor somewhere.
- try (NoteDbMigrator migrator =
- migratorBuilderProvider
- .get()
- .setThreads(threads)
- .setAutoMigrate(true)
- .setTrialMode(trial)
- .build()) {
- migrator.migrate();
- } catch (Exception e) {
- logger.atSevere().withCause(e).log("Error in online NoteDb migration");
- }
- gcAllUsers.runWithLogger();
- logger.atInfo().log("Online NoteDb migration completed in %ss", sw.elapsed(TimeUnit.SECONDS));
-
- if (upgradeIndex) {
- logger.atInfo().log("Starting deferred index schema upgrades");
- indexUpgrader.start();
- }
- }
-
- @Override
- public void stop() {
- // Do nothing; upgrade process uses daemon threads and knows how to recover from failures on
- // next attempt.
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java b/java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java
deleted file mode 100644
index acb80c02e9..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/PatchSetEvent.java
+++ /dev/null
@@ -1,86 +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.server.notedb.rebuild;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.util.List;
-import org.eclipse.jgit.errors.InvalidObjectIdException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-class PatchSetEvent extends Event {
- private final Change change;
- private final PatchSet ps;
- private final RevWalk rw;
- boolean createChange;
-
- PatchSetEvent(Change change, PatchSet ps, RevWalk rw) {
- super(
- ps.getId(),
- ps.getUploader(),
- ps.getUploader(),
- ps.getCreatedOn(),
- change.getCreatedOn(),
- null);
- this.change = change;
- this.ps = ps;
- this.rw = rw;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return true;
- }
-
- @Override
- void apply(ChangeUpdate update) throws IOException, OrmException {
- checkUpdate(update);
- if (createChange) {
- ChangeRebuilderImpl.createChange(update, change);
- } else {
- update.setSubject(change.getSubject());
- update.setSubjectForCommit("Create patch set " + ps.getPatchSetId());
- }
- setRevision(update, ps);
- update.setPsDescription(ps.getDescription());
- List<String> groups = ps.getGroups();
- if (!groups.isEmpty()) {
- update.setGroups(ps.getGroups());
- }
- }
-
- private void setRevision(ChangeUpdate update, PatchSet ps) throws IOException {
- String rev = ps.getRevision().get();
- String cert = ps.getPushCertificate();
- ObjectId id;
- try {
- id = ObjectId.fromString(rev);
- } catch (InvalidObjectIdException e) {
- update.setRevisionForMissingCommit(rev, cert);
- return;
- }
- try {
- update.setCommit(rw, id, cert);
- } catch (MissingObjectException e) {
- update.setRevisionForMissingCommit(rev, cert);
- return;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java b/java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java
deleted file mode 100644
index 2ecf969957..0000000000
--- a/java/com/google/gerrit/server/notedb/rebuild/ReviewerEvent.java
+++ /dev/null
@@ -1,63 +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.server.notedb.rebuild;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.collect.Table;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.sql.Timestamp;
-
-class ReviewerEvent extends Event {
- private Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer;
-
- ReviewerEvent(
- Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> reviewer,
- Timestamp changeCreatedOn) {
- super(
- // Reviewers aren't generally associated with a particular patch set
- // (although as an implementation detail they were in ReviewDb). Just
- // use the latest patch set at the time of the event.
- null,
- reviewer.getColumnKey(),
- // TODO(dborowitz): Real account ID shouldn't really matter for
- // reviewers, but we might have to deal with this to avoid ChangeBundle
- // diffs when run against real data.
- reviewer.getColumnKey(),
- reviewer.getValue(),
- changeCreatedOn,
- null);
- this.reviewer = reviewer;
- }
-
- @Override
- boolean uniquePerUpdate() {
- return false;
- }
-
- @Override
- void apply(ChangeUpdate update) throws IOException, OrmException {
- checkUpdate(update);
- update.putReviewer(reviewer.getColumnKey(), reviewer.getRowKey());
- }
-
- @Override
- protected void addToString(ToStringHelper helper) {
- helper.add("account", reviewer.getColumnKey()).add("state", reviewer.getRowKey());
- }
-}
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index 285c37d103..18aa8b9fe0 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -18,9 +18,9 @@ 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.server.GerritPersonIdent;
-import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.InMemoryInserter;
import com.google.gerrit.server.git.MergeUtil;
diff --git a/java/com/google/gerrit/server/patch/IntraLineDiff.java b/java/com/google/gerrit/server/patch/IntraLineDiff.java
index a1823359e5..1c3d78a91b 100644
--- a/java/com/google/gerrit/server/patch/IntraLineDiff.java
+++ b/java/com/google/gerrit/server/patch/IntraLineDiff.java
@@ -21,6 +21,7 @@ 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.jgit.diff.ReplaceEdit;
import com.google.gerrit.reviewdb.client.CodedEnum;
import java.io.IOException;
import java.io.InputStream;
@@ -32,7 +33,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.jgit.diff.Edit;
-import org.eclipse.jgit.diff.ReplaceEdit;
public class IntraLineDiff implements Serializable {
static final long serialVersionUID = IntraLineDiffKey.serialVersionUID;
diff --git a/java/com/google/gerrit/server/patch/IntraLineLoader.java b/java/com/google/gerrit/server/patch/IntraLineLoader.java
index 022fd9e0ac..34ac3d8c83 100644
--- a/java/com/google/gerrit/server/patch/IntraLineLoader.java
+++ b/java/com/google/gerrit/server/patch/IntraLineLoader.java
@@ -19,6 +19,7 @@ import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.jgit.diff.ReplaceEdit;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -34,7 +35,6 @@ import java.util.concurrent.TimeoutException;
import java.util.regex.Pattern;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.MyersDiff;
-import org.eclipse.jgit.diff.ReplaceEdit;
import org.eclipse.jgit.lib.Config;
class IntraLineLoader implements Callable<IntraLineDiff> {
diff --git a/java/com/google/gerrit/server/patch/PatchFile.java b/java/com/google/gerrit/server/patch/PatchFile.java
index e950c1b96e..0e5bcc17a0 100644
--- a/java/com/google/gerrit/server/patch/PatchFile.java
+++ b/java/com/google/gerrit/server/patch/PatchFile.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.patch;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.exceptions.NoSuchEntityException;
import com.google.gerrit.reviewdb.client.Patch;
import java.io.IOException;
import org.eclipse.jgit.errors.CorruptObjectException;
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index 61f0180879..c2caa01b75 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -133,9 +133,7 @@ class PatchScriptBuilder {
edits = new ArrayList<>(content.getEdits());
ImmutableSet<Edit> editsDueToRebase = content.getEditsDueToRebase();
- if (!isModify(content)) {
- intralineDifferenceIsPossible = false;
- } else if (diffPrefs.intralineDifference) {
+ if (isModify(content) && diffPrefs.intralineDifference && isIntralineModeAllowed(b)) {
IntraLineDiff d =
patchListCache.getIntraLineDiff(
IntraLineDiffKey.create(a.id, b.id, diffPrefs.ignoreWhitespace),
@@ -274,6 +272,16 @@ class PatchScriptBuilder {
}
}
+ private static boolean isIntralineModeAllowed(Side side) {
+ // The intraline diff cache keys are the same for these cases. It's better to not show
+ // intraline results than showing completely wrong diffs or to run into a server error.
+ return !Patch.isMagic(side.path) && !isSubmoduleCommit(side.mode);
+ }
+
+ private static boolean isSubmoduleCommit(FileMode mode) {
+ return mode.getObjectType() == Constants.OBJ_COMMIT;
+ }
+
private void correctForDifferencesInNewlineAtEnd() {
// a.src.size() is the size ignoring a newline at the end whereas a.size() considers it.
int aSize = a.src.size();
@@ -286,6 +294,12 @@ class PatchScriptBuilder {
return;
}
+ if (edits.isEmpty() && (aSize != bSize)) {
+ // Only edits due to rebase were present. If we now added the edits for the newlines, the
+ // code which later assembles the file contents would fail.
+ return;
+ }
+
Optional<Edit> lastEdit = getLast(edits);
if (isNewlineAtEndDeleted()) {
Optional<Edit> lastLineEdit = lastEdit.filter(edit -> edit.getEndA() == aSize);
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index bfcb3255b0..a7e77383ac 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -29,7 +29,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -44,7 +43,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -82,7 +80,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
private final PatchSetUtil psUtil;
private final Provider<PatchScriptBuilder> builderFactory;
private final PatchListCache patchListCache;
- private final ReviewDb db;
private final CommentsUtil commentsUtil;
private final String fileName;
@@ -112,7 +109,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
PatchSetUtil psUtil,
Provider<PatchScriptBuilder> builderFactory,
PatchListCache patchListCache,
- ReviewDb db,
CommentsUtil commentsUtil,
ChangeEditUtil editReader,
Provider<CurrentUser> userProvider,
@@ -127,7 +123,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
this.psUtil = psUtil;
this.builderFactory = builderFactory;
this.patchListCache = patchListCache;
- this.db = db;
this.notes = notes;
this.commentsUtil = commentsUtil;
this.editReader = editReader;
@@ -150,7 +145,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
PatchSetUtil psUtil,
Provider<PatchScriptBuilder> builderFactory,
PatchListCache patchListCache,
- ReviewDb db,
CommentsUtil commentsUtil,
ChangeEditUtil editReader,
Provider<CurrentUser> userProvider,
@@ -165,7 +159,6 @@ public class PatchScriptFactory implements Callable<PatchScript> {
this.psUtil = psUtil;
this.builderFactory = builderFactory;
this.patchListCache = patchListCache;
- this.db = db;
this.notes = notes;
this.commentsUtil = commentsUtil;
this.editReader = editReader;
@@ -193,18 +186,18 @@ public class PatchScriptFactory implements Callable<PatchScript> {
@Override
public PatchScript call()
- throws OrmException, LargeObjectException, AuthException, InvalidChangeOperationException,
- IOException, PermissionBackendException {
+ throws LargeObjectException, AuthException, InvalidChangeOperationException, IOException,
+ PermissionBackendException {
if (parentNum < 0) {
validatePatchSetId(psa);
}
validatePatchSetId(psb);
- PatchSet psEntityA = psa != null ? psUtil.get(db, notes, psa) : null;
- PatchSet psEntityB = psb.get() == 0 ? new PatchSet(psb) : psUtil.get(db, notes, 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).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
} catch (AuthException e) {
throw new NoSuchChangeException(changeId, e);
}
@@ -265,7 +258,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
return b;
}
- private ObjectId toObjectId(PatchSet ps) throws AuthException, IOException, OrmException {
+ private ObjectId toObjectId(PatchSet ps) throws AuthException, IOException {
if (ps.getId().get() == 0) {
return getEditRev();
}
@@ -281,7 +274,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}
}
- private ObjectId getEditRev() throws AuthException, IOException, OrmException {
+ private ObjectId getEditRev() throws AuthException, IOException {
edit = editReader.byChange(notes);
if (edit.isPresent()) {
return edit.get().getEditCommit();
@@ -297,8 +290,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}
}
- private void loadCommentsAndHistory(ChangeType changeType, String oldName, String newName)
- throws OrmException {
+ private void loadCommentsAndHistory(ChangeType changeType, String oldName, String newName) {
Map<Patch.Key, Patch> byKey = new HashMap<>();
if (loadHistory) {
@@ -308,7 +300,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
// proper rename detection between the patch sets.
//
history = new ArrayList<>();
- for (PatchSet ps : psUtil.byChange(db, notes)) {
+ for (PatchSet ps : psUtil.byChange(notes)) {
String name = fileName;
if (psa != null) {
switch (changeType) {
@@ -390,8 +382,8 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}
}
- private void loadPublished(Map<Patch.Key, Patch> byKey, String file) throws OrmException {
- for (Comment c : commentsUtil.publishedByChangeFile(db, notes, changeId, file)) {
+ 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);
@@ -402,9 +394,8 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}
}
- private void loadDrafts(Map<Patch.Key, Patch> byKey, Account.Id me, String file)
- throws OrmException {
- for (Comment c : commentsUtil.draftByChangeFileAuthor(db, notes, file, me)) {
+ 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);
diff --git a/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java b/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
index 41bade6218..0eb5588cb8 100644
--- a/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
@@ -14,18 +14,17 @@
package com.google.gerrit.server.patch;
+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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -54,8 +53,7 @@ public class PatchSetInfoFactory {
this.emails = emails;
}
- public PatchSetInfo get(RevWalk rw, RevCommit src, PatchSet.Id psi)
- throws IOException, OrmException {
+ public PatchSetInfo get(RevWalk rw, RevCommit src, PatchSet.Id psi) throws IOException {
rw.parseBody(src);
PatchSetInfo info = new PatchSetInfo(psi);
info.setSubject(src.getShortMessage());
@@ -66,12 +64,12 @@ public class PatchSetInfoFactory {
return info;
}
- public PatchSetInfo get(ReviewDb db, ChangeNotes notes, PatchSet.Id psId)
+ public PatchSetInfo get(ChangeNotes notes, PatchSet.Id psId)
throws PatchSetInfoNotAvailableException {
try {
- PatchSet patchSet = psUtil.get(db, notes, psId);
+ PatchSet patchSet = psUtil.get(notes, psId);
return get(notes.getProjectName(), patchSet);
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new PatchSetInfoNotAvailableException(e);
}
}
@@ -84,13 +82,13 @@ public class PatchSetInfoFactory {
PatchSetInfo info = get(rw, src, patchSet.getId());
info.setParents(toParentInfos(src.getParents(), rw));
return info;
- } catch (IOException | OrmException e) {
+ } catch (IOException | StorageException e) {
throw new PatchSetInfoNotAvailableException(e);
}
}
// TODO: The same method exists in EventFactory, find a common place for it
- private UserIdentity toUserIdentity(PersonIdent who) throws IOException, OrmException {
+ private UserIdentity toUserIdentity(PersonIdent who) throws IOException {
final UserIdentity u = new UserIdentity();
u.setName(who.getName());
u.setEmail(who.getEmailAddress());
diff --git a/java/com/google/gerrit/server/permissions/ChangeControl.java b/java/com/google/gerrit/server/permissions/ChangeControl.java
index f4e659eb7a..ee36200283 100644
--- a/java/com/google/gerrit/server/permissions/ChangeControl.java
+++ b/java/com/google/gerrit/server/permissions/ChangeControl.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.permissions;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.permissions.DefaultPermissionMappings.labelPermissionName;
import static com.google.gerrit.server.permissions.LabelPermission.ForUser.ON_BEHALF_OF;
@@ -23,19 +22,17 @@ 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Collection;
import java.util.EnumSet;
@@ -55,10 +52,8 @@ class ChangeControl {
this.notesFactory = notesFactory;
}
- ChangeControl create(
- RefControl refControl, ReviewDb db, Project.NameKey project, Change.Id changeId)
- throws OrmException {
- return create(refControl, notesFactory.create(db, project, changeId));
+ ChangeControl create(RefControl refControl, Project.NameKey project, Change.Id changeId) {
+ return create(refControl, notesFactory.create(project, changeId));
}
ChangeControl create(RefControl refControl, ChangeNotes notes) {
@@ -77,8 +72,8 @@ class ChangeControl {
this.notes = notes;
}
- ForChange asForChange(@Nullable ChangeData cd, @Nullable Provider<ReviewDb> db) {
- return new ForChangeImpl(cd, db);
+ ForChange asForChange(@Nullable ChangeData cd) {
+ return new ForChangeImpl(cd);
}
private CurrentUser getUser() {
@@ -94,8 +89,8 @@ class ChangeControl {
}
/** Can this user see this change? */
- private boolean isVisible(ReviewDb db, @Nullable ChangeData cd) throws OrmException {
- if (getChange().isPrivate() && !isPrivateVisible(db, cd)) {
+ private boolean isVisible(@Nullable ChangeData cd) {
+ if (getChange().isPrivate() && !isPrivateVisible(cd)) {
return false;
}
return refControl.isVisible();
@@ -158,9 +153,9 @@ class ChangeControl {
}
/** Is this user a reviewer for the change? */
- private boolean isReviewer(ReviewDb db, @Nullable ChangeData cd) throws OrmException {
+ private boolean isReviewer(@Nullable ChangeData cd) {
if (getUser().isIdentifiedUser()) {
- cd = cd != null ? cd : changeDataFactory.create(db, notes);
+ cd = cd != null ? cd : changeDataFactory.create(notes);
Collection<Account.Id> results = cd.reviewers().all();
return results.contains(getUser().getAccountId());
}
@@ -169,7 +164,7 @@ class ChangeControl {
/** Can this user edit the topic name? */
private boolean canEditTopicName() {
- if (getChange().getStatus().isOpen()) {
+ if (getChange().isNew()) {
return isOwner() // owner (aka creator) of the change can edit topic
|| refControl.isOwner() // branch owner can edit topic
|| getProjectControl().isOwner() // project owner can edit topic
@@ -180,9 +175,17 @@ class ChangeControl {
return refControl.canForceEditTopicName();
}
+ /** Can this user toggle WorkInProgress state? */
+ private boolean canToggleWorkInProgressState() {
+ return isOwner()
+ || getProjectControl().isOwner()
+ || refControl.canPerform(Permission.TOGGLE_WORK_IN_PROGRESS_STATE)
+ || getProjectControl().isAdmin();
+ }
+
/** Can this user edit the description? */
private boolean canEditDescription() {
- if (getChange().getStatus().isOpen()) {
+ if (getChange().isNew()) {
return isOwner() // owner (aka creator) of the change can edit desc
|| refControl.isOwner() // branch owner can edit desc
|| getProjectControl().isOwner() // project owner can edit desc
@@ -208,9 +211,9 @@ class ChangeControl {
|| getProjectControl().isAdmin();
}
- private boolean isPrivateVisible(ReviewDb db, ChangeData cd) throws OrmException {
+ private boolean isPrivateVisible(ChangeData cd) {
return isOwner()
- || isReviewer(db, cd)
+ || isReviewer(cd)
|| refControl.canPerform(Permission.VIEW_PRIVATE_CHANGES)
|| getUser().isInternalUser();
}
@@ -220,26 +223,13 @@ class ChangeControl {
private Map<String, PermissionRange> labels;
private String resourcePath;
- ForChangeImpl(@Nullable ChangeData cd, @Nullable Provider<ReviewDb> db) {
+ ForChangeImpl(@Nullable ChangeData cd) {
this.cd = cd;
- this.db = db;
- }
-
- private ReviewDb db() {
- if (db != null) {
- return db.get();
- } else if (cd != null) {
- return cd.db();
- } else {
- return null;
- }
}
private ChangeData changeData() {
if (cd == null) {
- ReviewDb reviewDb = db();
- checkState(reviewDb != null, "need ReviewDb");
- cd = changeDataFactory.create(reviewDb, notes);
+ cd = changeDataFactory.create(notes);
}
return cd;
}
@@ -295,7 +285,7 @@ class ChangeControl {
try {
switch (perm) {
case READ:
- return isVisible(db(), changeData());
+ return isVisible(changeData());
case ABANDON:
return canAbandon();
case DELETE:
@@ -316,12 +306,14 @@ class ChangeControl {
return canRestore();
case SUBMIT:
return refControl.canSubmit(isOwner());
+ case TOGGLE_WORK_IN_PROGRESS_STATE:
+ return canToggleWorkInProgressState();
case REMOVE_REVIEWER:
case SUBMIT_AS:
return refControl.canPerform(changePermissionName(perm));
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new PermissionBackendException("unavailable", e);
}
throw new PermissionBackendException(perm + " unsupported");
diff --git a/java/com/google/gerrit/server/permissions/ChangePermission.java b/java/com/google/gerrit/server/permissions/ChangePermission.java
index ca1c460b73..2fba4ef039 100644
--- a/java/com/google/gerrit/server/permissions/ChangePermission.java
+++ b/java/com/google/gerrit/server/permissions/ChangePermission.java
@@ -55,7 +55,8 @@ public enum ChangePermission implements ChangePermissionOrLabel {
*/
REBASE,
SUBMIT,
- SUBMIT_AS("submit on behalf of other users");
+ SUBMIT_AS("submit on behalf of other users"),
+ TOGGLE_WORK_IN_PROGRESS_STATE;
private final String description;
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index 1782ac9acb..b23c85f246 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -40,7 +40,6 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Collection;
-import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -109,7 +108,7 @@ public class DefaultPermissionBackend extends PermissionBackend {
PerThreadCache.getOrCompute(
PerThreadCache.Key.create(ProjectControl.class, project, user.getCacheKey()),
() -> projectControlFactory.create(user, state));
- return control.asForProject().database(db);
+ return control.asForProject();
} catch (Exception e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
return FailedPermissionBackend.project(
@@ -128,7 +127,7 @@ public class DefaultPermissionBackend extends PermissionBackend {
@Override
public <T extends GlobalOrPluginPermission> Set<T> test(Collection<T> permSet)
throws PermissionBackendException {
- Set<T> ok = newSet(permSet);
+ Set<T> ok = Sets.newHashSetWithExpectedSize(permSet.size());
for (T perm : permSet) {
if (can(perm)) {
ok.add(perm);
@@ -147,7 +146,7 @@ public class DefaultPermissionBackend extends PermissionBackend {
return can((GlobalPermission) perm);
} else if (perm instanceof PluginPermission) {
PluginPermission pluginPermission = (PluginPermission) perm;
- return has(DefaultPermissionMappings.pluginPermissionName(pluginPermission))
+ return has(DefaultPermissionMappings.pluginCapabilityName(pluginPermission))
|| (pluginPermission.fallBackToAdmin() && isAdmin());
}
throw new PermissionBackendException(perm + " unsupported");
@@ -267,14 +266,4 @@ public class DefaultPermissionBackend extends PermissionBackend {
return denied.isEmpty() || !user.getEffectiveGroups().containsAnyOf(denied);
}
}
-
- private static <T extends GlobalOrPluginPermission> Set<T> newSet(Collection<T> permSet) {
- if (permSet instanceof EnumSet) {
- @SuppressWarnings({"unchecked", "rawtypes"})
- Set<T> s = ((EnumSet) permSet).clone();
- s.clear();
- return s;
- }
- return Sets.newHashSetWithExpectedSize(permSet.size());
- }
}
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionMappings.java b/java/com/google/gerrit/server/permissions/DefaultPermissionMappings.java
index ece29df268..82150831d3 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionMappings.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionMappings.java
@@ -23,6 +23,7 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.api.access.PluginPermission;
+import com.google.gerrit.extensions.api.access.PluginProjectPermission;
import com.google.gerrit.server.permissions.LabelPermission.ForUser;
import java.util.EnumSet;
import java.util.Optional;
@@ -97,6 +98,9 @@ public class DefaultPermissionMappings {
.put(ChangePermission.REBASE, Permission.REBASE)
.put(ChangePermission.SUBMIT, Permission.SUBMIT)
.put(ChangePermission.SUBMIT_AS, Permission.SUBMIT_AS)
+ .put(
+ ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE,
+ Permission.TOGGLE_WORK_IN_PROGRESS_STATE)
.build();
private static <T extends Enum<T>> void checkMapContainsAllEnumValues(
@@ -117,14 +121,18 @@ public class DefaultPermissionMappings {
return Optional.ofNullable(CAPABILITIES.inverse().get(capabilityName));
}
- public static String pluginPermissionName(PluginPermission pluginPermission) {
+ public static String pluginCapabilityName(PluginPermission pluginPermission) {
return pluginPermission.pluginName() + '-' + pluginPermission.capability();
}
+ public static String pluginProjectPermissionName(PluginProjectPermission pluginPermission) {
+ return "plugin-" + pluginPermission.pluginName() + '-' + pluginPermission.permission();
+ }
+
public static String globalOrPluginPermissionName(GlobalOrPluginPermission permission) {
return permission instanceof GlobalPermission
? globalPermissionName((GlobalPermission) permission)
- : pluginPermissionName((PluginPermission) permission);
+ : pluginCapabilityName((PluginPermission) permission);
}
public static Optional<String> projectPermissionName(ProjectPermission projectPermission) {
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 55d7c6c62f..7fa712d5d4 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -14,18 +14,23 @@
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_CHANGES;
import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS_SELF;
import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toMap;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+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.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Description;
@@ -36,7 +41,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupCache;
@@ -50,9 +54,7 @@ import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.ArrayList;
@@ -76,7 +78,6 @@ class DefaultRefFilter {
private final TagCache tagCache;
private final ChangeNotes.Factory changeNotesFactory;
@Nullable private final SearchingChangeCacheImpl changeCache;
- private final Provider<ReviewDb> db;
private final GroupCache groupCache;
private final PermissionBackend permissionBackend;
private final ProjectControl projectControl;
@@ -94,7 +95,6 @@ class DefaultRefFilter {
TagCache tagCache,
ChangeNotes.Factory changeNotesFactory,
@Nullable SearchingChangeCacheImpl changeCache,
- Provider<ReviewDb> db,
GroupCache groupCache,
PermissionBackend permissionBackend,
@GerritServerConfig Config config,
@@ -103,7 +103,6 @@ class DefaultRefFilter {
this.tagCache = tagCache;
this.changeNotesFactory = changeNotesFactory;
this.changeCache = changeCache;
- this.db = db;
this.groupCache = groupCache;
this.permissionBackend = permissionBackend;
this.skipFullRefEvaluationIfAllRefsAreVisible =
@@ -113,7 +112,7 @@ class DefaultRefFilter {
this.user = projectControl.getUser();
this.projectState = projectControl.getProjectState();
this.permissionBackendForProject =
- permissionBackend.user(user).database(db).project(projectState.getNameKey());
+ permissionBackend.user(user).project(projectState.getNameKey());
this.fullFilterCount =
metricMaker.newCounter(
"permissions/ref_filter/full_filter_count",
@@ -127,20 +126,70 @@ class DefaultRefFilter {
.setRate());
}
+ /** Filters given refs and tags by visibility. */
Map<String, Ref> filter(Map<String, Ref> refs, Repository repo, RefFilterOptions opts)
throws PermissionBackendException {
+ // 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)) {
+ return ImmutableMap.of();
+ }
+ if (RefNames.isRefsChanges(refName)) {
+ return canSeeSingleChangeRef(refName) ? refs : 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
+ // 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);
+ }
+ } catch (IOException e) {
+ throw new PermissionBackendException(e);
+ }
+ }
+ }
+ 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
+ * 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);
}
+ // TODO(hiesel): Remove when optimization is done.
+ boolean hasReadOnRefsStar =
+ checkProjectPermission(permissionBackendForProject, ProjectPermission.READ);
if (skipFullRefEvaluationIfAllRefsAreVisible && !projectState.isAllUsers()) {
- if (projectState.statePermitsRead()
- && checkProjectPermission(permissionBackendForProject, ProjectPermission.READ)) {
+ if (projectState.statePermitsRead() && hasReadOnRefsStar) {
skipFilterCount.increment();
- return refs;
+ return new AutoValue_DefaultRefFilter_Result(refs, ImmutableList.of());
} else if (projectControl.allRefsAreVisible(ImmutableSet.of(RefNames.REFS_CONFIG))) {
skipFilterCount.increment();
- return fastHideRefsMetaConfig(refs);
+ return new AutoValue_DefaultRefFilter_Result(
+ fastHideRefsMetaConfig(refs), ImmutableList.of());
}
}
fullFilterCount.increment();
@@ -164,7 +213,6 @@ class DefaultRefFilter {
Map<String, Ref> result = new HashMap<>();
List<Ref> deferredTags = new ArrayList<>();
-
for (Ref ref : refs.values()) {
String name = ref.getName();
Change.Id changeId;
@@ -197,9 +245,22 @@ class DefaultRefFilter {
result.put(name, ref);
}
} else if (isTag(ref)) {
- // If its a tag, consider it later.
- if (ref.getObjectId() != null) {
- deferredTags.add(ref);
+ if (hasReadOnRefsStar) {
+ // The user has READ on refs/*. This is the broadest permission one can assign. There is
+ // no way to grant access to (specific) tags in Gerrit, so we have to assume that these
+ // users can see all tags because there could be tags that aren't reachable by any visible
+ // ref while the user can see all non-Gerrit refs. This matches Gerrit's historic
+ // behavior.
+ // This makes it so that these users could see commits that they can't see otherwise
+ // (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);
+ } else {
+ // If its a tag, consider it later.
+ if (ref.getObjectId() != null) {
+ deferredTags.add(ref);
+ }
}
} else if (name.startsWith(RefNames.REFS_SEQUENCES)) {
// Sequences are internal database implementation details.
@@ -226,32 +287,29 @@ class DefaultRefFilter {
}
}
}
+ return new AutoValue_DefaultRefFilter_Result(result, deferredTags);
+ }
- // If we have tags that were deferred, we need to do a revision walk
- // to identify what tags we can actually reach, and what we cannot.
- //
- if (!deferredTags.isEmpty() && (!result.isEmpty() || opts.filterTagsSeparately())) {
- TagMatcher tags =
- tagCache
- .get(projectState.getNameKey())
- .matcher(
- tagCache,
- repo,
- opts.filterTagsSeparately()
- ? filter(
- repo.getAllRefs(),
- repo,
- opts.toBuilder().setFilterTagsSeparately(false).build())
- .values()
- : result.values());
- for (Ref tag : deferredTags) {
- if (tags.isReachable(tag)) {
- result.put(tag.getName(), tag);
- }
- }
+ /**
+ * Returns all refs tag we regard as starting points for reachability computation for tags. In
+ * general, these are all refs not managed by Gerrit excluding symbolic refs and tags.
+ *
+ * <p>We exclude symbolic refs because their target will be included and this will suffice for
+ * computing reachability.
+ */
+ private static Map<String, Ref> getTaggableRefsMap(Repository repo)
+ throws PermissionBackendException {
+ try {
+ return repo.getRefDatabase().getRefs().stream()
+ .filter(
+ r ->
+ !RefNames.isGerritRef(r.getName())
+ && !r.getName().startsWith(RefNames.REFS_TAGS)
+ && !r.isSymbolic())
+ .collect(toMap(Ref::getName, r -> r));
+ } catch (IOException e) {
+ throw new PermissionBackendException(e);
}
-
- return result;
}
private Map<String, Ref> fastHideRefsMetaConfig(Map<String, Ref> refs)
@@ -296,6 +354,7 @@ class DefaultRefFilter {
if (id == null) {
return false;
}
+
if (user.isIdentifiedUser()
&& name.startsWith(RefNames.refsEditPrefix(user.asIdentifiedUser().getAccountId()))
&& visible(repo, id)) {
@@ -320,7 +379,7 @@ class DefaultRefFilter {
Project.NameKey project = projectState.getNameKey();
try {
Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
- for (ChangeData cd : changeCache.getChangeData(db.get(), project)) {
+ for (ChangeData cd : changeCache.getChangeData(project)) {
ChangeNotes notes = changeNotesFactory.createFromIndexedChange(cd.change());
if (!projectState.statePermitsRead()) {
continue;
@@ -333,7 +392,7 @@ class DefaultRefFilter {
}
}
return visibleChanges;
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log(
"Cannot load changes for project %s, assuming no changes are visible", project);
return Collections.emptyMap();
@@ -345,7 +404,7 @@ class DefaultRefFilter {
Project.NameKey p = projectState.getNameKey();
ImmutableList<ChangeNotesResult> changes;
try {
- changes = changeNotesFactory.scan(repo, db.get(), p).collect(toImmutableList());
+ changes = changeNotesFactory.scan(repo, p).collect(toImmutableList());
} catch (IOException e) {
logger.atSevere().withCause(e).log(
"Cannot load changes for project %s, assuming no changes are visible", p);
@@ -384,7 +443,7 @@ class DefaultRefFilter {
}
private boolean isMetadata(String name) {
- return name.startsWith(REFS_CHANGES) || RefNames.isRefsEdit(name);
+ return RefNames.isRefsChanges(name) || RefNames.isRefsEdit(name);
}
private static boolean isTag(Ref ref) {
@@ -423,4 +482,51 @@ class DefaultRefFilter {
return isAdmin
|| (user != null && user.getEffectiveGroups().contains(group.getOwnerGroupUUID()));
}
+
+ /**
+ * Returns true if the user can see the provided change ref. Uses NoteDb for evaluation, hence
+ * does not suffer from the limitations documented in {@link SearchingChangeCacheImpl}.
+ *
+ * <p>This code lets users fetch changes that are not among the fraction of most recently modified
+ * changes that {@link SearchingChangeCacheImpl} returns. This works only when Git Protocol v2
+ * with refs-in-wants is used as that enables Gerrit to skip traditional advertisement of all
+ * visible refs.
+ */
+ private boolean canSeeSingleChangeRef(String refName) throws PermissionBackendException {
+ // We are treating just a single change ref. We are therefore not going through regular ref
+ // filtering, but use NoteDb directly. This makes it so that we can always serve this ref
+ // even if the change is not part of the set of most recent changes that
+ // SearchingChangeCacheImpl returns.
+ Change.Id cId = Change.Id.fromRef(refName);
+ if (cId == null) {
+ // The ref is not a valid change ref. Treat it as non-visible since it's not representing a
+ // change.
+ logger.atWarning().log("invalid change ref %s is not visible", refName);
+ return false;
+ }
+ ChangeNotes notes;
+ try {
+ notes = changeNotesFactory.create(projectState.getNameKey(), cId);
+ } catch (StorageException e) {
+ throw new PermissionBackendException("can't construct change notes", e);
+ }
+ try {
+ permissionBackendForProject.change(notes).check(ChangePermission.READ);
+ return true;
+ } catch (AuthException e) {
+ return false;
+ }
+ }
+
+ @AutoValue
+ abstract static class Result {
+ /** Subset of the refs passed into the computation that is visible to the user. */
+ abstract Map<String, Ref> visibleRefs();
+
+ /**
+ * List of tags where we couldn't figure out visibility in the first pass and need to do an
+ * expensive ref walk.
+ */
+ abstract List<Ref> deferredTags();
+ }
}
diff --git a/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java b/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
index bd7c5498a7..5c7ee0d81d 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
import com.google.gerrit.server.permissions.PermissionBackend.ForProject;
@@ -25,7 +25,6 @@ import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackend.WithUser;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.inject.Provider;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
@@ -115,11 +114,6 @@ public class FailedPermissionBackend {
}
@Override
- public ForProject database(Provider<ReviewDb> db) {
- return this;
- }
-
- @Override
public String resourcePath() {
throw new UnsupportedOperationException(
"FailedPermissionBackend is not scoped to a resource");
@@ -131,18 +125,18 @@ public class FailedPermissionBackend {
}
@Override
- public void check(ProjectPermission perm) throws PermissionBackendException {
+ public void check(CoreOrPluginProjectPermission perm) throws PermissionBackendException {
throw new PermissionBackendException(message, cause);
}
@Override
- public Set<ProjectPermission> test(Collection<ProjectPermission> permSet)
+ public <T extends CoreOrPluginProjectPermission> Set<T> test(Collection<T> permSet)
throws PermissionBackendException {
throw new PermissionBackendException(message, cause);
}
@Override
- public BooleanCondition testCond(ProjectPermission perm) {
+ public BooleanCondition testCond(CoreOrPluginProjectPermission perm) {
throw new UnsupportedOperationException(
"FailedPermissionBackend does not support conditions");
}
@@ -164,11 +158,6 @@ public class FailedPermissionBackend {
}
@Override
- public ForRef database(Provider<ReviewDb> db) {
- return this;
- }
-
- @Override
public String resourcePath() {
throw new UnsupportedOperationException(
"FailedPermissionBackend is not scoped to a resource");
@@ -217,11 +206,6 @@ public class FailedPermissionBackend {
}
@Override
- public ForChange database(Provider<ReviewDb> db) {
- return this;
- }
-
- @Override
public String resourcePath() {
throw new UnsupportedOperationException(
"FailedPermissionBackend is not scoped to a resource");
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackend.java b/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 9ae0b5b70a..7960c65f1e 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -15,12 +15,16 @@
package com.google.gerrit.server.permissions;
import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import com.google.auto.value.AutoValue;
+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.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;
@@ -28,18 +32,15 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.ImplementedBy;
-import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -71,7 +72,7 @@ import org.eclipse.jgit.lib.Repository;
* private final PermissionBackend permissions;
* private final Provider<CurrentUser> user;
*
- * @Inject
+ * {@literal @}Inject
* Foo(PermissionBackend permissions, Provider<CurrentUser> user) {
* this.permissions = permissions;
* this.user = user;
@@ -150,42 +151,21 @@ public abstract class PermissionBackend {
// delegates to the appropriate testOrFalse method in PermissionBackend.
}
- /** PermissionBackend with an optional per-request ReviewDb handle. */
- public abstract static class AcceptsReviewDb<T> {
- protected Provider<ReviewDb> db;
-
- public T database(Provider<ReviewDb> db) {
- if (db != null) {
- this.db = db;
- }
- return self();
- }
-
- public T database(ReviewDb db) {
- return database(Providers.of(requireNonNull(db, "ReviewDb")));
- }
-
- @SuppressWarnings("unchecked")
- private T self() {
- return (T) this;
- }
- }
-
/** PermissionBackend scoped to a specific user. */
- public abstract static class WithUser extends AcceptsReviewDb<WithUser> {
+ public abstract static class WithUser {
/** Returns an instance scoped for the specified project. */
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()).database(db);
+ return project(ref.getParentKey()).ref(ref.get());
}
/** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeData cd) {
try {
return ref(cd.change().getDest()).change(cd);
- } catch (OrmException e) {
+ } catch (StorageException e) {
return FailedPermissionBackend.change("unavailable", e);
}
}
@@ -290,7 +270,7 @@ public abstract class PermissionBackend {
}
/** PermissionBackend scoped to a user and project. */
- public abstract static class ForProject extends AcceptsReviewDb<ForProject> {
+ public abstract static class ForProject {
/** Returns the fully qualified resource path that this instance is scoped to. */
public abstract String resourcePath();
@@ -301,7 +281,7 @@ public abstract class PermissionBackend {
public ForChange change(ChangeData cd) {
try {
return ref(cd.change().getDest().get()).change(cd);
- } catch (OrmException e) {
+ } catch (StorageException e) {
return FailedPermissionBackend.change("unavailable", e);
}
}
@@ -321,18 +301,23 @@ public abstract class PermissionBackend {
}
/** Verify scoped user can {@code perm}, throwing if denied. */
- public abstract void check(ProjectPermission perm)
+ public abstract void check(CoreOrPluginProjectPermission perm)
throws AuthException, PermissionBackendException;
/** Filter {@code permSet} to permissions scoped user might be able to perform. */
- public abstract Set<ProjectPermission> test(Collection<ProjectPermission> permSet)
+ public abstract <T extends CoreOrPluginProjectPermission> Set<T> test(Collection<T> permSet)
throws PermissionBackendException;
- public boolean test(ProjectPermission perm) throws PermissionBackendException {
- return test(EnumSet.of(perm)).contains(perm);
+ public boolean test(CoreOrPluginProjectPermission perm) throws PermissionBackendException {
+ if (perm instanceof ProjectPermission) {
+ return test(EnumSet.of((ProjectPermission) perm)).contains(perm);
+ }
+
+ // TODO(xchangcheng): implement for plugin defined project permissions.
+ return false;
}
- public boolean testOrFalse(ProjectPermission perm) {
+ public boolean testOrFalse(CoreOrPluginProjectPermission perm) {
try {
return test(perm);
} catch (PermissionBackendException e) {
@@ -341,7 +326,7 @@ public abstract class PermissionBackend {
}
}
- public abstract BooleanCondition testCond(ProjectPermission perm);
+ public abstract BooleanCondition testCond(CoreOrPluginProjectPermission perm);
/**
* Filter a map of references by visibility.
@@ -356,6 +341,21 @@ public abstract class PermissionBackend {
public abstract Map<String, Ref> filter(
Map<String, Ref> refs, Repository repo, RefFilterOptions opts)
throws PermissionBackendException;
+
+ /**
+ * Filter a list of references by visibility.
+ *
+ * @param refs a list of references to filter.
+ * @param repo an open {@link Repository} handle for this instance's project
+ * @param opts further options for filtering.
+ * @return a partition of the provided refs that are visible to the user that this instance is
+ * scoped to.
+ * @throws PermissionBackendException if failure consulting backend configuration.
+ */
+ public Map<String, Ref> filter(List<Ref> refs, Repository repo, RefFilterOptions opts)
+ throws PermissionBackendException {
+ return filter(refs.stream().collect(toMap(Ref::getName, r -> r, (a, b) -> b)), repo, opts);
+ }
}
/** Options for filtering refs using {@link ForProject}. */
@@ -364,22 +364,25 @@ public abstract class PermissionBackend {
/** Remove all NoteDb refs (refs/changes/*, refs/users/*, edit refs) from the result. */
public abstract boolean filterMeta();
- /** Separately add reachable tags. */
- public abstract boolean filterTagsSeparately();
+ /**
+ * Select only refs with names matching prefixes per {@link
+ * org.eclipse.jgit.lib.RefDatabase#getRefsByPrefix}.
+ */
+ public abstract ImmutableList<String> prefixes();
public abstract Builder toBuilder();
public static Builder builder() {
return new AutoValue_PermissionBackend_RefFilterOptions.Builder()
.setFilterMeta(false)
- .setFilterTagsSeparately(false);
+ .setPrefixes(Collections.singletonList(""));
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setFilterMeta(boolean val);
- public abstract Builder setFilterTagsSeparately(boolean val);
+ public abstract Builder setPrefixes(List<String> prefixes);
public abstract RefFilterOptions build();
}
@@ -390,7 +393,7 @@ public abstract class PermissionBackend {
}
/** PermissionBackend scoped to a user, project and reference. */
- public abstract static class ForRef extends AcceptsReviewDb<ForRef> {
+ public abstract static class ForRef {
/** Returns a fully qualified resource path that this instance is scoped to. */
public abstract String resourcePath();
@@ -440,7 +443,7 @@ public abstract class PermissionBackend {
}
/** PermissionBackend scoped to a user, project, reference and change. */
- public abstract static class ForChange extends AcceptsReviewDb<ForChange> {
+ public abstract static class ForChange {
/** Returns the fully qualified resource path that this instance is scoped to. */
public abstract String resourcePath();
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackendCondition.java b/java/com/google/gerrit/server/permissions/PermissionBackendCondition.java
index 1b6b0878a8..a92e5042f3 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackendCondition.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackendCondition.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.permissions;
+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.conditions.PrivateInternals_BooleanCondition;
@@ -100,10 +101,11 @@ public abstract class PermissionBackendCondition
public static class ForProject extends PermissionBackendCondition {
private final PermissionBackend.ForProject impl;
- private final ProjectPermission perm;
+ private final CoreOrPluginProjectPermission perm;
private final CurrentUser user;
- public ForProject(PermissionBackend.ForProject impl, ProjectPermission perm, CurrentUser user) {
+ public ForProject(
+ PermissionBackend.ForProject impl, CoreOrPluginProjectPermission perm, CurrentUser user) {
this.impl = impl;
this.perm = perm;
this.user = user;
@@ -113,7 +115,7 @@ public abstract class PermissionBackendCondition
return impl;
}
- public ProjectPermission permission() {
+ public CoreOrPluginProjectPermission permission() {
return perm;
}
diff --git a/java/com/google/gerrit/server/permissions/PluginPermissionsUtil.java b/java/com/google/gerrit/server/permissions/PluginPermissionsUtil.java
new file mode 100644
index 0000000000..b147926e4e
--- /dev/null
+++ b/java/com/google/gerrit/server/permissions/PluginPermissionsUtil.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.permissions;
+
+import static com.google.gerrit.extensions.api.access.PluginProjectPermission.PLUGIN_PERMISSION_NAME_PATTERN_STRING;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.config.CapabilityDefinition;
+import com.google.gerrit.extensions.config.PluginPermissionDefinition;
+import com.google.gerrit.extensions.config.PluginProjectPermissionDefinition;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.regex.Pattern;
+
+/** Utilities for plugin permissions. */
+@Singleton
+public final class PluginPermissionsUtil {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private static final String PLUGIN_NAME_PATTERN_STRING = "[a-zA-Z0-9-]+";
+
+ /**
+ * Name pattern for a plugin non-capability permission stored in the config file.
+ *
+ * <p>This pattern requires a plugin declared permission to have a name in the access section of
+ * {@code ProjectConfig} with a format like "plugin-{pluginName}-{permissionName}", which makes it
+ * easier to tell if a config name represents a plugin permission or not. Note "-" isn't clear
+ * enough for this purpose since some core permissions, e.g. "label-", also contain "-".
+ */
+ private static final Pattern PLUGIN_PERMISSION_NAME_IN_CONFIG_PATTERN =
+ Pattern.compile(
+ "^plugin-"
+ + PLUGIN_NAME_PATTERN_STRING
+ + "-"
+ + PLUGIN_PERMISSION_NAME_PATTERN_STRING
+ + "$");
+
+ /** Name pattern for a Gerrit plugin. */
+ private static final Pattern PLUGIN_NAME_PATTERN =
+ Pattern.compile("^" + PLUGIN_NAME_PATTERN_STRING + "$");
+
+ private final DynamicMap<CapabilityDefinition> capabilityDefinitions;
+ private final DynamicMap<PluginProjectPermissionDefinition> pluginProjectPermissionDefinitions;
+
+ @Inject
+ PluginPermissionsUtil(
+ DynamicMap<CapabilityDefinition> capabilityDefinitions,
+ DynamicMap<PluginProjectPermissionDefinition> pluginProjectPermissionDefinitions) {
+ this.capabilityDefinitions = capabilityDefinitions;
+ this.pluginProjectPermissionDefinitions = pluginProjectPermissionDefinitions;
+ }
+
+ /**
+ * Collects all the plugin declared capabilities.
+ *
+ * @return a map of plugin declared capabilities with "pluginName" as its keys and
+ * "pluginName-{permissionName}" as its values.
+ */
+ public ImmutableMap<String, String> collectPluginCapabilities() {
+ return collectPermissions(capabilityDefinitions, "");
+ }
+
+ /**
+ * Collects all the plugin declared project permissions.
+ *
+ * @return a map of plugin declared project permissions with "{pluginName}" as its keys and
+ * "plugin-{pluginName}-{permissionName}" as its values.
+ */
+ public ImmutableMap<String, String> collectPluginProjectPermissions() {
+ return collectPermissions(pluginProjectPermissionDefinitions, "plugin-");
+ }
+
+ private static <T extends PluginPermissionDefinition>
+ ImmutableMap<String, String> collectPermissions(DynamicMap<T> definitions, String prefix) {
+ ImmutableMap.Builder<String, String> permissionIdNames = ImmutableMap.builder();
+
+ for (Extension<T> extension : definitions) {
+ String pluginName = extension.getPluginName();
+ if (!PLUGIN_NAME_PATTERN.matcher(pluginName).matches()) {
+ logger.atWarning().log(
+ "Plugin name '%s' must match '%s' to use permissions; rename the plugin",
+ pluginName, PLUGIN_NAME_PATTERN.pattern());
+ continue;
+ }
+
+ String id = prefix + pluginName + "-" + extension.getExportName();
+ permissionIdNames.put(id, extension.get().getDescription());
+ }
+
+ return permissionIdNames.build();
+ }
+
+ /**
+ * Checks if a given name matches the plugin declared permission name pattern for configs.
+ *
+ * @param name a config name which may stand for a plugin permission.
+ * @return whether the name matches the plugin permission name pattern for configs.
+ */
+ public static boolean isValidPluginPermission(String name) {
+ return PLUGIN_PERMISSION_NAME_IN_CONFIG_PATTERN.matcher(name).matches();
+ }
+}
diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java
index 0094979732..dfc33395f2 100644
--- a/java/com/google/gerrit/server/permissions/ProjectControl.java
+++ b/java/com/google/gerrit/server/permissions/ProjectControl.java
@@ -16,13 +16,17 @@ 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.RefConfigSection.REGEX_PREFIX;
+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.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.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;
@@ -30,7 +34,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.config.GitReceivePackGroups;
@@ -44,12 +47,10 @@ import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SectionMatcher;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Collection;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -101,9 +102,9 @@ class ProjectControl {
return new ForProjectImpl();
}
- ChangeControl controlFor(ReviewDb db, Change change) throws OrmException {
+ ChangeControl controlFor(Change change) {
return changeControlFactory.create(
- controlForRef(change.getDest()), db, change.getProject(), change.getId());
+ controlForRef(change.getDest()), change.getProject(), change.getId());
}
ChangeControl controlFor(ChangeNotes notes) {
@@ -349,7 +350,7 @@ class ProjectControl {
@Override
public ForRef ref(String ref) {
- return controlForRef(ref).asForRef().database(db);
+ return controlForRef(ref).asForRef();
}
@Override
@@ -357,7 +358,7 @@ class ProjectControl {
try {
checkProject(cd.change());
return super.change(cd);
- } catch (OrmException e) {
+ } catch (StorageException e) {
return FailedPermissionBackend.change("unavailable", e);
}
}
@@ -378,17 +379,18 @@ class ProjectControl {
}
@Override
- public void check(ProjectPermission perm) throws AuthException, PermissionBackendException {
+ public void check(CoreOrPluginProjectPermission perm)
+ throws AuthException, PermissionBackendException {
if (!can(perm)) {
throw new AuthException(perm.describeForException() + " not permitted");
}
}
@Override
- public Set<ProjectPermission> test(Collection<ProjectPermission> permSet)
+ public <T extends CoreOrPluginProjectPermission> Set<T> test(Collection<T> permSet)
throws PermissionBackendException {
- EnumSet<ProjectPermission> ok = EnumSet.noneOf(ProjectPermission.class);
- for (ProjectPermission perm : permSet) {
+ Set<T> ok = Sets.newHashSetWithExpectedSize(permSet.size());
+ for (T perm : permSet) {
if (can(perm)) {
ok.add(perm);
}
@@ -397,7 +399,7 @@ class ProjectControl {
}
@Override
- public BooleanCondition testCond(ProjectPermission perm) {
+ public BooleanCondition testCond(CoreOrPluginProjectPermission perm) {
return new PermissionBackendCondition.ForProject(this, perm, getUser());
}
@@ -410,6 +412,17 @@ class ProjectControl {
return refFilter.filter(refs, repo, opts);
}
+ private boolean can(CoreOrPluginProjectPermission perm) throws PermissionBackendException {
+ if (perm instanceof ProjectPermission) {
+ return can((ProjectPermission) perm);
+ } else if (perm instanceof PluginProjectPermission) {
+ // TODO(xchangcheng): implement for plugin defined project permissions.
+ return false;
+ }
+
+ throw new PermissionBackendException(perm.describeForException() + " unsupported");
+ }
+
private boolean can(ProjectPermission perm) throws PermissionBackendException {
switch (perm) {
case ACCESS:
diff --git a/java/com/google/gerrit/server/permissions/ProjectPermission.java b/java/com/google/gerrit/server/permissions/ProjectPermission.java
index ca04f3b016..653303a29c 100644
--- a/java/com/google/gerrit/server/permissions/ProjectPermission.java
+++ b/java/com/google/gerrit/server/permissions/ProjectPermission.java
@@ -16,10 +16,11 @@ package com.google.gerrit.server.permissions;
import static java.util.Objects.requireNonNull;
+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 GerritPermission {
+public enum ProjectPermission implements CoreOrPluginProjectPermission {
/**
* Can access at least one reference or change within the repository.
*
diff --git a/java/com/google/gerrit/server/permissions/RefControl.java b/java/com/google/gerrit/server/permissions/RefControl.java
index 74b04a32bf..9a2ecdd17e 100644
--- a/java/com/google/gerrit/server/permissions/RefControl.java
+++ b/java/com/google/gerrit/server/permissions/RefControl.java
@@ -21,6 +21,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Change;
@@ -33,8 +34,6 @@ import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.util.MagicBranch;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.util.Providers;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
@@ -197,7 +196,6 @@ class RefControl {
case GIT:
return false;
- case JSON_RPC:
case REST_API:
case SSH_COMMAND:
case UNKNOWN:
@@ -229,7 +227,6 @@ class RefControl {
case GIT:
return canPushWithForce() || canPerform(Permission.DELETE);
- case JSON_RPC:
case REST_API:
case SSH_COMMAND:
case UNKNOWN:
@@ -444,10 +441,8 @@ class RefControl {
public ForChange change(ChangeData cd) {
try {
// TODO(hiesel) Force callers to call database() and use db instead of cd.db()
- return getProjectControl()
- .controlFor(cd.db(), cd.change())
- .asForChange(cd, Providers.of(cd.db()));
- } catch (OrmException e) {
+ return getProjectControl().controlFor(cd.change()).asForChange(cd);
+ } catch (StorageException e) {
return FailedPermissionBackend.change("unavailable", e);
}
}
@@ -461,12 +456,12 @@ class RefControl {
"expected change in project %s, not %s",
project,
change.getProject());
- return getProjectControl().controlFor(notes).asForChange(null, db);
+ return getProjectControl().controlFor(notes).asForChange(null);
}
@Override
public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
- return getProjectControl().controlFor(notes).asForChange(cd, db);
+ return getProjectControl().controlFor(notes).asForChange(cd);
}
@Override
diff --git a/java/com/google/gerrit/server/plugins/CopyConfigModule.java b/java/com/google/gerrit/server/plugins/CopyConfigModule.java
index 9f937e6807..090d257849 100644
--- a/java/com/google/gerrit/server/plugins/CopyConfigModule.java
+++ b/java/com/google/gerrit/server/plugins/CopyConfigModule.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.plugins;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.AnonymousCowardName;
@@ -24,7 +23,6 @@ import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.securestore.SecureStore;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Provides;
@@ -73,13 +71,6 @@ class CopyConfigModule extends AbstractModule {
return gerritServerConfig;
}
- @Inject private SchemaFactory<ReviewDb> schemaFactory;
-
- @Provides
- SchemaFactory<ReviewDb> getSchemaFactory() {
- return schemaFactory;
- }
-
@Inject private GitRepositoryManager gitRepositoryManager;
@Provides
diff --git a/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/java/com/google/gerrit/server/plugins/JarPluginProvider.java
index 5b800596ee..82f97c95e6 100644
--- a/java/com/google/gerrit/server/plugins/JarPluginProvider.java
+++ b/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -110,9 +110,6 @@ public class JarPluginProvider implements ServerPluginProvider {
public static Path storeInTemp(String pluginName, InputStream in, SitePaths sitePaths)
throws IOException {
- if (!Files.exists(sitePaths.tmp_dir)) {
- Files.createDirectories(sitePaths.tmp_dir);
- }
return PluginUtil.asTemp(in, tempNameFor(pluginName), ".jar", sitePaths.tmp_dir);
}
diff --git a/java/com/google/gerrit/server/plugins/JarScanner.java b/java/com/google/gerrit/server/plugins/JarScanner.java
index 1a9b85903d..0f871357ed 100644
--- a/java/com/google/gerrit/server/plugins/JarScanner.java
+++ b/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -114,7 +114,7 @@ public class JarScanner implements PluginContentScanner, AutoCloseable {
for (Class<? extends Annotation> annotoation : annotations) {
String descr = classObjToClassDescr.get(annotoation);
Collection<ClassData> discoverdData = rawMap.get(descr);
- Collection<ClassData> values = firstNonNull(discoverdData, Collections.<ClassData>emptySet());
+ Collection<ClassData> values = firstNonNull(discoverdData, Collections.emptySet());
result.put(
annotoation,
@@ -144,7 +144,7 @@ public class JarScanner implements PluginContentScanner, AutoCloseable {
continue;
}
- ClassData def = new ClassData(Collections.<String>emptySet());
+ ClassData def = new ClassData(Collections.emptySet());
try {
new ClassReader(read(jarFile, entry)).accept(def, SKIP_ALL);
} catch (RuntimeException err) {
@@ -195,7 +195,7 @@ public class JarScanner implements PluginContentScanner, AutoCloseable {
Collection<String> exports;
private ClassData(Collection<String> exports) {
- super(Opcodes.ASM6);
+ super(Opcodes.ASM7);
this.exports = exports;
}
@@ -263,7 +263,7 @@ public class JarScanner implements PluginContentScanner, AutoCloseable {
private abstract static class AbstractAnnotationVisitor extends AnnotationVisitor {
AbstractAnnotationVisitor() {
- super(Opcodes.ASM6);
+ super(Opcodes.ASM7);
}
@Override
@@ -331,13 +331,6 @@ public class JarScanner implements PluginContentScanner, AutoCloseable {
if (attributes == null) {
return Collections.emptyMap();
}
- return Maps.transformEntries(
- attributes,
- new Maps.EntryTransformer<Object, Object, String>() {
- @Override
- public String transformEntry(Object key, Object value) {
- return (String) value;
- }
- });
+ return Maps.transformEntries(attributes, (key, value) -> (String) value);
}
}
diff --git a/java/com/google/gerrit/server/plugins/ListPlugins.java b/java/com/google/gerrit/server/plugins/ListPlugins.java
index 84e63d09a5..438a0c4a10 100644
--- a/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -144,12 +144,14 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
public static PluginInfo toPluginInfo(Plugin p) {
String id;
String version;
+ String apiVersion;
String indexUrl;
String filename;
Boolean disabled;
id = Url.encode(p.getName());
version = p.getVersion();
+ apiVersion = p.getApiVersion();
disabled = p.isDisabled() ? true : null;
if (p.getSrcFile() != null) {
indexUrl = String.format("plugins/%s/", p.getName());
@@ -159,6 +161,6 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
filename = null;
}
- return new PluginInfo(id, version, indexUrl, filename, disabled);
+ return new PluginInfo(id, version, apiVersion, indexUrl, filename, disabled);
}
}
diff --git a/java/com/google/gerrit/server/plugins/Plugin.java b/java/com/google/gerrit/server/plugins/Plugin.java
index 57597054ed..238066b507 100644
--- a/java/com/google/gerrit/server/plugins/Plugin.java
+++ b/java/com/google/gerrit/server/plugins/Plugin.java
@@ -116,6 +116,11 @@ public abstract class Plugin {
return apiType;
}
+ @Nullable
+ public String getApiVersion() {
+ return null;
+ }
+
public Plugin.CacheKey getCacheKey() {
return cacheKey;
}
diff --git a/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java b/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
index ee4381be61..87e1ca9291 100644
--- a/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
+++ b/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
@@ -23,6 +23,7 @@ import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.extensions.annotations.RootRelative;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.registration.DynamicItem;
@@ -226,7 +227,8 @@ public class PluginGuiceEnvironment {
return httpModule != null;
}
- Module getHttpModule() {
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public Module getHttpModule() {
return httpModule;
}
diff --git a/java/com/google/gerrit/server/plugins/PluginLoader.java b/java/com/google/gerrit/server/plugins/PluginLoader.java
index 57e7e4993f..9279f0fee4 100644
--- a/java/com/google/gerrit/server/plugins/PluginLoader.java
+++ b/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -55,7 +55,6 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
@@ -290,12 +289,7 @@ public class PluginLoader implements LifecycleListener {
private void removeStalePluginFiles() {
DirectoryStream.Filter<Path> filter =
- new DirectoryStream.Filter<Path>() {
- @Override
- public boolean accept(Path entry) throws IOException {
- return entry.getFileName().toString().startsWith("plugin_");
- }
- };
+ entry -> entry.getFileName().toString().startsWith("plugin_");
try (DirectoryStream<Path> files = Files.newDirectoryStream(tempDir, filter)) {
for (Path file : files) {
logger.atInfo().log("Removing stale plugin file: %s", file.toFile().getName());
@@ -436,20 +430,21 @@ public class PluginLoader implements LifecycleListener {
cleanInBackground();
}
- private void addAllEntries(Map<String, Path> from, TreeSet<Entry<String, Path>> to) {
- Iterator<Entry<String, Path>> it = from.entrySet().iterator();
+ private void addAllEntries(Map<String, Path> from, TreeSet<Map.Entry<String, Path>> to) {
+ Iterator<Map.Entry<String, Path>> it = from.entrySet().iterator();
while (it.hasNext()) {
- Entry<String, Path> entry = it.next();
+ Map.Entry<String, Path> entry = it.next();
to.add(new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), entry.getValue()));
}
}
- private TreeSet<Entry<String, Path>> jarsFirstSortedPluginsSet(Map<String, Path> activePlugins) {
- TreeSet<Entry<String, Path>> sortedPlugins =
+ private TreeSet<Map.Entry<String, Path>> jarsFirstSortedPluginsSet(
+ Map<String, Path> activePlugins) {
+ TreeSet<Map.Entry<String, Path>> sortedPlugins =
Sets.newTreeSet(
- new Comparator<Entry<String, Path>>() {
+ new Comparator<Map.Entry<String, Path>>() {
@Override
- public int compare(Entry<String, Path> e1, Entry<String, Path> e2) {
+ public int compare(Map.Entry<String, Path> e1, Map.Entry<String, Path> e2) {
Path n1 = e1.getValue().getFileName();
Path n2 = e2.getValue().getFileName();
return ComparisonChain.start()
diff --git a/java/com/google/gerrit/server/plugins/PluginMetricMaker.java b/java/com/google/gerrit/server/plugins/PluginMetricMaker.java
index b1a01d369f..b49a19061a 100644
--- a/java/com/google/gerrit/server/plugins/PluginMetricMaker.java
+++ b/java/com/google/gerrit/server/plugins/PluginMetricMaker.java
@@ -47,7 +47,7 @@ public class PluginMetricMaker extends MetricMaker implements LifecycleListener
public PluginMetricMaker(MetricMaker root, String prefix) {
this.root = root;
this.prefix = prefix.endsWith("/") ? prefix : prefix + "/";
- cleanup = Collections.synchronizedSet(new HashSet<RegistrationHandle>());
+ cleanup = Collections.synchronizedSet(new HashSet<>());
}
@Override
@@ -160,12 +160,9 @@ public class PluginMetricMaker extends MetricMaker implements LifecycleListener
public RegistrationHandle newTrigger(Set<CallbackMetric<?>> metrics, Runnable trigger) {
final RegistrationHandle handle = root.newTrigger(metrics, trigger);
cleanup.add(handle);
- return new RegistrationHandle() {
- @Override
- public void remove() {
- handle.remove();
- cleanup.remove(handle);
- }
+ return () -> {
+ handle.remove();
+ cleanup.remove(handle);
};
}
diff --git a/java/com/google/gerrit/server/plugins/PluginUtil.java b/java/com/google/gerrit/server/plugins/PluginUtil.java
index 5e67e2cc94..4f00cd0849 100644
--- a/java/com/google/gerrit/server/plugins/PluginUtil.java
+++ b/java/com/google/gerrit/server/plugins/PluginUtil.java
@@ -34,17 +34,14 @@ public class PluginUtil {
return ImmutableList.of();
}
DirectoryStream.Filter<Path> filter =
- new DirectoryStream.Filter<Path>() {
- @Override
- public boolean accept(Path entry) throws IOException {
- String n = entry.getFileName().toString();
- boolean accept =
- !n.startsWith(".last_") && !n.startsWith(".next_") && Files.isRegularFile(entry);
- if (!Strings.isNullOrEmpty(suffix)) {
- accept &= n.endsWith(suffix);
- }
- return accept;
+ entry -> {
+ String n = entry.getFileName().toString();
+ boolean accept =
+ !n.startsWith(".last_") && !n.startsWith(".next_") && Files.isRegularFile(entry);
+ if (!Strings.isNullOrEmpty(suffix)) {
+ accept &= n.endsWith(suffix);
}
+ return accept;
};
try (DirectoryStream<Path> files = Files.newDirectoryStream(pluginsDir, filter)) {
return Ordering.natural().sortedCopy(files);
@@ -56,6 +53,9 @@ public class PluginUtil {
}
static Path asTemp(InputStream in, String prefix, String suffix, Path dir) throws IOException {
+ if (!Files.exists(dir)) {
+ Files.createDirectories(dir);
+ }
Path tmp = Files.createTempFile(dir, prefix, suffix);
boolean keep = false;
try (OutputStream out = Files.newOutputStream(tmp)) {
diff --git a/java/com/google/gerrit/server/plugins/ServerPlugin.java b/java/com/google/gerrit/server/plugins/ServerPlugin.java
index f2362020d0..320b618f85 100644
--- a/java/com/google/gerrit/server/plugins/ServerPlugin.java
+++ b/java/com/google/gerrit/server/plugins/ServerPlugin.java
@@ -154,6 +154,13 @@ public class ServerPlugin extends Plugin {
}
@Override
+ @Nullable
+ public String getApiVersion() {
+ Attributes main = manifest.getMainAttributes();
+ return main.getValue("Gerrit-ApiVersion");
+ }
+
+ @Override
protected boolean canReload() {
Attributes main = manifest.getMainAttributes();
String v = main.getValue("Gerrit-ReloadMode");
diff --git a/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java b/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
index 4d894820ca..22cd84c3cd 100644
--- a/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
+++ b/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.plugins;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.PluginName;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.nio.file.Path;
@@ -28,10 +28,10 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot;
class UniversalServerPluginProvider implements ServerPluginProvider {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final DynamicSet<ServerPluginProvider> serverPluginProviders;
+ private final PluginSetContext<ServerPluginProvider> serverPluginProviders;
@Inject
- UniversalServerPluginProvider(DynamicSet<ServerPluginProvider> sf) {
+ UniversalServerPluginProvider(PluginSetContext<ServerPluginProvider> sf) {
this.serverPluginProviders = sf;
}
@@ -80,15 +80,16 @@ class UniversalServerPluginProvider implements ServerPluginProvider {
private List<ServerPluginProvider> providersForHandlingPlugin(Path srcPath) {
List<ServerPluginProvider> providers = new ArrayList<>();
- for (ServerPluginProvider serverPluginProvider : serverPluginProviders) {
- boolean handles = serverPluginProvider.handles(srcPath);
- logger.atFine().log(
- "File %s handled by %s ? => %s",
- srcPath, serverPluginProvider.getProviderPluginName(), handles);
- if (handles) {
- providers.add(serverPluginProvider);
- }
- }
+ serverPluginProviders.runEach(
+ serverPluginProvider -> {
+ boolean handles = serverPluginProvider.handles(srcPath);
+ logger.atFine().log(
+ "File %s handled by %s ? => %s",
+ srcPath, serverPluginProvider.getProviderPluginName(), handles);
+ if (handles) {
+ providers.add(serverPluginProvider);
+ }
+ });
return providers;
}
}
diff --git a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
index 84b8af3923..b33fcb5c9c 100644
--- a/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
+++ b/java/com/google/gerrit/server/project/ContributorAgreementsChecker.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.project;
+import static com.google.common.base.Preconditions.checkArgument;
+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;
@@ -35,6 +38,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
@Singleton
public class ContributorAgreementsChecker {
@@ -94,6 +99,20 @@ public class ContributorAgreementsChecker {
List<AccountGroup.UUID> groupIds;
groupIds = okGroupIds;
+ // matchProjects defaults to match all projects when missing.
+ List<String> matchProjectsRegexes = ca.getMatchProjectsRegexes();
+ if (!matchProjectsRegexes.isEmpty()
+ && !projectMatchesAnyPattern(project.get(), matchProjectsRegexes)) {
+ // Doesn't match, isn't checked.
+ continue;
+ }
+ // excludeProjects defaults to exclude no projects when missing.
+ List<String> excludeProjectsRegexes = ca.getExcludeProjectsRegexes();
+ if (!excludeProjectsRegexes.isEmpty()
+ && projectMatchesAnyPattern(project.get(), excludeProjectsRegexes)) {
+ // Matches, isn't checked.
+ continue;
+ }
for (PermissionRule rule : ca.getAccepted()) {
if ((rule.getAction() == Action.ALLOW)
&& (rule.getGroup() != null)
@@ -103,7 +122,7 @@ public class ContributorAgreementsChecker {
}
}
- if (!iUser.getEffectiveGroups().containsAnyOf(okGroupIds)) {
+ if (!okGroupIds.isEmpty() && !iUser.getEffectiveGroups().containsAnyOf(okGroupIds)) {
final StringBuilder msg = new StringBuilder();
msg.append("No Contributor Agreement on file for user ")
.append(iUser.getNameEmail())
@@ -115,4 +134,23 @@ public class ContributorAgreementsChecker {
throw new AuthException(msg.toString());
}
}
+
+ private boolean projectMatchesAnyPattern(String projectName, List<String> regexes) {
+ requireNonNull(regexes);
+ checkArgument(!regexes.isEmpty());
+ for (String patternString : regexes) {
+ Pattern pattern;
+ try {
+ pattern = Pattern.compile(patternString);
+ } catch (PatternSyntaxException e) {
+ // Should never happen: Regular expressions validated when reading project.config.
+ throw new IllegalStateException(
+ "Invalid matchProjects or excludeProjects clause in project.config", e);
+ }
+ if (pattern.matcher(projectName).find()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/java/com/google/gerrit/server/project/CreateProjectArgs.java b/java/com/google/gerrit/server/project/CreateProjectArgs.java
index a68bd84860..df31c193d0 100644
--- a/java/com/google/gerrit/server/project/CreateProjectArgs.java
+++ b/java/com/google/gerrit/server/project/CreateProjectArgs.java
@@ -49,6 +49,7 @@ public class CreateProjectArgs {
enableSignedPush = InheritableBoolean.INHERIT;
requireSignedPush = InheritableBoolean.INHERIT;
submitType = SubmitType.MERGE_IF_NECESSARY;
+ rejectEmptyCommit = InheritableBoolean.INHERIT;
}
public Project.NameKey getProject() {
diff --git a/java/com/google/gerrit/server/project/CreateRefControl.java b/java/com/google/gerrit/server/project/CreateRefControl.java
index 485910eb6b..7a7598bf89 100644
--- a/java/com/google/gerrit/server/project/CreateRefControl.java
+++ b/java/com/google/gerrit/server/project/CreateRefControl.java
@@ -27,6 +27,8 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -125,7 +127,7 @@ public class CreateRefControl {
RevCommit commit,
Project.NameKey project,
PermissionBackend.ForRef forRef)
- throws AuthException, PermissionBackendException {
+ throws AuthException, PermissionBackendException, IOException {
try {
// If the user has update (push) permission, they can create the ref regardless
// of whether they are pushing any new objects along with the create.
@@ -134,7 +136,12 @@ public class CreateRefControl {
} catch (AuthException denied) {
// Fall through to check reachability.
}
- if (reachable.fromHeadsOrTags(project, repo, commit, user)) {
+ if (reachable.fromRefs(
+ project,
+ repo,
+ commit,
+ repo.getRefDatabase().getRefsByPrefix(Constants.R_HEADS, Constants.R_TAGS),
+ Optional.of(user))) {
// If the user has no push permissions, check whether the object is
// merged into a branch or tag readable by this user. If so, they are
// not effectively "pushing" more objects, so they can create the ref
@@ -142,9 +149,15 @@ public class CreateRefControl {
return;
}
- throw new AuthException(
+ AuthException e =
+ new AuthException(
+ String.format(
+ "%s for creating new commit object not permitted",
+ RefPermission.UPDATE.describeForException()));
+ e.setAdvice(
String.format(
- "%s for creating new commit object not permitted",
+ "use a SHA1 visible to you, or get %s permission on the ref",
RefPermission.UPDATE.describeForException()));
+ throw e;
}
}
diff --git a/java/com/google/gerrit/server/project/NoSuchChangeException.java b/java/com/google/gerrit/server/project/NoSuchChangeException.java
index 7946a3a15b..6f65659f04 100644
--- a/java/com/google/gerrit/server/project/NoSuchChangeException.java
+++ b/java/com/google/gerrit/server/project/NoSuchChangeException.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
/** Indicates the change does not exist. */
-public class NoSuchChangeException extends OrmException {
+public class NoSuchChangeException extends StorageException {
private static final long serialVersionUID = 1L;
public NoSuchChangeException(Change.Id key) {
diff --git a/java/com/google/gerrit/server/project/ProjectCache.java b/java/com/google/gerrit/server/project/ProjectCache.java
index c7858dde64..17250fa2de 100644
--- a/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/java/com/google/gerrit/server/project/ProjectCache.java
@@ -63,29 +63,27 @@ public interface ProjectCache {
* Invalidate the cached information about the given project, and triggers reindexing for it
*
* @param p project that is being evicted
- * @throws IOException thrown if the reindexing fails
*/
- void evict(Project p) throws IOException;
+ void evict(Project p);
/**
* Invalidate the cached information about the given project, and triggers reindexing for it
*
* @param p the NameKey of the project that is being evicted
- * @throws IOException thrown if the reindexing fails
*/
- void evict(Project.NameKey p) throws IOException;
+ void evict(Project.NameKey p);
/**
* Remove information about the given project from the cache. It will no longer be returned from
* {@link #all()}.
*/
- void remove(Project p) throws IOException;
+ void remove(Project p);
/**
* Remove information about the given project from the cache. It will no longer be returned from
* {@link #all()}.
*/
- void remove(Project.NameKey name) throws IOException;
+ void remove(Project.NameKey name);
/** @return sorted iteration of projects. */
ImmutableSortedSet<Project.NameKey> all();
diff --git a/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index d2ca7a48cc..bd26a8fa56 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -26,6 +26,10 @@ import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
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;
@@ -90,6 +94,7 @@ public class ProjectCacheImpl implements ProjectCache {
private final Lock listLock;
private final ProjectCacheClock clock;
private final Provider<ProjectIndexer> indexer;
+ private final Timer0 guessRelevantGroupsLatency;
@Inject
ProjectCacheImpl(
@@ -98,7 +103,8 @@ public class ProjectCacheImpl implements ProjectCache {
@Named(CACHE_NAME) LoadingCache<String, ProjectState> byName,
@Named(CACHE_LIST) LoadingCache<ListKey, ImmutableSortedSet<Project.NameKey>> list,
ProjectCacheClock clock,
- Provider<ProjectIndexer> indexer) {
+ Provider<ProjectIndexer> indexer,
+ MetricMaker metricMaker) {
this.allProjectsName = allProjectsName;
this.allUsersName = allUsersName;
this.byName = byName;
@@ -106,6 +112,13 @@ public class ProjectCacheImpl implements ProjectCache {
this.listLock = new ReentrantLock(true /* fair */);
this.clock = clock;
this.indexer = indexer;
+
+ this.guessRelevantGroupsLatency =
+ metricMaker.newTimer(
+ "group/guess_relevant_groups_latency",
+ new Description("Latency for guessing relevant groups")
+ .setCumulative()
+ .setUnit(Units.NANOSECONDS));
}
@Override
@@ -174,12 +187,12 @@ public class ProjectCacheImpl implements ProjectCache {
}
@Override
- public void evict(Project p) throws IOException {
+ public void evict(Project p) {
evict(p.getNameKey());
}
@Override
- public void evict(Project.NameKey p) throws IOException {
+ public void evict(Project.NameKey p) {
if (p != null) {
logger.atFine().log("Evict project '%s'", p.get());
byName.invalidate(p.get());
@@ -188,12 +201,12 @@ public class ProjectCacheImpl implements ProjectCache {
}
@Override
- public void remove(Project p) throws IOException {
+ public void remove(Project p) {
remove(p.getNameKey());
}
@Override
- public void remove(Project.NameKey name) throws IOException {
+ public void remove(Project.NameKey name) {
listLock.lock();
try {
list.put(
@@ -235,14 +248,16 @@ public class ProjectCacheImpl implements ProjectCache {
@Override
public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
- return all().stream()
- .map(n -> byName.getIfPresent(n.get()))
- .filter(Objects::nonNull)
- .flatMap(p -> p.getConfig().getAllGroupUUIDs().stream())
- // getAllGroupUUIDs shouldn't really return null UUIDs, but harden
- // against them just in case there is a bug or corner case.
- .filter(id -> id != null && id.get() != null)
- .collect(toSet());
+ try (Timer0.Context ignored = guessRelevantGroupsLatency.start()) {
+ return all().stream()
+ .map(n -> byName.getIfPresent(n.get()))
+ .filter(Objects::nonNull)
+ .flatMap(p -> p.getConfig().getAllGroupUUIDs().stream())
+ // getAllGroupUUIDs shouldn't really return null UUIDs, but harden
+ // against them just in case there is a bug or corner case.
+ .filter(id -> id != null && id.get() != null)
+ .collect(toSet());
+ }
}
@Override
@@ -262,12 +277,18 @@ public class ProjectCacheImpl implements ProjectCache {
private final ProjectState.Factory projectStateFactory;
private final GitRepositoryManager mgr;
private final ProjectCacheClock clock;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
- Loader(ProjectState.Factory psf, GitRepositoryManager g, ProjectCacheClock clock) {
+ Loader(
+ ProjectState.Factory psf,
+ GitRepositoryManager g,
+ ProjectCacheClock clock,
+ ProjectConfig.Factory projectConfigFactory) {
projectStateFactory = psf;
mgr = g;
this.clock = clock;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
@@ -276,7 +297,7 @@ public class ProjectCacheImpl implements ProjectCache {
long now = clock.read();
Project.NameKey key = new Project.NameKey(projectName);
try (Repository git = mgr.openRepository(key)) {
- ProjectConfig cfg = new ProjectConfig(key);
+ ProjectConfig cfg = projectConfigFactory.create(key);
cfg.load(key, git);
ProjectState state = projectStateFactory.create(cfg);
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 9ac1e54363..09279fabb5 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -18,6 +18,7 @@ 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.server.permissions.PluginPermissionsUtil.isValidPluginPermission;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
@@ -28,6 +29,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.primitives.Shorts;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.GlobalCapability;
@@ -39,9 +41,8 @@ import com.google.gerrit.common.data.LabelValue;
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.RefConfigSection;
import com.google.gerrit.common.data.SubscribeSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+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;
@@ -50,16 +51,19 @@ 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.UsedAt;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
+import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.BranchOrderSection;
import com.google.gerrit.server.git.NotifyConfig;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -72,7 +76,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -83,8 +86,11 @@ import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.util.FS;
public class ProjectConfig extends VersionedMetaData implements ValidationError.Sink {
public static final String COMMENTLINK = "commentlink";
@@ -128,6 +134,8 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final String KEY_ACCEPTED = "accepted";
private static final String KEY_AUTO_VERIFY = "autoVerify";
private static final String KEY_AGREEMENT_URL = "agreementUrl";
+ private static final String KEY_MATCH_PROJECTS = "matchProjects";
+ private static final String KEY_EXCLUDE_PROJECTS = "excludeProjects";
private static final String NOTIFY = "notify";
private static final String KEY_EMAIL = "email";
@@ -166,6 +174,59 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final Pattern EXCLUSIVE_PERMISSIONS_SPLIT_PATTERN = Pattern.compile("[, \t]{1,}");
+ // Don't use an assisted factory, since instances created by an assisted factory retain references
+ // to their enclosing injector. Instances of ProjectConfig are cached for a long time in the
+ // ProjectCache, so this would retain lots more memory.
+ @Singleton
+ public static class Factory {
+ @Nullable
+ public static StoredConfig getBaseConfig(
+ SitePaths sitePaths, AllProjectsName allProjects, Project.NameKey projectName) {
+ return projectName.equals(allProjects)
+ // Delay loading till onLoad method.
+ ? new FileBasedConfig(
+ sitePaths.etc_dir.resolve(allProjects.get()).resolve(PROJECT_CONFIG).toFile(),
+ FS.DETECTED)
+ : null;
+ }
+
+ private final SitePaths sitePaths;
+ private final AllProjectsName allProjects;
+
+ @Inject
+ Factory(SitePaths sitePaths, AllProjectsName allProjects) {
+ this.sitePaths = sitePaths;
+ this.allProjects = allProjects;
+ }
+
+ public ProjectConfig create(Project.NameKey projectName) {
+ return new ProjectConfig(projectName, getBaseConfig(sitePaths, allProjects, projectName));
+ }
+
+ public ProjectConfig read(MetaDataUpdate update) throws IOException, ConfigInvalidException {
+ ProjectConfig r = create(update.getProjectName());
+ r.load(update);
+ return r;
+ }
+
+ public ProjectConfig read(MetaDataUpdate update, ObjectId id)
+ throws IOException, ConfigInvalidException {
+ ProjectConfig r = create(update.getProjectName());
+ r.load(update, id);
+ return r;
+ }
+
+ @UsedAt(UsedAt.Project.COLLABNET)
+ public ProjectConfig read(Repository repo, Project.NameKey name)
+ throws IOException, ConfigInvalidException {
+ ProjectConfig r = create(name);
+ r.load(repo);
+ return r;
+ }
+ }
+
+ private final StoredConfig baseConfig;
+
private Project project;
private AccountsSection accountsSection;
private GroupList groupList;
@@ -176,7 +237,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private Map<String, LabelType> labelSections;
private ConfiguredMimeTypes mimeTypes;
private Map<Project.NameKey, SubscribeSection> subscribeSections;
- private List<CommentLinkInfoImpl> commentLinkSections;
+ private Map<String, CommentLinkInfoImpl> commentLinkSections;
private List<ValidationError> validationErrors;
private ObjectId rulesId;
private long maxObjectSizeLimit;
@@ -187,28 +248,6 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private Map<String, List<String>> extensionPanelSections;
private Map<String, GroupReference> groupsByName;
- public static ProjectConfig read(MetaDataUpdate update)
- throws IOException, ConfigInvalidException {
- ProjectConfig r = new ProjectConfig(update.getProjectName());
- r.load(update);
- return r;
- }
-
- public static ProjectConfig read(MetaDataUpdate update, ObjectId id)
- throws IOException, ConfigInvalidException {
- ProjectConfig r = new ProjectConfig(update.getProjectName());
- r.load(update, id);
- return r;
- }
-
- @UsedAt(UsedAt.Project.COLLABNET)
- public static ProjectConfig read(Repository repo, Project.NameKey name)
- throws IOException, ConfigInvalidException {
- ProjectConfig r = new ProjectConfig(name);
- r.load(repo);
- return r;
- }
-
public static CommentLinkInfoImpl buildCommentLink(Config cfg, String name, boolean allowRaw)
throws IllegalArgumentException {
String match = cfg.getString(COMMENTLINK, name, KEY_MATCH);
@@ -247,11 +286,12 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
public void addCommentLinkSection(CommentLinkInfoImpl commentLink) {
- commentLinkSections.add(commentLink);
+ commentLinkSections.put(commentLink.name, commentLink);
}
- public ProjectConfig(Project.NameKey projectName) {
+ private ProjectConfig(Project.NameKey projectName, @Nullable StoredConfig baseConfig) {
this.projectName = projectName;
+ this.baseConfig = baseConfig;
}
public void load(Repository repo) throws IOException, ConfigInvalidException {
@@ -328,7 +368,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
String name = section.getName();
if (sectionsWithUnknownPermissions.contains(name)) {
AccessSection a = accessSections.get(name);
- a.setPermissions(new ArrayList<Permission>());
+ a.setPermissions(new ArrayList<>());
} else {
accessSections.remove(name);
}
@@ -424,7 +464,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
public Collection<CommentLinkInfoImpl> getCommentLinkSections() {
- return commentLinkSections;
+ return commentLinkSections.values();
}
public ConfiguredMimeTypes getMimeTypes() {
@@ -513,11 +553,14 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
+ if (baseConfig != null) {
+ baseConfig.load();
+ }
readGroupList();
groupsByName = mapGroupReferences();
rulesId = getObjectId("rules.pl");
- Config rc = readConfig(PROJECT_CONFIG);
+ Config rc = readConfig(PROJECT_CONFIG, baseConfig);
project = new Project(projectName);
Project p = project;
@@ -602,6 +645,9 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
ca.setAgreementUrl(rc.getString(CONTRIBUTOR_AGREEMENT, name, KEY_AGREEMENT_URL));
ca.setAccepted(
loadPermissionRules(rc, CONTRIBUTOR_AGREEMENT, name, KEY_ACCEPTED, groupsByName, false));
+ ca.setExcludeProjectsRegexes(
+ loadPatterns(rc, CONTRIBUTOR_AGREEMENT, name, KEY_EXCLUDE_PROJECTS));
+ ca.setMatchProjectsRegexes(loadPatterns(rc, CONTRIBUTOR_AGREEMENT, name, KEY_MATCH_PROJECTS));
List<PermissionRule> rules =
loadPermissionRules(
@@ -703,13 +749,13 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
accessSections = new HashMap<>();
sectionsWithUnknownPermissions = new HashSet<>();
for (String refName : rc.getSubsections(ACCESS)) {
- if (RefConfigSection.isValid(refName) && isValidRegex(refName)) {
+ if (AccessSection.isValidRefSectionName(refName) && isValidRegex(refName)) {
AccessSection as = getAccessSection(refName, true);
for (String varName : rc.getStringList(ACCESS, refName, KEY_GROUP_PERMISSIONS)) {
for (String n : Splitter.on(EXCLUSIVE_PERMISSIONS_SPLIT_PATTERN).split(varName)) {
n = convertLegacyPermission(n);
- if (isPermission(n)) {
+ if (isCoreOrPluginPermission(n)) {
as.getPermission(n, true).setExclusiveGroup(true);
}
}
@@ -717,7 +763,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
for (String varName : rc.getNames(ACCESS, refName)) {
String convertedName = convertLegacyPermission(varName);
- if (isPermission(convertedName)) {
+ if (isCoreOrPluginPermission(convertedName)) {
Permission perm = as.getPermission(convertedName, true);
loadPermissionRules(
rc,
@@ -746,6 +792,12 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
}
+ private boolean isCoreOrPluginPermission(String permission) {
+ // Since plugins are loaded dynamically, here we can't load all plugin permissions and verify
+ // their existence.
+ return isPermission(permission) || isValidPluginPermission(permission);
+ }
+
private boolean isValidRegex(String refPattern) {
try {
RefPattern.validateRegExp(refPattern);
@@ -762,6 +814,22 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
}
+ private ImmutableList<String> loadPatterns(
+ Config rc, String section, String subsection, String varName) {
+ ImmutableList.Builder<String> patterns = ImmutableList.builder();
+ for (String patternString : rc.getStringList(section, subsection, varName)) {
+ try {
+ // While one could just use getStringList directly, compiling first will cause the server
+ // to fail fast if any of the patterns are invalid.
+ patterns.add(Pattern.compile(patternString).pattern());
+ } catch (PatternSyntaxException e) {
+ error(new ValidationError(PROJECT_CONFIG, "Invalid regular expression: " + e.getMessage()));
+ continue;
+ }
+ }
+ return patterns.build();
+ }
+
private ImmutableList<PermissionRule> loadPermissionRules(
Config rc,
String section,
@@ -843,9 +911,18 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
lowerNames.put(lower, name);
List<LabelValue> values = new ArrayList<>();
+ Set<Short> allValues = new HashSet<>();
for (String value : rc.getStringList(LABEL, name, KEY_VALUE)) {
try {
- values.add(parseLabelValue(value));
+ LabelValue labelValue = parseLabelValue(value);
+ if (allValues.add(labelValue.getValue())) {
+ values.add(labelValue);
+ } else {
+ error(
+ new ValidationError(
+ PROJECT_CONFIG,
+ String.format("Duplicate %s \"%s\" for label \"%s\"", KEY_VALUE, value, name)));
+ }
} catch (IllegalArgumentException notValue) {
error(
new ValidationError(
@@ -947,10 +1024,10 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private void loadCommentLinkSections(Config rc) {
Set<String> subsections = rc.getSubsections(COMMENTLINK);
- commentLinkSections = new ArrayList<>(subsections.size());
+ commentLinkSections = new LinkedHashMap<>(subsections.size());
for (String name : subsections) {
try {
- commentLinkSections.add(buildCommentLink(rc, name, false));
+ commentLinkSections.put(name, buildCommentLink(rc, name, false));
} catch (PatternSyntaxException e) {
error(
new ValidationError(
@@ -1137,7 +1214,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private void saveCommentLinkSections(Config rc) {
if (commentLinkSections != null) {
- for (CommentLinkInfoImpl cm : commentLinkSections) {
+ for (CommentLinkInfoImpl cm : commentLinkSections.values()) {
rc.setString(COMMENTLINK, cm.name, KEY_MATCH, cm.match);
if (!Strings.isNullOrEmpty(cm.html)) {
rc.setString(COMMENTLINK, cm.name, KEY_HTML, cm.html);
@@ -1172,13 +1249,23 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
ca.getName(),
KEY_ACCEPTED,
ruleToStringList(ca.getAccepted(), keepGroups));
+ rc.setStringList(
+ CONTRIBUTOR_AGREEMENT,
+ ca.getName(),
+ KEY_EXCLUDE_PROJECTS,
+ patternToStringList(ca.getExcludeProjectsRegexes()));
+ rc.setStringList(
+ CONTRIBUTOR_AGREEMENT,
+ ca.getName(),
+ KEY_MATCH_PROJECTS,
+ patternToStringList(ca.getMatchProjectsRegexes()));
}
}
private void saveNotifySections(Config rc, Set<AccountGroup.UUID> keepGroups) {
for (NotifyConfig nc : sort(notifySections.values())) {
nc.getGroups().stream()
- .map(gr -> gr.getUUID())
+ .map(GroupReference::getUUID)
.filter(Objects::nonNull)
.forEach(keepGroups::add);
List<String> email =
@@ -1213,6 +1300,10 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
}
+ private List<String> patternToStringList(List<String> list) {
+ return list;
+ }
+
private List<String> ruleToStringList(
List<PermissionRule> list, Set<AccountGroup.UUID> keepGroups) {
List<String> rules = new ArrayList<>();
@@ -1290,7 +1381,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
for (String varName : rc.getNames(ACCESS, refName)) {
- if (isPermission(convertLegacyPermission(varName))
+ if (isCoreOrPluginPermission(convertLegacyPermission(varName))
&& !have.contains(varName.toLowerCase())) {
rc.unset(ACCESS, refName, varName);
}
@@ -1298,7 +1389,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
for (String name : rc.getSubsections(ACCESS)) {
- if (RefConfigSection.isValid(name) && !accessSections.containsKey(name)) {
+ if (AccessSection.isValidRefSectionName(name) && !accessSections.containsKey(name)) {
rc.unsetSection(ACCESS, name);
}
}
@@ -1411,7 +1502,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
rc.unsetSection(PLUGIN, name);
}
- for (Entry<String, Config> e : pluginConfigs.entrySet()) {
+ for (Map.Entry<String, Config> e : pluginConfigs.entrySet()) {
String plugin = e.getKey();
Config pluginConfig = e.getValue();
for (String name : pluginConfig.getNames(PLUGIN, plugin)) {
@@ -1473,6 +1564,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
return m.stream().sorted().collect(toImmutableList());
}
+ @UsedAt(UsedAt.Project.GOOGLE)
public boolean hasLegacyPermissions() {
return hasLegacyPermissions;
}
diff --git a/java/com/google/gerrit/server/project/ProjectCreator.java b/java/com/google/gerrit/server/project/ProjectCreator.java
new file mode 100644
index 0000000000..35ecd7cff8
--- /dev/null
+++ b/java/com/google/gerrit/server/project/ProjectCreator.java
@@ -0,0 +1,256 @@
+// 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.project;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.data.AccessSection;
+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.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;
+import com.google.gerrit.server.config.RepositoryConfig;
+import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.RepositoryCaseMismatchException;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.util.List;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+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.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+public class ProjectCreator {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final GitRepositoryManager repoManager;
+ private final PluginSetContext<NewProjectCreatedListener> createdListeners;
+ private final ProjectCache projectCache;
+ private final GroupBackend groupBackend;
+ private final MetaDataUpdate.User metaDataUpdateFactory;
+ private final GitReferenceUpdated referenceUpdated;
+ private final RepositoryConfig repositoryCfg;
+ private final Provider<PersonIdent> serverIdent;
+ private final Provider<IdentifiedUser> identifiedUser;
+ private final ProjectConfig.Factory projectConfigFactory;
+
+ @Inject
+ ProjectCreator(
+ GitRepositoryManager repoManager,
+ PluginSetContext<NewProjectCreatedListener> createdListeners,
+ ProjectCache projectCache,
+ GroupBackend groupBackend,
+ MetaDataUpdate.User metaDataUpdateFactory,
+ GitReferenceUpdated referenceUpdated,
+ RepositoryConfig repositoryCfg,
+ @GerritPersonIdent Provider<PersonIdent> serverIdent,
+ Provider<IdentifiedUser> identifiedUser,
+ ProjectConfig.Factory projectConfigFactory) {
+ this.repoManager = repoManager;
+ this.createdListeners = createdListeners;
+ this.projectCache = projectCache;
+ this.groupBackend = groupBackend;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.referenceUpdated = referenceUpdated;
+ this.repositoryCfg = repositoryCfg;
+ this.serverIdent = serverIdent;
+ this.identifiedUser = identifiedUser;
+ this.projectConfigFactory = projectConfigFactory;
+ }
+
+ public ProjectState createProject(CreateProjectArgs args)
+ throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
+ final Project.NameKey nameKey = args.getProject();
+ try {
+ final String head = args.permissionsOnly ? RefNames.REFS_CONFIG : args.branch.get(0);
+ try (Repository repo = repoManager.openRepository(nameKey)) {
+ if (repo.getObjectDatabase().exists()) {
+ throw new ResourceConflictException("project \"" + nameKey + "\" exists");
+ }
+ } catch (RepositoryNotFoundException e) {
+ // It does not exist, safe to ignore.
+ }
+ try (Repository repo = repoManager.createRepository(nameKey)) {
+ RefUpdate u = repo.updateRef(Constants.HEAD);
+ u.disableRefLog();
+ u.link(head);
+
+ createProjectConfig(args);
+
+ if (!args.permissionsOnly && args.createEmptyCommit) {
+ createEmptyCommits(repo, nameKey, args.branch);
+ }
+
+ fire(nameKey, head);
+
+ return projectCache.get(nameKey);
+ }
+ } catch (RepositoryCaseMismatchException e) {
+ throw new ResourceConflictException(
+ "Cannot create "
+ + nameKey.get()
+ + " because the name is already occupied by another project."
+ + " The other project has the same name, only spelled in a"
+ + " different case.",
+ e);
+ } catch (RepositoryNotFoundException badName) {
+ throw new BadRequestException("invalid project name: " + nameKey, badName);
+ } catch (ConfigInvalidException e) {
+ String msg = "Cannot create " + nameKey;
+ logger.atSevere().withCause(e).log(msg);
+ throw e;
+ }
+ }
+
+ private void createProjectConfig(CreateProjectArgs args)
+ throws IOException, ConfigInvalidException {
+ try (MetaDataUpdate md = metaDataUpdateFactory.create(args.getProject())) {
+ ProjectConfig config = projectConfigFactory.read(md);
+
+ Project newProject = config.getProject();
+ newProject.setDescription(args.projectDescription);
+ newProject.setSubmitType(
+ MoreObjects.firstNonNull(
+ args.submitType, repositoryCfg.getDefaultSubmitType(args.getProject())));
+ newProject.setBooleanConfig(
+ BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS, args.contributorAgreements);
+ newProject.setBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY, args.signedOffBy);
+ newProject.setBooleanConfig(BooleanProjectConfig.USE_CONTENT_MERGE, args.contentMerge);
+ newProject.setBooleanConfig(
+ BooleanProjectConfig.CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET,
+ args.newChangeForAllNotInTarget);
+ newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, args.changeIdRequired);
+ newProject.setBooleanConfig(BooleanProjectConfig.REJECT_EMPTY_COMMIT, args.rejectEmptyCommit);
+ newProject.setMaxObjectSizeLimit(args.maxObjectSizeLimit);
+ newProject.setBooleanConfig(BooleanProjectConfig.ENABLE_SIGNED_PUSH, args.enableSignedPush);
+ newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_SIGNED_PUSH, args.requireSignedPush);
+ if (args.newParent != null) {
+ newProject.setParentName(args.newParent);
+ }
+
+ if (!args.ownerIds.isEmpty()) {
+ AccessSection all = config.getAccessSection(AccessSection.ALL, true);
+ for (AccountGroup.UUID ownerId : args.ownerIds) {
+ GroupDescription.Basic g = groupBackend.get(ownerId);
+ if (g != null) {
+ GroupReference group = config.resolve(GroupReference.forGroup(g));
+ all.getPermission(Permission.OWNER, true).add(new PermissionRule(group));
+ }
+ }
+ }
+
+ md.setMessage("Created project\n");
+ config.commit(md);
+ md.getRepository().setGitwebDescription(args.projectDescription);
+ }
+ projectCache.onCreateProject(args.getProject());
+ }
+
+ private void createEmptyCommits(Repository repo, Project.NameKey project, List<String> refs)
+ throws IOException {
+ try (ObjectInserter oi = repo.newObjectInserter()) {
+ CommitBuilder cb = new CommitBuilder();
+ cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {}));
+ cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent());
+ cb.setCommitter(serverIdent.get());
+ cb.setMessage("Initial empty repository\n");
+
+ ObjectId id = oi.insert(cb);
+ oi.flush();
+
+ for (String ref : refs) {
+ RefUpdate ru = repo.updateRef(ref);
+ ru.setNewObjectId(id);
+ Result result = ru.update();
+ switch (result) {
+ case NEW:
+ referenceUpdated.fire(
+ project, ru, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
+ break;
+ case FAST_FORWARD:
+ case FORCED:
+ case IO_FAILURE:
+ case LOCK_FAILURE:
+ case NOT_ATTEMPTED:
+ case NO_CHANGE:
+ case REJECTED:
+ case REJECTED_CURRENT_BRANCH:
+ case RENAMED:
+ case REJECTED_MISSING_OBJECT:
+ case REJECTED_OTHER_REASON:
+ default:
+ {
+ throw new IOException(
+ String.format("Failed to create ref \"%s\": %s", ref, result.name()));
+ }
+ }
+ }
+ } catch (IOException e) {
+ logger.atSevere().withCause(e).log("Cannot create empty commit for %s", project.get());
+ throw e;
+ }
+ }
+
+ private void fire(Project.NameKey name, String head) {
+ if (createdListeners.isEmpty()) {
+ return;
+ }
+
+ ProjectCreator.Event event = new ProjectCreator.Event(name, head);
+ createdListeners.runEach(l -> l.onNewProjectCreated(event));
+ }
+
+ static class Event extends AbstractNoNotifyEvent implements NewProjectCreatedListener.Event {
+ private final Project.NameKey name;
+ private final String head;
+
+ Event(Project.NameKey name, String head) {
+ this.name = name;
+ this.head = head;
+ }
+
+ @Override
+ public String getProjectName() {
+ return name.get();
+ }
+
+ @Override
+ public String getHeadName() {
+ return head;
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 0589ac1432..0e3a94027d 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -15,12 +15,10 @@
package com.google.gerrit.server.project;
import static com.google.gerrit.common.data.PermissionRule.Action.ALLOW;
-import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.AccessSection;
@@ -29,10 +27,8 @@ import com.google.gerrit.common.data.LabelType;
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.RefConfigSection;
import com.google.gerrit.common.data.SubscribeSection;
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
-import com.google.gerrit.extensions.api.projects.ThemeInfo;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.index.project.ProjectData;
@@ -49,7 +45,6 @@ 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.config.SitePaths;
import com.google.gerrit.server.git.BranchOrderSection;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.TransferConfig;
@@ -57,14 +52,11 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -88,7 +80,6 @@ public class ProjectState {
private final boolean isAllProjects;
private final boolean isAllUsers;
- private final SitePaths sitePaths;
private final AllProjectsName allProjectsName;
private final ProjectCache projectCache;
private final GitRepositoryManager gitMgr;
@@ -109,17 +100,11 @@ public class ProjectState {
/** Local access sections, wrapped in SectionMatchers for faster evaluation. */
private volatile List<SectionMatcher> localAccessSections;
- // TODO(dborowitz): Delete when the GWT UI gets deleted; in the meantime, don't bother with any
- // refactoring.
- /** Theme information loaded from site_path/themes. */
- private volatile ThemeInfo theme;
-
/** If this is all projects, the capabilities used by the server. */
private final CapabilityCollection capabilities;
@Inject
public ProjectState(
- SitePaths sitePaths,
ProjectCache projectCache,
AllProjectsName allProjectsName,
AllUsersName allUsersName,
@@ -129,7 +114,6 @@ public class ProjectState {
TransferConfig transferConfig,
MetricMaker metricMaker,
@Assisted ProjectConfig config) {
- this.sitePaths = sitePaths;
this.projectCache = projectCache;
this.isAllProjects = config.getProject().getNameKey().equals(allProjectsName);
this.isAllUsers = config.getProject().getNameKey().equals(allUsersName);
@@ -421,12 +405,7 @@ public class ProjectState {
* Starts from this project and progresses up the hierarchy to All-Projects.
*/
public Iterable<ProjectState> tree() {
- return new Iterable<ProjectState>() {
- @Override
- public Iterator<ProjectState> iterator() {
- return new ProjectHierarchyIterator(projectCache, allProjectsName, ProjectState.this);
- }
- };
+ return () -> new ProjectHierarchyIterator(projectCache, allProjectsName, ProjectState.this);
}
/**
@@ -515,7 +494,7 @@ public class ProjectState {
continue;
}
- if (RefConfigSection.isValid(refPattern) && match(destination, refPattern)) {
+ if (AccessSection.isValidRefSectionName(refPattern) && match(destination, refPattern)) {
r.add(l);
break;
}
@@ -566,24 +545,6 @@ public class ProjectState {
return ret;
}
- public ThemeInfo getTheme() {
- ThemeInfo theme = this.theme;
- if (theme == null) {
- synchronized (this) {
- theme = this.theme;
- if (theme == null) {
- theme = loadTheme();
- this.theme = theme;
- }
- }
- }
- if (theme == ThemeInfo.INHERIT) {
- ProjectState parent = Iterables.getFirst(parents(), null);
- return parent != null ? parent.getTheme() : null;
- }
- return theme;
- }
-
public Set<GroupReference> getAllGroups() {
return getGroups(getAllSections());
}
@@ -615,26 +576,6 @@ public class ProjectState {
return all;
}
- private ThemeInfo loadTheme() {
- String name = getConfig().getProject().getName();
- Path dir = sitePaths.themes_dir.resolve(name);
- if (!Files.exists(dir)) {
- return ThemeInfo.INHERIT;
- } else if (!Files.isDirectory(dir)) {
- logger.atWarning().log("Bad theme for %s: not a directory", name);
- return ThemeInfo.INHERIT;
- }
- try {
- return new ThemeInfo(
- readFile(dir.resolve(SitePaths.CSS_FILENAME)),
- readFile(dir.resolve(SitePaths.HEADER_FILENAME)),
- readFile(dir.resolve(SitePaths.FOOTER_FILENAME)));
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Error reading theme for %s", name);
- return ThemeInfo.INHERIT;
- }
- }
-
public ProjectData toProjectData() {
ProjectData project = null;
for (ProjectState state : treeInOrder()) {
@@ -643,10 +584,6 @@ public class ProjectState {
return project;
}
- private String readFile(Path p) throws IOException {
- return Files.exists(p) ? new String(Files.readAllBytes(p), UTF_8) : null;
- }
-
private boolean match(Branch.NameKey destination, String refPattern) {
return RefPatternMatcher.getMatcher(refPattern).match(destination.get(), null);
}
diff --git a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
index 9e967401f5..17fd69c4f8 100644
--- a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
@@ -24,6 +24,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.FixInput;
import com.google.gerrit.extensions.api.projects.CheckProjectInput;
import com.google.gerrit.extensions.api.projects.CheckProjectInput.AutoCloseableChangesCheckInput;
@@ -51,7 +52,6 @@ import com.google.gerrit.server.query.change.ProjectPredicate;
import com.google.gerrit.server.query.change.RefPredicate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryHelper.ActionType;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -94,7 +94,7 @@ public class ProjectsConsistencyChecker {
}
public CheckProjectResultInfo check(Project.NameKey projectName, CheckProjectInput input)
- throws IOException, OrmException, RestApiException {
+ throws IOException, RestApiException {
CheckProjectResultInfo r = new CheckProjectResultInfo();
if (input.autoCloseableChangesCheck != null) {
r.autoCloseableChangesCheckResult =
@@ -105,7 +105,7 @@ public class ProjectsConsistencyChecker {
private AutoCloseableChangesCheckResult checkForAutoCloseableChanges(
Project.NameKey projectName, AutoCloseableChangesCheckInput input)
- throws IOException, OrmException, RestApiException {
+ throws IOException, RestApiException {
AutoCloseableChangesCheckResult r = new AutoCloseableChangesCheckResult();
if (Strings.isNullOrEmpty(input.branch)) {
throw new BadRequestException("branch is required");
@@ -256,8 +256,7 @@ public class ProjectsConsistencyChecker {
List<Predicate<ChangeData>> predicates,
boolean fix,
Map<Change.Key, ObjectId> changeIdToMergedSha1,
- List<ObjectId> mergedSha1s)
- throws OrmException {
+ List<ObjectId> mergedSha1s) {
if (predicates.isEmpty()) {
return ImmutableList.of();
}
@@ -272,7 +271,7 @@ public class ProjectsConsistencyChecker {
.get()
.setRequestedFields(ChangeField.CHANGE, ChangeField.PATCH_SET)
.query(and(basePredicate, or(predicates))),
- OrmException.class::isInstance);
+ StorageException.class::isInstance);
// Result for this query that we want to return to the client.
List<ChangeInfo> autoCloseableChangesByBranch = new ArrayList<>();
@@ -306,15 +305,14 @@ public class ProjectsConsistencyChecker {
}
return null;
},
- OrmException.class::isInstance);
+ StorageException.class::isInstance);
}
}
return autoCloseableChangesByBranch;
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
- Throwables.throwIfInstanceOf(e, OrmException.class);
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/project/Reachable.java b/java/com/google/gerrit/server/project/Reachable.java
index f85aa62896..93cbf4b1f7 100644
--- a/java/com/google/gerrit/server/project/Reachable.java
+++ b/java/com/google/gerrit/server/project/Reachable.java
@@ -14,8 +14,6 @@
package com.google.gerrit.server.project;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
@@ -27,12 +25,10 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -52,17 +48,21 @@ public class Reachable {
this.permissionBackend = permissionBackend;
}
- /** @return true if a commit is reachable from a given set of refs. */
+ /**
+ * @return true if a commit is reachable from a given set of refs. This method enforces
+ * permissions on the given set of refs and performs a reachability check. Tags are not
+ * filtered separately and will only be returned if reachable by a provided ref.
+ */
public boolean fromRefs(
- Project.NameKey project, Repository repo, RevCommit commit, Map<String, Ref> refs) {
+ Project.NameKey project, Repository repo, RevCommit commit, List<Ref> refs) {
return fromRefs(project, repo, commit, refs, Optional.empty());
}
- private boolean fromRefs(
+ public boolean fromRefs(
Project.NameKey project,
Repository repo,
RevCommit commit,
- Map<String, Ref> refs,
+ List<Ref> refs,
Optional<Provider<? extends CurrentUser>> userProvider) {
try (RevWalk rw = new RevWalk(repo)) {
Map<String, Ref> filtered =
@@ -70,7 +70,7 @@ public class Reachable {
.map(up -> permissionBackend.user(up.get()))
.orElse(permissionBackend.currentUser())
.project(project)
- .filter(refs, repo, RefFilterOptions.builder().setFilterTagsSeparately(true).build());
+ .filter(refs, repo, RefFilterOptions.defaults());
return IncludedInResolver.includedInAny(repo, rw, commit, filtered.values());
} catch (IOException | PermissionBackendException e) {
logger.atSevere().withCause(e).log(
@@ -78,26 +78,4 @@ public class Reachable {
return false;
}
}
-
- /** @return true if a commit is reachable from a repo's branches and tags. */
- boolean fromHeadsOrTags(
- Project.NameKey project,
- Repository repo,
- RevCommit commit,
- Provider<? extends CurrentUser> userProvider) {
- try {
- RefDatabase refdb = repo.getRefDatabase();
- Collection<Ref> heads = refdb.getRefsByPrefix(Constants.R_HEADS);
- Collection<Ref> tags = refdb.getRefsByPrefix(Constants.R_TAGS);
- Map<String, Ref> refs = Maps.newHashMapWithExpectedSize(heads.size() + tags.size());
- for (Ref r : Iterables.concat(heads, tags)) {
- refs.put(r.getName(), r);
- }
- return fromRefs(project, repo, commit, refs, Optional.of(userProvider));
- } catch (IOException e) {
- logger.atSevere().withCause(e).log(
- "Cannot verify permissions to commit object %s in repository %s", commit.name(), project);
- return false;
- }
- }
}
diff --git a/java/com/google/gerrit/server/project/RefPattern.java b/java/com/google/gerrit/server/project/RefPattern.java
index b69a9c0e42..c52914b667 100644
--- a/java/com/google/gerrit/server/project/RefPattern.java
+++ b/java/com/google/gerrit/server/project/RefPattern.java
@@ -21,8 +21,7 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ParameterizedString;
-import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
import dk.brics.automaton.RegExp;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -91,11 +90,11 @@ public class RefPattern {
}
public static void validate(String refPattern) throws InvalidNameException {
- if (refPattern.startsWith(RefConfigSection.REGEX_PREFIX)) {
+ if (refPattern.startsWith(AccessSection.REGEX_PREFIX)) {
if (!Repository.isValidRefName(shortestExample(refPattern))) {
throw new InvalidNameException(refPattern);
}
- } else if (refPattern.equals(RefConfigSection.ALL)) {
+ } else if (refPattern.equals(AccessSection.ALL)) {
// This is a special case we have to allow, it fails below.
} else if (refPattern.endsWith("/*")) {
String prefix = refPattern.substring(0, refPattern.length() - 2);
diff --git a/java/com/google/gerrit/server/project/RemoveReviewerControl.java b/java/com/google/gerrit/server/project/RemoveReviewerControl.java
index df98f5ec81..eeb2a65419 100644
--- a/java/com/google/gerrit/server/project/RemoveReviewerControl.java
+++ b/java/com/google/gerrit/server/project/RemoveReviewerControl.java
@@ -18,7 +18,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -27,20 +26,16 @@ 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.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class RemoveReviewerControl {
private final PermissionBackend permissionBackend;
- private final Provider<ReviewDb> dbProvider;
@Inject
- RemoveReviewerControl(PermissionBackend permissionBackend, Provider<ReviewDb> dbProvider) {
+ RemoveReviewerControl(PermissionBackend permissionBackend) {
this.permissionBackend = permissionBackend;
- this.dbProvider = dbProvider;
}
/**
@@ -70,16 +65,12 @@ public class RemoveReviewerControl {
/** @return true if the user is allowed to remove this reviewer. */
public boolean testRemoveReviewer(
ChangeData cd, CurrentUser currentUser, Account.Id reviewer, int value)
- throws PermissionBackendException, OrmException {
+ throws PermissionBackendException {
if (canRemoveReviewerWithoutPermissionCheck(
permissionBackend, cd.change(), currentUser, reviewer, value)) {
return true;
}
- return permissionBackend
- .user(currentUser)
- .change(cd)
- .database(dbProvider)
- .test(ChangePermission.REMOVE_REVIEWER);
+ return permissionBackend.user(currentUser).change(cd).test(ChangePermission.REMOVE_REVIEWER);
}
private void checkRemoveReviewer(
@@ -90,11 +81,7 @@ public class RemoveReviewerControl {
return;
}
- permissionBackend
- .user(currentUser)
- .change(notes)
- .database(dbProvider)
- .check(ChangePermission.REMOVE_REVIEWER);
+ permissionBackend.user(currentUser).change(notes).check(ChangePermission.REMOVE_REVIEWER);
}
private static boolean canRemoveReviewerWithoutPermissionCheck(
@@ -104,7 +91,7 @@ public class RemoveReviewerControl {
Account.Id reviewer,
int value)
throws PermissionBackendException {
- if (change.getStatus().equals(Change.Status.MERGED)) {
+ if (change.isMerged()) {
return false;
}
diff --git a/java/com/google/gerrit/server/project/SectionMatcher.java b/java/com/google/gerrit/server/project/SectionMatcher.java
index 11b1f3709b..a8ebd98d62 100644
--- a/java/com/google/gerrit/server/project/SectionMatcher.java
+++ b/java/com/google/gerrit/server/project/SectionMatcher.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.project;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.CurrentUser;
/**
@@ -28,7 +27,7 @@ import com.google.gerrit.server.CurrentUser;
public class SectionMatcher extends RefPatternMatcher {
static SectionMatcher wrap(Project.NameKey project, AccessSection section) {
String ref = section.getName();
- if (AccessSection.isValid(ref)) {
+ if (AccessSection.isValidRefSectionName(ref)) {
return new SectionMatcher(project, section, getMatcher(ref));
}
return null;
@@ -57,7 +56,7 @@ public class SectionMatcher extends RefPatternMatcher {
return matcher;
}
- public NameKey getProject() {
+ public Project.NameKey getProject() {
return project;
}
}
diff --git a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 3b70eeea86..1b1869ca2e 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -18,13 +18,13 @@ 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.exceptions.StorageException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.OnlineReindexMode;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.PrologRule;
import com.google.gerrit.server.rules.SubmitRule;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Collection;
@@ -92,18 +92,18 @@ public class SubmitRuleEvaluator {
try {
change = cd.change();
if (change == null) {
- throw new OrmException("Change not found");
+ throw new StorageException("Change not found");
}
projectState = projectCache.get(cd.project());
if (projectState == null) {
throw new NoSuchProjectException(cd.project());
}
- } catch (OrmException | NoSuchProjectException e) {
+ } catch (StorageException | NoSuchProjectException e) {
return ruleError("Error looking up change " + cd.getId(), e);
}
- if ((!opts.allowClosed() || OnlineReindexMode.isActive()) && change.getStatus().isClosed()) {
+ if ((!opts.allowClosed() || OnlineReindexMode.isActive()) && change.isClosed()) {
SubmitRecord rec = new SubmitRecord();
rec.status = SubmitRecord.Status.CLOSED;
return Collections.singletonList(rec);
diff --git a/java/com/google/gerrit/server/project/testing/Util.java b/java/com/google/gerrit/server/project/testing/Util.java
index abfd2bd624..204fa7b8f3 100644
--- a/java/com/google/gerrit/server/project/testing/Util.java
+++ b/java/com/google/gerrit/server/project/testing/Util.java
@@ -124,16 +124,28 @@ public class Util {
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)) {
- PermissionRange.WithDefaults range = GlobalCapability.getRange(capabilityName);
- if (range != null) {
- rule.setRange(range.getDefaultMin(), range.getDefaultMax());
+ 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;
}
diff --git a/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
index bd7b7fe3be..f4ff4411d4 100644
--- a/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
@@ -19,7 +19,6 @@ import com.google.gerrit.index.query.IsVisibleToPredicate;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.IndexUtils;
-import com.google.gwtorm.server.OrmException;
public class AccountIsVisibleToPredicate extends IsVisibleToPredicate<AccountState> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -32,7 +31,7 @@ public class AccountIsVisibleToPredicate extends IsVisibleToPredicate<AccountSta
}
@Override
- public boolean match(AccountState accountState) throws OrmException {
+ public boolean match(AccountState accountState) {
boolean canSee = accountControl.canSee(accountState);
if (!canSee) {
logger.atFine().log("Filter out non-visisble account: %s", accountState);
diff --git a/java/com/google/gerrit/server/query/account/AccountPredicates.java b/java/com/google/gerrit/server/query/account/AccountPredicates.java
index 57a0dcc4ce..cb96bc5eaa 100644
--- a/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -27,7 +27,6 @@ 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;
-import com.google.gwtorm.server.OrmException;
import java.util.List;
public class AccountPredicates {
@@ -126,7 +125,7 @@ public class AccountPredicates {
public static Predicate<AccountState> cansee(
AccountQueryBuilder.Arguments args, ChangeNotes changeNotes) {
- return new CanSeeChangePredicate(args.db, args.permissionBackend, changeNotes);
+ return new CanSeeChangePredicate(args.permissionBackend, changeNotes);
}
static class AccountPredicate extends IndexPredicate<AccountState>
@@ -140,7 +139,7 @@ public class AccountPredicates {
}
@Override
- public boolean match(AccountState object) throws OrmException {
+ public boolean match(AccountState object) {
return true;
}
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index feb9ccbe35..38336c1c17 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -1,5 +1,4 @@
// 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
@@ -18,7 +17,8 @@ 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.common.errors.NotSignedInException;
+import com.google.gerrit.exceptions.NotSignedInException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.index.Index;
import com.google.gerrit.index.Schema;
@@ -27,7 +27,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
@@ -39,13 +38,12 @@ import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
/** Parses a query string meant to be applied to account objects. */
-public class AccountQueryBuilder extends QueryBuilder<AccountState> {
+public class AccountQueryBuilder extends QueryBuilder<AccountState, AccountQueryBuilder> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static final String FIELD_ACCOUNT = "account";
@@ -62,7 +60,6 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState> {
new QueryBuilder.Definition<>(AccountQueryBuilder.class);
public static class Arguments {
- final Provider<ReviewDb> db;
final ChangeFinder changeFinder;
final PermissionBackend permissionBackend;
@@ -73,12 +70,10 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState> {
public Arguments(
Provider<CurrentUser> self,
AccountIndexCollection indexes,
- Provider<ReviewDb> db,
ChangeFinder changeFinder,
PermissionBackend permissionBackend) {
this.self = self;
this.indexes = indexes;
- this.db = db;
this.changeFinder = changeFinder;
this.permissionBackend = permissionBackend;
}
@@ -113,24 +108,20 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState> {
@Inject
AccountQueryBuilder(Arguments args) {
- super(mydef);
+ super(mydef, null);
this.args = args;
}
@Operator
public Predicate<AccountState> cansee(String change)
- throws QueryParseException, OrmException, PermissionBackendException {
+ throws QueryParseException, PermissionBackendException {
ChangeNotes changeNotes = args.changeFinder.findOne(change);
if (changeNotes == null) {
throw error(String.format("change %s not found", change));
}
try {
- args.permissionBackend
- .user(args.getUser())
- .database(args.db)
- .change(changeNotes)
- .check(ChangePermission.READ);
+ args.permissionBackend.user(args.getUser()).change(changeNotes).check(ChangePermission.READ);
} catch (AuthException e) {
String msg = String.format("change %s not found", change);
logger.atSevere().withCause(e).log(msg);
@@ -206,7 +197,7 @@ public class AccountQueryBuilder extends QueryBuilder<AccountState> {
if (query.startsWith("cansee:")) {
try {
return cansee(query.substring(7));
- } catch (OrmException | QueryParseException | PermissionBackendException e) {
+ } catch (StorageException | QueryParseException | PermissionBackendException e) {
// Ignore, fall back to default query
}
}
diff --git a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
index fb3549c840..8bbeb24018 100644
--- a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
+++ b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
@@ -14,41 +14,35 @@
package com.google.gerrit.server.query.account;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.index.query.PostFilterPredicate;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountState;
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.gwtorm.server.OrmException;
-import com.google.inject.Provider;
public class CanSeeChangePredicate extends PostFilterPredicate<AccountState> {
- private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
private final ChangeNotes changeNotes;
- CanSeeChangePredicate(
- Provider<ReviewDb> db, PermissionBackend permissionBackend, ChangeNotes changeNotes) {
+ CanSeeChangePredicate(PermissionBackend permissionBackend, ChangeNotes changeNotes) {
super(AccountQueryBuilder.FIELD_CAN_SEE, changeNotes.getChangeId().toString());
- this.db = db;
this.permissionBackend = permissionBackend;
this.changeNotes = changeNotes;
}
@Override
- public boolean match(AccountState accountState) throws OrmException {
+ public boolean match(AccountState accountState) {
try {
permissionBackend
.absentUser(accountState.getAccount().getId())
- .database(db)
.change(changeNotes)
.check(ChangePermission.READ);
return true;
} catch (PermissionBackendException e) {
- throw new OrmException("Failed to check if account can see change", e);
+ throw new StorageException("Failed to check if account can see change", e);
} catch (AuthException e) {
return false;
}
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 6735afc1cb..f2385ec789 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -23,17 +23,16 @@ import com.google.common.collect.ImmutableList;
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.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.UsedAt;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.Arrays;
import java.util.List;
@@ -45,7 +44,7 @@ import java.util.Set;
* <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
* holding on to a single instance.
*/
-public class InternalAccountQuery extends InternalQuery<AccountState> {
+public class InternalAccountQuery extends InternalQuery<AccountState, InternalAccountQuery> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject
@@ -56,45 +55,20 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
super(queryProcessor, indexes, indexConfig);
}
- @Override
- public InternalAccountQuery setLimit(int n) {
- super.setLimit(n);
- return this;
- }
-
- @Override
- public InternalAccountQuery enforceVisibility(boolean enforce) {
- super.enforceVisibility(enforce);
- return this;
- }
-
- @SafeVarargs
- @Override
- public final InternalAccountQuery setRequestedFields(FieldDef<AccountState, ?>... fields) {
- super.setRequestedFields(fields);
- return this;
- }
-
- @Override
- public InternalAccountQuery noFields() {
- super.noFields();
- return this;
- }
-
- public List<AccountState> byDefault(String query) throws OrmException {
+ public List<AccountState> byDefault(String query) {
return query(AccountPredicates.defaultPredicate(schema(), true, query));
}
- public List<AccountState> byExternalId(String scheme, String id) throws OrmException {
+ public List<AccountState> byExternalId(String scheme, String id) {
return byExternalId(ExternalId.Key.create(scheme, id));
}
- public List<AccountState> byExternalId(ExternalId.Key externalId) throws OrmException {
+ public List<AccountState> byExternalId(ExternalId.Key externalId) {
return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString()));
}
@UsedAt(UsedAt.Project.COLLABNET)
- public AccountState oneByExternalId(ExternalId.Key externalId) throws OrmException {
+ public AccountState oneByExternalId(ExternalId.Key externalId) {
List<AccountState> accountStates = byExternalId(externalId);
if (accountStates.size() == 1) {
return accountStates.get(0);
@@ -109,7 +83,7 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
return null;
}
- public List<AccountState> byFullName(String fullName) throws OrmException {
+ public List<AccountState> byFullName(String fullName) {
return query(AccountPredicates.fullName(fullName));
}
@@ -118,9 +92,8 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
*
* @param email preferred email by which accounts should be found
* @return list of accounts that have a preferred email that exactly matches the given email
- * @throws OrmException if query cannot be parsed
*/
- public List<AccountState> byPreferredEmail(String email) throws OrmException {
+ public List<AccountState> byPreferredEmail(String email) {
if (hasPreferredEmailExact()) {
return query(AccountPredicates.preferredEmailExact(email));
}
@@ -140,9 +113,8 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
* @param emails preferred emails by which accounts should be found
* @return multimap of the given emails to accounts that have a preferred email that exactly
* matches this email
- * @throws OrmException if query cannot be parsed
*/
- public Multimap<String, AccountState> byPreferredEmail(String... emails) throws OrmException {
+ public Multimap<String, AccountState> byPreferredEmail(String... emails) {
List<String> emailList = Arrays.asList(emails);
if (hasPreferredEmailExact()) {
@@ -173,7 +145,7 @@ public class InternalAccountQuery extends InternalQuery<AccountState> {
return accountsByEmail;
}
- public List<AccountState> byWatchedProject(Project.NameKey project) throws OrmException {
+ public List<AccountState> byWatchedProject(Project.NameKey project) {
return query(AccountPredicates.watchedProject(project));
}
diff --git a/java/com/google/gerrit/server/query/change/AddedPredicate.java b/java/com/google/gerrit/server/query/change/AddedPredicate.java
index 099e841b50..1f526c5dad 100644
--- a/java/com/google/gerrit/server/query/change/AddedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AddedPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class AddedPredicate extends IntegerRangeChangePredicate {
public AddedPredicate(String value) throws QueryParseException {
@@ -24,7 +23,7 @@ public class AddedPredicate extends IntegerRangeChangePredicate {
}
@Override
- protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ protected Integer getValueInt(ChangeData changeData) {
return ChangeField.ADDED.get(changeData);
}
}
diff --git a/java/com/google/gerrit/server/query/change/AfterPredicate.java b/java/com/google/gerrit/server/query/change/AfterPredicate.java
index de57b3bfba..df5a71d923 100644
--- a/java/com/google/gerrit/server/query/change/AfterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AfterPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
import java.util.Date;
public class AfterPredicate extends TimestampRangeChangePredicate {
@@ -38,7 +37,7 @@ public class AfterPredicate extends TimestampRangeChangePredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.change().getLastUpdatedOn().getTime() >= cut.getTime();
}
}
diff --git a/java/com/google/gerrit/server/query/change/AgePredicate.java b/java/com/google/gerrit/server/query/change/AgePredicate.java
index 29f1b8af70..1cf2c2f9e6 100644
--- a/java/com/google/gerrit/server/query/change/AgePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AgePredicate.java
@@ -21,7 +21,6 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import java.sql.Timestamp;
public class AgePredicate extends TimestampRangeChangePredicate {
@@ -46,7 +45,7 @@ public class AgePredicate extends TimestampRangeChangePredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
return change != null && change.getLastUpdatedOn().getTime() <= cut;
}
diff --git a/java/com/google/gerrit/server/query/change/AndChangeSource.java b/java/com/google/gerrit/server/query/change/AndChangeSource.java
index 13ace045ad..749204f3f1 100644
--- a/java/com/google/gerrit/server/query/change/AndChangeSource.java
+++ b/java/com/google/gerrit/server/query/change/AndChangeSource.java
@@ -17,8 +17,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.AndSource;
import com.google.gerrit.index.query.IsVisibleToPredicate;
import com.google.gerrit.index.query.Predicate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
import java.util.Collection;
import java.util.List;
@@ -41,13 +39,9 @@ public class AndChangeSource extends AndSource<ChangeData> implements ChangeData
}
@Override
- protected List<ChangeData> transformBuffer(List<ChangeData> buffer) throws OrmRuntimeException {
+ protected List<ChangeData> transformBuffer(List<ChangeData> buffer) {
if (!hasChange()) {
- try {
- ChangeData.ensureChangeLoaded(buffer);
- } catch (OrmException e) {
- throw new OrmRuntimeException(e);
- }
+ ChangeData.ensureChangeLoaded(buffer);
}
return super.transformBuffer(buffer);
}
diff --git a/java/com/google/gerrit/server/query/change/AssigneePredicate.java b/java/com/google/gerrit/server/query/change/AssigneePredicate.java
index 63f7467bfd..fb19e850bb 100644
--- a/java/com/google/gerrit/server/query/change/AssigneePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AssigneePredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class AssigneePredicate extends ChangeIndexPredicate {
protected final Account.Id id;
@@ -27,7 +26,7 @@ public class AssigneePredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
if (id.get() == ChangeField.NO_ASSIGNEE) {
Account.Id assignee = object.change().getAssignee();
return assignee == null;
diff --git a/java/com/google/gerrit/server/query/change/AuthorPredicate.java b/java/com/google/gerrit/server/query/change/AuthorPredicate.java
index 3ee3352345..79914a3e12 100644
--- a/java/com/google/gerrit/server/query/change/AuthorPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AuthorPredicate.java
@@ -18,8 +18,6 @@ import static com.google.gerrit.server.index.change.ChangeField.AUTHOR;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_AUTHOR;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
public class AuthorPredicate extends ChangeIndexPredicate {
public AuthorPredicate(String value) {
@@ -27,12 +25,8 @@ public class AuthorPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- try {
- return ChangeField.getAuthorParts(object).contains(getValue().toLowerCase());
- } catch (IOException e) {
- throw new OrmException(e);
- }
+ public boolean match(ChangeData object) {
+ return ChangeField.getAuthorParts(object).contains(getValue().toLowerCase());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/BeforePredicate.java b/java/com/google/gerrit/server/query/change/BeforePredicate.java
index 4d6ed69333..dacabc0e72 100644
--- a/java/com/google/gerrit/server/query/change/BeforePredicate.java
+++ b/java/com/google/gerrit/server/query/change/BeforePredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
import java.util.Date;
public class BeforePredicate extends TimestampRangeChangePredicate {
@@ -38,7 +37,7 @@ public class BeforePredicate extends TimestampRangeChangePredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.change().getLastUpdatedOn().getTime() <= cut.getTime();
}
}
diff --git a/java/com/google/gerrit/server/query/change/BooleanPredicate.java b/java/com/google/gerrit/server/query/change/BooleanPredicate.java
index 5930b74d66..68f83e8008 100644
--- a/java/com/google/gerrit/server/query/change/BooleanPredicate.java
+++ b/java/com/google/gerrit/server/query/change/BooleanPredicate.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.index.FieldDef;
-import com.google.gwtorm.server.OrmException;
public class BooleanPredicate extends ChangeIndexPredicate {
public BooleanPredicate(FieldDef<ChangeData, String> field) {
@@ -23,7 +22,7 @@ public class BooleanPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
return getValue().equals(getField().get(object));
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 8e9a08f2e7..3cd64b3a6d 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.query.change;
-import static com.google.gerrit.server.ApprovalsUtil.sortApprovals;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
@@ -30,10 +29,12 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.primitives.Ints;
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.exceptions.StorageException;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -46,7 +47,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -64,7 +64,6 @@ import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.DiffSummary;
import com.google.gerrit.server.patch.DiffSummaryKey;
import com.google.gerrit.server.patch.PatchListCache;
@@ -75,8 +74,6 @@ 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.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
@@ -92,9 +89,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
@@ -104,9 +98,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
public class ChangeData {
- private static final int BATCH_SIZE = 50;
-
- public static List<Change> asChanges(List<ChangeData> changeDatas) throws OrmException {
+ public static List<Change> asChanges(List<ChangeData> changeDatas) {
List<Change> result = new ArrayList<>(changeDatas.size());
for (ChangeData cd : changeDatas) {
result.add(cd.change());
@@ -118,154 +110,65 @@ public class ChangeData {
return changes.stream().collect(toMap(ChangeData::getId, Function.identity()));
}
- public static void ensureChangeLoaded(Iterable<ChangeData> changes) throws OrmException {
+ public static void ensureChangeLoaded(Iterable<ChangeData> changes) {
ChangeData first = Iterables.getFirst(changes, null);
if (first == null) {
return;
- } else if (first.notesMigration.readChanges()) {
- for (ChangeData cd : changes) {
- cd.change();
- }
- return;
}
- Map<Change.Id, ChangeData> missing = new HashMap<>();
for (ChangeData cd : changes) {
- if (cd.change == null) {
- missing.put(cd.getId(), cd);
- }
- }
- if (missing.isEmpty()) {
- return;
- }
- for (ChangeNotes notes : first.notesFactory.create(first.db, missing.keySet())) {
- missing.get(notes.getChangeId()).change = notes.getChange();
+ cd.change();
}
}
- public static void ensureAllPatchSetsLoaded(Iterable<ChangeData> changes) throws OrmException {
+ public static void ensureAllPatchSetsLoaded(Iterable<ChangeData> changes) {
ChangeData first = Iterables.getFirst(changes, null);
if (first == null) {
return;
- } else if (first.notesMigration.readChanges()) {
- for (ChangeData cd : changes) {
- cd.patchSets();
- }
- return;
}
- List<ResultSet<PatchSet>> results = new ArrayList<>(BATCH_SIZE);
- for (List<ChangeData> batch : Iterables.partition(changes, BATCH_SIZE)) {
- results.clear();
- for (ChangeData cd : batch) {
- if (cd.patchSets == null) {
- results.add(cd.db.patchSets().byChange(cd.getId()));
- } else {
- results.add(null);
- }
- }
- for (int i = 0; i < batch.size(); i++) {
- ResultSet<PatchSet> result = results.get(i);
- if (result != null) {
- batch.get(i).patchSets = result.toList();
- }
- }
+ for (ChangeData cd : changes) {
+ cd.patchSets();
}
}
- public static void ensureCurrentPatchSetLoaded(Iterable<ChangeData> changes) throws OrmException {
+ public static void ensureCurrentPatchSetLoaded(Iterable<ChangeData> changes) {
ChangeData first = Iterables.getFirst(changes, null);
if (first == null) {
return;
- } else if (first.notesMigration.readChanges()) {
- for (ChangeData cd : changes) {
- cd.currentPatchSet();
- }
- return;
}
- Map<PatchSet.Id, ChangeData> missing = new HashMap<>();
for (ChangeData cd : changes) {
- if (cd.currentPatchSet == null && cd.patchSets == null) {
- missing.put(cd.change().currentPatchSetId(), cd);
- }
- }
- if (missing.isEmpty()) {
- return;
- }
- for (PatchSet ps : first.db.patchSets().get(missing.keySet())) {
- missing.get(ps.getId()).currentPatchSet = ps;
+ cd.currentPatchSet();
}
}
- public static void ensureCurrentApprovalsLoaded(Iterable<ChangeData> changes)
- throws OrmException {
+ public static void ensureCurrentApprovalsLoaded(Iterable<ChangeData> changes) {
ChangeData first = Iterables.getFirst(changes, null);
if (first == null) {
return;
- } else if (first.notesMigration.readChanges()) {
- for (ChangeData cd : changes) {
- cd.currentApprovals();
- }
- return;
}
- List<ResultSet<PatchSetApproval>> results = new ArrayList<>(BATCH_SIZE);
- for (List<ChangeData> batch : Iterables.partition(changes, BATCH_SIZE)) {
- results.clear();
- for (ChangeData cd : batch) {
- if (cd.currentApprovals == null) {
- PatchSet.Id psId = cd.change().currentPatchSetId();
- results.add(cd.db.patchSetApprovals().byPatchSet(psId));
- } else {
- results.add(null);
- }
- }
- for (int i = 0; i < batch.size(); i++) {
- ResultSet<PatchSetApproval> result = results.get(i);
- if (result != null) {
- batch.get(i).currentApprovals = sortApprovals(result);
- }
- }
+ for (ChangeData cd : changes) {
+ cd.currentApprovals();
}
}
- public static void ensureMessagesLoaded(Iterable<ChangeData> changes) throws OrmException {
+ public static void ensureMessagesLoaded(Iterable<ChangeData> changes) {
ChangeData first = Iterables.getFirst(changes, null);
if (first == null) {
return;
- } else if (first.notesMigration.readChanges()) {
- for (ChangeData cd : changes) {
- cd.messages();
- }
- return;
}
- List<ResultSet<ChangeMessage>> results = new ArrayList<>(BATCH_SIZE);
- for (List<ChangeData> batch : Iterables.partition(changes, BATCH_SIZE)) {
- results.clear();
- for (ChangeData cd : batch) {
- if (cd.messages == null) {
- PatchSet.Id psId = cd.change().currentPatchSetId();
- results.add(cd.db.changeMessages().byPatchSet(psId));
- } else {
- results.add(null);
- }
- }
- for (int i = 0; i < batch.size(); i++) {
- ResultSet<ChangeMessage> result = results.get(i);
- if (result != null) {
- batch.get(i).messages = result.toList();
- }
- }
+ for (ChangeData cd : changes) {
+ cd.messages();
}
}
- public static void ensureReviewedByLoadedForOpenChanges(Iterable<ChangeData> changes)
- throws OrmException {
+ public static void ensureReviewedByLoadedForOpenChanges(Iterable<ChangeData> changes) {
List<ChangeData> pending = new ArrayList<>();
for (ChangeData cd : changes) {
- if (cd.reviewedBy == null && cd.change().getStatus().isOpen()) {
+ if (cd.reviewedBy == null && cd.change().isNew()) {
pending.add(cd);
}
}
@@ -287,23 +190,22 @@ public class ChangeData {
this.assistedFactory = assistedFactory;
}
- public ChangeData create(ReviewDb db, Project.NameKey project, Change.Id id) {
- return assistedFactory.create(db, project, id, null, null);
+ public ChangeData create(Project.NameKey project, Change.Id id) {
+ return assistedFactory.create(project, id, null, null);
}
- public ChangeData create(ReviewDb db, Change change) {
- return assistedFactory.create(db, change.getProject(), change.getId(), change, null);
+ public ChangeData create(Change change) {
+ return assistedFactory.create(change.getProject(), change.getId(), change, null);
}
- public ChangeData create(ReviewDb db, ChangeNotes notes) {
+ public ChangeData create(ChangeNotes notes) {
return assistedFactory.create(
- db, notes.getChange().getProject(), notes.getChangeId(), notes.getChange(), notes);
+ notes.getChange().getProject(), notes.getChangeId(), notes.getChange(), notes);
}
}
public interface AssistedFactory {
ChangeData create(
- ReviewDb db,
Project.NameKey project,
Change.Id id,
@Nullable Change change,
@@ -324,7 +226,7 @@ public class ChangeData {
ChangeData cd =
new ChangeData(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, project, id, null, null);
+ null, project, id, null, null);
cd.currentPatchSet = new PatchSet(new PatchSet.Id(id, currentPatchSetId));
return cd;
}
@@ -339,7 +241,6 @@ public class ChangeData {
private final GitRepositoryManager repoManager;
private final MergeUtil.Factory mergeUtilFactory;
private final MergeabilityCache mergeabilityCache;
- private final NotesMigration notesMigration;
private final PatchListCache patchListCache;
private final PatchSetUtil psUtil;
private final ProjectCache projectCache;
@@ -348,7 +249,6 @@ public class ChangeData {
private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
// Required assisted injected fields.
- private final ReviewDb db;
private final Project.NameKey project;
private final Change.Id legacyId;
@@ -391,6 +291,7 @@ public class ChangeData {
private PersonIdent committer;
private int parentCount;
private Integer unresolvedCommentCount;
+ private Integer totalCommentCount;
private LabelTypes labelTypes;
private ImmutableList<byte[]> refStates;
@@ -407,14 +308,12 @@ public class ChangeData {
GitRepositoryManager repoManager,
MergeUtil.Factory mergeUtilFactory,
MergeabilityCache mergeabilityCache,
- NotesMigration notesMigration,
PatchListCache patchListCache,
PatchSetUtil psUtil,
ProjectCache projectCache,
TrackingFooters trackingFooters,
PureRevert pureRevert,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory,
- @Assisted ReviewDb db,
@Assisted Project.NameKey project,
@Assisted Change.Id id,
@Assisted @Nullable Change change,
@@ -427,7 +326,6 @@ public class ChangeData {
this.repoManager = repoManager;
this.mergeUtilFactory = mergeUtilFactory;
this.mergeabilityCache = mergeabilityCache;
- this.notesMigration = notesMigration;
this.patchListCache = patchListCache;
this.psUtil = psUtil;
this.projectCache = projectCache;
@@ -436,11 +334,6 @@ public class ChangeData {
this.pureRevert = pureRevert;
this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
- // May be null in tests when created via createForTest above, in which case lazy-loading will
- // intentionally fail with NPE. Still not marked @Nullable in the constructor, to force callers
- // using Guice to pass a non-null value.
- this.db = db;
-
this.project = project;
this.legacyId = id;
@@ -460,22 +353,18 @@ public class ChangeData {
return this;
}
- public ReviewDb db() {
- return db;
- }
-
public AllUsersName getAllUsersNameForIndexing() {
return allUsersName;
}
- public void setCurrentFilePaths(List<String> filePaths) throws OrmException {
+ public void setCurrentFilePaths(List<String> filePaths) {
PatchSet ps = currentPatchSet();
if (ps != null) {
currentFiles = ImmutableList.copyOf(filePaths);
}
}
- public List<String> currentFilePaths() throws IOException, OrmException {
+ public List<String> currentFilePaths() {
if (currentFiles == null) {
if (!lazyLoad) {
return Collections.emptyList();
@@ -486,7 +375,7 @@ public class ChangeData {
return currentFiles;
}
- private Optional<DiffSummary> getDiffSummary() throws OrmException, IOException {
+ private Optional<DiffSummary> getDiffSummary() {
if (diffSummary == null) {
if (!lazyLoad) {
return Optional.empty();
@@ -514,7 +403,7 @@ public class ChangeData {
return diffSummary;
}
- private Optional<ChangedLines> computeChangedLines() throws OrmException, IOException {
+ private Optional<ChangedLines> computeChangedLines() {
Optional<DiffSummary> ds = getDiffSummary();
if (ds.isPresent()) {
return Optional.of(ds.get().getChangedLines());
@@ -522,7 +411,7 @@ public class ChangeData {
return Optional.empty();
}
- public Optional<ChangedLines> changedLines() throws OrmException, IOException {
+ public Optional<ChangedLines> changedLines() {
if (changedLines == null) {
if (!lazyLoad) {
return Optional.empty();
@@ -556,7 +445,7 @@ public class ChangeData {
visibleTo = user;
}
- public Change change() throws OrmException {
+ public Change change() {
if (change == null && lazyLoad) {
reloadChange();
}
@@ -567,41 +456,41 @@ public class ChangeData {
change = c;
}
- public Change reloadChange() throws OrmException {
+ public Change reloadChange() {
try {
- notes = notesFactory.createChecked(db, project, legacyId);
+ notes = notesFactory.createChecked(project, legacyId);
} catch (NoSuchChangeException e) {
- throw new OrmException("Unable to load change " + legacyId, e);
+ throw new StorageException("Unable to load change " + legacyId, e);
}
change = notes.getChange();
setPatchSets(null);
return change;
}
- public LabelTypes getLabelTypes() throws OrmException {
+ public LabelTypes getLabelTypes() {
if (labelTypes == null) {
ProjectState state;
try {
state = projectCache.checkedGet(project());
} catch (IOException e) {
- throw new OrmException("project state not available", e);
+ throw new StorageException("project state not available", e);
}
labelTypes = state.getLabelTypes(change().getDest());
}
return labelTypes;
}
- public ChangeNotes notes() throws OrmException {
+ public ChangeNotes notes() {
if (notes == null) {
if (!lazyLoad) {
- throw new OrmException("ChangeNotes not available, lazyLoad = false");
+ throw new StorageException("ChangeNotes not available, lazyLoad = false");
}
- notes = notesFactory.create(db, project(), legacyId);
+ notes = notesFactory.create(project(), legacyId);
}
return notes;
}
- public PatchSet currentPatchSet() throws OrmException {
+ public PatchSet currentPatchSet() {
if (currentPatchSet == null) {
Change c = change();
if (c == null) {
@@ -617,7 +506,7 @@ public class ChangeData {
return currentPatchSet;
}
- public List<PatchSetApproval> currentApprovals() throws OrmException {
+ public List<PatchSetApproval> currentApprovals() {
if (currentApprovals == null) {
if (!lazyLoad) {
return Collections.emptyList();
@@ -629,8 +518,8 @@ public class ChangeData {
try {
currentApprovals =
ImmutableList.copyOf(
- approvalsUtil.byPatchSet(db, notes(), c.currentPatchSetId(), null, null));
- } catch (OrmException e) {
+ approvalsUtil.byPatchSet(notes(), c.currentPatchSetId(), null, null));
+ } catch (StorageException e) {
if (e.getCause() instanceof NoSuchChangeException) {
currentApprovals = Collections.emptyList();
} else {
@@ -646,7 +535,7 @@ public class ChangeData {
currentApprovals = approvals;
}
- public String commitMessage() throws IOException, OrmException {
+ public String commitMessage() {
if (commitMessage == null) {
if (!loadCommitData()) {
return null;
@@ -655,7 +544,7 @@ public class ChangeData {
return commitMessage;
}
- public List<FooterLine> commitFooters() throws IOException, OrmException {
+ public List<FooterLine> commitFooters() {
if (commitFooters == null) {
if (!loadCommitData()) {
return null;
@@ -664,11 +553,11 @@ public class ChangeData {
return commitFooters;
}
- public ListMultimap<String, String> trackingFooters() throws IOException, OrmException {
+ public ListMultimap<String, String> trackingFooters() {
return trackingFooters.extract(commitFooters());
}
- public PersonIdent getAuthor() throws IOException, OrmException {
+ public PersonIdent getAuthor() {
if (author == null) {
if (!loadCommitData()) {
return null;
@@ -677,7 +566,7 @@ public class ChangeData {
return author;
}
- public PersonIdent getCommitter() throws IOException, OrmException {
+ public PersonIdent getCommitter() {
if (committer == null) {
if (!loadCommitData()) {
return null;
@@ -686,9 +575,7 @@ public class ChangeData {
return committer;
}
- private boolean loadCommitData()
- throws OrmException, RepositoryNotFoundException, IOException, MissingObjectException,
- IncorrectObjectTypeException {
+ private boolean loadCommitData() {
PatchSet ps = currentPatchSet();
if (ps == null) {
return false;
@@ -702,17 +589,16 @@ public class ChangeData {
author = c.getAuthorIdent();
committer = c.getCommitterIdent();
parentCount = c.getParentCount();
+ } catch (IOException e) {
+ throw new StorageException(e);
}
return true;
}
- /**
- * @return patches for the change, in patch set ID order.
- * @throws OrmException an error occurred reading the database.
- */
- public Collection<PatchSet> patchSets() throws OrmException {
+ /** @return patches for the change, in patch set ID order. */
+ public Collection<PatchSet> patchSets() {
if (patchSets == null) {
- patchSets = psUtil.byChange(db, notes());
+ patchSets = psUtil.byChange(notes());
}
return patchSets;
}
@@ -722,11 +608,8 @@ public class ChangeData {
this.patchSets = patchSets;
}
- /**
- * @return patch with the given ID, or null if it does not exist.
- * @throws OrmException an error occurred reading the database.
- */
- public PatchSet patchSet(PatchSet.Id psId) throws OrmException {
+ /** @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)) {
return currentPatchSet;
}
@@ -741,27 +624,23 @@ public class ChangeData {
/**
* @return all patch set approvals for the change, keyed by ID, ordered by timestamp within each
* patch set.
- * @throws OrmException an error occurred reading the database.
*/
- public ListMultimap<PatchSet.Id, PatchSetApproval> approvals() throws OrmException {
+ public ListMultimap<PatchSet.Id, PatchSetApproval> approvals() {
if (allApprovals == null) {
if (!lazyLoad) {
return ImmutableListMultimap.of();
}
- allApprovals = approvalsUtil.byChange(db, notes());
+ allApprovals = approvalsUtil.byChange(notes());
}
return allApprovals;
}
- /**
- * @return The submit ('SUBM') approval label
- * @throws OrmException an error occurred reading the database.
- */
- public Optional<PatchSetApproval> getSubmitApproval() throws OrmException {
+ /** @return The submit ('SUBM') approval label */
+ public Optional<PatchSetApproval> getSubmitApproval() {
return currentApprovals().stream().filter(PatchSetApproval::isLegacySubmit).findFirst();
}
- public ReviewerSet reviewers() throws OrmException {
+ public ReviewerSet reviewers() {
if (reviewers == null) {
if (!lazyLoad) {
return ReviewerSet.empty();
@@ -779,7 +658,7 @@ public class ChangeData {
return reviewers;
}
- public ReviewerByEmailSet reviewersByEmail() throws OrmException {
+ public ReviewerByEmailSet reviewersByEmail() {
if (reviewersByEmail == null) {
if (!lazyLoad) {
return ReviewerByEmailSet.empty();
@@ -805,7 +684,7 @@ public class ChangeData {
return this.pendingReviewers;
}
- public ReviewerSet pendingReviewers() throws OrmException {
+ public ReviewerSet pendingReviewers() {
if (pendingReviewers == null) {
if (!lazyLoad) {
return ReviewerSet.empty();
@@ -823,7 +702,7 @@ public class ChangeData {
return pendingReviewersByEmail;
}
- public ReviewerByEmailSet pendingReviewersByEmail() throws OrmException {
+ public ReviewerByEmailSet pendingReviewersByEmail() {
if (pendingReviewersByEmail == null) {
if (!lazyLoad) {
return ReviewerByEmailSet.empty();
@@ -833,7 +712,7 @@ public class ChangeData {
return pendingReviewersByEmail;
}
- public List<ReviewerStatusUpdate> reviewerUpdates() throws OrmException {
+ public List<ReviewerStatusUpdate> reviewerUpdates() {
if (reviewerUpdates == null) {
if (!lazyLoad) {
return Collections.emptyList();
@@ -851,17 +730,17 @@ public class ChangeData {
return reviewerUpdates;
}
- public Collection<Comment> publishedComments() throws OrmException {
+ public Collection<Comment> publishedComments() {
if (publishedComments == null) {
if (!lazyLoad) {
return Collections.emptyList();
}
- publishedComments = commentsUtil.publishedByChange(db, notes());
+ publishedComments = commentsUtil.publishedByChange(notes());
}
return publishedComments;
}
- public Collection<RobotComment> robotComments() throws OrmException {
+ public Collection<RobotComment> robotComments() {
if (robotComments == null) {
if (!lazyLoad) {
return Collections.emptyList();
@@ -871,7 +750,7 @@ public class ChangeData {
return robotComments;
}
- public Integer unresolvedCommentCount() throws OrmException {
+ public Integer unresolvedCommentCount() {
if (unresolvedCommentCount == null) {
if (!lazyLoad) {
return null;
@@ -925,12 +804,29 @@ public class ChangeData {
this.unresolvedCommentCount = count;
}
- public List<ChangeMessage> messages() throws OrmException {
+ public Integer totalCommentCount() {
+ if (totalCommentCount == null) {
+ if (!lazyLoad) {
+ return null;
+ }
+
+ // Fail on overflow.
+ totalCommentCount =
+ Ints.checkedCast((long) publishedComments().size() + robotComments().size());
+ }
+ return totalCommentCount;
+ }
+
+ public void setTotalCommentCount(Integer count) {
+ this.totalCommentCount = count;
+ }
+
+ public List<ChangeMessage> messages() {
if (messages == null) {
if (!lazyLoad) {
return Collections.emptyList();
}
- messages = cmUtil.byChange(db, notes());
+ messages = cmUtil.byChange(notes());
}
return messages;
}
@@ -983,15 +879,15 @@ public class ChangeData {
}
@Nullable
- public Boolean isMergeable() throws OrmException {
+ public Boolean isMergeable() {
if (mergeable == null) {
Change c = change();
if (c == null) {
return null;
}
- if (c.getStatus() == Change.Status.MERGED) {
+ if (c.isMerged()) {
mergeable = true;
- } else if (c.getStatus() == Change.Status.ABANDONED) {
+ } else if (c.isAbandoned()) {
return null;
} else if (c.isWorkInProgress()) {
return null;
@@ -1023,18 +919,18 @@ public class ChangeData {
c.getDest(),
repo);
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
}
return mergeable;
}
- public Set<Account.Id> editsByUser() throws OrmException {
+ public Set<Account.Id> editsByUser() {
return editRefs().keySet();
}
- public Map<Account.Id, Ref> editRefs() throws OrmException {
+ public Map<Account.Id, Ref> editRefs() {
if (editsByUser == null) {
if (!lazyLoad) {
return Collections.emptyMap();
@@ -1056,17 +952,17 @@ public class ChangeData {
}
}
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
return editsByUser;
}
- public Set<Account.Id> draftsByUser() throws OrmException {
+ public Set<Account.Id> draftsByUser() {
return draftRefs().keySet();
}
- public Map<Account.Id, Ref> draftRefs() throws OrmException {
+ public Map<Account.Id, Ref> draftRefs() {
if (draftsByUser == null) {
if (!lazyLoad) {
return Collections.emptyMap();
@@ -1077,30 +973,24 @@ public class ChangeData {
}
draftsByUser = new HashMap<>();
- if (notesMigration.readChanges()) {
- for (Ref ref : commentsUtil.getDraftRefs(notes.getChangeId())) {
- Account.Id account = Account.Id.fromRefSuffix(ref.getName());
- if (account != null
- // Double-check that any drafts exist for this user after
- // filtering out zombies. If some but not all drafts in the ref
- // were zombies, the returned Ref still includes those zombies;
- // this is suboptimal, but is ok for the purposes of
- // draftsByUser(), and easier than trying to rebuild the change at
- // this point.
- && !notes().getDraftComments(account, ref).isEmpty()) {
- draftsByUser.put(account, ref);
- }
- }
- } else {
- for (Comment sc : commentsUtil.draftByChange(db, notes())) {
- draftsByUser.put(sc.author.getId(), null);
+ for (Ref ref : commentsUtil.getDraftRefs(notes.getChangeId())) {
+ Account.Id account = Account.Id.fromRefSuffix(ref.getName());
+ if (account != null
+ // Double-check that any drafts exist for this user after
+ // filtering out zombies. If some but not all drafts in the ref
+ // were zombies, the returned Ref still includes those zombies;
+ // this is suboptimal, but is ok for the purposes of
+ // draftsByUser(), and easier than trying to rebuild the change at
+ // this point.
+ && !notes().getDraftComments(account, ref).isEmpty()) {
+ draftsByUser.put(account, ref);
}
}
}
return draftsByUser;
}
- public boolean isReviewedBy(Account.Id accountId) throws OrmException {
+ public boolean isReviewedBy(Account.Id accountId) {
Collection<String> stars = stars(accountId);
PatchSet ps = currentPatchSet();
@@ -1117,7 +1007,7 @@ public class ChangeData {
return reviewedBy().contains(accountId);
}
- public Set<Account.Id> reviewedBy() throws OrmException {
+ public Set<Account.Id> reviewedBy() {
if (reviewedBy == null) {
if (!lazyLoad) {
return Collections.emptySet();
@@ -1149,7 +1039,7 @@ public class ChangeData {
this.reviewedBy = reviewedBy;
}
- public Set<String> hashtags() throws OrmException {
+ public Set<String> hashtags() {
if (hashtags == null) {
if (!lazyLoad) {
return Collections.emptySet();
@@ -1163,7 +1053,7 @@ public class ChangeData {
this.hashtags = hashtags;
}
- public ImmutableListMultimap<Account.Id, String> stars() throws OrmException {
+ public ImmutableListMultimap<Account.Id, String> stars() {
if (stars == null) {
if (!lazyLoad) {
return ImmutableListMultimap.of();
@@ -1181,7 +1071,7 @@ public class ChangeData {
this.stars = ImmutableListMultimap.copyOf(stars);
}
- public ImmutableMap<Account.Id, StarRef> starRefs() throws OrmException {
+ public ImmutableMap<Account.Id, StarRef> starRefs() {
if (starRefs == null) {
if (!lazyLoad) {
return ImmutableMap.of();
@@ -1191,7 +1081,7 @@ public class ChangeData {
return starRefs;
}
- public Set<String> stars(Account.Id accountId) throws OrmException {
+ public Set<String> stars(Account.Id accountId) {
if (starsOf != null) {
if (!starsOf.accountId().equals(accountId)) {
starsOf = null;
@@ -1215,14 +1105,14 @@ public class ChangeData {
* false otherwise.
*/
@Nullable
- public Boolean isPureRevert() throws OrmException {
+ public Boolean isPureRevert() {
if (change().getRevertOf() == null) {
return null;
}
try {
- return pureRevert.get(notes(), null).isPureRevert;
+ return pureRevert.get(notes(), Optional.empty());
} catch (IOException | BadRequestException | ResourceConflictException e) {
- throw new OrmException("could not compute pure revert", e);
+ throw new StorageException("could not compute pure revert", e);
}
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
index d541d189cf..74ad0ef52d 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
/** Predicate over Change-Id strings (aka Change.Key). */
public class ChangeIdPredicate extends ChangeIndexPredicate {
@@ -25,7 +24,7 @@ public class ChangeIdPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
Change change = cd.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
index 1eb27706e3..7428e3a1c3 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIndexPredicate.java
@@ -17,9 +17,22 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.query.IndexPredicate;
import com.google.gerrit.index.query.Matchable;
+import com.google.gerrit.index.query.Predicate;
public abstract class ChangeIndexPredicate extends IndexPredicate<ChangeData>
implements Matchable<ChangeData> {
+ /**
+ * Returns an index predicate that matches no changes in the index.
+ *
+ * <p>This predicate should be used in preference to a non-index predicate (such as {@code
+ * Predicate.not(Predicate.any())}), since it can be matched efficiently against the index.
+ *
+ * @return an index predicate matching no changes.
+ */
+ public static Predicate<ChangeData> none() {
+ return ChangeStatusPredicate.NONE;
+ }
+
protected ChangeIndexPredicate(FieldDef<ChangeData, ?> def, String value) {
super(def, value);
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index a3f6f7cff7..8015a33af7 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.InternalUser;
@@ -29,8 +29,9 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.Optional;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -38,22 +39,24 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException;
public class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- protected final Provider<ReviewDb> db;
+ public interface Factory {
+ ChangeIsVisibleToPredicate forUser(CurrentUser user);
+ }
+
protected final ChangeNotes.Factory notesFactory;
protected final CurrentUser user;
protected final PermissionBackend permissionBackend;
protected final ProjectCache projectCache;
private final Provider<AnonymousUser> anonymousUserProvider;
+ @Inject
public ChangeIsVisibleToPredicate(
- Provider<ReviewDb> db,
ChangeNotes.Factory notesFactory,
- CurrentUser user,
PermissionBackend permissionBackend,
ProjectCache projectCache,
- Provider<AnonymousUser> anonymousUserProvider) {
+ Provider<AnonymousUser> anonymousUserProvider,
+ @Assisted CurrentUser user) {
super(ChangeQueryBuilder.FIELD_VISIBLETO, IndexUtils.describe(user));
- this.db = db;
this.notesFactory = notesFactory;
this.user = user;
this.permissionBackend = permissionBackend;
@@ -62,7 +65,7 @@ public class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData>
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
if (cd.fastIsVisibleTo(user)) {
return true;
}
@@ -84,7 +87,7 @@ public class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData>
return false;
}
} catch (IOException e) {
- throw new OrmException("unable to read project state", e);
+ throw new StorageException("unable to read project state", e);
}
PermissionBackend.WithUser withUser =
@@ -95,7 +98,7 @@ public class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData>
.filter(u -> u instanceof SingleGroupUser || u instanceof InternalUser)
.orElseGet(anonymousUserProvider::get));
try {
- withUser.indexedChange(cd, notes).database(db).check(ChangePermission.READ);
+ withUser.indexedChange(cd, notes).check(ChangePermission.READ);
} catch (PermissionBackendException e) {
Throwable cause = e.getCause();
if (cause instanceof RepositoryNotFoundException) {
@@ -104,7 +107,7 @@ public class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData>
cd, cd.project());
return false;
}
- throw new OrmException("unable to check permissions on change " + cd.getId(), e);
+ throw new StorageException("unable to check permissions on change " + cd.getId(), e);
} catch (AuthException e) {
logger.atFine().log("Filter out non-visisble change: %s", cd);
return false;
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 61726b8479..f998ad3fc6 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.query.change;
import static com.google.gerrit.reviewdb.client.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;
import static java.util.stream.Collectors.toSet;
@@ -28,9 +29,9 @@ 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.common.errors.NotSignedInException;
+import com.google.gerrit.exceptions.NotSignedInException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.SchemaUtil;
@@ -45,14 +46,14 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
+import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.account.GroupMembers;
@@ -61,20 +62,20 @@ 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;
import com.google.gerrit.server.index.change.ChangeIndexRewriter;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ChildProjects;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.submit.SubmitDryRun;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
@@ -93,10 +94,11 @@ 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. */
-public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
+public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuilder> {
public interface ChangeOperatorFactory extends OperatorFactory<ChangeData, ChangeQueryBuilder> {}
/**
@@ -122,8 +124,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
static final int MAX_ACCOUNTS_PER_DEFAULT_FIELD = 10;
- // NOTE: As new search operations are added, please keep the
- // SearchSuggestOracle up to date.
+ // NOTE: As new search operations are added, please keep the suggestions in
+ // gr-search-bar.js up to date.
public static final String FIELD_ADDED = "added";
public static final String FIELD_AGE = "age";
@@ -137,7 +139,11 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
public static final String FIELD_COMMENTBY = "commentby";
public static final String FIELD_COMMIT = "commit";
public static final String FIELD_COMMITTER = "committer";
+ public static final String FIELD_DIRECTORY = "directory";
public static final String FIELD_EXACTCOMMITTER = "exactcommitter";
+ public static final String FIELD_EXTENSION = "extension";
+ public static final String FIELD_ONLY_EXTENSIONS = "onlyextensions";
+ public static final String FIELD_FOOTER = "footer";
public static final String FIELD_CONFLICTS = "conflicts";
public static final String FIELD_DELETED = "deleted";
public static final String FIELD_DELTA = "delta";
@@ -198,7 +204,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
final ChangeData.Factory changeDataFactory;
final ChangeIndex index;
final ChangeIndexRewriter rewriter;
- final ChangeNotes.Factory notesFactory;
final CommentsUtil commentsUtil;
final ConflictsCache conflictsCache;
final DynamicMap<ChangeHasOperandFactory> hasOperands;
@@ -207,23 +212,20 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
final GroupBackend groupBackend;
final IdentifiedUser.GenericFactory userFactory;
final IndexConfig indexConfig;
- final NotesMigration notesMigration;
final PatchListCache patchListCache;
final ProjectCache projectCache;
final Provider<InternalChangeQuery> queryProvider;
final ChildProjects childProjects;
- final Provider<ReviewDb> db;
final StarredChangesUtil starredChangesUtil;
final SubmitDryRun submitDryRun;
final GroupMembers groupMembers;
- final Provider<AnonymousUser> anonymousUserProvider;
+ final ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory;
private final Provider<CurrentUser> self;
@Inject
@VisibleForTesting
public Arguments(
- Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
ChangeIndexRewriter rewriter,
DynamicMap<ChangeOperatorFactory> opFactories,
@@ -231,7 +233,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
PermissionBackend permissionBackend,
- ChangeNotes.Factory notesFactory,
ChangeData.Factory changeDataFactory,
CommentsUtil commentsUtil,
AccountResolver accountResolver,
@@ -248,11 +249,9 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
IndexConfig indexConfig,
StarredChangesUtil starredChangesUtil,
AccountCache accountCache,
- NotesMigration notesMigration,
GroupMembers groupMembers,
- Provider<AnonymousUser> anonymousUserProvider) {
+ ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory) {
this(
- db,
queryProvider,
rewriter,
opFactories,
@@ -260,7 +259,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
userFactory,
self,
permissionBackend,
- notesFactory,
changeDataFactory,
commentsUtil,
accountResolver,
@@ -277,13 +275,11 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
indexConfig,
starredChangesUtil,
accountCache,
- notesMigration,
groupMembers,
- anonymousUserProvider);
+ changeIsVisbleToPredicateFactory);
}
private Arguments(
- Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
ChangeIndexRewriter rewriter,
DynamicMap<ChangeOperatorFactory> opFactories,
@@ -291,7 +287,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
PermissionBackend permissionBackend,
- ChangeNotes.Factory notesFactory,
ChangeData.Factory changeDataFactory,
CommentsUtil commentsUtil,
AccountResolver accountResolver,
@@ -308,17 +303,14 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
IndexConfig indexConfig,
StarredChangesUtil starredChangesUtil,
AccountCache accountCache,
- NotesMigration notesMigration,
GroupMembers groupMembers,
- Provider<AnonymousUser> anonymousUserProvider) {
- this.db = db;
+ ChangeIsVisibleToPredicate.Factory changeIsVisbleToPredicateFactory) {
this.queryProvider = queryProvider;
this.rewriter = rewriter;
this.opFactories = opFactories;
this.userFactory = userFactory;
this.self = self;
this.permissionBackend = permissionBackend;
- this.notesFactory = notesFactory;
this.changeDataFactory = changeDataFactory;
this.commentsUtil = commentsUtil;
this.accountResolver = accountResolver;
@@ -336,14 +328,12 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
this.starredChangesUtil = starredChangesUtil;
this.accountCache = accountCache;
this.hasOperands = hasOperands;
- this.notesMigration = notesMigration;
this.groupMembers = groupMembers;
- this.anonymousUserProvider = anonymousUserProvider;
+ this.changeIsVisbleToPredicateFactory = changeIsVisbleToPredicateFactory;
}
Arguments asUser(CurrentUser otherUser) {
return new Arguments(
- db,
queryProvider,
rewriter,
opFactories,
@@ -351,7 +341,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
userFactory,
Providers.of(otherUser),
permissionBackend,
- notesFactory,
changeDataFactory,
commentsUtil,
accountResolver,
@@ -368,9 +357,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
indexConfig,
starredChangesUtil,
accountCache,
- notesMigration,
groupMembers,
- anonymousUserProvider);
+ changeIsVisbleToPredicateFactory);
}
Arguments asUser(Account.Id otherId) {
@@ -412,27 +400,19 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
private final Arguments args;
+ private @Inject @GerritServerConfig Config cfg;
+
@Inject
ChangeQueryBuilder(Arguments args) {
- super(mydef);
- this.args = args;
- setupDynamicOperators();
+ this(mydef, args);
}
@VisibleForTesting
- protected ChangeQueryBuilder(
- Definition<ChangeData, ? extends QueryBuilder<ChangeData>> def, Arguments args) {
- super(def);
+ protected ChangeQueryBuilder(Definition<ChangeData, ChangeQueryBuilder> def, Arguments args) {
+ super(def, args.opFactories);
this.args = args;
}
- private void setupDynamicOperators() {
- for (Extension<ChangeOperatorFactory> e : args.opFactories) {
- String name = e.getExportName() + "_" + e.getPluginName();
- opFactories.put(name, e.getProvider().get());
- }
- }
-
public Arguments getArgs() {
return args;
}
@@ -564,9 +544,9 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
if (args.getSchema().hasField(ChangeField.WIP)) {
return Predicate.and(
Predicate.not(new BooleanPredicate(ChangeField.WIP)),
- ReviewerPredicate.reviewer(args, self()));
+ ReviewerPredicate.reviewer(self()));
}
- return ReviewerPredicate.reviewer(args, self());
+ return ReviewerPredicate.reviewer(self());
}
if ("cc".equalsIgnoreCase(value)) {
@@ -625,7 +605,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
@Operator
- public Predicate<ChangeData> conflicts(String value) throws OrmException, QueryParseException {
+ public Predicate<ChangeData> conflicts(String value) throws QueryParseException {
List<Change> changes = parseChange(value);
List<Predicate<ChangeData>> or = new ArrayList<>(changes.size());
for (Change c : changes) {
@@ -746,16 +726,76 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
@Operator
+ public Predicate<ChangeData> ext(String ext) throws QueryParseException {
+ return extension(ext);
+ }
+
+ @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");
+ }
+
+ @Operator
+ public Predicate<ChangeData> onlyexts(String extList) throws QueryParseException {
+ return onlyextensions(extList);
+ }
+
+ @Operator
+ public Predicate<ChangeData> onlyextensions(String extList) throws QueryParseException {
+ if (args.getSchema().hasField(ChangeField.ONLY_EXTENSIONS)) {
+ return new FileExtensionListPredicate(extList);
+ }
+ throw new QueryParseException(
+ "'onlyextensions' operator is not supported by change index version");
+ }
+
+ @Operator
+ public Predicate<ChangeData> footer(String footer) throws QueryParseException {
+ if (args.getSchema().hasField(ChangeField.FOOTER)) {
+ return new FooterPredicate(footer);
+ }
+ throw new QueryParseException("'footer' operator is not supported by change index version");
+ }
+
+ @Operator
+ public Predicate<ChangeData> dir(String directory) throws QueryParseException {
+ return directory(directory);
+ }
+
+ @Operator
+ public Predicate<ChangeData> directory(String directory) throws QueryParseException {
+ if (args.getSchema().hasField(ChangeField.DIRECTORY)) {
+ 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");
+ }
+
+ @Operator
public Predicate<ChangeData> label(String name)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Set<Account.Id> accounts = null;
AccountGroup.UUID group = null;
// Parse for:
- // label:CodeReview=1,user=jsmith or
- // label:CodeReview=1,jsmith or
- // label:CodeReview=1,group=android_approvers or
- // label:CodeReview=1,android_approvers
+ // label:Code-Review=1,user=jsmith or
+ // label:Code-Review=1,jsmith or
+ // label:Code-Review=1,group=android_approvers or
+ // label:Code-Review=1,android_approvers
// user/groups without a label will first attempt to match user
// Special case: votes by owners can be tracked with ",owner":
// label:Code-Review+2,owner
@@ -847,7 +887,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> starredby(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return starredby(parseAccount(who));
}
@@ -865,7 +905,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> watchedby(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Set<Account.Id> m = parseAccount(who);
List<IsWatchedByPredicate> p = Lists.newArrayListWithCapacity(m.size());
@@ -890,7 +930,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> draftby(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Set<Account.Id> m = parseAccount(who);
List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
@@ -903,21 +943,26 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
return new HasDraftByPredicate(who);
}
- private boolean isSelf(String who) {
- return "self".equals(who) || "me".equals(who);
- }
-
@Operator
public Predicate<ChangeData> visibleto(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
if (isSelf(who)) {
return isVisible();
}
- Set<Account.Id> m = args.accountResolver.findAll(who);
- if (m.size() == 1) {
- return visibleto(args.userFactory.create(Iterables.getOnlyElement(m)));
- } else if (m.size() > 1) {
- throw error(String.format("\"%s\" resolves to multiple accounts", who));
+ Set<Account.Id> accounts = null;
+ try {
+ accounts = parseAccount(who);
+ } catch (QueryParseException e) {
+ if (e instanceof QueryRequiresAuthException) {
+ throw e;
+ }
+ }
+ if (accounts != null) {
+ if (accounts.size() == 1) {
+ return visibleto(args.userFactory.create(Iterables.getOnlyElement(accounts)));
+ } else if (accounts.size() > 1) {
+ throw error(String.format("\"%s\" resolves to multiple accounts", who));
+ }
}
// If its not an account, maybe its a group?
@@ -934,13 +979,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
public Predicate<ChangeData> visibleto(CurrentUser user) {
- return new ChangeIsVisibleToPredicate(
- args.db,
- args.notesFactory,
- user,
- args.permissionBackend,
- args.projectCache,
- args.anonymousUserProvider);
+ return args.changeIsVisbleToPredicateFactory.forUser(user);
}
public Predicate<ChangeData> isVisible() throws QueryParseException {
@@ -949,14 +988,14 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> o(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return owner(who);
}
@Operator
public Predicate<ChangeData> owner(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
- return owner(parseAccount(who));
+ throws QueryParseException, IOException, ConfigInvalidException {
+ return owner(parseAccount(who, (AccountState s) -> true));
}
private Predicate<ChangeData> owner(Set<Account.Id> who) {
@@ -968,7 +1007,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
private Predicate<ChangeData> ownerDefaultField(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Set<Account.Id> accounts = parseAccount(who);
if (accounts.size() > MAX_ACCOUNTS_PER_DEFAULT_FIELD) {
return Predicate.any();
@@ -978,8 +1017,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> assignee(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
- return assignee(parseAccount(who));
+ throws QueryParseException, IOException, ConfigInvalidException {
+ return assignee(parseAccount(who, (AccountState s) -> true));
}
private Predicate<ChangeData> assignee(Set<Account.Id> who) {
@@ -1013,23 +1052,23 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> r(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return reviewer(who);
}
@Operator
public Predicate<ChangeData> reviewer(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return reviewer(who, false);
}
private Predicate<ChangeData> reviewerDefaultField(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return reviewer(who, true);
}
private Predicate<ChangeData> reviewer(String who, boolean forDefaultField)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Predicate<ChangeData> byState =
reviewerByState(who, ReviewerStateInternal.REVIEWER, forDefaultField);
if (Objects.equals(byState, Predicate.<ChangeData>any())) {
@@ -1043,7 +1082,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> cc(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return reviewerByState(who, ReviewerStateInternal.CC, false);
}
@@ -1097,7 +1136,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> commentby(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return commentby(parseAccount(who));
}
@@ -1111,7 +1150,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> from(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Set<Account.Id> ownerIds = parseAccount(who);
return Predicate.or(owner(ownerIds), commentby(ownerIds));
}
@@ -1136,7 +1175,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> reviewedby(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
return IsReviewedPredicate.create(parseAccount(who));
}
@@ -1226,7 +1265,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
if (!Objects.equals(p, Predicate.<ChangeData>any())) {
predicates.add(p);
}
- } catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
+ } catch (StorageException | IOException | ConfigInvalidException | QueryParseException e) {
// Skip.
}
try {
@@ -1234,13 +1273,13 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
if (!Objects.equals(p, Predicate.<ChangeData>any())) {
predicates.add(p);
}
- } catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
+ } catch (StorageException | IOException | ConfigInvalidException | QueryParseException e) {
// Skip.
}
predicates.add(file(query));
try {
predicates.add(label(query));
- } catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
+ } catch (StorageException | IOException | ConfigInvalidException | QueryParseException e) {
// Skip.
}
predicates.add(commit(query));
@@ -1294,15 +1333,28 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
private Set<Account.Id> parseAccount(String who)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
- if (isSelf(who)) {
- return Collections.singleton(self());
+ throws QueryParseException, IOException, ConfigInvalidException {
+ try {
+ return args.accountResolver.resolve(who).asNonEmptyIdSet();
+ } catch (UnresolvableAccountException e) {
+ if (e.isSelf()) {
+ throw new QueryRequiresAuthException(e.getMessage(), e);
+ }
+ throw new QueryParseException(e.getMessage(), e);
}
- Set<Account.Id> matches = args.accountResolver.findAll(who);
- if (matches.isEmpty()) {
- throw error("User " + who + " not found");
+ }
+
+ private Set<Account.Id> parseAccount(
+ String who, java.util.function.Predicate<AccountState> activityFilter)
+ throws QueryParseException, IOException, ConfigInvalidException {
+ try {
+ return args.accountResolver.resolve(who, activityFilter).asNonEmptyIdSet();
+ } catch (UnresolvableAccountException e) {
+ if (e.isSelf()) {
+ throw new QueryRequiresAuthException(e.getMessage(), e);
+ }
+ throw new QueryParseException(e.getMessage(), e);
}
- return matches;
}
private GroupReference parseGroup(String group) throws QueryParseException {
@@ -1313,7 +1365,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
return g;
}
- private List<Change> parseChange(String value) throws OrmException, QueryParseException {
+ private List<Change> parseChange(String value) throws QueryParseException {
if (PAT_LEGACY_ID.matcher(value).matches()) {
return asChanges(args.queryProvider.get().byLegacyChangeId(Change.Id.parse(value)));
} else if (PAT_CHANGE_ID.matcher(value).matches()) {
@@ -1340,7 +1392,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
public Predicate<ChangeData> reviewerByState(
String who, ReviewerStateInternal state, boolean forDefaultField)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+ throws QueryParseException, IOException, ConfigInvalidException {
Predicate<ChangeData> reviewerByEmailPredicate = null;
if (args.index.getSchema().hasField(ChangeField.REVIEWER_BY_EMAIL)) {
Address address = Address.tryParse(who);
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java b/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
index 9a49ffe327..40c04773ed 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
@@ -17,32 +17,31 @@ package com.google.gerrit.server.query.change;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_LIMIT;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
import com.google.gerrit.extensions.common.PluginDefinedInfo;
-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.index.IndexConfig;
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.QueryProcessor;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.DynamicOptions;
import com.google.gerrit.server.DynamicOptions.DynamicBean;
import com.google.gerrit.server.account.AccountLimits;
+import com.google.gerrit.server.change.ChangeAttributeFactory;
+import com.google.gerrit.server.change.PluginDefinedAttributesFactories;
+import com.google.gerrit.server.change.PluginDefinedAttributesFactory;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeIndexRewriter;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -53,24 +52,10 @@ import java.util.Set;
* holding on to a single instance.
*/
public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
- implements DynamicOptions.BeanReceiver, PluginDefinedAttributesFactory {
- /**
- * Register a ChangeAttributeFactory in a config Module like this:
- *
- * <p>bind(ChangeAttributeFactory.class) .annotatedWith(Exports.named("export-name"))
- * .to(YourClass.class);
- */
- public interface ChangeAttributeFactory {
- PluginDefinedInfo create(ChangeData a, ChangeQueryProcessor qp, String plugin);
- }
-
- private final Provider<ReviewDb> db;
+ implements DynamicOptions.BeanReceiver, DynamicOptions.BeanProvider {
private final Provider<CurrentUser> userProvider;
- private final ChangeNotes.Factory notesFactory;
- private final DynamicMap<ChangeAttributeFactory> attributeFactories;
- private final PermissionBackend permissionBackend;
- private final ProjectCache projectCache;
- private final Provider<AnonymousUser> anonymousUserProvider;
+ private final ImmutableListMultimap<String, ChangeAttributeFactory> attributeFactoriesByPlugin;
+ private final ChangeIsVisibleToPredicate.Factory changeIsVisibleToPredicateFactory;
private final Map<String, DynamicBean> dynamicBeans = new HashMap<>();
static {
@@ -88,12 +73,8 @@ public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
IndexConfig indexConfig,
ChangeIndexCollection indexes,
ChangeIndexRewriter rewriter,
- Provider<ReviewDb> db,
- ChangeNotes.Factory notesFactory,
- DynamicMap<ChangeAttributeFactory> attributeFactories,
- PermissionBackend permissionBackend,
- ProjectCache projectCache,
- Provider<AnonymousUser> anonymousUserProvider) {
+ DynamicSet<ChangeAttributeFactory> attributeFactories,
+ ChangeIsVisibleToPredicate.Factory changeIsVisibleToPredicateFactory) {
super(
metricMaker,
ChangeSchemaDefinitions.INSTANCE,
@@ -102,13 +83,15 @@ public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
rewriter,
FIELD_LIMIT,
() -> limitsFactory.create(userProvider.get()).getQueryLimit());
- this.db = db;
this.userProvider = userProvider;
- this.notesFactory = notesFactory;
- this.attributeFactories = attributeFactories;
- this.permissionBackend = permissionBackend;
- this.projectCache = projectCache;
- this.anonymousUserProvider = anonymousUserProvider;
+ this.changeIsVisibleToPredicateFactory = changeIsVisibleToPredicateFactory;
+
+ ImmutableListMultimap.Builder<String, ChangeAttributeFactory> factoriesBuilder =
+ ImmutableListMultimap.builder();
+ // Eagerly call Extension#get() rather than storing Extensions, since that method invokes the
+ // Provider on every call, which could be expensive if we invoke it once for every change.
+ attributeFactories.entries().forEach(e -> factoriesBuilder.put(e.getPluginName(), e.get()));
+ attributeFactoriesByPlugin = factoriesBuilder.build();
}
@Override
@@ -128,46 +111,27 @@ public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
dynamicBeans.put(plugin, dynamicBean);
}
+ @Override
public DynamicBean getDynamicBean(String plugin) {
return dynamicBeans.get(plugin);
}
- @Override
- public List<PluginDefinedInfo> create(ChangeData cd) {
- List<PluginDefinedInfo> plugins = new ArrayList<>(attributeFactories.plugins().size());
- for (String plugin : attributeFactories.plugins()) {
- for (Provider<ChangeAttributeFactory> provider :
- attributeFactories.byPlugin(plugin).values()) {
- PluginDefinedInfo pda = null;
- try {
- pda = provider.get().create(cd, this, plugin);
- } catch (RuntimeException e) {
- /* Eat runtime exceptions so that queries don't fail. */
- }
- if (pda != null) {
- pda.name = plugin;
- plugins.add(pda);
- }
- }
- }
- if (plugins.isEmpty()) {
- plugins = null;
- }
- return plugins;
+ public PluginDefinedAttributesFactory getAttributesFactory() {
+ return this::buildPluginInfo;
+ }
+
+ private ImmutableList<PluginDefinedInfo> buildPluginInfo(ChangeData cd) {
+ return PluginDefinedAttributesFactories.createAll(
+ cd,
+ this,
+ attributeFactoriesByPlugin.entries().stream()
+ .map(e -> new Extension<>(e.getKey(), e::getValue)));
}
@Override
protected Predicate<ChangeData> enforceVisibility(Predicate<ChangeData> pred) {
return new AndChangeSource(
- pred,
- new ChangeIsVisibleToPredicate(
- db,
- notesFactory,
- userProvider.get(),
- permissionBackend,
- projectCache,
- anonymousUserProvider),
- start);
+ pred, changeIsVisibleToPredicateFactory.forUser(userProvider.get()), start);
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index 8dc17d3040..66790e7579 100644
--- a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -22,7 +22,6 @@ 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 com.google.gwtorm.server.OrmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -41,7 +40,7 @@ import java.util.TreeMap;
*/
public final class ChangeStatusPredicate extends ChangeIndexPredicate {
private static final String INVALID_STATUS = "__invalid__";
- private static final Predicate<ChangeData> NONE = new ChangeStatusPredicate(null);
+ static final Predicate<ChangeData> NONE = new ChangeStatusPredicate(null);
private static final TreeMap<String, Predicate<ChangeData>> PREDICATES;
private static final Predicate<ChangeData> CLOSED;
@@ -119,7 +118,7 @@ public final class ChangeStatusPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
return change != null && Objects.equals(status, change.getStatus());
}
diff --git a/java/com/google/gerrit/server/query/change/CommentByPredicate.java b/java/com/google/gerrit/server/query/change/CommentByPredicate.java
index 7ad7afe28a..0747bb2784 100644
--- a/java/com/google/gerrit/server/query/change/CommentByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentByPredicate.java
@@ -18,7 +18,6 @@ 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.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
import java.util.Objects;
public class CommentByPredicate extends ChangeIndexPredicate {
@@ -34,7 +33,7 @@ public class CommentByPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
for (ChangeMessage m : cd.messages()) {
if (Objects.equals(m.getAuthor(), id)) {
return true;
diff --git a/java/com/google/gerrit/server/query/change/CommentPredicate.java b/java/com/google/gerrit/server/query/change/CommentPredicate.java
index 5a6d186ad8..d193bb68bc 100644
--- a/java/com/google/gerrit/server/query/change/CommentPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentPredicate.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.query.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.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gwtorm.server.OrmException;
public class CommentPredicate extends ChangeIndexPredicate {
protected final ChangeIndex index;
@@ -30,7 +30,7 @@ public class CommentPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
try {
Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
@@ -39,7 +39,7 @@ public class CommentPredicate extends ChangeIndexPredicate {
}
}
} catch (QueryParseException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
return false;
diff --git a/java/com/google/gerrit/server/query/change/CommitPredicate.java b/java/com/google/gerrit/server/query/change/CommitPredicate.java
index d1ae529e53..567f58d59a 100644
--- a/java/com/google/gerrit/server/query/change/CommitPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -20,7 +20,6 @@ import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwtorm.server.OrmException;
public class CommitPredicate extends ChangeIndexPredicate {
static FieldDef<ChangeData, ?> commitField(String id) {
@@ -35,7 +34,7 @@ public class CommitPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
String id = getValue().toLowerCase();
for (PatchSet p : object.patchSets()) {
if (equals(p, id)) {
diff --git a/java/com/google/gerrit/server/query/change/CommitterPredicate.java b/java/com/google/gerrit/server/query/change/CommitterPredicate.java
index 797cb9d04f..1dcf97f4d0 100644
--- a/java/com/google/gerrit/server/query/change/CommitterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommitterPredicate.java
@@ -18,8 +18,6 @@ import static com.google.gerrit.server.index.change.ChangeField.COMMITTER;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_COMMITTER;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
public class CommitterPredicate extends ChangeIndexPredicate {
public CommitterPredicate(String value) {
@@ -27,12 +25,8 @@ public class CommitterPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- try {
- return ChangeField.getCommitterParts(object).contains(getValue().toLowerCase());
- } catch (IOException e) {
- throw new OrmException(e);
- }
+ public boolean match(ChangeData object) {
+ return ChangeField.getCommitterParts(object).contains(getValue().toLowerCase());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/ConflictKey.java b/java/com/google/gerrit/server/query/change/ConflictKey.java
index 42f5b13314..01fdbfab69 100644
--- a/java/com/google/gerrit/server/query/change/ConflictKey.java
+++ b/java/com/google/gerrit/server/query/change/ConflictKey.java
@@ -20,10 +20,10 @@ import com.google.common.base.Converter;
import com.google.common.base.Enums;
import com.google.common.collect.Ordering;
import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.proto.Protos;
import com.google.gerrit.server.cache.proto.Cache.ConflictKeyProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
@@ -70,7 +70,7 @@ public abstract class ConflictKey {
@Override
public byte[] serialize(ConflictKey object) {
ObjectIdConverter idConverter = ObjectIdConverter.create();
- return ProtoCacheSerializers.toByteArray(
+ return Protos.toByteArray(
ConflictKeyProto.newBuilder()
.setCommit(idConverter.toByteString(object.commit()))
.setOtherCommit(idConverter.toByteString(object.otherCommit()))
@@ -81,7 +81,7 @@ public abstract class ConflictKey {
@Override
public ConflictKey deserialize(byte[] in) {
- ConflictKeyProto proto = ProtoCacheSerializers.parseUnchecked(ConflictKeyProto.parser(), in);
+ ConflictKeyProto proto = Protos.parseUnchecked(ConflictKeyProto.parser(), in);
ObjectIdConverter idConverter = ObjectIdConverter.create();
return create(
idConverter.fromByteString(proto.getCommit()),
diff --git a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index 7dc7a0b080..d415f71627 100644
--- a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -14,13 +14,20 @@
package com.google.gerrit.server.query.change;
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.flogger.LazyArgs.lazy;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitTypeRecord;
+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;
@@ -29,7 +36,6 @@ import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
import com.google.gerrit.server.submit.IntegrationException;
import com.google.gerrit.server.submit.SubmitDryRun;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -41,20 +47,27 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
public class ConflictsPredicate {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
// UI code may depend on this string, so use caution when changing.
protected static final String TOO_MANY_FILES = "too many files to find conflicts";
private ConflictsPredicate() {}
public static Predicate<ChangeData> create(Arguments args, String value, Change c)
- throws QueryParseException, OrmException {
+ throws QueryParseException {
ChangeData cd;
List<String> files;
try {
- cd = args.changeDataFactory.create(args.db.get(), c);
+ cd = args.changeDataFactory.create(c);
files = cd.currentFilePaths();
- } catch (IOException e) {
- throw new OrmException(e);
+ } catch (StorageException e) {
+ warnWithOccasionalStackTrace(
+ e,
+ "Error constructing conflicts predicates for change %s in %s",
+ c.getId(),
+ c.getProject());
+ return ChangeIndexPredicate.none();
}
if (3 + files.size() > args.indexConfig.maxTerms()) {
@@ -95,51 +108,66 @@ public class ConflictsPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- Change otherChange = object.change();
- if (otherChange == null || !otherChange.getDest().equals(dest)) {
- return false;
- }
-
- SubmitTypeRecord str = object.submitTypeRecord();
- if (!str.isOk()) {
- return false;
- }
-
- ProjectState projectState;
+ public boolean match(ChangeData object) {
+ Change.Id id = object.getId();
+ Project.NameKey otherProject = null;
+ ObjectId other = null;
try {
- projectState = changeDataCache.getProjectState();
- } catch (NoSuchProjectException e) {
- return false;
- }
+ Change otherChange = object.change();
+ if (otherChange == null || !otherChange.getDest().equals(dest)) {
+ return false;
+ }
+ otherProject = otherChange.getProject();
- ObjectId other = ObjectId.fromString(object.currentPatchSet().getRevision().get());
- ConflictKey conflictsKey =
- ConflictKey.create(
- changeDataCache.getTestAgainst(),
- other,
- str.type,
- projectState.is(BooleanProjectConfig.USE_CONTENT_MERGE));
- Boolean maybeConflicts = args.conflictsCache.getIfPresent(conflictsKey);
- if (maybeConflicts != null) {
- return maybeConflicts;
- }
+ SubmitTypeRecord str = object.submitTypeRecord();
+ if (!str.isOk()) {
+ return false;
+ }
- try (Repository repo = args.repoManager.openRepository(otherChange.getProject());
- CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
- boolean conflicts =
- !args.submitDryRun.run(
- str.type,
- repo,
- rw,
- otherChange.getDest(),
+ ProjectState projectState;
+ try {
+ projectState = changeDataCache.getProjectState();
+ } catch (NoSuchProjectException e) {
+ return false;
+ }
+
+ other = ObjectId.fromString(object.currentPatchSet().getRevision().get());
+ ConflictKey conflictsKey =
+ ConflictKey.create(
changeDataCache.getTestAgainst(),
other,
- getAlreadyAccepted(repo, rw));
- args.conflictsCache.put(conflictsKey, conflicts);
- return conflicts;
- } catch (IntegrationException | NoSuchProjectException | IOException e) {
- throw new OrmException(e);
+ str.type,
+ projectState.is(BooleanProjectConfig.USE_CONTENT_MERGE));
+ Boolean maybeConflicts = args.conflictsCache.getIfPresent(conflictsKey);
+ if (maybeConflicts != null) {
+ return maybeConflicts;
+ }
+
+ try (Repository repo = args.repoManager.openRepository(otherChange.getProject());
+ CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
+ boolean conflicts =
+ !args.submitDryRun.run(
+ null,
+ str.type,
+ repo,
+ rw,
+ otherChange.getDest(),
+ changeDataCache.getTestAgainst(),
+ other,
+ getAlreadyAccepted(repo, rw));
+ args.conflictsCache.put(conflictsKey, conflicts);
+ return conflicts;
+ }
+ } catch (IntegrationException | NoSuchProjectException | StorageException | IOException e) {
+ ObjectId finalOther = other;
+ warnWithOccasionalStackTrace(
+ e,
+ "Merge failure checking conflicts of change %s in %s (%s): %s",
+ id,
+ firstNonNull(otherProject, "unknown project"),
+ lazy(() -> finalOther != null ? finalOther.name() : "unknown commit"),
+ e.getMessage());
+ return false;
}
}
@@ -158,7 +186,7 @@ public class ConflictsPredicate {
accepted.add(rw.parseCommit(tip));
}
return accepted;
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
throw new IntegrationException("Failed to determine already accepted commits.", e);
}
}
@@ -177,7 +205,7 @@ public class ConflictsPredicate {
this.projectCache = projectCache;
}
- ObjectId getTestAgainst() throws OrmException {
+ ObjectId getTestAgainst() {
if (testAgainst == null) {
testAgainst = ObjectId.fromString(cd.currentPatchSet().getRevision().get());
}
@@ -201,4 +229,13 @@ public class ConflictsPredicate {
return alreadyAccepted;
}
}
+
+ private static void warnWithOccasionalStackTrace(Throwable cause, String format, Object... args) {
+ logger.atWarning().logVarargs(format, args);
+ logger
+ .atWarning()
+ .withCause(cause)
+ .atMostEvery(1, MINUTES)
+ .logVarargs("(Re-logging with stack trace) " + format, args);
+ }
}
diff --git a/java/com/google/gerrit/server/query/change/DeletedPredicate.java b/java/com/google/gerrit/server/query/change/DeletedPredicate.java
index 6232fc5815..d4bdc67788 100644
--- a/java/com/google/gerrit/server/query/change/DeletedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DeletedPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class DeletedPredicate extends IntegerRangeChangePredicate {
public DeletedPredicate(String value) throws QueryParseException {
@@ -24,7 +23,7 @@ public class DeletedPredicate extends IntegerRangeChangePredicate {
}
@Override
- protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ protected Integer getValueInt(ChangeData changeData) {
return ChangeField.DELETED.get(changeData);
}
}
diff --git a/java/com/google/gerrit/server/query/change/DeltaPredicate.java b/java/com/google/gerrit/server/query/change/DeltaPredicate.java
index aae0a201b2..821ec944a1 100644
--- a/java/com/google/gerrit/server/query/change/DeltaPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DeltaPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class DeltaPredicate extends IntegerRangeChangePredicate {
public DeltaPredicate(String value) throws QueryParseException {
@@ -24,7 +23,7 @@ public class DeltaPredicate extends IntegerRangeChangePredicate {
}
@Override
- protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ protected Integer getValueInt(ChangeData changeData) {
return ChangeField.DELTA.get(changeData);
}
}
diff --git a/java/com/google/gerrit/server/query/change/DestinationPredicate.java b/java/com/google/gerrit/server/query/change/DestinationPredicate.java
index a824a87ceb..702745e9b4 100644
--- a/java/com/google/gerrit/server/query/change/DestinationPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DestinationPredicate.java
@@ -17,7 +17,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.PostFilterPredicate;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
import java.util.Set;
public class DestinationPredicate extends PostFilterPredicate<ChangeData> {
@@ -29,7 +28,7 @@ public class DestinationPredicate extends PostFilterPredicate<ChangeData> {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/DirectoryPredicate.java b/java/com/google/gerrit/server/query/change/DirectoryPredicate.java
new file mode 100644
index 0000000000..3ab3e2684c
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/DirectoryPredicate.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.query.change;
+
+import com.google.common.base.CharMatcher;
+import com.google.gerrit.server.index.change.ChangeField;
+import java.util.Locale;
+
+public class DirectoryPredicate extends ChangeIndexPredicate {
+ private static String clean(String directory) {
+ return CharMatcher.is('/').trimFrom(directory).toLowerCase(Locale.US);
+ }
+
+ DirectoryPredicate(String value) {
+ super(ChangeField.DIRECTORY, clean(value));
+ }
+
+ @Override
+ public boolean match(ChangeData cd) {
+ return ChangeField.getDirectories(cd).contains(value);
+ }
+
+ @Override
+ public int getCost() {
+ return 0;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/EditByPredicate.java b/java/com/google/gerrit/server/query/change/EditByPredicate.java
index 3238dc9faa..dfe73105e2 100644
--- a/java/com/google/gerrit/server/query/change/EditByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EditByPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class EditByPredicate extends ChangeIndexPredicate {
protected final Account.Id id;
@@ -27,7 +26,7 @@ public class EditByPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.editsByUser().contains(id);
}
diff --git a/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java b/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
index b5a2d05ad5..9c033b60ad 100644
--- a/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
@@ -17,7 +17,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
-import com.google.gwtorm.server.OrmException;
public class EqualsFilePredicate extends ChangeIndexPredicate {
public static Predicate<ChangeData> create(Arguments args, String value) {
@@ -33,7 +32,7 @@ public class EqualsFilePredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
return ChangeField.getFileParts(object).contains(value);
}
diff --git a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index 54e22f328d..acf2e25d03 100644
--- a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -21,7 +21,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -29,15 +28,12 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Provider;
import java.io.IOException;
public class EqualsLabelPredicate extends ChangeIndexPredicate {
protected final ProjectCache projectCache;
protected final PermissionBackend permissionBackend;
protected final IdentifiedUser.GenericFactory userFactory;
- protected final Provider<ReviewDb> dbProvider;
protected final String label;
protected final int expVal;
protected final Account.Id account;
@@ -49,7 +45,6 @@ public class EqualsLabelPredicate extends ChangeIndexPredicate {
this.permissionBackend = args.permissionBackend;
this.projectCache = args.projectCache;
this.userFactory = args.userFactory;
- this.dbProvider = args.dbProvider;
this.group = args.group;
this.label = label;
this.expVal = expVal;
@@ -57,7 +52,7 @@ public class EqualsLabelPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change c = object.change();
if (c == null) {
// The change has disappeared.
@@ -123,8 +118,7 @@ public class EqualsLabelPredicate extends ChangeIndexPredicate {
// Check the user has 'READ' permission.
try {
- PermissionBackend.ForChange perm =
- permissionBackend.absentUser(approver).database(dbProvider).change(cd);
+ PermissionBackend.ForChange perm = permissionBackend.absentUser(approver).change(cd);
ProjectState projectState = projectCache.checkedGet(cd.project());
if (projectState == null || !projectState.statePermitsRead()) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java b/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
index fc00283e87..76936fae5f 100644
--- a/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
@@ -15,10 +15,7 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
import java.util.Collections;
-import java.util.List;
public class EqualsPathPredicate extends ChangeIndexPredicate {
public EqualsPathPredicate(String fieldName, String value) {
@@ -26,14 +23,8 @@ public class EqualsPathPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- List<String> files;
- try {
- files = object.currentFilePaths();
- } catch (IOException e) {
- throw new OrmException(e);
- }
- return Collections.binarySearch(files, value) >= 0;
+ public boolean match(ChangeData object) {
+ return Collections.binarySearch(object.currentFilePaths(), value) >= 0;
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java b/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java
index bca5d3bfba..c1b6928c75 100644
--- a/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java
@@ -18,8 +18,6 @@ import static com.google.gerrit.server.index.change.ChangeField.EXACT_AUTHOR;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_EXACTAUTHOR;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
import java.util.Locale;
public class ExactAuthorPredicate extends ChangeIndexPredicate {
@@ -28,12 +26,8 @@ public class ExactAuthorPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- try {
- return ChangeField.getAuthorNameAndEmail(object).contains(getValue());
- } catch (IOException e) {
- throw new OrmException(e);
- }
+ public boolean match(ChangeData object) {
+ return ChangeField.getAuthorNameAndEmail(object).contains(getValue());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java b/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java
index 3fae5e5286..dac63aff8d 100644
--- a/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java
@@ -18,8 +18,6 @@ import static com.google.gerrit.server.index.change.ChangeField.EXACT_COMMITTER;
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_EXACTCOMMITTER;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
import java.util.Locale;
public class ExactCommitterPredicate extends ChangeIndexPredicate {
@@ -28,12 +26,8 @@ public class ExactCommitterPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- try {
- return ChangeField.getCommitterNameAndEmail(object).contains(getValue());
- } catch (IOException e) {
- throw new OrmException(e);
- }
+ public boolean match(ChangeData object) {
+ return ChangeField.getCommitterNameAndEmail(object).contains(getValue());
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java b/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
index 138cce50e0..c6ade75e94 100644
--- a/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
@@ -17,7 +17,6 @@ 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.gwtorm.server.OrmException;
public class ExactTopicPredicate extends ChangeIndexPredicate {
public ExactTopicPredicate(String topic) {
@@ -25,7 +24,7 @@ public class ExactTopicPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java b/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java
new file mode 100644
index 0000000000..bddd2ec92c
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java
@@ -0,0 +1,44 @@
+// 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 static java.util.stream.Collectors.joining;
+
+import com.google.common.base.Splitter;
+import com.google.gerrit.server.index.change.ChangeField;
+
+public class FileExtensionListPredicate extends ChangeIndexPredicate {
+ private static String clean(String extList) {
+ return Splitter.on(',').splitToList(extList).stream()
+ .map(FileExtensionPredicate::clean)
+ .distinct()
+ .sorted()
+ .collect(joining(","));
+ }
+
+ FileExtensionListPredicate(String value) {
+ super(ChangeField.ONLY_EXTENSIONS, clean(value));
+ }
+
+ @Override
+ public boolean match(ChangeData cd) {
+ return ChangeField.getAllExtensionsAsList(cd).equals(value);
+ }
+
+ @Override
+ public int getCost() {
+ return 0;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/FileExtensionPredicate.java b/java/com/google/gerrit/server/query/change/FileExtensionPredicate.java
new file mode 100644
index 0000000000..ee573a7fe5
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/FileExtensionPredicate.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.query.change;
+
+import com.google.gerrit.server.index.change.ChangeField;
+import java.util.Locale;
+
+public class FileExtensionPredicate extends ChangeIndexPredicate {
+ static String clean(String ext) {
+ if (ext.startsWith(".")) {
+ ext = ext.substring(1);
+ }
+ return ext.toLowerCase(Locale.US);
+ }
+
+ FileExtensionPredicate(String value) {
+ super(ChangeField.EXTENSION, clean(value));
+ }
+
+ @Override
+ public boolean match(ChangeData object) {
+ return ChangeField.getExtensions(object).contains(value);
+ }
+
+ @Override
+ public int getCost() {
+ return 0;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java b/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.java
new file mode 100644
index 0000000000..d886bafa0c
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/FileWithNoExtensionInElasticPredicate.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.query.change;
+
+import com.google.gerrit.index.query.PostFilterPredicate;
+import com.google.gerrit.server.index.change.ChangeField;
+
+public class FileWithNoExtensionInElasticPredicate extends PostFilterPredicate<ChangeData> {
+
+ private static final String NO_EXT = "";
+
+ public FileWithNoExtensionInElasticPredicate() {
+ super(ChangeField.EXTENSION.getName(), NO_EXT);
+ }
+
+ @Override
+ public boolean match(ChangeData cd) {
+ return ChangeField.getExtensions(cd).contains(NO_EXT);
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/FooterPredicate.java b/java/com/google/gerrit/server/query/change/FooterPredicate.java
new file mode 100644
index 0000000000..4d7588cf61
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/FooterPredicate.java
@@ -0,0 +1,45 @@
+// 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.server.index.change.ChangeField;
+import java.util.Locale;
+
+public class FooterPredicate extends ChangeIndexPredicate {
+ private static String clean(String value) {
+ int indexEquals = value.indexOf('=');
+ int indexColon = value.indexOf(':');
+
+ // footer key cannot contain '='
+ if (indexEquals > 0 && (indexEquals < indexColon || indexColon < 0)) {
+ value = value.substring(0, indexEquals) + ": " + value.substring(indexEquals + 1);
+ }
+ return value.toLowerCase(Locale.US);
+ }
+
+ FooterPredicate(String value) {
+ super(ChangeField.FOOTER, clean(value));
+ }
+
+ @Override
+ public boolean match(ChangeData cd) {
+ return ChangeField.getFooters(cd).contains(value);
+ }
+
+ @Override
+ public int getCost() {
+ return 0;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
index 545b6689f6..140f26b165 100644
--- a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
@@ -17,12 +17,12 @@ 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.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;
-import com.google.gwtorm.server.OrmException;
public class FuzzyTopicPredicate extends ChangeIndexPredicate {
protected final ChangeIndex index;
@@ -33,7 +33,7 @@ public class FuzzyTopicPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
Change change = cd.change();
if (change == null) {
return false;
@@ -48,7 +48,7 @@ public class FuzzyTopicPredicate extends ChangeIndexPredicate {
index.getSource(and(thisId, this), IndexedChangeQuery.oneResult()).read();
return !Iterables.isEmpty(results);
} catch (QueryParseException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/query/change/GroupPredicate.java b/java/com/google/gerrit/server/query/change/GroupPredicate.java
index d2645dcf5b..7f7bcff528 100644
--- a/java/com/google/gerrit/server/query/change/GroupPredicate.java
+++ b/java/com/google/gerrit/server/query/change/GroupPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
import java.util.List;
public class GroupPredicate extends ChangeIndexPredicate {
@@ -25,7 +24,7 @@ public class GroupPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
for (PatchSet ps : cd.patchSets()) {
List<String> groups = ps.getGroups();
if (groups != null && groups.contains(getValue())) {
diff --git a/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java b/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
index e422b74418..e57a8b39d0 100644
--- a/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class HasDraftByPredicate extends ChangeIndexPredicate {
protected final Account.Id accountId;
@@ -27,7 +26,7 @@ public class HasDraftByPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.draftsByUser().contains(accountId);
}
diff --git a/java/com/google/gerrit/server/query/change/HasStarsPredicate.java b/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
index b17fffd0b3..0c99cdf0bc 100644
--- a/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class HasStarsPredicate extends ChangeIndexPredicate {
protected final Account.Id accountId;
@@ -27,7 +26,7 @@ public class HasStarsPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.stars().containsKey(accountId);
}
diff --git a/java/com/google/gerrit/server/query/change/HashtagPredicate.java b/java/com/google/gerrit/server/query/change/HashtagPredicate.java
index 95ecf89469..1fe4af4a05 100644
--- a/java/com/google/gerrit/server/query/change/HashtagPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HashtagPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.server.change.HashtagsUtil;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class HashtagPredicate extends ChangeIndexPredicate {
public HashtagPredicate(String hashtag) {
@@ -26,7 +25,7 @@ public class HashtagPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
for (String hashtag : object.notes().load().getHashtags()) {
if (hashtag.equalsIgnoreCase(getValue())) {
return true;
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 495d27c800..74a0f71d1a 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -25,7 +25,6 @@ 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.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.query.InternalQuery;
import com.google.gerrit.index.query.Predicate;
@@ -33,10 +32,8 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -46,6 +43,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -56,7 +54,7 @@ import org.eclipse.jgit.lib.Repository;
* <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
* holding on to a single instance.
*/
-public class InternalChangeQuery extends InternalQuery<ChangeData> {
+public class InternalChangeQuery extends InternalQuery<ChangeData, InternalChangeQuery> {
private static Predicate<ChangeData> ref(Branch.NameKey branch) {
return new RefPredicate(branch.get());
}
@@ -92,44 +90,19 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
this.notesFactory = notesFactory;
}
- @Override
- public InternalChangeQuery setLimit(int n) {
- super.setLimit(n);
- return this;
- }
-
- @Override
- public InternalChangeQuery enforceVisibility(boolean enforce) {
- super.enforceVisibility(enforce);
- return this;
- }
-
- @SafeVarargs
- @Override
- public final InternalChangeQuery setRequestedFields(FieldDef<ChangeData, ?>... fields) {
- super.setRequestedFields(fields);
- return this;
- }
-
- @Override
- public InternalChangeQuery noFields() {
- super.noFields();
- return this;
- }
-
- public List<ChangeData> byKey(Change.Key key) throws OrmException {
+ public List<ChangeData> byKey(Change.Key key) {
return byKeyPrefix(key.get());
}
- public List<ChangeData> byKeyPrefix(String prefix) throws OrmException {
+ public List<ChangeData> byKeyPrefix(String prefix) {
return query(new ChangeIdPredicate(prefix));
}
- public List<ChangeData> byLegacyChangeId(Change.Id id) throws OrmException {
+ public List<ChangeData> byLegacyChangeId(Change.Id id) {
return query(new LegacyChangeIdPredicate(id));
}
- public List<ChangeData> byLegacyChangeIds(Collection<Change.Id> ids) throws OrmException {
+ 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));
@@ -137,12 +110,11 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
return query(or(preds));
}
- public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) throws OrmException {
+ public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) {
return query(byBranchKeyPred(branch, key));
}
- public List<ChangeData> byBranchKeyOpen(Project.NameKey project, String branch, Change.Key key)
- throws OrmException {
+ public List<ChangeData> byBranchKeyOpen(Project.NameKey project, String branch, Change.Key key) {
return query(and(byBranchKeyPred(new Branch.NameKey(project, branch), key), open()));
}
@@ -155,24 +127,22 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
return and(ref(branch), project(branch.getParentKey()), change(key));
}
- public List<ChangeData> byProject(Project.NameKey project) throws OrmException {
+ public List<ChangeData> byProject(Project.NameKey project) {
return query(project(project));
}
- public List<ChangeData> byBranchOpen(Branch.NameKey branch) throws OrmException {
+ public List<ChangeData> byBranchOpen(Branch.NameKey branch) {
return query(and(ref(branch), project(branch.getParentKey()), open()));
}
- public List<ChangeData> byBranchNew(Branch.NameKey branch) throws OrmException {
+ public List<ChangeData> byBranchNew(Branch.NameKey branch) {
return query(and(ref(branch), project(branch.getParentKey()), status(Change.Status.NEW)));
}
public Iterable<ChangeData> byCommitsOnBranchNotMerged(
- Repository repo, ReviewDb db, Branch.NameKey branch, Collection<String> hashes)
- throws OrmException, IOException {
+ Repository repo, Branch.NameKey branch, Collection<String> hashes) throws IOException {
return byCommitsOnBranchNotMerged(
repo,
- db,
branch,
hashes,
// Account for all commit predicates plus ref, project, status.
@@ -181,21 +151,16 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
@VisibleForTesting
Iterable<ChangeData> byCommitsOnBranchNotMerged(
- Repository repo,
- ReviewDb db,
- Branch.NameKey branch,
- Collection<String> hashes,
- int indexLimit)
- throws OrmException, IOException {
+ Repository repo, Branch.NameKey branch, Collection<String> hashes, int indexLimit)
+ throws IOException {
if (hashes.size() > indexLimit) {
- return byCommitsOnBranchNotMergedFromDatabase(repo, db, branch, hashes);
+ return byCommitsOnBranchNotMergedFromDatabase(repo, branch, hashes);
}
return byCommitsOnBranchNotMergedFromIndex(branch, hashes);
}
private Iterable<ChangeData> byCommitsOnBranchNotMergedFromDatabase(
- Repository repo, ReviewDb db, Branch.NameKey branch, Collection<String> hashes)
- throws OrmException, IOException {
+ Repository repo, Branch.NameKey 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)) {
@@ -215,18 +180,17 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
List<ChangeNotes> notes =
notesFactory.create(
- db,
branch.getParentKey(),
changeIds,
cn -> {
Change c = cn.getChange();
- return c.getDest().equals(branch) && c.getStatus() != Change.Status.MERGED;
+ return c.getDest().equals(branch) && !c.isMerged();
});
- return Lists.transform(notes, n -> changeDataFactory.create(db, n));
+ return Lists.transform(notes, n -> changeDataFactory.create(n));
}
private Iterable<ChangeData> byCommitsOnBranchNotMergedFromIndex(
- Branch.NameKey branch, Collection<String> hashes) throws OrmException {
+ Branch.NameKey branch, Collection<String> hashes) {
return query(
and(
ref(branch),
@@ -243,50 +207,45 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
return commits;
}
- public List<ChangeData> byProjectOpen(Project.NameKey project) throws OrmException {
+ public List<ChangeData> byProjectOpen(Project.NameKey project) {
return query(and(project(project), open()));
}
- public List<ChangeData> byTopicOpen(String topic) throws OrmException {
+ public List<ChangeData> byTopicOpen(String topic) {
return query(and(new ExactTopicPredicate(topic), open()));
}
- public List<ChangeData> byCommit(ObjectId id) throws OrmException {
+ public List<ChangeData> byCommit(ObjectId id) {
return byCommit(id.name());
}
- public List<ChangeData> byCommit(String hash) throws OrmException {
+ public List<ChangeData> byCommit(String hash) {
return query(commit(hash));
}
- public List<ChangeData> byProjectCommit(Project.NameKey project, ObjectId id)
- throws OrmException {
+ public List<ChangeData> byProjectCommit(Project.NameKey project, ObjectId id) {
return byProjectCommit(project, id.name());
}
- public List<ChangeData> byProjectCommit(Project.NameKey project, String hash)
- throws OrmException {
+ public List<ChangeData> byProjectCommit(Project.NameKey project, String hash) {
return query(and(project(project), commit(hash)));
}
- public List<ChangeData> byProjectCommits(Project.NameKey project, List<String> hashes)
- throws OrmException {
+ public List<ChangeData> byProjectCommits(Project.NameKey project, List<String> hashes) {
int n = indexConfig.maxTerms() - 1;
checkArgument(hashes.size() <= n, "cannot exceed %s commits", n);
return query(and(project(project), or(commits(hashes))));
}
- public List<ChangeData> byBranchCommit(String project, String branch, String hash)
- throws OrmException {
+ public List<ChangeData> byBranchCommit(String project, String branch, String hash) {
return query(byBranchCommitPred(project, branch, hash));
}
- public List<ChangeData> byBranchCommit(Branch.NameKey branch, String hash) throws OrmException {
+ public List<ChangeData> byBranchCommit(Branch.NameKey branch, String hash) {
return byBranchCommit(branch.getParentKey().get(), branch.get(), hash);
}
- public List<ChangeData> byBranchCommitOpen(String project, String branch, String hash)
- throws OrmException {
+ public List<ChangeData> byBranchCommitOpen(String project, String branch, String hash) {
return query(and(byBranchCommitPred(project, branch, hash), open()));
}
@@ -300,41 +259,48 @@ public class InternalChangeQuery extends InternalQuery<ChangeData> {
return and(new ProjectPredicate(project), new RefPredicate(branch), commit(hash));
}
- public List<ChangeData> bySubmissionId(String cs) throws OrmException {
+ public List<ChangeData> bySubmissionId(String cs) {
if (Strings.isNullOrEmpty(cs)) {
return Collections.emptyList();
}
return query(new SubmissionIdPredicate(cs));
}
- private List<ChangeData> byProjectGroups(Project.NameKey project, Collection<String> groups)
- throws OrmException {
+ private static Predicate<ChangeData> byProjectGroupsPredicate(
+ IndexConfig indexConfig, Project.NameKey project, Collection<String> groups) {
int n = indexConfig.maxTerms() - 1;
checkArgument(groups.size() <= n, "cannot exceed %s groups", n);
List<GroupPredicate> groupPredicates = new ArrayList<>(groups.size());
for (String g : groups) {
groupPredicates.add(new GroupPredicate(g));
}
- return query(and(project(project), or(groupPredicates)));
+ return and(project(project), or(groupPredicates));
}
- // Batching via multiple queries requires passing in a Provider since the underlying
- // QueryProcessor instance is not reusable.
public static List<ChangeData> byProjectGroups(
Provider<InternalChangeQuery> queryProvider,
IndexConfig indexConfig,
Project.NameKey project,
- Collection<String> groups)
- throws OrmException {
+ Collection<String> groups) {
+ // These queries may be complex along multiple dimensions:
+ // * Many groups per change, if there are very many patch sets. This requires partitioning the
+ // list of predicates and combining results.
+ // * Many changes with the same set of groups, if the relation chain is very long. This
+ // requires querying exhaustively with pagination.
+ // For both cases, we need to invoke the queryProvider multiple times, since each
+ // InternalChangeQuery is single-use.
+
+ Supplier<InternalChangeQuery> querySupplier = () -> queryProvider.get().enforceVisibility(true);
int batchSize = indexConfig.maxTerms() - 1;
if (groups.size() <= batchSize) {
- return queryProvider.get().enforceVisibility(true).byProjectGroups(project, groups);
+ return queryExhaustively(
+ querySupplier, byProjectGroupsPredicate(indexConfig, project, groups));
}
Set<Change.Id> seen = new HashSet<>();
List<ChangeData> result = new ArrayList<>();
for (List<String> part : Iterables.partition(groups, batchSize)) {
for (ChangeData cd :
- queryProvider.get().enforceVisibility(true).byProjectGroups(project, part)) {
+ queryExhaustively(querySupplier, byProjectGroupsPredicate(indexConfig, project, part))) {
if (!seen.add(cd.getId())) {
result.add(cd);
}
diff --git a/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java b/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
index 7ff5a28600..ddb6f329b9 100644
--- a/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
@@ -19,7 +19,6 @@ import static com.google.gerrit.server.index.change.ChangeField.REVIEWEDBY;
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.gwtorm.server.OrmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -48,7 +47,7 @@ public class IsReviewedPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
Set<Account.Id> reviewedBy = cd.reviewedBy();
return !reviewedBy.isEmpty() ? reviewedBy.contains(id) : id.equals(NOT_REVIEWED);
}
diff --git a/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java b/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
index 225dc454da..27309afbcd 100644
--- a/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class IsUnresolvedPredicate extends IntegerRangeChangePredicate {
public IsUnresolvedPredicate() throws QueryParseException {
@@ -28,7 +27,7 @@ public class IsUnresolvedPredicate extends IntegerRangeChangePredicate {
}
@Override
- protected Integer getValueInt(ChangeData changeData) throws OrmException {
+ protected Integer getValueInt(ChangeData changeData) {
return ChangeField.UNRESOLVED_COMMENT_COUNT.get(changeData);
}
}
diff --git a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
index 8789b2bfe8..c52d2a925c 100644
--- a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
@@ -81,7 +81,7 @@ public class IsWatchedByPredicate extends AndPredicate<ChangeData> {
}
}
if (r.isEmpty()) {
- return none();
+ return ImmutableList.of(ChangeIndexPredicate.none());
} else if (checkIsVisible) {
return ImmutableList.of(or(r), builder.isVisible());
} else {
@@ -95,12 +95,7 @@ public class IsWatchedByPredicate extends AndPredicate<ChangeData> {
if (user.isIdentifiedUser()) {
return user.asIdentifiedUser().state().getProjectWatches().keySet();
}
- return Collections.<ProjectWatchKey>emptySet();
- }
-
- protected static List<Predicate<ChangeData>> none() {
- Predicate<ChangeData> any = any();
- return ImmutableList.of(not(any));
+ return Collections.emptySet();
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/LabelPredicate.java b/java/com/google/gerrit/server/query/change/LabelPredicate.java
index f8bd2e3e91..b5d375cd64 100644
--- a/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -21,12 +21,10 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.LabelVote;
-import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -38,7 +36,6 @@ public class LabelPredicate extends OrPredicate<ChangeData> {
protected final ProjectCache projectCache;
protected final PermissionBackend permissionBackend;
protected final IdentifiedUser.GenericFactory userFactory;
- protected final Provider<ReviewDb> dbProvider;
protected final String value;
protected final Set<Account.Id> accounts;
protected final AccountGroup.UUID group;
@@ -47,14 +44,12 @@ public class LabelPredicate extends OrPredicate<ChangeData> {
ProjectCache projectCache,
PermissionBackend permissionBackend,
IdentifiedUser.GenericFactory userFactory,
- Provider<ReviewDb> dbProvider,
String value,
Set<Account.Id> accounts,
AccountGroup.UUID group) {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.userFactory = userFactory;
- this.dbProvider = dbProvider;
this.value = value;
this.accounts = accounts;
this.group = group;
@@ -82,8 +77,7 @@ public class LabelPredicate extends OrPredicate<ChangeData> {
AccountGroup.UUID group) {
super(
predicates(
- new Args(
- a.projectCache, a.permissionBackend, a.userFactory, a.db, value, accounts, group)));
+ new Args(a.projectCache, a.permissionBackend, a.userFactory, value, accounts, group)));
this.value = value;
}
diff --git a/java/com/google/gerrit/server/query/change/MessagePredicate.java b/java/com/google/gerrit/server/query/change/MessagePredicate.java
index 0cfcedbfb8..0bd8c881c4 100644
--- a/java/com/google/gerrit/server/query/change/MessagePredicate.java
+++ b/java/com/google/gerrit/server/query/change/MessagePredicate.java
@@ -14,12 +14,12 @@
package com.google.gerrit.server.query.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.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndex;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gwtorm.server.OrmException;
/** Predicate to match changes that contains specified text in commit messages body. */
public class MessagePredicate extends ChangeIndexPredicate {
@@ -31,7 +31,7 @@ public class MessagePredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
try {
Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
@@ -40,7 +40,7 @@ public class MessagePredicate extends ChangeIndexPredicate {
}
}
} catch (QueryParseException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
return false;
diff --git a/java/com/google/gerrit/server/query/change/OrSource.java b/java/com/google/gerrit/server/query/change/OrSource.java
index b3e1c271ef..ba06b89f15 100644
--- a/java/com/google/gerrit/server/query/change/OrSource.java
+++ b/java/com/google/gerrit/server/query/change/OrSource.java
@@ -14,13 +14,13 @@
package com.google.gerrit.server.query.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.OrPredicate;
import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.ResultSet;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.ListResultSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -35,7 +35,7 @@ public class OrSource extends OrPredicate<ChangeData> implements ChangeDataSourc
}
@Override
- public ResultSet<ChangeData> read() throws OrmException {
+ public ResultSet<ChangeData> read() {
// TODO(spearce) This probably should be more lazy.
//
List<ChangeData> r = new ArrayList<>();
@@ -48,14 +48,14 @@ public class OrSource extends OrPredicate<ChangeData> implements ChangeDataSourc
}
}
} else {
- throw new OrmException("No ChangeDataSource: " + p);
+ throw new StorageException("No ChangeDataSource: " + p);
}
}
return new ListResultSet<>(r);
}
@Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
+ public ResultSet<FieldBundle> readRaw() {
throw new UnsupportedOperationException("not implemented");
}
diff --git a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 17c23b6fdf..08e6f3339b 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.DynamicOptions;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.data.ChangeAttribute;
@@ -35,7 +35,6 @@ 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.gson.Gson;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.BufferedWriter;
import java.io.IOException;
@@ -76,7 +75,8 @@ public class OutputStreamQuery {
JSON
}
- private final ReviewDb db;
+ public static final Gson GSON = new Gson();
+
private final GitRepositoryManager repoManager;
private final ChangeQueryBuilder queryBuilder;
private final ChangeQueryProcessor queryProcessor;
@@ -100,14 +100,12 @@ public class OutputStreamQuery {
@Inject
OutputStreamQuery(
- ReviewDb db,
GitRepositoryManager repoManager,
ChangeQueryBuilder queryBuilder,
ChangeQueryProcessor queryProcessor,
EventFactory eventFactory,
TrackingFooters trackingFooters,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
- this.db = db;
this.repoManager = repoManager;
this.queryBuilder = queryBuilder;
this.queryProcessor = queryProcessor;
@@ -120,6 +118,10 @@ public class OutputStreamQuery {
queryProcessor.setUserProvidedLimit(n);
}
+ public void setNoLimit(boolean on) {
+ queryProcessor.setNoLimit(on);
+ }
+
public void setStart(int n) {
queryProcessor.setStart(n);
}
@@ -217,7 +219,7 @@ public class OutputStreamQuery {
stats.moreChanges = results.more();
stats.runTimeMilliseconds = TimeUtil.nowMs() - stats.runTimeMilliseconds;
show(stats);
- } catch (OrmException err) {
+ } catch (StorageException err) {
logger.atSevere().withCause(err).log("Cannot execute query: %s", queryString);
ErrorMessage m = new ErrorMessage();
@@ -240,9 +242,9 @@ public class OutputStreamQuery {
private ChangeAttribute buildChangeAttribute(
ChangeData d, Map<Project.NameKey, Repository> repos, Map<Project.NameKey, RevWalk> revWalks)
- throws OrmException, IOException {
+ throws IOException {
LabelTypes labelTypes = d.getLabelTypes();
- ChangeAttribute c = eventFactory.asChangeAttribute(db, d.change(), d.notes());
+ ChangeAttribute c = eventFactory.asChangeAttribute(d.change(), d.notes());
eventFactory.extend(c, d.change());
if (!trackingFooters.isEmpty()) {
@@ -250,7 +252,7 @@ public class OutputStreamQuery {
}
if (includeAllReviewers) {
- eventFactory.addAllReviewers(db, c, d.notes());
+ eventFactory.addAllReviewers(c, d.notes());
}
if (includeSubmitRecords) {
@@ -277,7 +279,6 @@ public class OutputStreamQuery {
if (includePatchSets) {
eventFactory.addPatchSets(
- db,
rw,
c,
d.patchSets(),
@@ -290,7 +291,7 @@ public class OutputStreamQuery {
if (includeCurrentPatchSet) {
PatchSet current = d.currentPatchSet();
if (current != null) {
- c.currentPatchSet = eventFactory.asPatchSetAttribute(db, rw, d.change(), current);
+ c.currentPatchSet = eventFactory.asPatchSetAttribute(rw, d.change(), current);
eventFactory.addApprovals(c.currentPatchSet, d.currentApprovals(), labelTypes);
if (includeFiles) {
@@ -306,7 +307,6 @@ public class OutputStreamQuery {
eventFactory.addComments(c, d.messages());
if (includePatchSets) {
eventFactory.addPatchSets(
- db,
rw,
c,
d.patchSets(),
@@ -324,7 +324,7 @@ public class OutputStreamQuery {
eventFactory.addDependencies(rw, c, d.change(), d.currentPatchSet());
}
- c.plugins = queryProcessor.create(d);
+ c.plugins = queryProcessor.getAttributesFactory().create(d);
return c;
}
@@ -357,7 +357,7 @@ public class OutputStreamQuery {
break;
case JSON:
- out.print(new Gson().toJson(data));
+ out.print(GSON.toJson(data));
out.print('\n');
break;
}
@@ -399,7 +399,7 @@ public class OutputStreamQuery {
// Idention for multi-line text is
// current depth indetion + length of field + length of ": "
indent = indent(indent.length() + field.length() + spacesDepthRatio);
- out.print(((String) value).replaceAll("\n", "\n" + indent).trim());
+ out.print(((String) value).replace("\n", "\n" + indent).trim());
out.print('\n');
} else if (value instanceof Long && isDateField(field)) {
out.print(' ');
diff --git a/java/com/google/gerrit/server/query/change/OwnerPredicate.java b/java/com/google/gerrit/server/query/change/OwnerPredicate.java
index ff494fc9f3..100a66c7a7 100644
--- a/java/com/google/gerrit/server/query/change/OwnerPredicate.java
+++ b/java/com/google/gerrit/server/query/change/OwnerPredicate.java
@@ -17,7 +17,6 @@ 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.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class OwnerPredicate extends ChangeIndexPredicate {
protected final Account.Id id;
@@ -32,7 +31,7 @@ public class OwnerPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
return change != null && id.equals(change.getOwner());
}
diff --git a/java/com/google/gerrit/server/query/change/OwnerinPredicate.java b/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
index c48bdd5673..41b32040aa 100644
--- a/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
+++ b/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
@@ -18,7 +18,6 @@ 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;
-import com.google.gwtorm.server.OrmException;
public class OwnerinPredicate extends PostFilterPredicate<ChangeData> {
protected final IdentifiedUser.GenericFactory userFactory;
@@ -31,7 +30,7 @@ public class OwnerinPredicate extends PostFilterPredicate<ChangeData> {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
final Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java b/java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java
deleted file mode 100644
index a7950259a9..0000000000
--- a/java/com/google/gerrit/server/query/change/PluginDefinedAttributesFactory.java
+++ /dev/null
@@ -1,22 +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.server.query.change;
-
-import com.google.gerrit.extensions.common.PluginDefinedInfo;
-import java.util.List;
-
-public interface PluginDefinedAttributesFactory {
- List<PluginDefinedInfo> create(ChangeData cd);
-}
diff --git a/java/com/google/gerrit/server/query/change/ProjectPredicate.java b/java/com/google/gerrit/server/query/change/ProjectPredicate.java
index 09a46a42bd..febd6c64f5 100644
--- a/java/com/google/gerrit/server/query/change/ProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ProjectPredicate.java
@@ -17,7 +17,6 @@ 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.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class ProjectPredicate extends ChangeIndexPredicate {
public ProjectPredicate(String id) {
@@ -29,7 +28,7 @@ public class ProjectPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java b/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
index 28b1302d15..744f4d27ef 100644
--- a/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class ProjectPrefixPredicate extends ChangeIndexPredicate {
public ProjectPrefixPredicate(String prefix) {
@@ -24,7 +23,7 @@ public class ProjectPrefixPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change c = object.change();
return c != null && c.getDest().getParentKey().get().startsWith(getValue());
}
diff --git a/java/com/google/gerrit/server/query/change/RefPredicate.java b/java/com/google/gerrit/server/query/change/RefPredicate.java
index c9314e49be..2ed4c99dd4 100644
--- a/java/com/google/gerrit/server/query/change/RefPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RefPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class RefPredicate extends ChangeIndexPredicate {
public RefPredicate(String ref) {
@@ -24,7 +23,7 @@ public class RefPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java b/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java
new file mode 100644
index 0000000000..1787c76f25
--- /dev/null
+++ b/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.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.query.change;
+
+import com.google.gerrit.server.index.change.ChangeField;
+import dk.brics.automaton.RegExp;
+import dk.brics.automaton.RunAutomaton;
+
+public class RegexDirectoryPredicate extends ChangeRegexPredicate {
+ protected final RunAutomaton pattern;
+
+ public RegexDirectoryPredicate(String re) {
+ super(ChangeField.DIRECTORY, re);
+
+ if (re.startsWith("^")) {
+ re = re.substring(1);
+ }
+
+ if (re.endsWith("$") && !re.endsWith("\\$")) {
+ re = re.substring(0, re.length() - 1);
+ }
+
+ this.pattern = new RunAutomaton(new RegExp(re).toAutomaton());
+ }
+
+ @Override
+ public boolean match(ChangeData cd) {
+ return ChangeField.getDirectories(cd).stream().anyMatch(pattern::run);
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/java/com/google/gerrit/server/query/change/RegexPathPredicate.java b/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
index f6949041a9..4c3c04c198 100644
--- a/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
@@ -16,9 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.ioutil.RegexListSearcher;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.util.List;
public class RegexPathPredicate extends ChangeRegexPredicate {
public RegexPathPredicate(String re) {
@@ -26,14 +23,11 @@ public class RegexPathPredicate extends ChangeRegexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
- List<String> files;
- try {
- files = object.currentFilePaths();
- } catch (IOException e) {
- throw new OrmException(e);
- }
- return RegexListSearcher.ofStrings(getValue()).search(files).findAny().isPresent();
+ public boolean match(ChangeData object) {
+ return RegexListSearcher.ofStrings(getValue())
+ .search(object.currentFilePaths())
+ .findAny()
+ .isPresent();
}
@Override
diff --git a/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java b/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
index 1efc77d87f..46a17f6798 100644
--- a/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
@@ -17,7 +17,6 @@ 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.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
@@ -39,7 +38,7 @@ public class RegexProjectPredicate extends ChangeRegexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/RegexRefPredicate.java b/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
index 92abafb6ab..af211e630a 100644
--- a/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
@@ -38,7 +37,7 @@ public class RegexRefPredicate extends ChangeRegexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java b/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
index 2b58c88dbc..0441afa4b5 100644
--- a/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
@@ -17,7 +17,6 @@ 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.gwtorm.server.OrmException;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
@@ -39,7 +38,7 @@ public class RegexTopicPredicate extends ChangeRegexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null || change.getTopic() == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/RevertOfPredicate.java b/java/com/google/gerrit/server/query/change/RevertOfPredicate.java
index 7f4ade0a71..eea1b1e232 100644
--- a/java/com/google/gerrit/server/query/change/RevertOfPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RevertOfPredicate.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.query.change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class RevertOfPredicate extends ChangeIndexPredicate {
public RevertOfPredicate(String revertOf) {
@@ -23,7 +22,7 @@ public class RevertOfPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
if (cd.change().getRevertOf() == null) {
return false;
}
diff --git a/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
index 667c630814..070f8004ca 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
@@ -20,7 +20,6 @@ import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.mail.Address;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gwtorm.server.OrmException;
class ReviewerByEmailPredicate extends ChangeIndexPredicate {
@@ -43,7 +42,7 @@ class ReviewerByEmailPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.reviewersByEmail().asTable().get(state, adr) != null;
}
diff --git a/java/com/google/gerrit/server/query/change/ReviewerPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
index 5364a66316..19104d31ef 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
@@ -15,15 +15,11 @@
package com.google.gerrit.server.query.change;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.stream.Collectors.toList;
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;
-import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
-import com.google.gwtorm.server.OrmException;
-import java.util.stream.Stream;
public class ReviewerPredicate extends ChangeIndexPredicate {
protected static Predicate<ChangeData> forState(Account.Id id, ReviewerStateInternal state) {
@@ -31,31 +27,14 @@ public class ReviewerPredicate extends ChangeIndexPredicate {
return new ReviewerPredicate(state, id);
}
- protected static Predicate<ChangeData> reviewer(Arguments args, Account.Id id) {
- if (args.notesMigration.readChanges()) {
- // With NoteDb, Reviewer/CC are clearly distinct states, so only choose reviewer.
- return new ReviewerPredicate(ReviewerStateInternal.REVIEWER, id);
- }
- // Without NoteDb, Reviewer/CC are a bit unpredictable; maintain the old behavior of matching
- // any reviewer state.
- return anyReviewerState(id);
+ protected static Predicate<ChangeData> reviewer(Account.Id id) {
+ return new ReviewerPredicate(ReviewerStateInternal.REVIEWER, id);
}
protected static Predicate<ChangeData> cc(Account.Id id) {
- // As noted above, CC is nebulous without NoteDb, but it certainly doesn't make sense to return
- // Reviewers for cc:foo. Most likely this will just not match anything, but let the index sort
- // it out.
return new ReviewerPredicate(ReviewerStateInternal.CC, id);
}
- protected static Predicate<ChangeData> anyReviewerState(Account.Id id) {
- return Predicate.or(
- Stream.of(ReviewerStateInternal.values())
- .filter(s -> s != ReviewerStateInternal.REMOVED)
- .map(s -> new ReviewerPredicate(s, id))
- .collect(toList()));
- }
-
protected final ReviewerStateInternal state;
protected final Account.Id id;
@@ -70,7 +49,7 @@ public class ReviewerPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.reviewers().asTable().get(state, id) != null;
}
diff --git a/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
index a0aa8b5868..542a357804 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
@@ -19,7 +19,6 @@ 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;
-import com.google.gwtorm.server.OrmException;
public class ReviewerinPredicate extends PostFilterPredicate<ChangeData> {
protected final IdentifiedUser.GenericFactory userFactory;
@@ -36,7 +35,7 @@ public class ReviewerinPredicate extends PostFilterPredicate<ChangeData> {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
for (Account.Id accountId : object.reviewers().byState(ReviewerStateInternal.REVIEWER)) {
IdentifiedUser reviewer = userFactory.create(accountId);
if (reviewer.getEffectiveGroups().contains(uuid)) {
diff --git a/java/com/google/gerrit/server/query/change/StarPredicate.java b/java/com/google/gerrit/server/query/change/StarPredicate.java
index 12d4753d8a..6c5fd78798 100644
--- a/java/com/google/gerrit/server/query/change/StarPredicate.java
+++ b/java/com/google/gerrit/server/query/change/StarPredicate.java
@@ -17,7 +17,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class StarPredicate extends ChangeIndexPredicate {
protected final Account.Id accountId;
@@ -30,7 +29,7 @@ public class StarPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.stars().get(accountId).contains(label);
}
diff --git a/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java b/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
index 5fdeb68da6..0995a590f6 100644
--- a/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class SubmissionIdPredicate extends ChangeIndexPredicate {
public SubmissionIdPredicate(String changeSet) {
@@ -24,7 +23,7 @@ public class SubmissionIdPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData object) throws OrmException {
+ public boolean match(ChangeData object) {
Change change = object.change();
if (change == null) {
return false;
diff --git a/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java b/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
index 6a81ff60df..e59ae436bf 100644
--- a/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
@@ -20,7 +20,6 @@ import com.google.gerrit.common.data.SubmitRecord;
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.gwtorm.server.OrmException;
import java.util.Set;
public class SubmitRecordPredicate extends ChangeIndexPredicate {
@@ -41,7 +40,7 @@ public class SubmitRecordPredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData in) throws OrmException {
+ public boolean match(ChangeData in) {
return ChangeField.formatSubmitRecordValues(in).contains(getValue());
}
diff --git a/java/com/google/gerrit/server/query/change/SubmittablePredicate.java b/java/com/google/gerrit/server/query/change/SubmittablePredicate.java
index 8a2c8896b9..c507f1cd65 100644
--- a/java/com/google/gerrit/server/query/change/SubmittablePredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmittablePredicate.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.query.change;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
public class SubmittablePredicate extends ChangeIndexPredicate {
protected final SubmitRecord.Status status;
@@ -27,7 +26,7 @@ public class SubmittablePredicate extends ChangeIndexPredicate {
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
+ public boolean match(ChangeData cd) {
return cd.submitRecords(ChangeField.SUBMIT_RULE_OPTIONS_STRICT).stream()
.anyMatch(r -> r.status == status);
}
diff --git a/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java b/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
index 4f751c58da..622fa2c4fc 100644
--- a/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
@@ -14,26 +14,16 @@
package com.google.gerrit.server.query.change;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
public class TrackingIdPredicate extends ChangeIndexPredicate {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
public TrackingIdPredicate(String trackingId) {
super(ChangeField.TR, trackingId);
}
@Override
- public boolean match(ChangeData cd) throws OrmException {
- try {
- return cd.trackingFooters().containsValue(getValue());
- } catch (IOException e) {
- logger.atWarning().withCause(e).log("Cannot extract footers from %s", cd.getId());
- }
- return false;
+ public boolean match(ChangeData cd) {
+ return cd.trackingFooters().containsValue(getValue());
}
@Override
diff --git a/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java
index 144a81ca52..8248bf51df 100644
--- a/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java
@@ -15,14 +15,13 @@
package com.google.gerrit.server.query.group;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.index.query.IsVisibleToPredicate;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.IndexUtils;
import com.google.gerrit.server.query.account.AccountQueryBuilder;
-import com.google.gwtorm.server.OrmException;
public class GroupIsVisibleToPredicate extends IsVisibleToPredicate<InternalGroup> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -38,7 +37,7 @@ public class GroupIsVisibleToPredicate extends IsVisibleToPredicate<InternalGrou
}
@Override
- public boolean match(InternalGroup group) throws OrmException {
+ public boolean match(InternalGroup group) {
try {
boolean canSee = groupControlFactory.controlFor(user, group.getGroupUUID()).isVisible();
if (!canSee) {
diff --git a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
index 296dc17d05..215e36f6d0 100644
--- a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
@@ -24,14 +24,15 @@ 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;
import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.List;
@@ -40,7 +41,7 @@ import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
/** Parses a query string meant to be applied to group objects. */
-public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
+public class GroupQueryBuilder extends QueryBuilder<InternalGroup, GroupQueryBuilder> {
public static final String FIELD_UUID = "uuid";
public static final String FIELD_DESCRIPTION = "description";
public static final String FIELD_INNAME = "inname";
@@ -68,7 +69,7 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
@Inject
GroupQueryBuilder(Arguments args) {
- super(mydef);
+ super(mydef, null);
this.args = args;
}
@@ -133,7 +134,7 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
@Operator
public Predicate<InternalGroup> member(String query)
- throws QueryParseException, OrmException, ConfigInvalidException, IOException {
+ throws QueryParseException, ConfigInvalidException, IOException {
Set<Account.Id> accounts = parseAccount(query);
List<Predicate<InternalGroup>> predicates =
accounts.stream().map(GroupPredicates::member).collect(toImmutableList());
@@ -156,12 +157,15 @@ public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
}
private Set<Account.Id> parseAccount(String nameOrEmail)
- throws QueryParseException, OrmException, IOException, ConfigInvalidException {
- Set<Account.Id> foundAccounts = args.accountResolver.findAll(nameOrEmail);
- if (foundAccounts.isEmpty()) {
- throw error("User " + nameOrEmail + " not found");
+ throws QueryParseException, IOException, ConfigInvalidException {
+ try {
+ return args.accountResolver.resolve(nameOrEmail).asNonEmptyIdSet();
+ } catch (UnresolvableAccountException e) {
+ if (e.isSelf()) {
+ throw new QueryRequiresAuthException(e.getMessage(), e);
+ }
+ throw new QueryParseException(e.getMessage(), e);
}
- return foundAccounts;
}
private AccountGroup.UUID parseGroup(String groupNameOrUuid) throws QueryParseException {
diff --git a/java/com/google/gerrit/server/query/group/InternalGroupQuery.java b/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
index d9808f214d..57498099b2 100644
--- a/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
+++ b/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
@@ -26,7 +26,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.List;
import java.util.Optional;
@@ -37,7 +36,7 @@ import java.util.Optional;
* <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
* holding on to a single instance.
*/
-public class InternalGroupQuery extends InternalQuery<InternalGroup> {
+public class InternalGroupQuery extends InternalQuery<InternalGroup, InternalGroupQuery> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject
@@ -46,24 +45,24 @@ public class InternalGroupQuery extends InternalQuery<InternalGroup> {
super(queryProcessor, indexes, indexConfig);
}
- public Optional<InternalGroup> byName(AccountGroup.NameKey groupName) throws OrmException {
+ public Optional<InternalGroup> byName(AccountGroup.NameKey groupName) {
return getOnlyGroup(GroupPredicates.name(groupName.get()), "group name '" + groupName + "'");
}
- public Optional<InternalGroup> byId(AccountGroup.Id groupId) throws OrmException {
+ public Optional<InternalGroup> byId(AccountGroup.Id groupId) {
return getOnlyGroup(GroupPredicates.id(groupId), "group id '" + groupId + "'");
}
- public List<InternalGroup> byMember(Account.Id memberId) throws OrmException {
+ public List<InternalGroup> byMember(Account.Id memberId) {
return query(GroupPredicates.member(memberId));
}
- public List<InternalGroup> bySubgroup(AccountGroup.UUID subgroupId) throws OrmException {
+ public List<InternalGroup> bySubgroup(AccountGroup.UUID subgroupId) {
return query(GroupPredicates.subgroup(subgroupId));
}
private Optional<InternalGroup> getOnlyGroup(
- Predicate<InternalGroup> predicate, String groupDescription) throws OrmException {
+ Predicate<InternalGroup> predicate, String groupDescription) {
List<InternalGroup> groups = query(predicate);
if (groups.isEmpty()) {
return Optional.empty();
diff --git a/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
index b1c5af0553..2c84b9a30c 100644
--- a/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
@@ -22,7 +22,6 @@ import com.google.gerrit.server.index.IndexUtils;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.query.account.AccountQueryBuilder;
-import com.google.gwtorm.server.OrmException;
public class ProjectIsVisibleToPredicate extends IsVisibleToPredicate<ProjectData> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -37,7 +36,7 @@ public class ProjectIsVisibleToPredicate extends IsVisibleToPredicate<ProjectDat
}
@Override
- public boolean match(ProjectData pd) throws OrmException {
+ public boolean match(ProjectData pd) {
if (!pd.getProject().getState().permitsRead()) {
logger.atFine().log("Filter out non-readable project: %s", pd);
return false;
diff --git a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
index 57db5e7278..8b4cb538ae 100644
--- a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -28,7 +28,7 @@ import com.google.inject.Inject;
import java.util.List;
/** Parses a query string meant to be applied to project objects. */
-public class ProjectQueryBuilder extends QueryBuilder<ProjectData> {
+public class ProjectQueryBuilder extends QueryBuilder<ProjectData, ProjectQueryBuilder> {
public static final String FIELD_LIMIT = "limit";
private static final QueryBuilder.Definition<ProjectData, ProjectQueryBuilder> mydef =
@@ -36,7 +36,7 @@ public class ProjectQueryBuilder extends QueryBuilder<ProjectData> {
@Inject
ProjectQueryBuilder() {
- super(mydef);
+ super(mydef, null);
}
@Operator
diff --git a/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
new file mode 100644
index 0000000000..d39e55ca80
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/DefaultQuotaBackend.java
@@ -0,0 +1,182 @@
+// 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.quota;
+
+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.server.CurrentUser;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
+import com.google.gerrit.server.quota.QuotaResponse.Aggregated;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+
+@Singleton
+public class DefaultQuotaBackend implements QuotaBackend {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Provider<CurrentUser> userProvider;
+ private final PluginSetContext<QuotaEnforcer> quotaEnforcers;
+
+ @Inject
+ DefaultQuotaBackend(
+ Provider<CurrentUser> userProvider, PluginSetContext<QuotaEnforcer> quotaEnforcers) {
+ this.userProvider = userProvider;
+ this.quotaEnforcers = quotaEnforcers;
+ }
+
+ @Override
+ public WithUser currentUser() {
+ return new WithUser(quotaEnforcers, userProvider.get());
+ }
+
+ @Override
+ public WithUser user(CurrentUser user) {
+ return new WithUser(quotaEnforcers, user);
+ }
+
+ private static QuotaResponse.Aggregated request(
+ PluginSetContext<QuotaEnforcer> quotaEnforcers,
+ String quotaGroup,
+ QuotaRequestContext requestContext,
+ long numTokens,
+ boolean deduct) {
+ checkState(numTokens > 0, "numTokens must be a positive, non-zero long");
+
+ // PluginSets can change their content when plugins (de-)register. Copy the currently registered
+ // plugins so that we can iterate twice on a stable list.
+ List<PluginSetEntryContext<QuotaEnforcer>> enforcers = ImmutableList.copyOf(quotaEnforcers);
+ List<QuotaResponse> responses = new ArrayList<>(enforcers.size());
+ for (PluginSetEntryContext<QuotaEnforcer> enforcer : enforcers) {
+ try {
+ if (deduct) {
+ responses.add(enforcer.call(p -> p.requestTokens(quotaGroup, requestContext, numTokens)));
+ } else {
+ responses.add(enforcer.call(p -> p.dryRun(quotaGroup, requestContext, numTokens)));
+ }
+ } catch (RuntimeException e) {
+ // Roll back the quota request for all enforcers that deducted the quota. Rethrow the
+ // exception to adhere to the API contract.
+ if (deduct) {
+ refillAfterErrorOrException(enforcers, responses, quotaGroup, requestContext, numTokens);
+ }
+ throw e;
+ }
+ }
+
+ if (deduct && responses.stream().anyMatch(r -> r.status().isError())) {
+ // Roll back the quota request for all enforcers that deducted the quota (= the request
+ // succeeded). Don't touch failed enforcers as the interface contract said that failed
+ // requests should not be deducted.
+ refillAfterErrorOrException(enforcers, responses, quotaGroup, requestContext, numTokens);
+ }
+
+ logger.atFine().log(
+ "Quota request for %s with %s (deduction=%s) for %s token returned %s",
+ quotaGroup,
+ requestContext,
+ deduct ? "(deduction=yes)" : "(deduction=no)",
+ numTokens,
+ responses);
+ return QuotaResponse.Aggregated.create(ImmutableList.copyOf(responses));
+ }
+
+ private static QuotaResponse.Aggregated availableTokens(
+ PluginSetContext<QuotaEnforcer> quotaEnforcers,
+ String quotaGroup,
+ QuotaRequestContext requestContext) {
+ // PluginSets can change their content when plugins (de-)register. Copy the currently registered
+ // plugins so that we can iterate twice on a stable list.
+ List<PluginSetEntryContext<QuotaEnforcer>> enforcers = ImmutableList.copyOf(quotaEnforcers);
+ List<QuotaResponse> responses = new ArrayList<>(enforcers.size());
+ for (PluginSetEntryContext<QuotaEnforcer> enforcer : enforcers) {
+ responses.add(enforcer.call(p -> p.availableTokens(quotaGroup, requestContext)));
+ }
+ return QuotaResponse.Aggregated.create(responses);
+ }
+
+ private static void refillAfterErrorOrException(
+ List<PluginSetEntryContext<QuotaEnforcer>> enforcers,
+ List<QuotaResponse> collectedResponses,
+ String quotaGroup,
+ QuotaRequestContext requestContext,
+ long numTokens) {
+ for (int i = 0; i < collectedResponses.size(); i++) {
+ if (collectedResponses.get(i).status().isOk()) {
+ enforcers.get(i).run(p -> p.refill(quotaGroup, requestContext, numTokens));
+ }
+ }
+ }
+
+ static class WithUser extends WithResource implements QuotaBackend.WithUser {
+ WithUser(PluginSetContext<QuotaEnforcer> quotaEnforcers, CurrentUser user) {
+ super(quotaEnforcers, QuotaRequestContext.builder().user(user).build());
+ }
+
+ @Override
+ public QuotaBackend.WithResource account(Account.Id account) {
+ QuotaRequestContext ctx = requestContext.toBuilder().account(account).build();
+ return new WithResource(quotaEnforcers, ctx);
+ }
+
+ @Override
+ public QuotaBackend.WithResource project(Project.NameKey project) {
+ QuotaRequestContext ctx = requestContext.toBuilder().project(project).build();
+ return new WithResource(quotaEnforcers, ctx);
+ }
+
+ @Override
+ public QuotaBackend.WithResource change(Change.Id change, Project.NameKey project) {
+ QuotaRequestContext ctx = requestContext.toBuilder().change(change).project(project).build();
+ return new WithResource(quotaEnforcers, ctx);
+ }
+ }
+
+ static class WithResource implements QuotaBackend.WithResource {
+ protected final QuotaRequestContext requestContext;
+ protected final PluginSetContext<QuotaEnforcer> quotaEnforcers;
+
+ private WithResource(
+ PluginSetContext<QuotaEnforcer> quotaEnforcers, QuotaRequestContext quotaRequestContext) {
+ this.quotaEnforcers = quotaEnforcers;
+ this.requestContext = quotaRequestContext;
+ }
+
+ @Override
+ public QuotaResponse.Aggregated requestTokens(String quotaGroup, long numTokens) {
+ return DefaultQuotaBackend.request(
+ quotaEnforcers, quotaGroup, requestContext, numTokens, true);
+ }
+
+ @Override
+ public QuotaResponse.Aggregated dryRun(String quotaGroup, long numTokens) {
+ return DefaultQuotaBackend.request(
+ quotaEnforcers, quotaGroup, requestContext, numTokens, false);
+ }
+
+ @Override
+ public Aggregated availableTokens(String quotaGroup) {
+ return DefaultQuotaBackend.availableTokens(quotaEnforcers, quotaGroup, requestContext);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/quota/QuotaBackend.java b/java/com/google/gerrit/server/quota/QuotaBackend.java
new file mode 100644
index 0000000000..11ce61f74e
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/QuotaBackend.java
@@ -0,0 +1,103 @@
+// 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.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.server.CurrentUser;
+import com.google.inject.ImplementedBy;
+
+/**
+ * Backend interface to perform quota requests on. By default, this interface is backed by {@link
+ * DefaultQuotaBackend} which calls all plugins that implement {@link QuotaEnforcer}. A different
+ * implementation might be bound in tests. Plugins are not supposed to implement this interface, but
+ * bind a {@link QuotaEnforcer} implementation instead.
+ *
+ * <p>All quota requests require a quota group and a user. Enriching them with a top-level entity
+ * {@code Change, Project, Account} is optional but should be done if the request is targeted.
+ *
+ * <p>Example usage:
+ *
+ * <pre>
+ * quotaBackend.currentUser().project(projectName).requestToken("/projects/create").throwOnError();
+ * quotaBackend.user(user).requestToken("/restapi/config/put").throwOnError();
+ * QuotaResponse.Aggregated result = quotaBackend.currentUser().account(accountId).requestToken("/restapi/accounts/emails/validate");
+ * QuotaResponse.Aggregated result = quotaBackend.currentUser().project(projectName).requestTokens("/projects/git/upload", numBytesInPush);
+ * </pre>
+ *
+ * <p>All quota groups must be documented in {@code quota.txt} and detail the metadata that is
+ * provided (i.e. the parameters used to scope down the quota request).
+ */
+@ImplementedBy(DefaultQuotaBackend.class)
+public interface QuotaBackend {
+ /** Constructs a request for the current user. */
+ WithUser currentUser();
+
+ /**
+ * See {@link #currentUser()}. Use this method only if you can't guarantee that the request is for
+ * the current user (e.g. impersonation).
+ */
+ WithUser user(CurrentUser user);
+
+ /**
+ * An interface capable of issuing quota requests. Scope can be futher reduced by providing a
+ * top-level entity.
+ */
+ interface WithUser extends WithResource {
+ /** Scope the request down to an account. */
+ WithResource account(Account.Id account);
+
+ /** Scope the request down to a project. */
+ WithResource project(Project.NameKey project);
+
+ /** Scope the request down to a change. */
+ WithResource change(Change.Id change, Project.NameKey project);
+ }
+
+ /** An interface capable of issuing quota requests. */
+ interface WithResource {
+ /** Issues a single quota request for {@code 1} token. */
+ default QuotaResponse.Aggregated requestToken(String quotaGroup) {
+ return requestTokens(quotaGroup, 1);
+ }
+
+ /** Issues a single quota request for {@code numTokens} tokens. */
+ QuotaResponse.Aggregated requestTokens(String quotaGroup, long numTokens);
+
+ /**
+ * Issues a single quota request for {@code numTokens} tokens but signals the implementations
+ * not to deduct any quota yet. Can be used to do pre-flight requests where necessary
+ */
+ QuotaResponse.Aggregated dryRun(String quotaGroup, long tokens);
+
+ /**
+ * Requests a minimum number of tokens available in implementations. This is a pre-flight check
+ * for the exceptional case when the requested number of tokens is not known in advance but
+ * boundary can be specified. For instance, when the commit is received its size is not known
+ * until the transfer happens however one can specify how many bytes can be accepted to meet the
+ * repository size quota.
+ *
+ * <p>By definition, this is not an allocating request, therefore, it should be followed by the
+ * call to {@link #requestTokens(String, long)} when the size gets determined so that quota
+ * could be properly adjusted. It is in developer discretion to ensure that it gets called.
+ * There might be a case when particular quota gets temporarily overbooked when multiple
+ * requests are performed but the following calls to {@link #requestTokens(String, long)} will
+ * fail at the moment when a quota is exhausted. It is not a subject of quota backend to reclaim
+ * tokens that were used due to overbooking.
+ */
+ QuotaResponse.Aggregated availableTokens(String quotaGroup);
+ }
+}
diff --git a/java/com/google/gerrit/server/quota/QuotaEnforcer.java b/java/com/google/gerrit/server/quota/QuotaEnforcer.java
new file mode 100644
index 0000000000..4c1de14197
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/QuotaEnforcer.java
@@ -0,0 +1,85 @@
+// 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.quota;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/**
+ * Allows plugins to enforce different types of quota.
+ *
+ * <p>Enforcing quotas can be helpful in many scenarios. For example:
+ *
+ * <ul>
+ * <li>Reducing the number of QPS a user can send to Gerrit on the REST API
+ * <li>Limiting the size of a repository (project)
+ * <li>Limiting the number of changes in a repository
+ * <li>Limiting the number of actions that have the potential for spam, abuse or flooding if not
+ * limited
+ * </ul>
+ *
+ * This endpoint gives plugins the capability to enforce any of these limits. The server will ask
+ * all plugins that registered this endpoint and collect all results. In case {@link
+ * #requestTokens(String, QuotaRequestContext, long)} was called and one or more plugins returned an
+ * erroneous result, the server will call {@link #refill(String, QuotaRequestContext, long)} on all
+ * plugins with the same parameters. Plugins that deducted tokens in the {@link
+ * #requestTokens(String, QuotaRequestContext, long)} call can refill them so that users don't get
+ * charged any quota for failed requests.
+ *
+ * <p>Not all implementations will need to deduct quota on {@link #requestTokens(String,
+ * QuotaRequestContext, long)}}. Implementations that work on top of instance-attributes, such as
+ * the number of projects per instance can choose not to keep any state and always check how many
+ * existing projects there are and if adding the inquired number would exceed the limit. In this
+ * case, {@link #requestTokens(String, QuotaRequestContext, long)} and {@link #dryRun(String,
+ * QuotaRequestContext, long)} share the same implementation and {@link #refill(String,
+ * QuotaRequestContext, long)} is a no-op.
+ */
+@ExtensionPoint
+public interface QuotaEnforcer {
+ /**
+ * Checks if there is at least {@code numTokens} quota to fulfil the request. Bucket-based
+ * implementations can deduct the inquired number of tokens from the bucket.
+ */
+ QuotaResponse requestTokens(String quotaGroup, QuotaRequestContext ctx, long numTokens);
+
+ /**
+ * Checks if there is at least {@code numTokens} quota to fulfil the request. This is a pre-flight
+ * request, implementations should not deduct tokens from a bucket, yet.
+ */
+ QuotaResponse dryRun(String quotaGroup, QuotaRequestContext ctx, long numTokens);
+
+ /**
+ * Returns available tokens that can be later requested.
+ *
+ * <p>This is used as a pre-flight check for the exceptional case when the requested number of
+ * tokens is not known in advance. Implementation should not deduct tokens from a bucket. It
+ * should be followed by a call to {@link #requestTokens(String, QuotaRequestContext, long)} with
+ * the number of tokens that were eventually used. It is in {@link QuotaBackend} callers
+ * discretion to ensure that {@link
+ * com.google.gerrit.server.quota.QuotaBackend.WithResource#requestTokens(String, long)} is
+ * called.
+ */
+ QuotaResponse availableTokens(String quotaGroup, QuotaRequestContext ctx);
+
+ /**
+ * A previously requested and deducted quota has to be refilled (if possible) because the request
+ * failed other quota checks. Implementations can choose to leave this a no-op in case they are
+ * the first line of defence (e.g. always deduct HTTP quota even if the request failed for other
+ * quota issues so that the user gets throttled).
+ *
+ * <p>Will not be called if the {@link #requestTokens(String, QuotaRequestContext, long)} call
+ * returned {@link QuotaResponse.Status#NO_OP}.
+ */
+ void refill(String quotaGroup, QuotaRequestContext ctx, long numTokens);
+}
diff --git a/java/com/google/gerrit/server/quota/QuotaException.java b/java/com/google/gerrit/server/quota/QuotaException.java
new file mode 100644
index 0000000000..be13c0ec07
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/QuotaException.java
@@ -0,0 +1,29 @@
+// 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.quota;
+
+import com.google.gerrit.extensions.restapi.RestApiException;
+
+/**
+ * Exception that was encountered while checking if there is sufficient quota to fulfil the request.
+ * Can be propagated directly to the REST API.
+ */
+public class QuotaException extends RestApiException {
+ private static final long serialVersionUID = 1L;
+
+ public QuotaException(String reason) {
+ super(reason);
+ }
+}
diff --git a/java/com/google/gerrit/server/quota/QuotaGroupDefinitions.java b/java/com/google/gerrit/server/quota/QuotaGroupDefinitions.java
new file mode 100644
index 0000000000..5110538866
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/QuotaGroupDefinitions.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.quota;
+
+public class QuotaGroupDefinitions {
+ /**
+ * Definition of repository size quota group. {@link QuotaEnforcer} implementations for repository
+ * size quota have to act on requests with this group name.
+ */
+ public static final String REPOSITORY_SIZE_GROUP = "/repository:size";
+
+ private QuotaGroupDefinitions() {}
+}
diff --git a/java/com/google/gerrit/server/quota/QuotaRequestContext.java b/java/com/google/gerrit/server/quota/QuotaRequestContext.java
new file mode 100644
index 0000000000..90b501c39f
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/QuotaRequestContext.java
@@ -0,0 +1,54 @@
+// 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.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.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import java.util.Optional;
+
+@AutoValue
+public abstract class QuotaRequestContext {
+
+ public static Builder builder() {
+ return new AutoValue_QuotaRequestContext.Builder().user(new AnonymousUser());
+ }
+
+ public abstract CurrentUser user();
+
+ public abstract Optional<Project.NameKey> project();
+
+ public abstract Optional<Change.Id> change();
+
+ public abstract Optional<Account.Id> account();
+
+ public abstract Builder toBuilder();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract QuotaRequestContext.Builder user(CurrentUser user);
+
+ public abstract QuotaRequestContext.Builder account(Account.Id account);
+
+ public abstract QuotaRequestContext.Builder project(Project.NameKey project);
+
+ public abstract QuotaRequestContext.Builder change(Change.Id change);
+
+ public abstract QuotaRequestContext build();
+ }
+}
diff --git a/java/com/google/gerrit/server/quota/QuotaResponse.java b/java/com/google/gerrit/server/quota/QuotaResponse.java
new file mode 100644
index 0000000000..940f731327
--- /dev/null
+++ b/java/com/google/gerrit/server/quota/QuotaResponse.java
@@ -0,0 +1,133 @@
+// 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.quota;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.OptionalLong;
+import java.util.stream.Collectors;
+
+@AutoValue
+public abstract class QuotaResponse {
+ public enum Status {
+ /** The quota requests succeeded. */
+ OK,
+
+ /**
+ * The quota succeeded, but was a no-op because the plugin does not enforce this quota group
+ * (equivalent to OK, but relevant for debugging).
+ */
+ NO_OP,
+
+ /**
+ * The requested quota could not be allocated. This status code is not used to indicate
+ * processing failures as these are propagated as {@code RuntimeException}s.
+ */
+ ERROR;
+
+ public boolean isOk() {
+ return this == OK;
+ }
+
+ public boolean isError() {
+ return this == ERROR;
+ }
+ }
+
+ public static QuotaResponse ok() {
+ return new AutoValue_QuotaResponse.Builder().status(Status.OK).build();
+ }
+
+ public static QuotaResponse ok(long tokens) {
+ return new AutoValue_QuotaResponse.Builder().status(Status.OK).availableTokens(tokens).build();
+ }
+
+ public static QuotaResponse noOp() {
+ return new AutoValue_QuotaResponse.Builder().status(Status.NO_OP).build();
+ }
+
+ public static QuotaResponse error(String message) {
+ return new AutoValue_QuotaResponse.Builder().status(Status.ERROR).message(message).build();
+ }
+
+ public abstract Status status();
+
+ public abstract Optional<Long> availableTokens();
+
+ public abstract Optional<String> message();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract QuotaResponse.Builder status(Status status);
+
+ public abstract QuotaResponse.Builder availableTokens(Long tokens);
+
+ public abstract QuotaResponse.Builder message(String message);
+
+ public abstract QuotaResponse build();
+ }
+
+ @AutoValue
+ public abstract static class Aggregated {
+ public static Aggregated create(Collection<QuotaResponse> responses) {
+ return new AutoValue_QuotaResponse_Aggregated(ImmutableList.copyOf(responses));
+ }
+
+ protected abstract ImmutableList<QuotaResponse> responses();
+
+ public boolean hasError() {
+ return responses().stream().anyMatch(r -> r.status().isError());
+ }
+
+ public ImmutableList<QuotaResponse> all() {
+ return responses();
+ }
+
+ public ImmutableList<QuotaResponse> ok() {
+ return responses().stream().filter(r -> r.status().isOk()).collect(toImmutableList());
+ }
+
+ public OptionalLong availableTokens() {
+ return responses().stream()
+ .filter(r -> r.status().isOk() && r.availableTokens().isPresent())
+ .mapToLong(r -> r.availableTokens().get())
+ .min();
+ }
+
+ public ImmutableList<QuotaResponse> error() {
+ return responses().stream().filter(r -> r.status().isError()).collect(toImmutableList());
+ }
+
+ public String errorMessage() {
+ return error().stream()
+ .map(QuotaResponse::message)
+ .flatMap(Streams::stream)
+ .collect(Collectors.joining(", "));
+ }
+
+ public void throwOnError() throws QuotaException {
+ String errorMessage = errorMessage();
+ if (!Strings.isNullOrEmpty(errorMessage)) {
+ throw new QuotaException(errorMessage);
+ }
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index ad54404f6f..dfb95940ff 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -10,10 +10,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/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
"//java/com/google/gerrit/index/project",
+ "//java/com/google/gerrit/jgit",
+ "//java/com/google/gerrit/json",
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/prettify:server",
@@ -22,13 +25,11 @@ java_library(
"//java/com/google/gerrit/server/ioutil",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/util/cli",
- "//java/org/eclipse/jgit:server",
"//lib:args4j",
"//lib:blame-cache",
"//lib:gson",
"//lib:guava",
- "//lib:gwtorm",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/commons:codec",
diff --git a/java/com/google/gerrit/server/restapi/access/ListAccess.java b/java/com/google/gerrit/server/restapi/access/ListAccess.java
index 3f01c6c828..a3e95301b4 100644
--- a/java/com/google/gerrit/server/restapi/access/ListAccess.java
+++ b/java/com/google/gerrit/server/restapi/access/ListAccess.java
@@ -22,7 +22,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -50,7 +49,7 @@ public class ListAccess implements RestReadView<TopLevelResource> {
@Override
public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
throws ResourceNotFoundException, ResourceConflictException, IOException,
- PermissionBackendException, OrmException {
+ PermissionBackendException {
Map<String, ProjectAccessInfo> access = new TreeMap<>();
for (String p : projects) {
access.put(p, getAccess.apply(new Project.NameKey(p)));
diff --git a/java/com/google/gerrit/server/restapi/account/AccountsCollection.java b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
index c301ab2f32..119e2e4d98 100644
--- a/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
+++ b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
@@ -21,11 +21,9 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
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.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
import com.google.gerrit.server.account.AccountResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -35,32 +33,30 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
public class AccountsCollection implements RestCollection<TopLevelResource, AccountResource> {
private final AccountResolver accountResolver;
- private final AccountControl.Factory accountControlFactory;
private final Provider<QueryAccounts> list;
private final DynamicMap<RestView<AccountResource>> views;
@Inject
public AccountsCollection(
AccountResolver accountResolver,
- AccountControl.Factory accountControlFactory,
Provider<QueryAccounts> list,
DynamicMap<RestView<AccountResource>> views) {
this.accountResolver = accountResolver;
- this.accountControlFactory = accountControlFactory;
this.list = list;
this.views = views;
}
@Override
public AccountResource parse(TopLevelResource root, IdString id)
- throws ResourceNotFoundException, AuthException, OrmException, IOException,
- ConfigInvalidException {
- IdentifiedUser user = accountResolver.parseId(id.get());
- if (user == null || !accountControlFactory.get().canSee(user.getAccount())) {
- throw new ResourceNotFoundException(
- String.format("Account '%s' is not found or ambiguous", id));
+ throws ResourceNotFoundException, AuthException, IOException, ConfigInvalidException {
+ try {
+ return new AccountResource(accountResolver.resolve(id.get()).asUniqueUser());
+ } catch (UnresolvableAccountException e) {
+ if (e.isSelf()) {
+ throw new AuthException(e.getMessage(), e);
+ }
+ throw new ResourceNotFoundException(e.getMessage(), e);
}
- return new AccountResource(user);
}
@Override
diff --git a/java/com/google/gerrit/server/restapi/account/AddSshKey.java b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
index 4bdf52cc6b..7c05f4e07f 100644
--- a/java/com/google/gerrit/server/restapi/account/AddSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
@@ -18,8 +18,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteSource;
-import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
import com.google.gerrit.extensions.api.accounts.SshKeyInput;
import com.google.gerrit.extensions.common.SshKeyInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -37,7 +37,6 @@ 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.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -72,7 +71,7 @@ public class AddSshKey
@Override
public Response<SshKeyInfo> apply(AccountResource rsrc, SshKeyInput input)
- throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
+ throws AuthException, BadRequestException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
@@ -109,7 +108,7 @@ public class AddSshKey
}
user.getUserName().ifPresent(sshKeyCache::evict);
- return Response.<SshKeyInfo>created(GetSshKeys.newSshKeyInfo(sshKey));
+ return Response.created(GetSshKeys.newSshKeyInfo(sshKey));
} catch (InvalidSshKeyException e) {
throw new BadRequestException(e.getMessage());
}
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index 42b3daf419..bf50e10e76 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -22,12 +22,11 @@ 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.common.errors.InvalidSshKeyException;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.accounts.AccountInput;
import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -37,7 +36,6 @@ 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.Sequences;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountExternalIdCreator;
import com.google.gerrit.server.account.AccountLoader;
@@ -50,9 +48,10 @@ import com.google.gerrit.server.group.GroupResolver;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -73,7 +72,7 @@ public class CreateAccount
private final SshKeyCache sshKeyCache;
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final AccountLoader.Factory infoLoader;
- private final DynamicSet<AccountExternalIdCreator> externalIdCreators;
+ private final PluginSetContext<AccountExternalIdCreator> externalIdCreators;
private final Provider<GroupsUpdate> groupsUpdate;
private final OutgoingEmailValidator validator;
@@ -85,7 +84,7 @@ public class CreateAccount
SshKeyCache sshKeyCache,
@UserInitiated Provider<AccountsUpdate> accountsUpdateProvider,
AccountLoader.Factory infoLoader,
- DynamicSet<AccountExternalIdCreator> externalIdCreators,
+ PluginSetContext<AccountExternalIdCreator> externalIdCreators,
@UserInitiated Provider<GroupsUpdate> groupsUpdate,
OutgoingEmailValidator validator) {
this.seq = seq;
@@ -103,13 +102,13 @@ public class CreateAccount
public Response<AccountInfo> apply(
TopLevelResource rsrc, IdString id, @Nullable AccountInput input)
throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
- OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+ IOException, ConfigInvalidException, PermissionBackendException {
return apply(id, input != null ? input : new AccountInput());
}
public Response<AccountInfo> apply(IdString id, AccountInput input)
throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
- OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+ IOException, ConfigInvalidException, PermissionBackendException {
String username = id.get();
if (input.username != null && !username.equals(input.username)) {
throw new BadRequestException("username must match URL");
@@ -131,9 +130,7 @@ public class CreateAccount
}
extIds.add(ExternalId.createUsername(username, accountId, input.httpPassword));
- for (AccountExternalIdCreator c : externalIdCreators) {
- extIds.addAll(c.create(accountId, username, input.email));
- }
+ externalIdCreators.runEach(c -> extIds.addAll(c.create(accountId, username, input.email)));
try {
accountsUpdateProvider
@@ -191,7 +188,7 @@ public class CreateAccount
}
private void addGroupMember(AccountGroup.UUID groupUuid, Account.Id accountId)
- throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+ throws IOException, NoSuchGroupException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.union(memberIds, ImmutableSet.of(accountId)))
diff --git a/java/com/google/gerrit/server/restapi/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
index e4e8525656..cc4cf21af7 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.restapi.account;
import static com.google.gerrit.extensions.client.AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.common.EmailInfo;
@@ -41,7 +41,6 @@ import com.google.gerrit.server.mail.send.RegisterNewEmailSender;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -84,7 +83,7 @@ public class CreateEmail
@Override
public Response<EmailInfo> apply(AccountResource rsrc, IdString id, EmailInput input)
- throws RestApiException, OrmException, EmailException, MethodNotAllowedException, IOException,
+ throws RestApiException, EmailException, MethodNotAllowedException, IOException,
ConfigInvalidException, PermissionBackendException {
if (input == null) {
input = new EmailInput();
@@ -103,7 +102,7 @@ public class CreateEmail
/** To be used from plugins that want to create emails without permission checks. */
public Response<EmailInfo> apply(IdentifiedUser user, IdString id, EmailInput input)
- throws RestApiException, OrmException, EmailException, MethodNotAllowedException, IOException,
+ throws RestApiException, EmailException, MethodNotAllowedException, IOException,
ConfigInvalidException, PermissionBackendException {
String email = id.get().trim();
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteActive.java b/java/com/google/gerrit/server/restapi/account/DeleteActive.java
index 43025137b6..ffd7893a33 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteActive.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteActive.java
@@ -24,7 +24,6 @@ import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.SetInactiveFlag;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -46,7 +45,7 @@ public class DeleteActive implements RestModifyView<AccountResource, Input> {
@Override
public Response<?> apply(AccountResource rsrc, Input input)
- throws RestApiException, OrmException, IOException, ConfigInvalidException {
+ throws RestApiException, IOException, ConfigInvalidException {
if (self.get().hasSameAccountId(rsrc.getUser())) {
throw new ResourceConflictException("cannot deactivate own account");
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
index 2a7fe9acd0..478830143e 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
@@ -32,11 +32,9 @@ 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.Account.Id;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
@@ -58,7 +56,6 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -74,7 +71,6 @@ import java.util.Objects;
public class DeleteDraftComments
implements RestModifyView<AccountResource, DeleteDraftCommentsInput> {
- private final Provider<ReviewDb> db;
private final Provider<CurrentUser> userProvider;
private final BatchUpdate.Factory batchUpdateFactory;
private final Provider<ChangeQueryBuilder> queryBuilderProvider;
@@ -88,7 +84,6 @@ public class DeleteDraftComments
@Inject
DeleteDraftComments(
- Provider<ReviewDb> db,
Provider<CurrentUser> userProvider,
Factory batchUpdateFactory,
Provider<ChangeQueryBuilder> queryBuilderProvider,
@@ -99,7 +94,6 @@ public class DeleteDraftComments
CommentsUtil commentsUtil,
PatchSetUtil psUtil,
PatchListCache patchListCache) {
- this.db = db;
this.userProvider = userProvider;
this.batchUpdateFactory = batchUpdateFactory;
this.queryBuilderProvider = queryBuilderProvider;
@@ -115,7 +109,7 @@ public class DeleteDraftComments
@Override
public ImmutableList<DeletedDraftCommentInfo> apply(
AccountResource rsrc, DeleteDraftCommentsInput input)
- throws RestApiException, OrmException, UpdateException {
+ throws RestApiException, UpdateException {
CurrentUser user = userProvider.get();
if (!user.isIdentifiedUser()) {
throw new AuthException("Authentication required");
@@ -142,7 +136,7 @@ public class DeleteDraftComments
.query(predicate(accountId, input))) {
BatchUpdate update =
updates.computeIfAbsent(
- cd.project(), p -> batchUpdateFactory.create(db.get(), p, rsrc.getUser(), now));
+ cd.project(), p -> batchUpdateFactory.create(p, rsrc.getUser(), now));
Op op = new Op(commentFormatter, accountId);
update.addOp(cd.getId(), op);
ops.add(op);
@@ -151,7 +145,7 @@ public class DeleteDraftComments
// Currently there's no way to let some updates succeed even if others fail. Even if there were,
// all updates from this operation only happen in All-Users and thus are fully atomic, so
// allowing partial failure would have little value.
- batchUpdateFactory.execute(updates.values(), BatchUpdateListener.NONE, false);
+ BatchUpdate.execute(updates.values(), BatchUpdateListener.NONE, false);
return ops.stream().map(Op::getResult).filter(Objects::nonNull).collect(toImmutableList());
}
@@ -174,22 +168,21 @@ public class DeleteDraftComments
private final Account.Id accountId;
private DeletedDraftCommentInfo result;
- Op(CommentFormatter commentFormatter, Id accountId) {
+ Op(CommentFormatter commentFormatter, Account.Id accountId) {
this.commentFormatter = commentFormatter;
this.accountId = accountId;
}
@Override
public boolean updateChange(ChangeContext ctx)
- throws OrmException, PatchListNotAvailableException, PermissionBackendException {
+ throws PatchListNotAvailableException, PermissionBackendException {
ImmutableList.Builder<CommentInfo> comments = ImmutableList.builder();
boolean dirty = false;
- for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getDb(), ctx.getNotes(), accountId)) {
+ 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.getDb(), ctx.getNotes(), psId));
- commentsUtil.deleteComments(ctx.getDb(), ctx.getUpdate(psId), Collections.singleton(c));
+ setCommentRevId(c, patchListCache, ctx.getChange(), psUtil.get(ctx.getNotes(), psId));
+ commentsUtil.deleteComments(ctx.getUpdate(psId), Collections.singleton(c));
comments.add(commentFormatter.format(c));
}
if (dirty) {
@@ -197,10 +190,9 @@ public class DeleteDraftComments
result.change =
changeJsonFactory
.create(ListChangesOption.SKIP_MERGEABLE)
- .format(changeDataFactory.create(ctx.getDb(), ctx.getNotes()));
+ .format(changeDataFactory.create(ctx.getNotes()));
result.deleted = comments.build();
}
- ctx.dontBumpLastUpdatedOn();
return dirty;
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
index 26fd438a3d..72a67db858 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
@@ -38,7 +38,6 @@ import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -75,7 +74,7 @@ public class DeleteEmail implements RestModifyView<AccountResource.Email, Input>
@Override
public Response<?> apply(AccountResource.Email rsrc, Input input)
throws AuthException, ResourceNotFoundException, ResourceConflictException,
- MethodNotAllowedException, OrmException, IOException, ConfigInvalidException,
+ MethodNotAllowedException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
@@ -85,7 +84,7 @@ public class DeleteEmail implements RestModifyView<AccountResource.Email, Input>
public Response<?> apply(IdentifiedUser user, String email)
throws ResourceNotFoundException, ResourceConflictException, MethodNotAllowedException,
- OrmException, IOException, ConfigInvalidException {
+ IOException, ConfigInvalidException {
Account.Id accountId = user.getAccountId();
if (realm.accountBelongsToRealm(externalIds.byAccount(accountId))
&& !realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) {
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
index 52e25470de..0b51bf836b 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
@@ -33,7 +33,6 @@ import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -66,8 +65,7 @@ public class DeleteExternalIds implements RestModifyView<AccountResource, List<S
@Override
public Response<?> apply(AccountResource resource, List<String> extIds)
- throws RestApiException, IOException, OrmException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(resource.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
index a60fd8e8b7..054f4bc984 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
@@ -29,7 +29,6 @@ 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.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -63,8 +62,8 @@ public class DeleteSshKey implements RestModifyView<AccountResource.SshKey, Inpu
@Override
public Response<?> apply(AccountResource.SshKey rsrc, Input input)
- throws AuthException, OrmException, RepositoryNotFoundException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ throws AuthException, RepositoryNotFoundException, 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/DeleteWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
index deb70fe8fd..798aad17b3 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
@@ -31,7 +31,6 @@ import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -59,8 +58,8 @@ public class DeleteWatchedProjects
@Override
public Response<?> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
- throws AuthException, UnprocessableEntityException, OrmException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ throws AuthException, UnprocessableEntityException, 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/GetAccount.java b/java/com/google/gerrit/server/restapi/account/GetAccount.java
index 6b73ae3b29..6544f8dced 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAccount.java
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -33,7 +32,7 @@ public class GetAccount implements RestReadView<AccountResource> {
}
@Override
- public AccountInfo apply(AccountResource rsrc) throws OrmException, PermissionBackendException {
+ public AccountInfo apply(AccountResource rsrc) throws PermissionBackendException {
AccountLoader loader = infoFactory.create(true);
AccountInfo info = loader.get(rsrc.getUser().getAccountId());
loader.fill();
diff --git a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
index 7889f6e411..77f166899b 100644
--- a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.restapi.account;
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
import static com.google.gerrit.server.permissions.DefaultPermissionMappings.globalOrPluginPermissionName;
import static com.google.gerrit.server.permissions.DefaultPermissionMappings.globalPermissionName;
-import static com.google.gerrit.server.permissions.DefaultPermissionMappings.pluginPermissionName;
+import static com.google.gerrit.server.permissions.DefaultPermissionMappings.pluginCapabilityName;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.GlobalCapability;
@@ -30,9 +30,9 @@ 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.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.OutputFormat;
import com.google.gerrit.server.account.AccountLimits;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountResource.Capability;
@@ -113,7 +113,7 @@ public class GetCapabilities implements RestReadView<AccountResource> {
for (String pluginName : pluginCapabilities.plugins()) {
for (String capability : pluginCapabilities.byPlugin(pluginName).keySet()) {
PluginPermission p = new PluginPermission(pluginName, capability);
- if (want(pluginPermissionName(p))) {
+ if (want(pluginCapabilityName(p))) {
toTest.add(p);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetDetail.java b/java/com/google/gerrit/server/restapi/account/GetDetail.java
index 97d0c60b3b..72044c40d1 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDetail.java
@@ -21,7 +21,6 @@ import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.InternalAccountDirectory;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Collections;
@@ -37,8 +36,7 @@ public class GetDetail implements RestReadView<AccountResource> {
}
@Override
- public AccountDetailInfo apply(AccountResource rsrc)
- throws OrmException, PermissionBackendException {
+ public AccountDetailInfo apply(AccountResource rsrc) throws PermissionBackendException {
Account a = rsrc.getUser().getAccount();
AccountDetailInfo info = new AccountDetailInfo(a.getId().get());
info.registeredOn = a.getRegisteredOn();
diff --git a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
index 7a420abe59..ef448dc7cc 100644
--- a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -60,7 +59,7 @@ public class GetExternalIds implements RestReadView<AccountResource> {
@Override
public List<AccountExternalIdInfo> apply(AccountResource resource)
- throws RestApiException, IOException, OrmException, PermissionBackendException {
+ throws RestApiException, IOException, PermissionBackendException {
if (!self.get().hasSameAccountId(resource.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetGroups.java b/java/com/google/gerrit/server/restapi/account/GetGroups.java
index ad9746e15b..569ff76a4c 100644
--- a/java/com/google/gerrit/server/restapi/account/GetGroups.java
+++ b/java/com/google/gerrit/server/restapi/account/GetGroups.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.restapi.account;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Account;
@@ -24,7 +24,6 @@ import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.group.GroupJson;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
@@ -43,8 +42,7 @@ public class GetGroups implements RestReadView<AccountResource> {
}
@Override
- public List<GroupInfo> apply(AccountResource resource)
- throws OrmException, PermissionBackendException {
+ public List<GroupInfo> apply(AccountResource resource) throws PermissionBackendException {
IdentifiedUser user = resource.getUser();
Account.Id userId = user.getAccountId();
Set<AccountGroup.UUID> knownGroups = user.getEffectiveGroups().getKnownGroups();
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
index a49f9df58c..408aa5fd48 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
@@ -27,7 +27,6 @@ import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -55,8 +54,8 @@ public class GetSshKeys implements RestReadView<AccountResource> {
@Override
public List<SshKeyInfo> apply(AccountResource rsrc)
- throws AuthException, OrmException, RepositoryNotFoundException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+ PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index ba8c72a65d..fce324e481 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -33,7 +33,6 @@ import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -57,8 +56,8 @@ public class GetWatchedProjects implements RestReadView<AccountResource> {
@Override
public List<ProjectWatchInfo> apply(AccountResource rsrc)
- throws OrmException, AuthException, IOException, ConfigInvalidException,
- PermissionBackendException, ResourceNotFoundException {
+ throws AuthException, IOException, ConfigInvalidException, PermissionBackendException,
+ ResourceNotFoundException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
diff --git a/java/com/google/gerrit/server/restapi/account/Module.java b/java/com/google/gerrit/server/restapi/account/Module.java
index 9b012f7e7d..f41764d3c5 100644
--- a/java/com/google/gerrit/server/restapi/account/Module.java
+++ b/java/com/google/gerrit/server/restapi/account/Module.java
@@ -118,7 +118,7 @@ public class Module extends RestApiModule {
@ServerInitiated
AccountsUpdate provideServerInitiatedAccountsUpdate(
AccountsUpdate.Factory accountsUpdateFactory, ExternalIdNotes.Factory extIdNotesFactory) {
- return accountsUpdateFactory.create(null, extIdNotesFactory);
+ return accountsUpdateFactory.createWithServerIdent(extIdNotesFactory);
}
@Provides
diff --git a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index f29a0ebf4a..14bd492d99 100644
--- a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -29,7 +29,6 @@ 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.restapi.project.ProjectsCollection;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -66,8 +65,7 @@ public class PostWatchedProjects
@Override
public List<ProjectWatchInfo> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
- throws OrmException, RestApiException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ 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/PutActive.java b/java/com/google/gerrit/server/restapi/account/PutActive.java
index 82557814cb..a6ffaa6c69 100644
--- a/java/com/google/gerrit/server/restapi/account/PutActive.java
+++ b/java/com/google/gerrit/server/restapi/account/PutActive.java
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.SetInactiveFlag;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -41,7 +40,7 @@ public class PutActive implements RestModifyView<AccountResource, Input> {
@Override
public Response<String> apply(AccountResource rsrc, Input input)
- throws RestApiException, OrmException, IOException, ConfigInvalidException {
+ throws RestApiException, IOException, ConfigInvalidException {
return setInactiveFlag.activate(rsrc.getUser().getAccountId());
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutAgreement.java b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
index ee244d0866..147eec8554 100644
--- a/java/com/google/gerrit/server/restapi/account/PutAgreement.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
@@ -17,7 +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.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.api.accounts.AgreementInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -35,7 +35,6 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.events.AgreementSignup;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.restapi.group.AddMembers;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -67,7 +66,7 @@ public class PutAgreement implements RestModifyView<AccountResource, AgreementIn
@Override
public Response<String> apply(AccountResource resource, AgreementInput input)
- throws IOException, OrmException, RestApiException, ConfigInvalidException {
+ throws IOException, RestApiException, ConfigInvalidException {
if (!agreementsEnabled) {
throw new MethodNotAllowedException("contributor agreements disabled");
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index d671ebf1eb..f029ef94b1 100644
--- a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -18,7 +18,8 @@ import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USE
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.common.HttpPasswordInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -27,7 +28,6 @@ import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountsUpdate;
@@ -37,7 +37,6 @@ import com.google.gerrit.server.mail.send.HttpPasswordUpdateSender;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -85,8 +84,8 @@ public class PutHttpPassword implements RestModifyView<AccountResource, HttpPass
@Override
public Response<String> apply(AccountResource rsrc, HttpPasswordInput input)
- throws AuthException, ResourceNotFoundException, ResourceConflictException, OrmException,
- IOException, ConfigInvalidException, PermissionBackendException {
+ throws AuthException, ResourceNotFoundException, ResourceConflictException, IOException,
+ ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
@@ -111,7 +110,7 @@ public class PutHttpPassword implements RestModifyView<AccountResource, HttpPass
@UsedAt(UsedAt.Project.PLUGIN_SERVICEUSER)
public Response<String> apply(IdentifiedUser user, String newPassword)
- throws ResourceNotFoundException, ResourceConflictException, OrmException, IOException,
+ throws ResourceNotFoundException, ResourceConflictException, IOException,
ConfigInvalidException {
String userName =
user.getUserName().orElseThrow(() -> new ResourceConflictException("username must be set"));
@@ -137,7 +136,7 @@ public class PutHttpPassword implements RestModifyView<AccountResource, HttpPass
"Cannot send HttpPassword update message to %s", user.getAccount().getPreferredEmail());
}
- return Strings.isNullOrEmpty(newPassword) ? Response.<String>none() : Response.ok(newPassword);
+ return Strings.isNullOrEmpty(newPassword) ? Response.none() : Response.ok(newPassword);
}
@UsedAt(UsedAt.Project.PLUGIN_SERVICEUSER)
diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
index 4918aa38dc..9f9dd073ec 100644
--- a/java/com/google/gerrit/server/restapi/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -34,7 +34,6 @@ import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -65,8 +64,8 @@ public class PutName implements RestModifyView<AccountResource, NameInput> {
@Override
public Response<String> apply(AccountResource rsrc, NameInput input)
- throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
- IOException, PermissionBackendException, ConfigInvalidException {
+ throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
+ PermissionBackendException, ConfigInvalidException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
@@ -75,8 +74,7 @@ public class PutName implements RestModifyView<AccountResource, NameInput> {
public Response<String> apply(IdentifiedUser user, NameInput input)
throws MethodNotAllowedException, ResourceNotFoundException, IOException,
- ConfigInvalidException, OrmException {
-
+ ConfigInvalidException {
if (input == null) {
input = new NameInput();
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
index 265bf530ec..b8edec3f6e 100644
--- a/java/com/google/gerrit/server/restapi/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -34,7 +34,6 @@ import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -69,8 +68,7 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
@Override
public Response<String> apply(AccountResource.Email rsrc, Input input)
- throws RestApiException, OrmException, IOException, PermissionBackendException,
- ConfigInvalidException {
+ throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
@@ -78,7 +76,7 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
}
public Response<String> apply(IdentifiedUser user, String preferredEmail)
- throws RestApiException, IOException, ConfigInvalidException, OrmException {
+ throws RestApiException, IOException, ConfigInvalidException {
AtomicReference<Optional<RestApiException>> exception = new AtomicReference<>(Optional.empty());
AtomicBoolean alreadyPreferred = new AtomicBoolean(false);
accountsUpdateProvider
diff --git a/java/com/google/gerrit/server/restapi/account/PutStatus.java b/java/com/google/gerrit/server/restapi/account/PutStatus.java
index 9aee0a3274..4f1128d5da 100644
--- a/java/com/google/gerrit/server/restapi/account/PutStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/PutStatus.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -54,8 +53,8 @@ public class PutStatus implements RestModifyView<AccountResource, StatusInput> {
@Override
public Response<String> apply(AccountResource rsrc, StatusInput input)
- throws AuthException, ResourceNotFoundException, OrmException, IOException,
- PermissionBackendException, ConfigInvalidException {
+ throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException,
+ ConfigInvalidException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
@@ -63,7 +62,7 @@ public class PutStatus implements RestModifyView<AccountResource, StatusInput> {
}
public Response<String> apply(IdentifiedUser user, StatusInput input)
- throws ResourceNotFoundException, IOException, ConfigInvalidException, OrmException {
+ throws ResourceNotFoundException, IOException, ConfigInvalidException {
if (input == null) {
input = new StatusInput();
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index bc95153cbe..8d949b8dad 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -17,6 +17,7 @@ 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.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.api.accounts.UsernameInput;
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -36,8 +37,6 @@ 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.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -73,7 +72,7 @@ public class PutUsername implements RestModifyView<AccountResource, UsernameInpu
@Override
public String apply(AccountResource rsrc, UsernameInput input)
throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
- ResourceConflictException, OrmException, IOException, ConfigInvalidException,
+ ResourceConflictException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
@@ -109,7 +108,7 @@ public class PutUsername implements RestModifyView<AccountResource, UsernameInpu
"Set Username via API",
accountId,
u -> u.addExternalId(ExternalId.create(key, accountId, null, null)));
- } catch (OrmDuplicateKeyException dupeErr) {
+ } catch (DuplicateKeyException dupeErr) {
// 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)) {
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index 7f27edda60..522301d0a6 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.account;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.client.ListAccountsOption;
+import com.google.gerrit.extensions.client.ListOption;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.AccountVisibility;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -40,7 +41,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.account.AccountPredicates;
import com.google.gerrit.server.query.account.AccountQueryBuilder;
import com.google.gerrit.server.query.account.AccountQueryProcessor;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collections;
@@ -99,7 +99,7 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
@Option(name = "-O", usage = "Output option flags, in hex")
void setOptionFlagsHex(String hex) {
- options.addAll(ListAccountsOption.fromBits(Integer.parseInt(hex, 16)));
+ options.addAll(ListOption.fromBits(ListAccountsOption.class, Integer.parseInt(hex, 16)));
}
@Option(
@@ -150,7 +150,7 @@ public class QueryAccounts implements RestReadView<TopLevelResource> {
@Override
public List<AccountInfo> apply(TopLevelResource rsrc)
- throws OrmException, RestApiException, PermissionBackendException {
+ throws RestApiException, PermissionBackendException {
if (Strings.isNullOrEmpty(query)) {
throw new BadRequestException("missing query field");
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index f4fa3544cd..ee72ab7f2a 100644
--- a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -56,7 +55,7 @@ public class SetDiffPreferences implements RestModifyView<AccountResource, DiffP
@Override
public DiffPreferencesInfo apply(AccountResource rsrc, DiffPreferencesInfo input)
throws RestApiException, ConfigInvalidException, RepositoryNotFoundException, IOException,
- PermissionBackendException, OrmException {
+ PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index 4e3f1d5aea..27d32f2c34 100644
--- a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -57,7 +56,7 @@ public class SetEditPreferences implements RestModifyView<AccountResource, EditP
@Override
public EditPreferencesInfo apply(AccountResource rsrc, EditPreferencesInfo input)
throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
- PermissionBackendException, OrmException {
+ PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index 2471689644..c6623dbf7b 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -34,7 +34,6 @@ import com.google.gerrit.server.account.Preferences;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -62,8 +61,7 @@ public class SetPreferences implements RestModifyView<AccountResource, GeneralPr
@Override
public GeneralPreferencesInfo apply(AccountResource rsrc, GeneralPreferencesInfo input)
- throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
- OrmException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
diff --git a/java/com/google/gerrit/server/restapi/account/SshKeys.java b/java/com/google/gerrit/server/restapi/account/SshKeys.java
index 58021316ac..a6c4d804b4 100644
--- a/java/com/google/gerrit/server/restapi/account/SshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/SshKeys.java
@@ -28,7 +28,6 @@ import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -64,7 +63,7 @@ public class SshKeys implements ChildCollection<AccountResource, AccountResource
@Override
public AccountResource.SshKey parse(AccountResource rsrc, IdString id)
- throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException,
+ throws ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
try {
diff --git a/java/com/google/gerrit/server/restapi/account/StarredChanges.java b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
index 88ffab816a..c108dcb2a2 100644
--- a/java/com/google/gerrit/server/restapi/account/StarredChanges.java
+++ b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.restapi.account;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -40,8 +42,6 @@ import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.server.restapi.change.QueryChanges;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -68,7 +68,7 @@ public class StarredChanges
@Override
public AccountResource.StarredChange parse(AccountResource parent, IdString id)
- throws RestApiException, OrmException, PermissionBackendException, IOException {
+ throws RestApiException, PermissionBackendException, IOException {
IdentifiedUser user = parent.getUser();
ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
if (starredChangesUtil
@@ -86,15 +86,12 @@ public class StarredChanges
@Override
public RestView<AccountResource> list() throws ResourceNotFoundException {
- return new RestReadView<AccountResource>() {
- @Override
- public Object apply(AccountResource self)
- throws BadRequestException, AuthException, OrmException, PermissionBackendException {
- QueryChanges query = changes.list();
- query.addQuery("starredby:" + self.getUser().getAccountId().get());
- return query.apply(TopLevelResource.INSTANCE);
- }
- };
+ return (RestReadView<AccountResource>)
+ self -> {
+ QueryChanges query = changes.list();
+ query.addQuery("starredby:" + self.getUser().getAccountId().get());
+ return query.apply(TopLevelResource.INSTANCE);
+ };
}
@Singleton
@@ -117,7 +114,7 @@ public class StarredChanges
@Override
public Response<?> apply(AccountResource rsrc, IdString id, EmptyInput in)
- throws RestApiException, OrmException, IOException {
+ throws RestApiException, IOException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to add starred change");
}
@@ -127,7 +124,7 @@ public class StarredChanges
change = changes.parse(TopLevelResource.INSTANCE, id);
} catch (ResourceNotFoundException e) {
throw new UnprocessableEntityException(String.format("change %s not found", id.get()), e);
- } catch (OrmException | PermissionBackendException | IOException e) {
+ } catch (StorageException | PermissionBackendException | IOException e) {
logger.atSevere().withCause(e).log("cannot resolve change");
throw new UnprocessableEntityException("internal server error", e);
}
@@ -143,7 +140,7 @@ public class StarredChanges
throw new ResourceConflictException(e.getMessage());
} catch (IllegalLabelException e) {
throw new BadRequestException(e.getMessage());
- } catch (OrmDuplicateKeyException e) {
+ } catch (DuplicateKeyException e) {
return Response.none();
}
return Response.none();
@@ -182,7 +179,7 @@ public class StarredChanges
@Override
public Response<?> apply(AccountResource.StarredChange rsrc, EmptyInput in)
- throws AuthException, OrmException, IOException, IllegalLabelException {
+ throws AuthException, IOException, IllegalLabelException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed remove starred change");
}
diff --git a/java/com/google/gerrit/server/restapi/account/Stars.java b/java/com/google/gerrit/server/restapi/account/Stars.java
index 5c4c4d5253..c610adfd70 100644
--- a/java/com/google/gerrit/server/restapi/account/Stars.java
+++ b/java/com/google/gerrit/server/restapi/account/Stars.java
@@ -36,7 +36,6 @@ import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.server.restapi.change.QueryChanges;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -68,7 +67,7 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
@Override
public Star parse(AccountResource parent, IdString id)
- throws RestApiException, OrmException, PermissionBackendException, IOException {
+ throws RestApiException, PermissionBackendException, IOException {
IdentifiedUser user = parent.getUser();
ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
Set<String> labels = starredChangesUtil.getLabels(user.getAccountId(), change.getId());
@@ -99,7 +98,7 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
@Override
@SuppressWarnings("unchecked")
public List<ChangeInfo> apply(AccountResource rsrc)
- throws BadRequestException, AuthException, OrmException, PermissionBackendException {
+ throws BadRequestException, AuthException, PermissionBackendException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to list stars of another account");
}
@@ -121,7 +120,7 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
}
@Override
- public SortedSet<String> apply(AccountResource.Star rsrc) throws AuthException, OrmException {
+ public 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");
}
@@ -142,7 +141,7 @@ public class Stars implements ChildCollection<AccountResource, AccountResource.S
@Override
public Collection<String> apply(AccountResource.Star rsrc, StarsInput in)
- throws AuthException, BadRequestException, OrmException {
+ throws AuthException, BadRequestException {
if (!self.get().hasSameAccountId(rsrc.getUser())) {
throw new AuthException("not allowed to update stars of another account");
}
diff --git a/java/com/google/gerrit/server/restapi/change/Abandon.java b/java/com/google/gerrit/server/restapi/change/Abandon.java
index ccce99879e..c3327e4439 100644
--- a/java/com/google/gerrit/server/restapi/change/Abandon.java
+++ b/java/com/google/gerrit/server/restapi/change/Abandon.java
@@ -14,25 +14,21 @@
package com.google.gerrit.server.restapi.change;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
+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.api.changes.RecipientType;
import com.google.gerrit.extensions.common.ChangeInfo;
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.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.AbandonOp;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -41,9 +37,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -53,37 +47,34 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
implements UiAction<ChangeResource> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> dbProvider;
private final ChangeJson.Factory json;
private final AbandonOp.Factory abandonOpFactory;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final PatchSetUtil patchSetUtil;
@Inject
Abandon(
- Provider<ReviewDb> dbProvider,
ChangeJson.Factory json,
RetryHelper retryHelper,
AbandonOp.Factory abandonOpFactory,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
PatchSetUtil patchSetUtil) {
super(retryHelper);
- this.dbProvider = dbProvider;
this.json = json;
this.abandonOpFactory = abandonOpFactory;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.patchSetUtil = patchSetUtil;
}
@Override
protected ChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AbandonInput input)
- throws RestApiException, UpdateException, OrmException, PermissionBackendException,
- IOException, ConfigInvalidException {
+ throws RestApiException, UpdateException, PermissionBackendException, IOException,
+ ConfigInvalidException {
// Not allowed to abandon if the current patch set is locked.
patchSetUtil.checkPatchSetNotLocked(rsrc.getNotes());
- rsrc.permissions().database(dbProvider).check(ChangePermission.ABANDON);
+ rsrc.permissions().check(ChangePermission.ABANDON);
NotifyHandling notify = input.notify == null ? defaultNotify(rsrc.getChange()) : input.notify;
Change change =
@@ -92,8 +83,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
rsrc.getNotes(),
rsrc.getUser(),
input.message,
- notify,
- notifyUtil.resolveAccounts(input.notifyDetails));
+ notifyResolver.resolve(notify, input.notifyDetails));
return json.noOptions().format(change);
}
@@ -108,8 +98,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
notes,
user,
"",
- defaultNotify(notes.getChange()),
- ImmutableListMultimap.of());
+ NotifyResolver.Result.create(defaultNotify(notes.getChange())));
}
public Change abandon(
@@ -120,8 +109,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
notes,
user,
msgTxt,
- defaultNotify(notes.getChange()),
- ImmutableListMultimap.of());
+ NotifyResolver.Result.create(defaultNotify(notes.getChange())));
}
public Change abandon(
@@ -129,13 +117,12 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
ChangeNotes notes,
CurrentUser user,
String msgTxt,
- NotifyHandling notifyHandling,
- ListMultimap<RecipientType, Account.Id> accountsToNotify)
+ NotifyResolver.Result notify)
throws RestApiException, UpdateException {
AccountState accountState = user.isIdentifiedUser() ? user.asIdentifiedUser().state() : null;
- AbandonOp op = abandonOpFactory.create(accountState, msgTxt, notifyHandling, accountsToNotify);
- try (BatchUpdate u =
- updateFactory.create(dbProvider.get(), notes.getProjectName(), user, TimeUtil.nowTs())) {
+ AbandonOp op = abandonOpFactory.create(accountState, msgTxt);
+ try (BatchUpdate u = updateFactory.create(notes.getProjectName(), user, TimeUtil.nowTs())) {
+ u.setNotify(notify);
u.addOp(notes.getChangeId(), op).execute();
}
return op.getChange();
@@ -150,7 +137,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
.setVisible(false);
Change change = rsrc.getChange();
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
return description;
}
@@ -158,7 +145,7 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
if (patchSetUtil.isPatchSetLocked(rsrc.getNotes())) {
return description;
}
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
logger.atSevere().withCause(e).log(
"Failed to check if the current patch set of change %s is locked", change.getId());
return description;
diff --git a/java/com/google/gerrit/server/restapi/change/ApplyFix.java b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
index e4940ecaed..085f9dee83 100644
--- a/java/com/google/gerrit/server/restapi/change/ApplyFix.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.EditInfo;
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;
@@ -34,7 +35,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -67,7 +67,7 @@ public class ApplyFix implements RestModifyView<FixResource, Void> {
@Override
public Response<EditInfo> apply(FixResource fixResource, Void nothing)
- throws AuthException, OrmException, ResourceConflictException, IOException,
+ throws AuthException, BadRequestException, ResourceConflictException, IOException,
ResourceNotFoundException, PermissionBackendException {
RevisionResource revisionResource = fixResource.getRevisionResource();
Project.NameKey project = revisionResource.getProject();
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index b666fe460c..f0689a8c68 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -58,7 +58,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -99,7 +98,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public ChangeEditResource parse(ChangeResource rsrc, IdString id)
- throws ResourceNotFoundException, AuthException, IOException, OrmException {
+ throws ResourceNotFoundException, AuthException, IOException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
if (!edit.isPresent()) {
throw new ResourceNotFoundException(id);
@@ -123,7 +122,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<?> apply(ChangeResource resource, IdString id, Put.Input input)
- throws AuthException, ResourceConflictException, IOException, OrmException,
+ throws AuthException, ResourceConflictException, BadRequestException, IOException,
PermissionBackendException, BadRequestException {
putEdit.apply(resource, id.get(), input.content);
return Response.none();
@@ -141,7 +140,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<?> apply(ChangeResource rsrc, IdString id, Input in)
- throws IOException, AuthException, ResourceConflictException, OrmException,
+ throws IOException, AuthException, BadRequestException, ResourceConflictException,
PermissionBackendException {
return deleteContent.apply(rsrc, id.get());
}
@@ -188,8 +187,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<EditInfo> apply(ChangeResource rsrc)
- throws AuthException, IOException, ResourceNotFoundException, OrmException,
- PermissionBackendException {
+ throws AuthException, IOException, ResourceNotFoundException, PermissionBackendException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
if (!edit.isPresent()) {
return Response.none();
@@ -244,7 +242,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<?> apply(ChangeResource resource, Post.Input input)
- throws AuthException, IOException, ResourceConflictException, OrmException,
+ throws AuthException, BadRequestException, IOException, ResourceConflictException,
PermissionBackendException {
Project.NameKey project = resource.getProject();
try (Repository repository = repositoryManager.openRepository(project)) {
@@ -295,13 +293,13 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<?> apply(ChangeEditResource rsrc, Input input)
- throws AuthException, ResourceConflictException, IOException, OrmException,
+ throws AuthException, ResourceConflictException, BadRequestException, IOException,
PermissionBackendException, BadRequestException {
return apply(rsrc.getChangeResource(), rsrc.getPath(), input.content);
}
public Response<?> apply(ChangeResource rsrc, String path, RawInput newContent)
- throws ResourceConflictException, AuthException, IOException, OrmException,
+ throws ResourceConflictException, AuthException, BadRequestException, IOException,
PermissionBackendException, BadRequestException {
if (Patch.COMMIT_MSG.equals(path)) {
EditMessage.Input editCommitMessageInput = new EditMessage.Input();
@@ -342,13 +340,13 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<?> apply(ChangeEditResource rsrc, Input input)
- throws AuthException, ResourceConflictException, OrmException, IOException,
+ throws AuthException, BadRequestException, ResourceConflictException, IOException,
PermissionBackendException {
return apply(rsrc.getChangeResource(), rsrc.getPath());
}
public Response<?> apply(ChangeResource rsrc, String filePath)
- throws AuthException, IOException, OrmException, ResourceConflictException,
+ throws AuthException, BadRequestException, IOException, ResourceConflictException,
PermissionBackendException {
try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
editModifier.deleteFile(repository, rsrc.getNotes(), filePath);
@@ -378,8 +376,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
}
@Override
- public Response<BinaryResult> apply(ChangeEditResource rsrc)
- throws AuthException, IOException, OrmException {
+ public Response<BinaryResult> apply(ChangeEditResource rsrc) throws AuthException, IOException {
try {
if (Patch.COMMIT_MSG.equals(rsrc.getPath())) {
return Response.ok(getMessage.apply(rsrc.getChangeResource()));
@@ -451,7 +448,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public Response<?> apply(ChangeResource rsrc, Input input)
throws AuthException, IOException, BadRequestException, ResourceConflictException,
- OrmException, PermissionBackendException {
+ PermissionBackendException {
if (input == null || Strings.isNullOrEmpty(input.message)) {
throw new BadRequestException("commit message must be provided");
}
@@ -485,7 +482,7 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
@Override
public BinaryResult apply(ChangeResource rsrc)
- throws AuthException, IOException, ResourceNotFoundException, OrmException {
+ throws AuthException, IOException, ResourceNotFoundException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
String msg;
if (edit.isPresent()) {
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
index 12b3797c4e..6ec4fdbc53 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
@@ -18,33 +18,27 @@ import com.google.gerrit.extensions.api.changes.IncludedInInfo;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.IncludedIn;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
@Singleton
public class ChangeIncludedIn implements RestReadView<ChangeResource> {
- private Provider<ReviewDb> db;
private PatchSetUtil psUtil;
private IncludedIn includedIn;
@Inject
- ChangeIncludedIn(Provider<ReviewDb> db, PatchSetUtil psUtil, IncludedIn includedIn) {
- this.db = db;
+ ChangeIncludedIn(PatchSetUtil psUtil, IncludedIn includedIn) {
this.psUtil = psUtil;
this.includedIn = includedIn;
}
@Override
- public IncludedInInfo apply(ChangeResource rsrc)
- throws RestApiException, OrmException, IOException {
- PatchSet ps = psUtil.current(db.get(), rsrc.getNotes());
+ public IncludedInInfo apply(ChangeResource rsrc) throws RestApiException, IOException {
+ PatchSet ps = psUtil.current(rsrc.getNotes());
return includedIn.apply(rsrc.getProject(), ps.getRevision().get());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
index 25fc350720..96c517fe25 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
@@ -23,7 +23,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;
@@ -52,7 +51,7 @@ public class ChangeMessages implements ChildCollection<ChangeResource, ChangeMes
@Override
public ChangeMessageResource parse(ChangeResource parent, IdString id)
- throws OrmException, ResourceNotFoundException, PermissionBackendException {
+ throws ResourceNotFoundException, PermissionBackendException {
String uuid = id.get();
List<ChangeMessageInfo> changeMessages = listChangeMessages.apply(parent);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
index fd28ffba78..028eb52a5e 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
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.RestApiException;
import com.google.gerrit.extensions.restapi.RestCollection;
@@ -24,7 +25,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeResource;
@@ -34,7 +34,6 @@ import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -43,7 +42,6 @@ import java.util.List;
@Singleton
public class ChangesCollection implements RestCollection<TopLevelResource, ChangeResource> {
- private final Provider<ReviewDb> db;
private final Provider<CurrentUser> user;
private final Provider<QueryChanges> queryFactory;
private final DynamicMap<RestView<ChangeResource>> views;
@@ -54,7 +52,6 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
@Inject
public ChangesCollection(
- Provider<ReviewDb> db,
Provider<CurrentUser> user,
Provider<QueryChanges> queryFactory,
DynamicMap<RestView<ChangeResource>> views,
@@ -62,7 +59,6 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
ChangeResource.Factory changeResourceFactory,
PermissionBackend permissionBackend,
ProjectCache projectCache) {
- this.db = db;
this.user = user;
this.queryFactory = queryFactory;
this.views = views;
@@ -84,7 +80,7 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
@Override
public ChangeResource parse(TopLevelResource root, IdString id)
- throws RestApiException, OrmException, PermissionBackendException, IOException {
+ throws RestApiException, PermissionBackendException, IOException {
List<ChangeNotes> notes = changeFinder.find(id.encoded());
if (notes.isEmpty()) {
throw new ResourceNotFoundException(id);
@@ -101,7 +97,8 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
}
public ChangeResource parse(Change.Id id)
- throws RestApiException, OrmException, PermissionBackendException, IOException {
+ throws ResourceConflictException, ResourceNotFoundException, PermissionBackendException,
+ IOException {
List<ChangeNotes> notes = changeFinder.find(id);
if (notes.isEmpty()) {
throw new ResourceNotFoundException(toIdString(id));
@@ -127,7 +124,7 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
private boolean canRead(ChangeNotes notes) throws PermissionBackendException, IOException {
try {
- permissionBackend.currentUser().change(notes).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
} catch (AuthException e) {
return false;
}
@@ -139,7 +136,7 @@ public class ChangesCollection implements RestCollection<TopLevelResource, Chang
}
private void checkProjectStatePermitsRead(Project.NameKey project)
- throws IOException, RestApiException {
+ throws IOException, ResourceNotFoundException, ResourceConflictException {
ProjectState projectState = projectCache.checkedGet(project);
if (projectState == null) {
throw new ResourceNotFoundException("project not found: " + project.get());
diff --git a/java/com/google/gerrit/server/restapi/change/Check.java b/java/com/google/gerrit/server/restapi/change/Check.java
index dd0ce1091d..f62aa5ac49 100644
--- a/java/com/google/gerrit/server/restapi/change/Check.java
+++ b/java/com/google/gerrit/server/restapi/change/Check.java
@@ -29,7 +29,6 @@ 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.NoSuchProjectException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -47,14 +46,13 @@ public class Check
}
@Override
- public Response<ChangeInfo> apply(ChangeResource rsrc) throws RestApiException, OrmException {
+ public Response<ChangeInfo> apply(ChangeResource rsrc) throws RestApiException {
return Response.withMustRevalidate(newChangeJson().format(rsrc));
}
@Override
public Response<ChangeInfo> apply(ChangeResource rsrc, FixInput input)
- throws RestApiException, OrmException, PermissionBackendException, NoSuchProjectException,
- IOException {
+ throws RestApiException, PermissionBackendException, NoSuchProjectException, IOException {
PermissionBackend.WithUser perm = permissionBackend.currentUser();
if (!rsrc.isUserOwner()) {
try {
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index b68122e6f5..72781c3bea 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -41,7 +41,6 @@ import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -78,8 +77,8 @@ public class CherryPick
@Override
public CherryPickChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
- throws OrmException, IOException, UpdateException, RestApiException,
- PermissionBackendException, ConfigInvalidException, NoSuchProjectException {
+ 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");
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 5def390150..132310d048 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -14,12 +14,15 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.common.base.MoreObjects.firstNonNull;
+
import com.google.auto.value.AutoValue;
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.extensions.api.changes.CherryPickInput;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -28,18 +31,15 @@ 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.Change.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.change.ChangeInserter;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@@ -47,6 +47,7 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
@@ -59,7 +60,6 @@ import com.google.gerrit.server.update.BatchUpdate;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -71,6 +71,7 @@ import java.util.Set;
import java.util.TimeZone;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
@@ -94,7 +95,6 @@ public class CherryPickChange {
abstract ImmutableSet<String> filesWithGitConflicts();
}
- private final Provider<ReviewDb> dbProvider;
private final Sequences seq;
private final Provider<InternalChangeQuery> queryProvider;
private final GitRepositoryManager gitManager;
@@ -106,11 +106,10 @@ public class CherryPickChange {
private final ChangeNotes.Factory changeNotesFactory;
private final ProjectCache projectCache;
private final ApprovalsUtil approvalsUtil;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
@Inject
CherryPickChange(
- Provider<ReviewDb> dbProvider,
Sequences seq,
Provider<InternalChangeQuery> queryProvider,
@GerritPersonIdent PersonIdent myIdent,
@@ -122,8 +121,7 @@ public class CherryPickChange {
ChangeNotes.Factory changeNotesFactory,
ProjectCache projectCache,
ApprovalsUtil approvalsUtil,
- NotifyUtil notifyUtil) {
- this.dbProvider = dbProvider;
+ NotifyResolver notifyResolver) {
this.seq = seq;
this.queryProvider = queryProvider;
this.gitManager = gitManager;
@@ -135,7 +133,7 @@ public class CherryPickChange {
this.changeNotesFactory = changeNotesFactory;
this.projectCache = projectCache;
this.approvalsUtil = approvalsUtil;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
}
public Result cherryPick(
@@ -144,8 +142,8 @@ public class CherryPickChange {
PatchSet patch,
CherryPickInput input,
Branch.NameKey dest)
- throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
- UpdateException, RestApiException, ConfigInvalidException, NoSuchProjectException {
+ throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
+ RestApiException, ConfigInvalidException, NoSuchProjectException {
return cherryPick(
batchUpdateFactory,
change,
@@ -162,8 +160,8 @@ public class CherryPickChange {
ObjectId sourceCommit,
CherryPickInput input,
Branch.NameKey dest)
- throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
- UpdateException, RestApiException, ConfigInvalidException, NoSuchProjectException {
+ throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
+ RestApiException, ConfigInvalidException, NoSuchProjectException {
IdentifiedUser identifiedUser = user.get();
try (Repository git = gitManager.openRepository(project);
@@ -243,14 +241,14 @@ public class CherryPickChange {
+ " reside on the same branch. "
+ "Cannot create a new patch set.");
}
- try (BatchUpdate bu =
- batchUpdateFactory.create(dbProvider.get(), project, identifiedUser, now)) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, identifiedUser, now)) {
bu.setRepository(git, revWalk, oi);
+ bu.setNotify(resolveNotify(input));
Change.Id changeId;
if (destChanges.size() == 1) {
// The change key exists on the destination branch. The cherry pick
// will be added as a new patch set.
- changeId = insertPatchSet(bu, git, destChanges.get(0).notes(), cherryPickCommit, input);
+ changeId = insertPatchSet(bu, git, destChanges.get(0).notes(), cherryPickCommit);
} else {
// Change key not found on destination branch. We can create a new
// change.
@@ -272,7 +270,7 @@ public class CherryPickChange {
}
private RevCommit getBaseCommit(Ref destRef, String project, RevWalk revWalk, String base)
- throws RestApiException, IOException, OrmException {
+ throws RestApiException, IOException {
RevCommit destRefTip = revWalk.parseCommit(destRef.getObjectId());
// The tip commit of the destination ref is the default base for the newly created change.
if (Strings.isNullOrEmpty(base)) {
@@ -287,7 +285,14 @@ public class CherryPickChange {
String.format("Base %s doesn't represent a valid SHA-1", base), e);
}
- RevCommit baseCommit = revWalk.parseCommit(baseObjectId);
+ RevCommit baseCommit;
+ try {
+ baseCommit = revWalk.parseCommit(baseObjectId);
+ } catch (MissingObjectException e) {
+ throw new UnprocessableEntityException(
+ String.format("Base %s doesn't exist", baseObjectId.name()), e);
+ }
+
InternalChangeQuery changeQuery = queryProvider.get();
changeQuery.enforceVisibility(true);
List<ChangeData> changeDatas = changeQuery.byBranchCommit(project, destRef.getName(), base);
@@ -304,31 +309,24 @@ public class CherryPickChange {
}
Change change = changeDatas.get(0).change();
- Change.Status status = change.getStatus();
- if (status == Status.NEW || status == Status.MERGED) {
+ if (!change.isAbandoned()) {
// The base commit is a valid change revision.
return baseCommit;
}
throw new ResourceConflictException(
String.format(
- "Change %s with commit %s is %s", change.getChangeId(), base, status.asChangeStatus()));
+ "Change %s with commit %s is %s",
+ change.getChangeId(), base, ChangeUtil.status(change)));
}
private Change.Id insertPatchSet(
- BatchUpdate bu,
- Repository git,
- ChangeNotes destNotes,
- CodeReviewCommit cherryPickCommit,
- CherryPickInput input)
- throws IOException, OrmException, BadRequestException, ConfigInvalidException {
+ BatchUpdate bu, Repository git, ChangeNotes destNotes, CodeReviewCommit cherryPickCommit)
+ throws IOException {
Change destChange = destNotes.getChange();
PatchSet.Id psId = ChangeUtil.nextPatchSetId(git, destChange.currentPatchSetId());
PatchSetInserter inserter = patchSetInserterFactory.create(destNotes, psId, cherryPickCommit);
- inserter
- .setMessage("Uploaded patch set " + inserter.getPatchSetId().get() + ".")
- .setNotify(input.notify)
- .setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
+ inserter.setMessage("Uploaded patch set " + inserter.getPatchSetId().get() + ".");
bu.addOp(destChange.getId(), inserter);
return destChange.getId();
}
@@ -341,7 +339,7 @@ public class CherryPickChange {
@Nullable Change sourceChange,
ObjectId sourceCommit,
CherryPickInput input)
- throws OrmException, IOException, BadRequestException, ConfigInvalidException {
+ throws IOException {
Change.Id changeId = new Change.Id(seq.nextChangeId());
ChangeInserter ins = changeInserterFactory.create(changeId, cherryPickCommit, refName);
Branch.NameKey sourceBranch = sourceChange == null ? null : sourceChange.getDest();
@@ -351,13 +349,10 @@ public class CherryPickChange {
.setTopic(topic)
.setWorkInProgress(
(sourceChange != null && sourceChange.isWorkInProgress())
- || !cherryPickCommit.getFilesWithGitConflicts().isEmpty())
- .setNotify(input.notify)
- .setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
+ || !cherryPickCommit.getFilesWithGitConflicts().isEmpty());
if (input.keepReviewers && sourceChange != null) {
ReviewerSet reviewerSet =
- approvalsUtil.getReviewers(
- dbProvider.get(), changeNotesFactory.createChecked(dbProvider.get(), sourceChange));
+ approvalsUtil.getReviewers(changeNotesFactory.createChecked(sourceChange));
Set<Account.Id> reviewers =
new HashSet<>(reviewerSet.byState(ReviewerStateInternal.REVIEWER));
reviewers.add(sourceChange.getOwner());
@@ -370,6 +365,12 @@ public class CherryPickChange {
return changeId;
}
+ private NotifyResolver.Result resolveNotify(CherryPickInput input)
+ throws BadRequestException, ConfigInvalidException, IOException {
+ return notifyResolver.resolve(
+ firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
+ }
+
private String messageForDestinationChange(
PatchSet.Id patchSetId,
Branch.NameKey sourceBranch,
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index f76689c4e2..f34f1787fd 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -37,7 +37,6 @@ import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -73,8 +72,8 @@ public class CherryPickCommit
@Override
public CherryPickChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
- throws OrmException, IOException, UpdateException, RestApiException,
- PermissionBackendException, ConfigInvalidException, NoSuchProjectException {
+ 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;
diff --git a/java/com/google/gerrit/server/restapi/change/Comments.java b/java/com/google/gerrit/server/restapi/change/Comments.java
index f563cc620c..d9a0d57201 100644
--- a/java/com/google/gerrit/server/restapi/change/Comments.java
+++ b/java/com/google/gerrit/server/restapi/change/Comments.java
@@ -20,32 +20,26 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.CommentResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class Comments implements ChildCollection<RevisionResource, CommentResource> {
private final DynamicMap<RestView<CommentResource>> views;
private final ListRevisionComments list;
- private final Provider<ReviewDb> dbProvider;
private final CommentsUtil commentsUtil;
@Inject
Comments(
DynamicMap<RestView<CommentResource>> views,
ListRevisionComments list,
- Provider<ReviewDb> dbProvider,
CommentsUtil commentsUtil) {
this.views = views;
this.list = list;
- this.dbProvider = dbProvider;
this.commentsUtil = commentsUtil;
}
@@ -60,13 +54,11 @@ public class Comments implements ChildCollection<RevisionResource, CommentResour
}
@Override
- public CommentResource parse(RevisionResource rev, IdString id)
- throws ResourceNotFoundException, OrmException {
+ public CommentResource parse(RevisionResource rev, IdString id) throws ResourceNotFoundException {
String uuid = id.get();
ChangeNotes notes = rev.getNotes();
- for (Comment c :
- commentsUtil.publishedByPatchSet(dbProvider.get(), notes, rev.getPatchSet().getId())) {
+ for (Comment c : commentsUtil.publishedByPatchSet(notes, rev.getPatchSet().getId())) {
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 f906f7cc9f..f626db2e68 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -14,14 +14,15 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.common.base.MoreObjects.firstNonNull;
import static org.eclipse.jgit.lib.Constants.SIGNED_OFF_BY_TAG;
import com.google.common.base.Joiner;
-import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
@@ -30,6 +31,7 @@ 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.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
@@ -39,23 +41,21 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -72,18 +72,19 @@ import com.google.gerrit.server.update.RetryingRestCollectionModifyView;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.errors.MissingObjectException;
+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.ObjectId;
@@ -102,7 +103,6 @@ public class CreateChange
extends RetryingRestCollectionModifyView<
TopLevelResource, ChangeResource, ChangeInput, Response<ChangeInfo>> {
private final String anonymousCowardName;
- private final Provider<ReviewDb> db;
private final GitRepositoryManager gitManager;
private final Sequences seq;
private final TimeZone serverTimeZone;
@@ -116,14 +116,13 @@ public class CreateChange
private final PatchSetUtil psUtil;
private final MergeUtil.Factory mergeUtilFactory;
private final SubmitType submitType;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final ContributorAgreementsChecker contributorAgreements;
private final boolean disablePrivateChanges;
@Inject
CreateChange(
@AnonymousCowardName String anonymousCowardName,
- Provider<ReviewDb> db,
GitRepositoryManager gitManager,
Sequences seq,
@GerritPersonIdent PersonIdent myIdent,
@@ -138,11 +137,10 @@ public class CreateChange
PatchSetUtil psUtil,
@GerritServerConfig Config config,
MergeUtil.Factory mergeUtilFactory,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
ContributorAgreementsChecker contributorAgreements) {
super(retryHelper);
this.anonymousCowardName = anonymousCowardName;
- this.db = db;
this.gitManager = gitManager;
this.seq = seq;
this.serverTimeZone = myIdent.getTimeZone();
@@ -157,15 +155,44 @@ public class CreateChange
this.submitType = config.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY);
this.disablePrivateChanges = config.getBoolean("change", null, "disablePrivateChanges", false);
this.mergeUtilFactory = mergeUtilFactory;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.contributorAgreements = contributorAgreements;
}
@Override
protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
- throws OrmException, IOException, InvalidChangeOperationException, RestApiException,
- UpdateException, PermissionBackendException, ConfigInvalidException {
+ throws IOException, InvalidChangeOperationException, RestApiException, UpdateException,
+ PermissionBackendException, ConfigInvalidException {
+ IdentifiedUser me = user.get().asIdentifiedUser();
+ checkAndSanitizeChangeInput(input, me);
+
+ ProjectResource projectResource = projectsCollection.parse(input.project);
+ ProjectState projectState = projectResource.getProjectState();
+ projectState.checkStatePermitsWrite();
+
+ Project.NameKey project = projectResource.getNameKey();
+ contributorAgreements.check(project, user.get());
+
+ checkRequiredPermissions(project, input.branch);
+
+ Change newChange = createNewChange(input, me, projectState, updateFactory);
+ ChangeJson json = jsonFactory.noOptions();
+ return Response.created(json.format(newChange));
+ }
+
+ /**
+ * Checks and sanitizes the user input, e.g. check whether the input is legal; clean the input so
+ * that it meets the requirement for creating a change; set a field based on the global configs,
+ * etc.
+ *
+ * @param input the {@code ChangeInput} from the request. Note this method modify the {@code
+ * ChangeInput} object so that it can be reused directly by follow-up code.
+ * @param me the user who sent the current request to create a change.
+ * @throws BadRequestException if the input is not legal.
+ */
+ private void checkAndSanitizeChangeInput(ChangeInput input, IdentifiedUser me)
+ throws RestApiException, PermissionBackendException, IOException {
if (Strings.isNullOrEmpty(input.project)) {
throw new BadRequestException("project must be non-empty");
}
@@ -173,164 +200,231 @@ public class CreateChange
if (Strings.isNullOrEmpty(input.branch)) {
throw new BadRequestException("branch must be non-empty");
}
+ input.branch = RefNames.fullName(input.branch);
- String subject = clean(Strings.nullToEmpty(input.subject));
- if (Strings.isNullOrEmpty(subject)) {
+ String subject = Strings.nullToEmpty(input.subject);
+ subject = subject.replaceAll("(?m)^#.*$\n?", "").trim();
+ if (subject.isEmpty()) {
throw new BadRequestException("commit message must be non-empty");
}
+ input.subject = subject;
- if (input.status != null) {
- if (input.status != ChangeStatus.NEW) {
- throw new BadRequestException("unsupported change status");
- }
+ if (input.topic != null) {
+ input.topic = Strings.emptyToNull(input.topic.trim());
+ }
+
+ if (input.status != null && input.status != ChangeStatus.NEW) {
+ throw new BadRequestException("unsupported change status");
}
if (input.baseChange != null && input.baseCommit != null) {
throw new BadRequestException("only provide one of base_change or base_commit");
}
- ProjectResource rsrc = projectsCollection.parse(input.project);
- boolean privateByDefault = rsrc.getProjectState().is(BooleanProjectConfig.PRIVATE_BY_DEFAULT);
+ ProjectResource projectResource = projectsCollection.parse(input.project);
+ // Checks whether the change to be created should be a private change.
+ boolean privateByDefault =
+ projectResource.getProjectState().is(BooleanProjectConfig.PRIVATE_BY_DEFAULT);
boolean isPrivate = input.isPrivate == null ? privateByDefault : input.isPrivate;
-
if (isPrivate && disablePrivateChanges) {
throw new MethodNotAllowedException("private changes are disabled");
}
+ input.isPrivate = isPrivate;
+
+ ProjectState projectState = projectResource.getProjectState();
+
+ if (input.workInProgress == null) {
+ if (projectState.is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)) {
+ input.workInProgress = true;
+ } else {
+ input.workInProgress =
+ firstNonNull(me.state().getGeneralPreferences().workInProgressByDefault, false);
+ }
+ }
- contributorAgreements.check(rsrc.getNameKey(), rsrc.getUser());
+ if (input.merge != null) {
+ if (!(submitType.equals(SubmitType.MERGE_ALWAYS)
+ || submitType.equals(SubmitType.MERGE_IF_NECESSARY))) {
+ throw new BadRequestException("Submit type: " + submitType + " is not supported");
+ }
+ }
+ }
+
+ private void checkRequiredPermissions(Project.NameKey project, String refName)
+ throws ResourceNotFoundException, AuthException, PermissionBackendException {
+ try {
+ permissionBackend.currentUser().project(project).ref(refName).check(RefPermission.READ);
+ } catch (AuthException e) {
+ throw new ResourceNotFoundException(String.format("ref %s not found", refName), e);
+ }
- Project.NameKey project = rsrc.getNameKey();
- String refName = RefNames.fullName(input.branch);
permissionBackend
.currentUser()
.project(project)
.ref(refName)
.check(RefPermission.CREATE_CHANGE);
- rsrc.getProjectState().checkStatePermitsWrite();
+ }
- try (Repository git = gitManager.openRepository(project);
+ private Change createNewChange(
+ ChangeInput input,
+ IdentifiedUser me,
+ ProjectState projectState,
+ BatchUpdate.Factory updateFactory)
+ throws RestApiException, PermissionBackendException, IOException, ConfigInvalidException,
+ UpdateException {
+ try (Repository git = gitManager.openRepository(projectState.getNameKey());
ObjectInserter oi = git.newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
- ObjectId parentCommit;
- List<String> groups;
- Ref destRef = git.getRefDatabase().exactRef(refName);
+ PatchSet basePatchSet = null;
+ List<String> groups = Collections.emptyList();
if (input.baseChange != null) {
- List<ChangeNotes> notes = changeFinder.find(input.baseChange);
- if (notes.size() != 1) {
- throw new UnprocessableEntityException("Base change not found: " + input.baseChange);
- }
- ChangeNotes change = Iterables.getOnlyElement(notes);
- try {
- permissionBackend.currentUser().change(change).database(db).check(ChangePermission.READ);
- } catch (AuthException e) {
- throw new UnprocessableEntityException("Read not permitted for " + input.baseChange, e);
- }
- PatchSet ps = psUtil.current(db.get(), change);
- parentCommit = ObjectId.fromString(ps.getRevision().get());
- groups = ps.getGroups();
- } else if (input.baseCommit != null) {
- try {
- parentCommit = ObjectId.fromString(input.baseCommit);
- } catch (InvalidObjectIdException e) {
- throw new UnprocessableEntityException(
- String.format("Base %s doesn't represent a valid SHA-1", input.baseCommit), e);
- }
- RevCommit parentRevCommit = rw.parseCommit(parentCommit);
- RevCommit destRefRevCommit = rw.parseCommit(destRef.getObjectId());
- if (!rw.isMergedInto(parentRevCommit, destRefRevCommit)) {
- throw new BadRequestException(
- String.format("Commit %s doesn't exist on ref %s", input.baseCommit, refName));
- }
- groups = Collections.emptyList();
- } else {
- if (destRef != null) {
- if (Boolean.TRUE.equals(input.newBranch)) {
- throw new ResourceConflictException(
- String.format("Branch %s already exists.", refName));
- }
- parentCommit = destRef.getObjectId();
- } else {
- if (Boolean.TRUE.equals(input.newBranch)) {
- parentCommit = null;
- } else {
- throw new BadRequestException("Must provide a destination branch");
- }
- }
- groups = Collections.emptyList();
+ ChangeNotes baseChange = getBaseChange(input.baseChange);
+ basePatchSet = psUtil.current(baseChange);
+ groups = basePatchSet.getGroups();
}
+ ObjectId parentCommit =
+ getParentCommit(
+ git, rw, input.branch, input.newBranch, basePatchSet, input.baseCommit, input.merge);
+
RevCommit mergeTip = parentCommit == null ? null : rw.parseCommit(parentCommit);
Timestamp now = TimeUtil.nowTs();
- IdentifiedUser me = user.get().asIdentifiedUser();
PersonIdent author = me.newCommitterIdent(now, serverTimeZone);
- AccountState accountState = me.state();
- GeneralPreferencesInfo info = accountState.getGeneralPreferences();
-
- boolean isWorkInProgress =
- input.workInProgress == null
- ? rsrc.getProjectState().is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)
- || MoreObjects.firstNonNull(info.workInProgressByDefault, false)
- : input.workInProgress;
-
- // Add a Change-Id line if there isn't already one
- String commitMessage = subject;
- if (ChangeIdUtil.indexOfChangeId(commitMessage, "\n") == -1) {
- ObjectId id = CommitMessageUtil.generateChangeId();
- commitMessage = ChangeIdUtil.insertId(commitMessage, id);
- }
-
- if (Boolean.TRUE.equals(info.signedOffBy)) {
- commitMessage =
- Joiner.on("\n")
- .join(
- commitMessage.trim(),
- String.format(
- "%s%s",
- SIGNED_OFF_BY_TAG,
- accountState.getAccount().getNameEmail(anonymousCowardName)));
- }
+ String commitMessage = getCommitMessage(input.subject, me);
RevCommit c;
if (input.merge != null) {
// create a merge commit
- if (!(submitType.equals(SubmitType.MERGE_ALWAYS)
- || submitType.equals(SubmitType.MERGE_IF_NECESSARY))) {
- throw new BadRequestException("Submit type: " + submitType + " is not supported");
- }
- c =
- newMergeCommit(
- git, oi, rw, rsrc.getProjectState(), mergeTip, input.merge, author, commitMessage);
+ c = newMergeCommit(git, oi, rw, projectState, mergeTip, input.merge, author, commitMessage);
} else {
// create an empty commit
c = newCommit(oi, rw, author, mergeTip, commitMessage);
}
Change.Id changeId = new Change.Id(seq.nextChangeId());
- ChangeInserter ins = changeInserterFactory.create(changeId, c, refName);
+ ChangeInserter ins = changeInserterFactory.create(changeId, c, input.branch);
ins.setMessage(String.format("Uploaded patch set %s.", ins.getPatchSetId().get()));
- String topic = input.topic;
- if (topic != null) {
- topic = Strings.emptyToNull(topic.trim());
- }
- ins.setTopic(topic);
- ins.setPrivate(isPrivate);
- ins.setWorkInProgress(isWorkInProgress);
+ ins.setTopic(input.topic);
+ ins.setPrivate(input.isPrivate);
+ ins.setWorkInProgress(input.workInProgress);
ins.setGroups(groups);
- ins.setNotify(input.notify);
- ins.setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
- try (BatchUpdate bu = updateFactory.create(db.get(), project, me, now)) {
+ try (BatchUpdate bu = updateFactory.create(projectState.getNameKey(), me, now)) {
bu.setRepository(git, rw, oi);
+ bu.setNotify(
+ notifyResolver.resolve(
+ firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
bu.insertChange(ins);
bu.execute();
}
- ChangeJson json = jsonFactory.noOptions();
- return Response.created(json.format(ins.getChange()));
+ return ins.getChange();
} catch (IllegalArgumentException e) {
throw new BadRequestException(e.getMessage());
}
}
+ private ChangeNotes getBaseChange(String baseChange)
+ throws UnprocessableEntityException, PermissionBackendException {
+ List<ChangeNotes> notes = changeFinder.find(baseChange);
+ if (notes.size() != 1) {
+ throw new UnprocessableEntityException("Base change not found: " + baseChange);
+ }
+ ChangeNotes change = Iterables.getOnlyElement(notes);
+ try {
+ permissionBackend.currentUser().change(change).check(ChangePermission.READ);
+ } catch (AuthException e) {
+ throw new UnprocessableEntityException("Read not permitted for " + baseChange, e);
+ }
+
+ return change;
+ }
+
+ @Nullable
+ private ObjectId getParentCommit(
+ Repository repo,
+ RevWalk revWalk,
+ String inputBranch,
+ @Nullable Boolean newBranch,
+ @Nullable PatchSet basePatchSet,
+ @Nullable String baseCommit,
+ @Nullable MergeInput mergeInput)
+ throws BadRequestException, IOException, UnprocessableEntityException,
+ ResourceConflictException {
+ if (basePatchSet != null) {
+ return ObjectId.fromString(basePatchSet.getRevision().get());
+ }
+
+ Ref destRef = repo.getRefDatabase().exactRef(inputBranch);
+ ObjectId parentCommit;
+ if (baseCommit != null) {
+ try {
+ parentCommit = ObjectId.fromString(baseCommit);
+ } catch (InvalidObjectIdException e) {
+ throw new UnprocessableEntityException(
+ String.format("Base %s doesn't represent a valid SHA-1", baseCommit), e);
+ }
+
+ RevCommit parentRevCommit;
+ try {
+ parentRevCommit = revWalk.parseCommit(parentCommit);
+ } catch (MissingObjectException e) {
+ throw new UnprocessableEntityException(
+ String.format("Base %s doesn't exist", baseCommit), e);
+ }
+
+ if (destRef == null) {
+ throw new BadRequestException("Destination branch does not exist");
+ }
+ RevCommit destRefRevCommit = revWalk.parseCommit(destRef.getObjectId());
+
+ if (!revWalk.isMergedInto(parentRevCommit, destRefRevCommit)) {
+ throw new BadRequestException(
+ String.format("Commit %s doesn't exist on ref %s", baseCommit, inputBranch));
+ }
+ } else {
+ if (destRef != null) {
+ if (Boolean.TRUE.equals(newBranch)) {
+ throw new ResourceConflictException(
+ String.format("Branch %s already exists.", inputBranch));
+ }
+ parentCommit = destRef.getObjectId();
+ } else {
+ if (Boolean.TRUE.equals(newBranch)) {
+ if (mergeInput != null) {
+ throw new BadRequestException("Cannot create merge: destination branch does not exist");
+ }
+ parentCommit = null;
+ } else {
+ throw new BadRequestException("Destination branch does not exist");
+ }
+ }
+ }
+
+ return parentCommit;
+ }
+
+ private String getCommitMessage(String subject, IdentifiedUser me) {
+ // Add a Change-Id line if there isn't already one
+ String commitMessage = subject;
+ if (ChangeIdUtil.indexOfChangeId(commitMessage, "\n") == -1) {
+ ObjectId id = CommitMessageUtil.generateChangeId();
+ commitMessage = ChangeIdUtil.insertId(commitMessage, id);
+ }
+
+ if (Boolean.TRUE.equals(me.state().getGeneralPreferences().signedOffBy)) {
+ commitMessage =
+ Joiner.on("\n")
+ .join(
+ commitMessage.trim(),
+ String.format(
+ "%s%s",
+ SIGNED_OFF_BY_TAG,
+ me.state().getAccount().getNameEmail(anonymousCowardName)));
+ }
+
+ return commitMessage;
+ }
+
private static RevCommit newCommit(
ObjectInserter oi,
RevWalk rw,
@@ -373,22 +467,29 @@ public class CreateChange
MergeUtil mergeUtil = mergeUtilFactory.create(projectState);
// default merge strategy from project settings
String mergeStrategy =
- MoreObjects.firstNonNull(
- Strings.emptyToNull(merge.strategy), mergeUtil.mergeStrategyName());
-
- return MergeUtil.createMergeCommit(
- oi,
- repo.getConfig(),
- mergeTip,
- sourceCommit,
- mergeStrategy,
- authorIdent,
- commitMessage,
- rw);
+ firstNonNull(Strings.emptyToNull(merge.strategy), mergeUtil.mergeStrategyName());
+
+ try {
+ return MergeUtil.createMergeCommit(
+ oi,
+ repo.getConfig(),
+ mergeTip,
+ sourceCommit,
+ mergeStrategy,
+ authorIdent,
+ commitMessage,
+ rw);
+ } catch (NoMergeBaseException e) {
+ if (MergeBaseFailureReason.TOO_MANY_MERGE_BASES == e.getReason()
+ || MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION == e.getReason()) {
+ throw new ResourceConflictException(
+ String.format("Cannot create merge commit: %s", e.getMessage()), e);
+ }
+ throw e;
+ }
}
- private static ObjectId insert(ObjectInserter inserter, CommitBuilder commit)
- throws IOException, UnsupportedEncodingException {
+ private static ObjectId insert(ObjectInserter inserter, CommitBuilder commit) throws IOException {
ObjectId id = inserter.insert(commit);
inserter.flush();
return id;
@@ -397,16 +498,4 @@ public class CreateChange
private static ObjectId emptyTreeId(ObjectInserter inserter) throws IOException {
return inserter.insert(new TreeFormatter());
}
-
- /**
- * Remove comment lines from a commit message.
- *
- * <p>Based on {@link org.eclipse.jgit.util.ChangeIdUtil#clean}.
- *
- * @param msg
- * @return message without comment lines, possibly empty.
- */
- private String clean(String msg) {
- return msg.replaceAll("(?m)^#.*$\n?", "").trim();
- }
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index 0e93c5578d..b6e7628731 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -28,7 +28,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
@@ -42,7 +41,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -51,7 +49,6 @@ import java.util.Collections;
@Singleton
public class CreateDraftComment
extends RetryingRestModifyView<RevisionResource, DraftInput, Response<CommentInfo>> {
- private final Provider<ReviewDb> db;
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
private final PatchSetUtil psUtil;
@@ -59,14 +56,12 @@ public class CreateDraftComment
@Inject
CreateDraftComment(
- Provider<ReviewDb> db,
RetryHelper retryHelper,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil,
PatchSetUtil psUtil,
PatchListCache patchListCache) {
super(retryHelper);
- this.db = db;
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
this.psUtil = psUtil;
@@ -76,7 +71,7 @@ public class CreateDraftComment
@Override
protected Response<CommentInfo> applyImpl(
BatchUpdate.Factory updateFactory, RevisionResource rsrc, DraftInput in)
- throws RestApiException, UpdateException, OrmException, PermissionBackendException {
+ throws RestApiException, UpdateException, PermissionBackendException {
if (Strings.isNullOrEmpty(in.path)) {
throw new BadRequestException("path must be non-empty");
} else if (in.message == null || in.message.trim().isEmpty()) {
@@ -88,7 +83,7 @@ public class CreateDraftComment
}
try (BatchUpdate bu =
- updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
Op op = new Op(rsrc.getPatchSet().getId(), in);
bu.addOp(rsrc.getChange().getId(), op);
bu.execute();
@@ -110,9 +105,9 @@ public class CreateDraftComment
@Override
public boolean updateChange(ChangeContext ctx)
- throws ResourceNotFoundException, OrmException, UnprocessableEntityException,
+ throws ResourceNotFoundException, UnprocessableEntityException,
PatchListNotAvailableException {
- PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ PatchSet ps = psUtil.get(ctx.getNotes(), psId);
if (ps == null) {
throw new ResourceNotFoundException("patch set not found: " + psId);
}
@@ -126,9 +121,7 @@ public class CreateDraftComment
setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
- commentsUtil.putComments(
- ctx.getDb(), ctx.getUpdate(psId), Status.DRAFT, Collections.singleton(comment));
- ctx.dontBumpLastUpdatedOn();
+ commentsUtil.putComments(ctx.getUpdate(psId), 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 296ca3f7bf..b7bcaf9852 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -17,7 +17,6 @@ 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.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.MergeInput;
@@ -33,7 +32,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -42,6 +40,7 @@ import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
@@ -58,7 +57,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -79,7 +77,6 @@ import org.eclipse.jgit.util.ChangeIdUtil;
@Singleton
public class CreateMergePatchSet
extends RetryingRestModifyView<ChangeResource, MergePatchSetInput, Response<ChangeInfo>> {
- private final Provider<ReviewDb> db;
private final GitRepositoryManager gitManager;
private final CommitsCollection commits;
private final TimeZone serverTimeZone;
@@ -94,7 +91,6 @@ public class CreateMergePatchSet
@Inject
CreateMergePatchSet(
- Provider<ReviewDb> db,
GitRepositoryManager gitManager,
CommitsCollection commits,
@GerritPersonIdent PersonIdent myIdent,
@@ -108,7 +104,6 @@ public class CreateMergePatchSet
ChangeFinder changeFinder,
PermissionBackend permissionBackend) {
super(retryHelper);
- this.db = db;
this.gitManager = gitManager;
this.commits = commits;
this.serverTimeZone = myIdent.getTimeZone();
@@ -125,12 +120,11 @@ public class CreateMergePatchSet
@Override
protected Response<ChangeInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, MergePatchSetInput in)
- throws OrmException, IOException, RestApiException, UpdateException,
- PermissionBackendException {
+ throws IOException, RestApiException, UpdateException, PermissionBackendException {
// Not allowed to create a new patch set if the current patch set is locked.
psUtil.checkPatchSetNotLocked(rsrc.getNotes());
- rsrc.permissions().database(db).check(ChangePermission.ADD_PATCH_SET);
+ rsrc.permissions().check(ChangePermission.ADD_PATCH_SET);
ProjectState projectState = projectCache.checkedGet(rsrc.getProject());
projectState.checkStatePermitsWrite();
@@ -141,7 +135,7 @@ public class CreateMergePatchSet
}
in.baseChange = Strings.nullToEmpty(in.baseChange).trim();
- PatchSet ps = psUtil.current(db.get(), rsrc.getNotes());
+ PatchSet ps = psUtil.current(rsrc.getNotes());
Change change = rsrc.getChange();
Project.NameKey project = change.getProject();
Branch.NameKey dest = change.getDest();
@@ -185,13 +179,12 @@ public class CreateMergePatchSet
PatchSet.Id nextPsId = ChangeUtil.nextPatchSetId(ps.getId());
PatchSetInserter psInserter =
patchSetInserterFactory.create(rsrc.getNotes(), nextPsId, newCommit);
- try (BatchUpdate bu = updateFactory.create(db.get(), project, me, now)) {
+ try (BatchUpdate bu = updateFactory.create(project, me, now)) {
bu.setRepository(git, rw, oi);
+ bu.setNotify(NotifyResolver.Result.none());
psInserter
.setMessage("Uploaded patch set " + nextPsId.get() + ".")
- .setNotify(NotifyHandling.NONE)
- .setCheckAddPatchSetPermission(false)
- .setNotify(NotifyHandling.NONE);
+ .setCheckAddPatchSetPermission(false);
if (groups != null) {
psInserter.setGroups(groups);
}
@@ -205,18 +198,18 @@ public class CreateMergePatchSet
}
private PatchSet findBasePatchSet(String baseChange)
- throws PermissionBackendException, OrmException, UnprocessableEntityException {
+ throws PermissionBackendException, UnprocessableEntityException {
List<ChangeNotes> notes = changeFinder.find(baseChange);
if (notes.size() != 1) {
throw new UnprocessableEntityException("Base change not found: " + baseChange);
}
ChangeNotes change = Iterables.getOnlyElement(notes);
try {
- permissionBackend.currentUser().change(change).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(change).check(ChangePermission.READ);
} catch (AuthException e) {
throw new UnprocessableEntityException("Read not permitted for " + baseChange, e);
}
- return psUtil.current(db.get(), change);
+ return psUtil.current(change);
}
private RevCommit createMergeCommit(
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 7d68022d49..02387be556 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -21,7 +21,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountLoader;
@@ -39,9 +38,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
@@ -49,7 +46,6 @@ public class DeleteAssignee
extends RetryingRestModifyView<ChangeResource, Input, Response<AccountInfo>> {
private final ChangeMessagesUtil cmUtil;
- private final Provider<ReviewDb> db;
private final AssigneeChanged assigneeChanged;
private final IdentifiedUser.GenericFactory userFactory;
private final AccountLoader.Factory accountLoaderFactory;
@@ -58,13 +54,11 @@ public class DeleteAssignee
DeleteAssignee(
RetryHelper retryHelper,
ChangeMessagesUtil cmUtil,
- Provider<ReviewDb> db,
AssigneeChanged assigneeChanged,
IdentifiedUser.GenericFactory userFactory,
AccountLoader.Factory accountLoaderFactory) {
super(retryHelper);
this.cmUtil = cmUtil;
- this.db = db;
this.assigneeChanged = assigneeChanged;
this.userFactory = userFactory;
this.accountLoaderFactory = accountLoaderFactory;
@@ -73,11 +67,11 @@ public class DeleteAssignee
@Override
protected Response<AccountInfo> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
- throws RestApiException, UpdateException, OrmException, PermissionBackendException {
+ throws RestApiException, UpdateException, PermissionBackendException {
rsrc.permissions().check(ChangePermission.EDIT_ASSIGNEE);
try (BatchUpdate bu =
- updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
Op op = new Op();
bu.addOp(rsrc.getChange().getId(), op);
bu.execute();
@@ -93,7 +87,7 @@ public class DeleteAssignee
private AccountState deletedAssignee;
@Override
- public boolean updateChange(ChangeContext ctx) throws RestApiException, OrmException {
+ public boolean updateChange(ChangeContext ctx) throws RestApiException {
change = ctx.getChange();
ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
Account.Id currentAssigneeId = change.getAssignee();
@@ -114,18 +108,18 @@ public class DeleteAssignee
return deletedAssignee != null ? deletedAssignee.getAccount().getId() : null;
}
- private void addMessage(ChangeContext ctx, ChangeUpdate update, IdentifiedUser deletedAssignee)
- throws OrmException {
+ private void addMessage(
+ ChangeContext ctx, ChangeUpdate update, IdentifiedUser deletedAssignee) {
ChangeMessage cmsg =
ChangeMessagesUtil.newMessage(
ctx,
"Assignee deleted: " + deletedAssignee.getNameEmail(),
ChangeMessagesUtil.TAG_DELETE_ASSIGNEE);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
assigneeChanged.fire(change, ctx.getAccount(), deletedAssignee, ctx.getWhen());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChange.java b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
index fe2c5166c9..3021d8142b 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChange.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
@@ -22,51 +22,44 @@ 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.server.ReviewDb;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.DeleteChangeOp;
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.update.BatchUpdate;
-import com.google.gerrit.server.update.Order;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
implements UiAction<ChangeResource> {
- private final Provider<ReviewDb> db;
- private final Provider<DeleteChangeOp> opProvider;
+ private final DeleteChangeOp.Factory opFactory;
@Inject
- public DeleteChange(
- Provider<ReviewDb> db, RetryHelper retryHelper, Provider<DeleteChangeOp> opProvider) {
+ public DeleteChange(RetryHelper retryHelper, DeleteChangeOp.Factory opFactory) {
super(retryHelper);
- this.db = db;
- this.opProvider = opProvider;
+ this.opFactory = opFactory;
}
@Override
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
- if (!isChangeDeletable(rsrc.getChange().getStatus())) {
+ if (!isChangeDeletable(rsrc)) {
throw new MethodNotAllowedException("delete not permitted");
}
- rsrc.permissions().database(db).check(ChangePermission.DELETE);
+ rsrc.permissions().check(ChangePermission.DELETE);
try (BatchUpdate bu =
- updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
Change.Id id = rsrc.getChange().getId();
- bu.setOrder(Order.DB_BEFORE_REPO);
- bu.addOp(id, opProvider.get());
+ bu.addOp(id, opFactory.create(id));
bu.execute();
}
return Response.none();
@@ -74,16 +67,16 @@ public class DeleteChange extends RetryingRestModifyView<ChangeResource, Input,
@Override
public UiAction.Description getDescription(ChangeResource rsrc) {
- Change.Status status = rsrc.getChange().getStatus();
- PermissionBackend.ForChange perm = rsrc.permissions().database(db);
+ PermissionBackend.ForChange perm = rsrc.permissions();
return new UiAction.Description()
.setLabel("Delete")
.setTitle("Delete change " + rsrc.getId())
- .setVisible(and(isChangeDeletable(status), perm.testCond(ChangePermission.DELETE)));
+ .setVisible(and(isChangeDeletable(rsrc), perm.testCond(ChangePermission.DELETE)));
}
- private static boolean isChangeDeletable(Change.Status status) {
- if (status == Change.Status.MERGED) {
+ private static boolean isChangeDeletable(ChangeResource rsrc) {
+ Change change = rsrc.getChange();
+ if (change.isMerged()) {
// Merged changes should never be deleted.
return false;
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
index d49a804b2e..f7f808aaf4 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
@@ -23,7 +23,6 @@ import com.google.gerrit.server.change.ChangeEditResource;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -41,7 +40,7 @@ public class DeleteChangeEdit
@Override
public Response<?> apply(ChangeResource rsrc, Input input)
- throws AuthException, ResourceNotFoundException, IOException, OrmException {
+ throws AuthException, ResourceNotFoundException, IOException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
if (edit.isPresent()) {
editUtil.delete(edit.get());
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
index 881d106366..0fb8e18403 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
@@ -28,7 +28,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountLoader;
@@ -44,7 +43,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -58,7 +56,6 @@ public class DeleteChangeMessage
ChangeMessageResource, DeleteChangeMessageInput, Response<ChangeMessageInfo>> {
private final Provider<CurrentUser> userProvider;
- private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend;
private final ChangeMessagesUtil changeMessagesUtil;
private final AccountLoader.Factory accountLoaderFactory;
@@ -67,7 +64,6 @@ public class DeleteChangeMessage
@Inject
public DeleteChangeMessage(
Provider<CurrentUser> userProvider,
- Provider<ReviewDb> dbProvider,
PermissionBackend permissionBackend,
ChangeMessagesUtil changeMessagesUtil,
AccountLoader.Factory accountLoaderFactory,
@@ -75,7 +71,6 @@ public class DeleteChangeMessage
RetryHelper retryHelper) {
super(retryHelper);
this.userProvider = userProvider;
- this.dbProvider = dbProvider;
this.permissionBackend = permissionBackend;
this.changeMessagesUtil = changeMessagesUtil;
this.accountLoaderFactory = accountLoaderFactory;
@@ -87,18 +82,16 @@ public class DeleteChangeMessage
BatchUpdate.Factory updateFactory,
ChangeMessageResource resource,
DeleteChangeMessageInput input)
- throws RestApiException, PermissionBackendException, OrmException, UpdateException,
- IOException {
+ throws RestApiException, PermissionBackendException, UpdateException, IOException {
CurrentUser user = userProvider.get();
permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
String newChangeMessage =
createNewChangeMessage(user.asIdentifiedUser().getName(), input.reason);
DeleteChangeMessageOp deleteChangeMessageOp =
- new DeleteChangeMessageOp(resource.getChangeMessageIndex(), newChangeMessage);
+ new DeleteChangeMessageOp(resource.getChangeMessageId(), newChangeMessage);
try (BatchUpdate batchUpdate =
- updateFactory.create(
- dbProvider.get(), resource.getChangeResource().getProject(), user, TimeUtil.nowTs())) {
+ updateFactory.create(resource.getChangeResource().getProject(), user, TimeUtil.nowTs())) {
batchUpdate.addOp(resource.getChangeId(), deleteChangeMessageOp).execute();
}
@@ -108,9 +101,8 @@ public class DeleteChangeMessage
}
private ChangeMessageInfo createUpdatedChangeMessageInfo(Change.Id id, int targetIdx)
- throws OrmException, PermissionBackendException {
- List<ChangeMessage> messages =
- changeMessagesUtil.byChange(dbProvider.get(), notesFactory.createChecked(id));
+ throws PermissionBackendException {
+ List<ChangeMessage> messages = changeMessagesUtil.byChange(notesFactory.createChecked(id));
ChangeMessage updatedChangeMessage = messages.get(targetIdx);
AccountLoader accountLoader = accountLoaderFactory.create(true);
ChangeMessageInfo info = createChangeMessageInfo(updatedChangeMessage, accountLoader);
@@ -136,19 +128,18 @@ public class DeleteChangeMessage
}
private class DeleteChangeMessageOp implements BatchUpdateOp {
- private final int targetMessageIdx;
+ private final String targetMessageId;
private final String newMessage;
- DeleteChangeMessageOp(int targetMessageIdx, String newMessage) {
- this.targetMessageIdx = targetMessageIdx;
+ DeleteChangeMessageOp(String targetMessageIdx, String newMessage) {
+ this.targetMessageId = targetMessageIdx;
this.newMessage = newMessage;
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
PatchSet.Id psId = ctx.getChange().currentPatchSetId();
- changeMessagesUtil.replaceChangeMessage(
- ctx.getDb(), ctx.getUpdate(psId), targetMessageIdx, newMessage);
+ changeMessagesUtil.replaceChangeMessage(ctx.getUpdate(psId), targetMessageId, newMessage);
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
index 2ddf359a66..30a8efd713 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -22,7 +22,6 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.CommentResource;
@@ -37,7 +36,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -51,7 +49,6 @@ public class DeleteComment
extends RetryingRestModifyView<CommentResource, DeleteCommentInput, CommentInfo> {
private final Provider<CurrentUser> userProvider;
- private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend;
private final CommentsUtil commentsUtil;
private final Provider<CommentJson> commentJson;
@@ -60,7 +57,6 @@ public class DeleteComment
@Inject
public DeleteComment(
Provider<CurrentUser> userProvider,
- Provider<ReviewDb> dbProvider,
PermissionBackend permissionBackend,
RetryHelper retryHelper,
CommentsUtil commentsUtil,
@@ -68,7 +64,6 @@ public class DeleteComment
ChangeNotes.Factory notesFactory) {
super(retryHelper);
this.userProvider = userProvider;
- this.dbProvider = dbProvider;
this.permissionBackend = permissionBackend;
this.commentsUtil = commentsUtil;
this.commentJson = commentJson;
@@ -78,8 +73,8 @@ public class DeleteComment
@Override
public CommentInfo applyImpl(
BatchUpdate.Factory batchUpdateFactory, CommentResource rsrc, DeleteCommentInput input)
- throws RestApiException, IOException, ConfigInvalidException, OrmException,
- PermissionBackendException, UpdateException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
+ UpdateException {
CurrentUser user = userProvider.get();
permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
@@ -91,13 +86,13 @@ public class DeleteComment
DeleteCommentOp deleteCommentOp = new DeleteCommentOp(rsrc, newMessage);
try (BatchUpdate batchUpdate =
batchUpdateFactory.create(
- dbProvider.get(), rsrc.getRevisionResource().getProject(), user, TimeUtil.nowTs())) {
+ rsrc.getRevisionResource().getProject(), user, TimeUtil.nowTs())) {
batchUpdate.addOp(rsrc.getRevisionResource().getChange().getId(), deleteCommentOp).execute();
}
ChangeNotes updatedNotes =
notesFactory.createChecked(rsrc.getRevisionResource().getChange().getId());
- List<Comment> changeComments = commentsUtil.publishedByChange(dbProvider.get(), updatedNotes);
+ List<Comment> changeComments = commentsUtil.publishedByChange(updatedNotes);
Optional<Comment> updatedComment =
changeComments.stream().filter(c -> c.key.equals(rsrc.getComment().key)).findFirst();
if (!updatedComment.isPresent()) {
@@ -127,14 +122,10 @@ public class DeleteComment
@Override
public boolean updateChange(ChangeContext ctx)
- throws ResourceConflictException, OrmException, ResourceNotFoundException {
+ throws ResourceConflictException, ResourceNotFoundException {
PatchSet.Id psId = ctx.getChange().currentPatchSetId();
commentsUtil.deleteCommentByRewritingHistory(
- ctx.getDb(),
- ctx.getUpdate(psId),
- rsrc.getComment().key,
- rsrc.getPatchSet().getId(),
- newMessage);
+ ctx.getUpdate(psId), rsrc.getComment().key, newMessage);
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index f8e3add7db..de04d36184 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -23,7 +23,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.DraftCommentResource;
@@ -36,9 +35,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Collections;
import java.util.Optional;
@@ -47,20 +44,17 @@ import java.util.Optional;
public class DeleteDraftComment
extends RetryingRestModifyView<DraftCommentResource, Input, Response<CommentInfo>> {
- private final Provider<ReviewDb> db;
private final CommentsUtil commentsUtil;
private final PatchSetUtil psUtil;
private final PatchListCache patchListCache;
@Inject
DeleteDraftComment(
- Provider<ReviewDb> db,
CommentsUtil commentsUtil,
PatchSetUtil psUtil,
RetryHelper retryHelper,
PatchListCache patchListCache) {
super(retryHelper);
- this.db = db;
this.commentsUtil = commentsUtil;
this.psUtil = psUtil;
this.patchListCache = patchListCache;
@@ -71,8 +65,7 @@ public class DeleteDraftComment
BatchUpdate.Factory updateFactory, DraftCommentResource rsrc, Input input)
throws RestApiException, UpdateException {
try (BatchUpdate bu =
- updateFactory.create(
- db.get(), rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
Op op = new Op(rsrc.getComment().key);
bu.addOp(rsrc.getChange().getId(), op);
bu.execute();
@@ -89,21 +82,20 @@ public class DeleteDraftComment
@Override
public boolean updateChange(ChangeContext ctx)
- throws ResourceNotFoundException, OrmException, PatchListNotAvailableException {
+ throws ResourceNotFoundException, PatchListNotAvailableException {
Optional<Comment> maybeComment =
- commentsUtil.getDraft(ctx.getDb(), ctx.getNotes(), ctx.getIdentifiedUser(), key);
+ commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
if (!maybeComment.isPresent()) {
return false; // Nothing to do.
}
PatchSet.Id psId = new PatchSet.Id(ctx.getChange().getId(), key.patchSetId);
- PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ 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);
- commentsUtil.deleteComments(ctx.getDb(), ctx.getUpdate(psId), Collections.singleton(c));
- ctx.dontBumpLastUpdatedOn();
+ 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 9ecd56e0f5..8601e683b4 100644
--- a/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivate.java
@@ -22,8 +22,6 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.SetPrivateOp;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -34,27 +32,20 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class DeletePrivate
extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, Response<String>> {
- private final ChangeMessagesUtil cmUtil;
- private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend;
private final SetPrivateOp.Factory setPrivateOpFactory;
@Inject
DeletePrivate(
- Provider<ReviewDb> dbProvider,
RetryHelper retryHelper,
- ChangeMessagesUtil cmUtil,
PermissionBackend permissionBackend,
SetPrivateOp.Factory setPrivateOpFactory) {
super(retryHelper);
- this.dbProvider = dbProvider;
- this.cmUtil = cmUtil;
this.permissionBackend = permissionBackend;
this.setPrivateOpFactory = setPrivateOpFactory;
}
@@ -71,10 +62,9 @@ public class DeletePrivate
throw new ResourceConflictException("change is not private");
}
- SetPrivateOp op = setPrivateOpFactory.create(cmUtil, false, input);
+ SetPrivateOp op = setPrivateOpFactory.create(false, input);
try (BatchUpdate u =
- updateFactory.create(
- dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
u.addOp(rsrc.getId(), op).execute();
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java b/java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java
index f73705b105..c86d0ca8da 100644
--- a/java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java
+++ b/java/com/google/gerrit/server/restapi/change/DeletePrivateByPost.java
@@ -17,26 +17,21 @@ package com.google.gerrit.server.restapi.change;
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.SetPrivateOp;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.update.RetryHelper;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class DeletePrivateByPost extends DeletePrivate implements UiAction<ChangeResource> {
@Inject
DeletePrivateByPost(
- Provider<ReviewDb> dbProvider,
RetryHelper retryHelper,
- ChangeMessagesUtil cmUtil,
PermissionBackend permissionBackend,
SetPrivateOp.Factory setPrivateOpFactory) {
- super(dbProvider, retryHelper, cmUtil, permissionBackend, setPrivateOpFactory);
+ super(retryHelper, permissionBackend, setPrivateOpFactory);
}
@Override
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
index 83ae7b3a64..12dbcdd6d6 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteReviewer.java
@@ -15,11 +15,13 @@
package com.google.gerrit.server.restapi.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.server.ReviewDb;
+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;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -28,25 +30,21 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class DeleteReviewer
extends RetryingRestModifyView<ReviewerResource, DeleteReviewerInput, Response<?>> {
- private final Provider<ReviewDb> dbProvider;
private final DeleteReviewerOp.Factory deleteReviewerOpFactory;
private final DeleteReviewerByEmailOp.Factory deleteReviewerByEmailOpFactory;
@Inject
DeleteReviewer(
- Provider<ReviewDb> dbProvider,
RetryHelper retryHelper,
DeleteReviewerOp.Factory deleteReviewerOpFactory,
DeleteReviewerByEmailOp.Factory deleteReviewerByEmailOpFactory) {
super(retryHelper);
- this.dbProvider = dbProvider;
this.deleteReviewerOpFactory = deleteReviewerOpFactory;
this.deleteReviewerByEmailOpFactory = deleteReviewerByEmailOpFactory;
}
@@ -61,13 +59,13 @@ public class DeleteReviewer
try (BatchUpdate bu =
updateFactory.create(
- dbProvider.get(),
rsrc.getChangeResource().getProject(),
rsrc.getChangeResource().getUser(),
TimeUtil.nowTs())) {
+ bu.setNotify(getNotify(rsrc.getChange(), input));
BatchUpdateOp op;
if (rsrc.isByEmail()) {
- op = deleteReviewerByEmailOpFactory.create(rsrc.getReviewerByEmail(), input);
+ op = deleteReviewerByEmailOpFactory.create(rsrc.getReviewerByEmail());
} else {
op = deleteReviewerOpFactory.create(rsrc.getReviewerUser().state(), input);
}
@@ -76,4 +74,12 @@ public class DeleteReviewer
}
return Response.none();
}
+
+ private static NotifyResolver.Result getNotify(Change change, DeleteReviewerInput input) {
+ NotifyHandling notifyHandling = input.notify;
+ if (notifyHandling == null) {
+ notifyHandling = change.isWorkInProgress() ? NotifyHandling.NONE : NotifyHandling.ALL;
+ }
+ return NotifyResolver.Result.create(notifyHandling);
+ }
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 57649fad6c..77894fb246 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.common.base.MoreObjects.firstNonNull;
import static java.util.Objects.requireNonNull;
import com.google.common.flogger.FluentLogger;
@@ -29,16 +30,14 @@ 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.LabelId;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.change.VoteResource;
import com.google.gerrit.server.extensions.events.VoteDeleted;
@@ -57,33 +56,29 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteInput, Response<?>> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> db;
private final ApprovalsUtil approvalsUtil;
private final PatchSetUtil psUtil;
private final ChangeMessagesUtil cmUtil;
private final IdentifiedUser.GenericFactory userFactory;
private final VoteDeleted voteDeleted;
private final DeleteVoteSender.Factory deleteVoteSenderFactory;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final RemoveReviewerControl removeReviewerControl;
private final ProjectCache projectCache;
@Inject
DeleteVote(
- Provider<ReviewDb> db,
RetryHelper retryHelper,
ApprovalsUtil approvalsUtil,
PatchSetUtil psUtil,
@@ -91,18 +86,17 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
IdentifiedUser.GenericFactory userFactory,
VoteDeleted voteDeleted,
DeleteVoteSender.Factory deleteVoteSenderFactory,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
RemoveReviewerControl removeReviewerControl,
ProjectCache projectCache) {
super(retryHelper);
- this.db = db;
this.approvalsUtil = approvalsUtil;
this.psUtil = psUtil;
this.cmUtil = cmUtil;
this.userFactory = userFactory;
this.voteDeleted = voteDeleted;
this.deleteVoteSenderFactory = deleteVoteSenderFactory;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.removeReviewerControl = removeReviewerControl;
this.projectCache = projectCache;
}
@@ -110,7 +104,7 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
@Override
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, VoteResource rsrc, DeleteVoteInput input)
- throws RestApiException, UpdateException, IOException {
+ throws RestApiException, UpdateException, IOException, ConfigInvalidException {
if (input == null) {
input = new DeleteVoteInput();
}
@@ -129,7 +123,10 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
try (BatchUpdate bu =
updateFactory.create(
- db.get(), change.getProject(), r.getChangeResource().getUser(), TimeUtil.nowTs())) {
+ change.getProject(), r.getChangeResource().getUser(), TimeUtil.nowTs())) {
+ bu.setNotify(
+ notifyResolver.resolve(
+ firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
bu.addOp(
change.getId(),
new Op(
@@ -165,11 +162,10 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
@Override
public boolean updateChange(ChangeContext ctx)
- throws OrmException, AuthException, ResourceNotFoundException, IOException,
- PermissionBackendException {
+ throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException {
change = ctx.getChange();
PatchSet.Id psId = change.currentPatchSetId();
- ps = psUtil.current(db.get(), ctx.getNotes());
+ ps = psUtil.current(ctx.getNotes());
boolean found = false;
LabelTypes labelTypes = projectState.getLabelTypes(ctx.getNotes());
@@ -178,12 +174,7 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
- ctx.getDb(),
- ctx.getNotes(),
- psId,
- accountId,
- ctx.getRevWalk(),
- ctx.getRepoView().getConfig())) {
+ ctx.getNotes(), psId, accountId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
if (labelTypes.byLabel(a.getLabelId()) == null) {
continue; // Ignore undefined labels.
} else if (!a.getLabel().equals(label)) {
@@ -210,7 +201,6 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
}
ctx.getUpdate(psId).removeApprovalFor(accountId, label);
- ctx.getDb().patchSetApprovals().upsert(Collections.singleton(deletedApproval(ctx)));
StringBuilder msg = new StringBuilder();
msg.append("Removed ");
@@ -218,22 +208,11 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
msg.append(" by ").append(userFactory.create(accountId).getNameEmail()).append("\n");
changeMessage =
ChangeMessagesUtil.newMessage(ctx, msg.toString(), ChangeMessagesUtil.TAG_DELETE_VOTE);
- cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), changeMessage);
+ cmUtil.addChangeMessage(ctx.getUpdate(psId), changeMessage);
return true;
}
- private PatchSetApproval deletedApproval(ChangeContext ctx) {
- // Set the effective user to the account we're trying to remove, and don't
- // set the real user; this preserves the calling user as the NoteDb
- // committer.
- return new PatchSetApproval(
- new PatchSetApproval.Key(
- ps.getId(), accountState.getAccount().getId(), new LabelId(label)),
- (short) 0,
- ctx.getWhen());
- }
-
@Override
public void postUpdate(Context ctx) {
if (changeMessage == null) {
@@ -241,17 +220,17 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
}
IdentifiedUser user = ctx.getIdentifiedUser();
- if (NotifyUtil.shouldNotify(input.notify, input.notifyDetails)) {
- try {
+ try {
+ NotifyResolver.Result notify = ctx.getNotify(change.getId());
+ if (notify.shouldNotify()) {
ReplyToChangeSender cm = deleteVoteSenderFactory.create(ctx.getProject(), change.getId());
cm.setFrom(user.getAccountId());
cm.setChangeMessage(changeMessage.getMessage(), ctx.getWhen());
- cm.setNotify(input.notify);
- cm.setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
+ cm.setNotify(notify);
cm.send();
- } catch (Exception e) {
- logger.atSevere().withCause(e).log("Cannot email update for change %s", change.getId());
}
+ } catch (Exception e) {
+ logger.atSevere().withCause(e).log("Cannot email update for change %s", change.getId());
}
voteDeleted.fire(
diff --git a/java/com/google/gerrit/server/restapi/change/DownloadContent.java b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
index b6564c0641..1022cad965 100644
--- a/java/com/google/gerrit/server/restapi/change/DownloadContent.java
+++ b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
@@ -22,7 +22,6 @@ import com.google.gerrit.server.change.FileResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.lib.ObjectId;
@@ -43,7 +42,7 @@ public class DownloadContent implements RestReadView<FileResource> {
@Override
public BinaryResult apply(FileResource rsrc)
- throws ResourceNotFoundException, IOException, NoSuchChangeException, OrmException {
+ throws ResourceNotFoundException, IOException, NoSuchChangeException {
String path = rsrc.getPatchKey().get();
RevisionResource rev = rsrc.getRevision();
ObjectId revstr = ObjectId.fromString(rev.getPatchSet().getRevision().get());
diff --git a/java/com/google/gerrit/server/restapi/change/DraftComments.java b/java/com/google/gerrit/server/restapi/change/DraftComments.java
index b8e24a5408..dd61ca0855 100644
--- a/java/com/google/gerrit/server/restapi/change/DraftComments.java
+++ b/java/com/google/gerrit/server/restapi/change/DraftComments.java
@@ -21,12 +21,10 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.DraftCommentResource;
import com.google.gerrit.server.change.RevisionResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -36,7 +34,6 @@ public class DraftComments implements ChildCollection<RevisionResource, DraftCom
private final DynamicMap<RestView<DraftCommentResource>> views;
private final Provider<CurrentUser> user;
private final ListRevisionDrafts list;
- private final Provider<ReviewDb> dbProvider;
private final CommentsUtil commentsUtil;
@Inject
@@ -44,12 +41,10 @@ public class DraftComments implements ChildCollection<RevisionResource, DraftCom
DynamicMap<RestView<DraftCommentResource>> views,
Provider<CurrentUser> user,
ListRevisionDrafts list,
- Provider<ReviewDb> dbProvider,
CommentsUtil commentsUtil) {
this.views = views;
this.user = user;
this.list = list;
- this.dbProvider = dbProvider;
this.commentsUtil = commentsUtil;
}
@@ -66,12 +61,12 @@ public class DraftComments implements ChildCollection<RevisionResource, DraftCom
@Override
public DraftCommentResource parse(RevisionResource rev, IdString id)
- throws ResourceNotFoundException, OrmException, AuthException {
+ throws ResourceNotFoundException, AuthException {
checkIdentifiedUser();
String uuid = id.get();
for (Comment c :
commentsUtil.draftByPatchSetAuthor(
- dbProvider.get(), rev.getPatchSet().getId(), rev.getAccountId(), rev.getNotes())) {
+ rev.getPatchSet().getId(), 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 84283e7fcd..d8928105a2 100644
--- a/java/com/google/gerrit/server/restapi/change/Files.java
+++ b/java/com/google/gerrit/server/restapi/change/Files.java
@@ -18,6 +18,7 @@ import com.google.common.collect.Lists;
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.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -34,7 +35,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.AccountPatchReviewStore;
@@ -50,7 +50,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -114,7 +113,6 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
@Option(name = "-q")
String query;
- private final Provider<ReviewDb> db;
private final Provider<CurrentUser> self;
private final FileInfoJson fileInfoJson;
private final Revisions revisions;
@@ -126,7 +124,6 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
@Inject
ListFiles(
- Provider<ReviewDb> db,
Provider<CurrentUser> self,
FileInfoJson fileInfoJson,
Revisions revisions,
@@ -135,7 +132,6 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
PatchSetUtil psUtil,
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
GerritApi gApi) {
- this.db = db;
this.self = self;
this.fileInfoJson = fileInfoJson;
this.revisions = revisions;
@@ -153,7 +149,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
@Override
public Response<?> apply(RevisionResource resource)
- throws RestApiException, OrmException, RepositoryNotFoundException, IOException,
+ throws RestApiException, RepositoryNotFoundException, IOException,
PatchListNotAvailableException, PermissionBackendException {
checkOptions();
if (reviewed) {
@@ -239,8 +235,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
}
}
- private Collection<String> reviewed(RevisionResource resource)
- throws AuthException, OrmException {
+ private Collection<String> reviewed(RevisionResource resource) throws AuthException {
CurrentUser user = self.get();
if (!(user.isIdentifiedUser())) {
throw new AuthException("Authentication required");
@@ -249,9 +244,7 @@ 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), OrmException.class);
+ o = accountPatchReviewStore.call(s -> s.findReviewed(patchSetId.getId(), userId));
if (o.isPresent()) {
PatchSetWithReviewedFiles res = o.get();
@@ -273,14 +266,14 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
private List<String> copy(
Set<String> paths, PatchSet.Id old, RevisionResource resource, Account.Id userId)
- throws IOException, PatchListNotAvailableException, OrmException {
+ throws IOException, PatchListNotAvailableException {
Project.NameKey project = resource.getChange().getProject();
try (Repository git = gitManager.openRepository(project);
ObjectReader reader = git.newObjectReader();
RevWalk rw = new RevWalk(reader);
TreeWalk tw = new TreeWalk(reader)) {
Change change = resource.getChange();
- PatchSet patchSet = psUtil.get(db.get(), resource.getNotes(), old);
+ PatchSet patchSet = psUtil.get(resource.getNotes(), old);
if (patchSet == null) {
throw new PatchListNotAvailableException(
String.format(
@@ -334,8 +327,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
}
accountPatchReviewStore.run(
- s -> s.markReviewed(resource.getPatchSet().getId(), userId, pathList),
- OrmException.class);
+ s -> s.markReviewed(resource.getPatchSet().getId(), userId, pathList));
return pathList;
}
}
@@ -345,7 +337,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
return this;
}
- public ListFiles setBase(String base) {
+ public ListFiles setBase(@Nullable String base) {
this.base = base;
return this;
}
diff --git a/java/com/google/gerrit/server/restapi/change/Fixes.java b/java/com/google/gerrit/server/restapi/change/Fixes.java
index 1d8726d0b9..855d1f48cb 100644
--- a/java/com/google/gerrit/server/restapi/change/Fixes.java
+++ b/java/com/google/gerrit/server/restapi/change/Fixes.java
@@ -25,7 +25,6 @@ import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.FixResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;
@@ -50,7 +49,7 @@ public class Fixes implements ChildCollection<RevisionResource, FixResource> {
@Override
public FixResource parse(RevisionResource revisionResource, IdString id)
- throws ResourceNotFoundException, OrmException {
+ throws ResourceNotFoundException {
String fixId = id.get();
ChangeNotes changeNotes = revisionResource.getNotes();
diff --git a/java/com/google/gerrit/server/restapi/change/GetAssignee.java b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
index e95f8d8408..f89fe1b0ea 100644
--- a/java/com/google/gerrit/server/restapi/change/GetAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
@@ -21,7 +21,6 @@ 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;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Optional;
@@ -36,8 +35,7 @@ public class GetAssignee implements RestReadView<ChangeResource> {
}
@Override
- public Response<AccountInfo> apply(ChangeResource rsrc)
- throws OrmException, PermissionBackendException {
+ public Response<AccountInfo> apply(ChangeResource rsrc) throws PermissionBackendException {
Optional<Account.Id> assignee = Optional.ofNullable(rsrc.getChange().getAssignee());
if (assignee.isPresent()) {
return Response.ok(accountLoaderFactory.create(true).fillOne(assignee.get()));
diff --git a/java/com/google/gerrit/server/restapi/change/GetBlame.java b/java/com/google/gerrit/server/restapi/change/GetBlame.java
index c7a80156f0..98a3a8aa5a 100644
--- a/java/com/google/gerrit/server/restapi/change/GetBlame.java
+++ b/java/com/google/gerrit/server/restapi/change/GetBlame.java
@@ -32,7 +32,6 @@ import com.google.gerrit.server.patch.AutoMerger;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gitiles.blame.cache.BlameCache;
import com.google.gitiles.blame.cache.Region;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -77,9 +76,14 @@ public class GetBlame implements RestReadView<FileResource> {
this.autoMerger = autoMerger;
}
+ public GetBlame setBase(boolean base) {
+ this.base = base;
+ return this;
+ }
+
@Override
public Response<List<BlameInfo>> apply(FileResource resource)
- throws RestApiException, OrmException, IOException, InvalidChangeOperationException {
+ throws RestApiException, IOException, InvalidChangeOperationException {
Project.NameKey project = resource.getRevision().getChange().getProject();
try (Repository repository = repoManager.openRepository(project);
ObjectInserter ins = repository.newObjectInserter();
diff --git a/java/com/google/gerrit/server/restapi/change/GetChange.java b/java/com/google/gerrit/server/restapi/change/GetChange.java
index a8f8bbb14b..c28741bb7f 100644
--- a/java/com/google/gerrit/server/restapi/change/GetChange.java
+++ b/java/com/google/gerrit/server/restapi/change/GetChange.java
@@ -14,43 +14,79 @@
package com.google.gerrit.server.restapi.change;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
import com.google.gerrit.extensions.client.ListChangesOption;
+import com.google.gerrit.extensions.client.ListOption;
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.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.server.DynamicOptions.DynamicBean;
+import com.google.gerrit.server.change.ChangeAttributeFactory;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PluginDefinedAttributesFactories;
import com.google.gerrit.server.change.RevisionResource;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
import org.kohsuke.args4j.Option;
-public class GetChange implements RestReadView<ChangeResource> {
+public class GetChange
+ implements RestReadView<ChangeResource>,
+ DynamicOptions.BeanReceiver,
+ DynamicOptions.BeanProvider {
private final ChangeJson.Factory json;
+ private final DynamicSet<ChangeAttributeFactory> attrFactories;
private final EnumSet<ListChangesOption> options = EnumSet.noneOf(ListChangesOption.class);
+ private final Map<String, DynamicBean> dynamicBeans = new HashMap<>();
@Option(name = "-o", usage = "Output options")
- void addOption(ListChangesOption o) {
+ public void addOption(ListChangesOption o) {
options.add(o);
}
@Option(name = "-O", usage = "Output option flags, in hex")
void setOptionFlagsHex(String hex) {
- options.addAll(ListChangesOption.fromBits(Integer.parseInt(hex, 16)));
+ options.addAll(ListOption.fromBits(ListChangesOption.class, Integer.parseInt(hex, 16)));
}
@Inject
- GetChange(ChangeJson.Factory json) {
+ GetChange(ChangeJson.Factory json, DynamicSet<ChangeAttributeFactory> attrFactories) {
this.json = json;
+ this.attrFactories = attrFactories;
}
@Override
- public Response<ChangeInfo> apply(ChangeResource rsrc) throws OrmException {
- return Response.withMustRevalidate(json.create(options).format(rsrc));
+ public void setDynamicBean(String plugin, DynamicOptions.DynamicBean dynamicBean) {
+ dynamicBeans.put(plugin, dynamicBean);
}
- Response<ChangeInfo> apply(RevisionResource rsrc) throws OrmException {
- return Response.withMustRevalidate(json.create(options).format(rsrc));
+ @Override
+ public DynamicBean getDynamicBean(String plugin) {
+ return dynamicBeans.get(plugin);
+ }
+
+ @Override
+ public Response<ChangeInfo> apply(ChangeResource rsrc) {
+ return Response.withMustRevalidate(newChangeJson().format(rsrc));
+ }
+
+ Response<ChangeInfo> apply(RevisionResource rsrc) {
+ return Response.withMustRevalidate(newChangeJson().format(rsrc));
+ }
+
+ private ChangeJson newChangeJson() {
+ return json.create(options, this::buildPluginInfo);
+ }
+
+ private ImmutableList<PluginDefinedInfo> buildPluginInfo(ChangeData cd) {
+ return PluginDefinedAttributesFactories.createAll(
+ cd, this, Streams.stream(attrFactories.entries()));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetComment.java b/java/com/google/gerrit/server/restapi/change/GetComment.java
index d067dff449..0109c9501f 100644
--- a/java/com/google/gerrit/server/restapi/change/GetComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetComment.java
@@ -18,7 +18,6 @@ import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.CommentResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -34,7 +33,7 @@ public class GetComment implements RestReadView<CommentResource> {
}
@Override
- public CommentInfo apply(CommentResource rsrc) throws OrmException, PermissionBackendException {
+ public CommentInfo apply(CommentResource rsrc) throws PermissionBackendException {
return commentJson.get().newCommentFormatter().format(rsrc.getComment());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetContent.java b/java/com/google/gerrit/server/restapi/change/GetContent.java
index 6b9bf17fbb..1d35ab5915 100644
--- a/java/com/google/gerrit/server/restapi/change/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/change/GetContent.java
@@ -21,7 +21,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.FileContentUtil;
import com.google.gerrit.server.change.FileResource;
@@ -31,9 +30,7 @@ import com.google.gerrit.server.patch.ComparisonType;
import com.google.gerrit.server.patch.Text;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
@@ -43,7 +40,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Option;
public class GetContent implements RestReadView<FileResource> {
- private final Provider<ReviewDb> db;
private final GitRepositoryManager gitManager;
private final PatchSetUtil psUtil;
private final FileContentUtil fileContentUtil;
@@ -54,12 +50,10 @@ public class GetContent implements RestReadView<FileResource> {
@Inject
GetContent(
- Provider<ReviewDb> db,
GitRepositoryManager gitManager,
PatchSetUtil psUtil,
FileContentUtil fileContentUtil,
ProjectCache projectCache) {
- this.db = db;
this.gitManager = gitManager;
this.psUtil = psUtil;
this.fileContentUtil = fileContentUtil;
@@ -68,7 +62,7 @@ public class GetContent implements RestReadView<FileResource> {
@Override
public BinaryResult apply(FileResource rsrc)
- throws ResourceNotFoundException, IOException, BadRequestException, OrmException {
+ throws ResourceNotFoundException, IOException, BadRequestException {
String path = rsrc.getPatchKey().get();
if (Patch.COMMIT_MSG.equals(path)) {
String msg = getMessage(rsrc.getRevision().getChangeResource().getNotes());
@@ -88,9 +82,9 @@ public class GetContent implements RestReadView<FileResource> {
parent);
}
- private String getMessage(ChangeNotes notes) throws OrmException, IOException {
+ private String getMessage(ChangeNotes notes) throws IOException {
Change.Id changeId = notes.getChangeId();
- PatchSet ps = psUtil.current(db.get(), notes);
+ PatchSet ps = psUtil.current(notes);
if (ps == null) {
throw new NoSuchChangeException(changeId);
}
@@ -104,9 +98,9 @@ public class GetContent implements RestReadView<FileResource> {
}
}
- private byte[] getMergeList(ChangeNotes notes) throws OrmException, IOException {
+ private byte[] getMergeList(ChangeNotes notes) throws IOException {
Change.Id changeId = notes.getChangeId();
- PatchSet ps = psUtil.current(db.get(), notes);
+ PatchSet ps = psUtil.current(notes);
if (ps == null) {
throw new NoSuchChangeException(changeId);
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetDetail.java b/java/com/google/gerrit/server/restapi/change/GetDetail.java
index ab75ab7791..e31d84b85d 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDetail.java
@@ -18,12 +18,13 @@ import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.server.DynamicOptions.DynamicBean;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import org.kohsuke.args4j.Option;
-public class GetDetail implements RestReadView<ChangeResource> {
+public class GetDetail implements RestReadView<ChangeResource>, DynamicOptions.BeanReceiver {
private final GetChange delegate;
@Option(name = "-o", usage = "Output options")
@@ -47,7 +48,17 @@ public class GetDetail implements RestReadView<ChangeResource> {
}
@Override
- public Response<ChangeInfo> apply(ChangeResource rsrc) throws OrmException {
+ public void setDynamicBean(String plugin, DynamicBean dynamicBean) {
+ delegate.setDynamicBean(plugin, dynamicBean);
+ }
+
+ @Override
+ public Class<? extends DynamicOptions.BeanReceiver> getExportedBeanReceiver() {
+ return delegate.getExportedBeanReceiver();
+ }
+
+ @Override
+ public Response<ChangeInfo> apply(ChangeResource rsrc) {
return delegate.apply(rsrc);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
index af35acf5ac..6a7f1faa84 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -41,6 +41,7 @@ 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.jgit.diff.ReplaceEdit;
import com.google.gerrit.prettify.common.SparseFileContent;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -57,14 +58,12 @@ import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.diff.Edit;
-import org.eclipse.jgit.diff.ReplaceEdit;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.NamedOptionDef;
@@ -112,9 +111,6 @@ public class GetDiff implements RestReadView<FileResource> {
@Option(name = "--intraline")
boolean intraline;
- @Option(name = "--weblinks-only")
- boolean webLinksOnly;
-
@Inject
GetDiff(
ProjectCache projectCache,
@@ -129,7 +125,7 @@ public class GetDiff implements RestReadView<FileResource> {
@Override
public Response<DiffInfo> apply(FileResource resource)
- throws ResourceConflictException, ResourceNotFoundException, OrmException, AuthException,
+ throws ResourceConflictException, ResourceNotFoundException, AuthException,
InvalidChangeOperationException, IOException, PermissionBackendException {
DiffPreferencesInfo prefs = new DiffPreferencesInfo();
if (whitespace != null) {
@@ -217,52 +213,50 @@ public class GetDiff implements RestReadView<FileResource> {
ps.getNewName());
result.webLinks = links.isEmpty() ? null : links;
- if (!webLinksOnly) {
- if (ps.isBinary()) {
- result.binary = true;
- }
- if (ps.getDisplayMethodA() != DisplayMethod.NONE) {
- result.metaA = new FileMeta();
- result.metaA.name = MoreObjects.firstNonNull(ps.getOldName(), ps.getNewName());
- result.metaA.contentType =
- FileContentUtil.resolveContentType(
- state, result.metaA.name, ps.getFileModeA(), ps.getMimeTypeA());
- result.metaA.lines = ps.getA().size();
- result.metaA.webLinks = getFileWebLinks(state.getProject(), revA, result.metaA.name);
- result.metaA.commitId = content.commitIdA;
- }
+ if (ps.isBinary()) {
+ result.binary = true;
+ }
+ if (ps.getDisplayMethodA() != DisplayMethod.NONE) {
+ result.metaA = new FileMeta();
+ result.metaA.name = MoreObjects.firstNonNull(ps.getOldName(), ps.getNewName());
+ result.metaA.contentType =
+ FileContentUtil.resolveContentType(
+ state, result.metaA.name, ps.getFileModeA(), ps.getMimeTypeA());
+ result.metaA.lines = ps.getA().size();
+ result.metaA.webLinks = getFileWebLinks(state.getProject(), revA, result.metaA.name);
+ result.metaA.commitId = content.commitIdA;
+ }
- if (ps.getDisplayMethodB() != DisplayMethod.NONE) {
- result.metaB = new FileMeta();
- result.metaB.name = ps.getNewName();
- result.metaB.contentType =
- FileContentUtil.resolveContentType(
- state, result.metaB.name, ps.getFileModeB(), ps.getMimeTypeB());
- result.metaB.lines = ps.getB().size();
- result.metaB.webLinks = getFileWebLinks(state.getProject(), revB, result.metaB.name);
- result.metaB.commitId = content.commitIdB;
- }
+ if (ps.getDisplayMethodB() != DisplayMethod.NONE) {
+ result.metaB = new FileMeta();
+ result.metaB.name = ps.getNewName();
+ result.metaB.contentType =
+ FileContentUtil.resolveContentType(
+ state, result.metaB.name, ps.getFileModeB(), ps.getMimeTypeB());
+ result.metaB.lines = ps.getB().size();
+ result.metaB.webLinks = getFileWebLinks(state.getProject(), revB, result.metaB.name);
+ result.metaB.commitId = content.commitIdB;
+ }
- if (intraline) {
- if (ps.hasIntralineTimeout()) {
- result.intralineStatus = IntraLineStatus.TIMEOUT;
- } else if (ps.hasIntralineFailure()) {
- result.intralineStatus = IntraLineStatus.FAILURE;
- } else {
- result.intralineStatus = IntraLineStatus.OK;
- }
+ if (intraline) {
+ if (ps.hasIntralineTimeout()) {
+ result.intralineStatus = IntraLineStatus.TIMEOUT;
+ } else if (ps.hasIntralineFailure()) {
+ result.intralineStatus = IntraLineStatus.FAILURE;
+ } else {
+ result.intralineStatus = IntraLineStatus.OK;
}
+ }
- result.changeType = CHANGE_TYPE.get(ps.getChangeType());
- if (result.changeType == null) {
- throw new IllegalStateException("unknown change type: " + ps.getChangeType());
- }
+ result.changeType = CHANGE_TYPE.get(ps.getChangeType());
+ if (result.changeType == null) {
+ throw new IllegalStateException("unknown change type: " + ps.getChangeType());
+ }
- if (!ps.getPatchHeader().isEmpty()) {
- result.diffHeader = ps.getPatchHeader();
- }
- result.content = content.lines;
+ if (!ps.getPatchHeader().isEmpty()) {
+ result.diffHeader = ps.getPatchHeader();
}
+ result.content = content.lines;
Response<DiffInfo> r = Response.ok(result);
if (resource.isCacheable()) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
index 604960748d..ca5b56f16d 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
@@ -18,7 +18,6 @@ import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.DraftCommentResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -34,8 +33,7 @@ public class GetDraftComment implements RestReadView<DraftCommentResource> {
}
@Override
- public CommentInfo apply(DraftCommentResource rsrc)
- throws OrmException, PermissionBackendException {
+ public CommentInfo apply(DraftCommentResource rsrc) throws PermissionBackendException {
return commentJson.get().newCommentFormatter().format(rsrc.getComment());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetHashtags.java b/java/com/google/gerrit/server/restapi/change/GetHashtags.java
index 8369acf207..aff3a4467a 100644
--- a/java/com/google/gerrit/server/restapi/change/GetHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/GetHashtags.java
@@ -20,7 +20,6 @@ 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.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collections;
@@ -30,7 +29,7 @@ import java.util.Set;
public class GetHashtags implements RestReadView<ChangeResource> {
@Override
public Response<Set<String>> apply(ChangeResource req)
- throws AuthException, OrmException, IOException, BadRequestException {
+ throws AuthException, IOException, BadRequestException {
ChangeNotes notes = req.getNotes().load();
Set<String> hashtags = notes.getHashtags();
if (hashtags == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetMergeList.java b/java/com/google/gerrit/server/restapi/change/GetMergeList.java
index 8e7e693bdd..0c18a8f04e 100644
--- a/java/com/google/gerrit/server/restapi/change/GetMergeList.java
+++ b/java/com/google/gerrit/server/restapi/change/GetMergeList.java
@@ -76,7 +76,7 @@ public class GetMergeList implements RestReadView<RevisionResource> {
}
if (commit.getParentCount() < 2) {
- return createResponse(rsrc, ImmutableList.<CommitInfo>of());
+ return createResponse(rsrc, ImmutableList.of());
}
List<RevCommit> commits = MergeListBuilder.build(rw, commit, uninterestingParent);
diff --git a/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
index 279cfe3b21..1d56669ac6 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
@@ -23,7 +23,6 @@ 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;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Collections;
@@ -40,8 +39,7 @@ public class GetPastAssignees implements RestReadView<ChangeResource> {
}
@Override
- public Response<List<AccountInfo>> apply(ChangeResource rsrc)
- throws OrmException, PermissionBackendException {
+ public Response<List<AccountInfo>> apply(ChangeResource rsrc) throws PermissionBackendException {
Set<Account.Id> pastAssignees = rsrc.getNotes().load().getPastAssignees();
if (pastAssignees == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
index 75019af9ab..fa5cc36f5e 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
@@ -22,9 +22,9 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.PureRevert;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
+import java.util.Optional;
import org.kohsuke.args4j.Option;
public class GetPureRevert implements RestReadView<ChangeResource> {
@@ -47,8 +47,8 @@ public class GetPureRevert implements RestReadView<ChangeResource> {
@Override
public PureRevertInfo apply(ChangeResource rsrc)
- throws ResourceConflictException, IOException, BadRequestException, OrmException,
- AuthException {
- return pureRevert.get(rsrc.getNotes(), claimedOriginal);
+ throws ResourceConflictException, IOException, BadRequestException, AuthException {
+ boolean isPureRevert = pureRevert.get(rsrc.getNotes(), Optional.ofNullable(claimedOriginal));
+ return 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 3eea73671d..332cc4de08 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRelated.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRelated.java
@@ -27,7 +27,7 @@ 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.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CommonConverters;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
@@ -36,7 +36,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -44,13 +43,13 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.revwalk.RevCommit;
@Singleton
public class GetRelated implements RestReadView<RevisionResource> {
- private final Provider<ReviewDb> db;
private final Provider<InternalChangeQuery> queryProvider;
private final PatchSetUtil psUtil;
private final RelatedChangesSorter sorter;
@@ -58,12 +57,10 @@ public class GetRelated implements RestReadView<RevisionResource> {
@Inject
GetRelated(
- Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
PatchSetUtil psUtil,
RelatedChangesSorter sorter,
IndexConfig indexConfig) {
- this.db = db;
this.queryProvider = queryProvider;
this.psUtil = psUtil;
this.sorter = sorter;
@@ -72,7 +69,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
@Override
public RelatedChangesInfo apply(RevisionResource rsrc)
- throws RepositoryNotFoundException, IOException, OrmException, NoSuchProjectException,
+ throws RepositoryNotFoundException, IOException, NoSuchProjectException,
PermissionBackendException {
RelatedChangesInfo relatedChangesInfo = new RelatedChangesInfo();
relatedChangesInfo.changes = getRelated(rsrc);
@@ -80,8 +77,8 @@ public class GetRelated implements RestReadView<RevisionResource> {
}
private List<RelatedChangeAndCommitInfo> getRelated(RevisionResource rsrc)
- throws OrmException, IOException, PermissionBackendException {
- Set<String> groups = getAllGroups(rsrc.getNotes(), db.get(), psUtil);
+ throws IOException, PermissionBackendException {
+ Set<String> groups = getAllGroups(rsrc.getNotes(), psUtil);
if (groups.isEmpty()) {
return Collections.emptyList();
}
@@ -125,14 +122,11 @@ public class GetRelated implements RestReadView<RevisionResource> {
}
@VisibleForTesting
- public static Set<String> getAllGroups(ChangeNotes notes, ReviewDb db, PatchSetUtil psUtil)
- throws OrmException {
- return psUtil.byChange(db, notes).stream()
- .flatMap(ps -> ps.getGroups().stream())
- .collect(toSet());
+ public static Set<String> getAllGroups(ChangeNotes notes, PatchSetUtil psUtil) {
+ return psUtil.byChange(notes).stream().flatMap(ps -> ps.getGroups().stream()).collect(toSet());
}
- private void reloadChangeIfStale(List<ChangeData> cds, PatchSet wantedPs) throws OrmException {
+ 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) {
@@ -153,7 +147,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
info._revisionNumber = ps != null ? ps.getPatchSetId() : null;
PatchSet.Id curr = change.currentPatchSetId();
info._currentRevisionNumber = curr != null ? curr.get() : null;
- info.status = change.getStatus().asChangeStatus().toString();
+ info.status = ChangeUtil.status(change).toUpperCase(Locale.US);
}
info.commit = new CommitInfo();
diff --git a/java/com/google/gerrit/server/restapi/change/GetReview.java b/java/com/google/gerrit/server/restapi/change/GetReview.java
index 40e132d504..8d941ab043 100644
--- a/java/com/google/gerrit/server/restapi/change/GetReview.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReview.java
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.RevisionResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -35,7 +34,7 @@ public class GetReview implements RestReadView<RevisionResource> {
}
@Override
- public Response<ChangeInfo> apply(RevisionResource rsrc) throws OrmException {
+ public Response<ChangeInfo> apply(RevisionResource rsrc) {
return delegate.apply(rsrc);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetReviewer.java b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
index a11380bdc1..73760dab96 100644
--- a/java/com/google/gerrit/server/restapi/change/GetReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.ReviewerJson;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;
@@ -34,8 +33,7 @@ public class GetReviewer implements RestReadView<ReviewerResource> {
}
@Override
- public List<ReviewerInfo> apply(ReviewerResource rsrc)
- throws OrmException, PermissionBackendException {
+ public List<ReviewerInfo> apply(ReviewerResource rsrc) throws PermissionBackendException {
return json.format(rsrc);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java b/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
index 03b95a643e..c4da3b61a7 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
@@ -16,10 +16,10 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.restapi.ETagView;
import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ActionJson;
import com.google.gerrit.server.change.ChangeResource;
@@ -29,8 +29,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.submit.ChangeSet;
import com.google.gerrit.server.submit.MergeSuperSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -42,26 +40,23 @@ import org.eclipse.jgit.lib.Config;
public class GetRevisionActions implements ETagView<RevisionResource> {
private final ActionJson delegate;
private final Config config;
- private final Provider<ReviewDb> dbProvider;
private final Provider<MergeSuperSet> mergeSuperSet;
private final ChangeResource.Factory changeResourceFactory;
@Inject
GetRevisionActions(
ActionJson delegate,
- Provider<ReviewDb> dbProvider,
Provider<MergeSuperSet> mergeSuperSet,
ChangeResource.Factory changeResourceFactory,
@GerritServerConfig Config config) {
this.delegate = delegate;
- this.dbProvider = dbProvider;
this.mergeSuperSet = mergeSuperSet;
this.changeResourceFactory = changeResourceFactory;
this.config = config;
}
@Override
- public Response<Map<String, ActionInfo>> apply(RevisionResource rsrc) throws OrmException {
+ public Response<Map<String, ActionInfo>> apply(RevisionResource rsrc) {
return Response.withMustRevalidate(delegate.format(rsrc));
}
@@ -72,14 +67,13 @@ public class GetRevisionActions implements ETagView<RevisionResource> {
try {
rsrc.getChangeResource().prepareETag(h, user);
h.putBoolean(MergeSuperSet.wholeTopicEnabled(config));
- ReviewDb db = dbProvider.get();
- ChangeSet cs = mergeSuperSet.get().completeChangeSet(db, rsrc.getChange(), user);
+ ChangeSet cs = mergeSuperSet.get().completeChangeSet(rsrc.getChange(), user);
for (ChangeData cd : cs.changes()) {
changeResourceFactory.create(cd.notes(), user).prepareETag(h, user);
}
h.putBoolean(cs.furtherHiddenChanges());
- } catch (IOException | OrmException | PermissionBackendException e) {
- throw new OrmRuntimeException(e);
+ } catch (IOException | PermissionBackendException e) {
+ throw new StorageException(e);
}
return h.hash().toString();
}
diff --git a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
index 01970683ab..75d994d1ce 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
@@ -18,7 +18,6 @@ import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.RobotCommentResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -34,8 +33,7 @@ public class GetRobotComment implements RestReadView<RobotCommentResource> {
}
@Override
- public RobotCommentInfo apply(RobotCommentResource rsrc)
- throws OrmException, PermissionBackendException {
+ public RobotCommentInfo apply(RobotCommentResource rsrc) throws PermissionBackendException {
return commentJson.get().newRobotCommentFormatter().format(rsrc.getComment());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Ignore.java b/java/com/google/gerrit/server/restapi/change/Ignore.java
index e31945171a..25cf311ff2 100644
--- a/java/com/google/gerrit/server/restapi/change/Ignore.java
+++ b/java/com/google/gerrit/server/restapi/change/Ignore.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -26,7 +27,6 @@ import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.StarredChangesUtil.MutuallyExclusiveLabelsException;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -51,7 +51,7 @@ public class Ignore implements RestModifyView<ChangeResource, Input>, UiAction<C
@Override
public Response<String> apply(ChangeResource rsrc, Input input)
- throws RestApiException, OrmException, IllegalLabelException {
+ throws RestApiException, IllegalLabelException {
try {
if (rsrc.isUserOwner()) {
throw new BadRequestException("cannot ignore own change");
@@ -73,7 +73,7 @@ public class Ignore implements RestModifyView<ChangeResource, Input>, UiAction<C
private boolean isIgnored(ChangeResource rsrc) {
try {
return stars.isIgnored(rsrc);
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("failed to check ignored star");
}
return false;
diff --git a/java/com/google/gerrit/server/restapi/change/Index.java b/java/com/google/gerrit/server/restapi/change/Index.java
index a5dd868e3a..90dad9800b 100644
--- a/java/com/google/gerrit/server/restapi/change/Index.java
+++ b/java/com/google/gerrit/server/restapi/change/Index.java
@@ -17,7 +17,6 @@ package com.google.gerrit.server.restapi.change;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -26,27 +25,18 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
@Singleton
public class Index extends RetryingRestModifyView<ChangeResource, Input, Response<?>> {
-
- private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
private final ChangeIndexer indexer;
@Inject
- Index(
- Provider<ReviewDb> db,
- RetryHelper retryHelper,
- PermissionBackend permissionBackend,
- ChangeIndexer indexer) {
+ Index(RetryHelper retryHelper, PermissionBackend permissionBackend, ChangeIndexer indexer) {
super(retryHelper);
- this.db = db;
this.permissionBackend = permissionBackend;
this.indexer = indexer;
}
@@ -54,9 +44,9 @@ public class Index extends RetryingRestModifyView<ChangeResource, Input, Respons
@Override
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
- throws IOException, AuthException, OrmException, PermissionBackendException {
+ throws IOException, AuthException, PermissionBackendException {
permissionBackend.currentUser().check(GlobalPermission.MAINTAIN_SERVER);
- indexer.index(db.get(), rsrc.getChange());
+ indexer.index(rsrc.getChange());
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index 72c5589156..403922db99 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -15,31 +15,27 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class ListChangeComments extends ListChangeDrafts {
-
@Inject
ListChangeComments(
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil) {
- super(db, changeDataFactory, commentJson, commentsUtil);
+ super(changeDataFactory, commentJson, commentsUtil);
}
@Override
- protected Iterable<Comment> listComments(ChangeResource rsrc) throws OrmException {
- ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
- return commentsUtil.publishedByChange(db.get(), cd.notes());
+ protected Iterable<Comment> listComments(ChangeResource rsrc) {
+ ChangeData cd = changeDataFactory.create(rsrc.getNotes());
+ return commentsUtil.publishedByChange(cd.notes());
}
@Override
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
index 93e9284202..f8fda731c9 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
@@ -18,13 +18,11 @@ import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.restapi.change.CommentJson.CommentFormatter;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -33,26 +31,23 @@ import java.util.Map;
@Singleton
public class ListChangeDrafts implements RestReadView<ChangeResource> {
- protected final Provider<ReviewDb> db;
protected final ChangeData.Factory changeDataFactory;
protected final Provider<CommentJson> commentJson;
protected final CommentsUtil commentsUtil;
@Inject
ListChangeDrafts(
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil) {
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
}
- protected Iterable<Comment> listComments(ChangeResource rsrc) throws OrmException {
- ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
- return commentsUtil.draftByChangeAuthor(db.get(), cd.notes(), rsrc.getUser().getAccountId());
+ protected Iterable<Comment> listComments(ChangeResource rsrc) {
+ ChangeData cd = changeDataFactory.create(rsrc.getNotes());
+ return commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
}
protected boolean includeAuthorInfo() {
@@ -65,7 +60,7 @@ public class ListChangeDrafts implements RestReadView<ChangeResource> {
@Override
public Map<String, List<CommentInfo>> apply(ChangeResource rsrc)
- throws AuthException, OrmException, PermissionBackendException {
+ throws AuthException, PermissionBackendException {
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
@@ -73,7 +68,7 @@ public class ListChangeDrafts implements RestReadView<ChangeResource> {
}
public List<CommentInfo> getComments(ChangeResource rsrc)
- throws AuthException, OrmException, PermissionBackendException {
+ throws AuthException, PermissionBackendException {
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
index 9ce21574bb..a2e3d4b589 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
@@ -19,39 +19,30 @@ import static com.google.gerrit.server.ChangeMessagesUtil.createChangeMessageInf
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.List;
import java.util.stream.Collectors;
@Singleton
public class ListChangeMessages implements RestReadView<ChangeResource> {
- private final Provider<ReviewDb> dbProvider;
private final ChangeMessagesUtil changeMessagesUtil;
private final AccountLoader accountLoader;
@Inject
public ListChangeMessages(
- Provider<ReviewDb> dbProvider,
- ChangeMessagesUtil changeMessagesUtil,
- AccountLoader.Factory accountLoaderFactory) {
- this.dbProvider = dbProvider;
+ ChangeMessagesUtil changeMessagesUtil, AccountLoader.Factory accountLoaderFactory) {
this.changeMessagesUtil = changeMessagesUtil;
this.accountLoader = accountLoaderFactory.create(true);
}
@Override
- public List<ChangeMessageInfo> apply(ChangeResource resource)
- throws OrmException, PermissionBackendException {
- List<ChangeMessage> messages =
- changeMessagesUtil.byChange(dbProvider.get(), resource.getNotes());
+ public List<ChangeMessageInfo> apply(ChangeResource resource) throws PermissionBackendException {
+ List<ChangeMessage> messages = changeMessagesUtil.byChange(resource.getNotes());
List<ChangeMessageInfo> messageInfos =
messages.stream()
.map(m -> createChangeMessageInfo(m, accountLoader))
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
index dc92ced396..e5840fdfad 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
@@ -17,30 +17,25 @@ 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.RestReadView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.List;
import java.util.Map;
public class ListChangeRobotComments implements RestReadView<ChangeResource> {
- private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
@Inject
ListChangeRobotComments(
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil) {
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
@@ -48,8 +43,8 @@ public class ListChangeRobotComments implements RestReadView<ChangeResource> {
@Override
public Map<String, List<RobotCommentInfo>> apply(ChangeResource rsrc)
- throws AuthException, OrmException, PermissionBackendException {
- ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
+ throws AuthException, PermissionBackendException {
+ ChangeData cd = changeDataFactory.create(rsrc.getNotes());
return commentJson
.get()
.setFillAccounts(true)
diff --git a/java/com/google/gerrit/server/restapi/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
index 3fb4b02022..12732ffed9 100644
--- a/java/com/google/gerrit/server/restapi/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -18,15 +18,12 @@ import com.google.gerrit.extensions.api.changes.ReviewerInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.mail.Address;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ReviewerJson;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.LinkedHashMap;
import java.util.List;
@@ -34,29 +31,22 @@ import java.util.Map;
@Singleton
public class ListReviewers implements RestReadView<ChangeResource> {
- private final Provider<ReviewDb> dbProvider;
private final ApprovalsUtil approvalsUtil;
private final ReviewerJson json;
private final ReviewerResource.Factory resourceFactory;
@Inject
ListReviewers(
- Provider<ReviewDb> dbProvider,
- ApprovalsUtil approvalsUtil,
- ReviewerResource.Factory resourceFactory,
- ReviewerJson json) {
- this.dbProvider = dbProvider;
+ ApprovalsUtil approvalsUtil, ReviewerResource.Factory resourceFactory, ReviewerJson json) {
this.approvalsUtil = approvalsUtil;
this.resourceFactory = resourceFactory;
this.json = json;
}
@Override
- public List<ReviewerInfo> apply(ChangeResource rsrc)
- throws OrmException, PermissionBackendException {
+ public List<ReviewerInfo> apply(ChangeResource rsrc) throws PermissionBackendException {
Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
- ReviewDb db = dbProvider.get();
- for (Account.Id accountId : approvalsUtil.getReviewers(db, rsrc.getNotes()).all()) {
+ for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
if (!reviewers.containsKey(accountId.toString())) {
reviewers.put(accountId.toString(), resourceFactory.create(rsrc, accountId));
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
index 964e56043f..b39ba63ed7 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
@@ -15,11 +15,9 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -27,9 +25,8 @@ import com.google.inject.Singleton;
@Singleton
public class ListRevisionComments extends ListRevisionDrafts {
@Inject
- ListRevisionComments(
- Provider<ReviewDb> db, Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
- super(db, commentJson, commentsUtil);
+ ListRevisionComments(Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
+ super(commentJson, commentsUtil);
}
@Override
@@ -38,8 +35,8 @@ public class ListRevisionComments extends ListRevisionDrafts {
}
@Override
- protected Iterable<Comment> listComments(RevisionResource rsrc) throws OrmException {
+ protected Iterable<Comment> listComments(RevisionResource rsrc) {
ChangeNotes notes = rsrc.getNotes();
- return commentsUtil.publishedByPatchSet(db.get(), notes, rsrc.getPatchSet().getId());
+ return commentsUtil.publishedByPatchSet(notes, rsrc.getPatchSet().getId());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index dbd0ccf233..a46bd6c802 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -18,11 +18,9 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Comment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -31,21 +29,18 @@ import java.util.Map;
@Singleton
public class ListRevisionDrafts implements RestReadView<RevisionResource> {
- protected final Provider<ReviewDb> db;
protected final Provider<CommentJson> commentJson;
protected final CommentsUtil commentsUtil;
@Inject
- ListRevisionDrafts(
- Provider<ReviewDb> db, Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
- this.db = db;
+ ListRevisionDrafts(Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
}
- protected Iterable<Comment> listComments(RevisionResource rsrc) throws OrmException {
+ protected Iterable<Comment> listComments(RevisionResource rsrc) {
return commentsUtil.draftByPatchSetAuthor(
- db.get(), rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes());
+ rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes());
}
protected boolean includeAuthorInfo() {
@@ -54,7 +49,7 @@ public class ListRevisionDrafts implements RestReadView<RevisionResource> {
@Override
public Map<String, List<CommentInfo>> apply(RevisionResource rsrc)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
return commentJson
.get()
.setFillAccounts(includeAuthorInfo())
@@ -63,7 +58,7 @@ public class ListRevisionDrafts implements RestReadView<RevisionResource> {
}
public ImmutableList<CommentInfo> getComments(RevisionResource rsrc)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
return commentJson
.get()
.setFillAccounts(includeAuthorInfo())
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 7add54851e..920cde9ea1 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -19,15 +19,12 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.mail.Address;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ReviewerJson;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.LinkedHashMap;
import java.util.List;
@@ -35,18 +32,13 @@ import java.util.Map;
@Singleton
class ListRevisionReviewers implements RestReadView<RevisionResource> {
- private final Provider<ReviewDb> dbProvider;
private final ApprovalsUtil approvalsUtil;
private final ReviewerJson json;
private final ReviewerResource.Factory resourceFactory;
@Inject
ListRevisionReviewers(
- Provider<ReviewDb> dbProvider,
- ApprovalsUtil approvalsUtil,
- ReviewerResource.Factory resourceFactory,
- ReviewerJson json) {
- this.dbProvider = dbProvider;
+ ApprovalsUtil approvalsUtil, ReviewerResource.Factory resourceFactory, ReviewerJson json) {
this.approvalsUtil = approvalsUtil;
this.resourceFactory = resourceFactory;
this.json = json;
@@ -54,14 +46,13 @@ class ListRevisionReviewers implements RestReadView<RevisionResource> {
@Override
public List<ReviewerInfo> apply(RevisionResource rsrc)
- throws OrmException, MethodNotAllowedException, PermissionBackendException {
+ throws MethodNotAllowedException, PermissionBackendException {
if (!rsrc.isCurrent()) {
throw new MethodNotAllowedException("Cannot list reviewers on non-current patch set");
}
Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
- ReviewDb db = dbProvider.get();
- for (Account.Id accountId : approvalsUtil.getReviewers(db, rsrc.getNotes()).all()) {
+ for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
if (!reviewers.containsKey(accountId.toString())) {
reviewers.put(accountId.toString(), resourceFactory.create(rsrc, accountId));
}
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index 99366aa7d4..bbf46a3f70 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -18,11 +18,9 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.RobotComment;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -31,21 +29,18 @@ import java.util.Map;
@Singleton
public class ListRobotComments implements RestReadView<RevisionResource> {
- protected final Provider<ReviewDb> db;
protected final Provider<CommentJson> commentJson;
protected final CommentsUtil commentsUtil;
@Inject
- ListRobotComments(
- Provider<ReviewDb> db, Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
- this.db = db;
+ ListRobotComments(Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
}
@Override
public Map<String, List<RobotCommentInfo>> apply(RevisionResource rsrc)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
return commentJson
.get()
.setFillAccounts(true)
@@ -54,7 +49,7 @@ public class ListRobotComments implements RestReadView<RevisionResource> {
}
public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
return commentJson
.get()
.setFillAccounts(true)
@@ -62,7 +57,7 @@ public class ListRobotComments implements RestReadView<RevisionResource> {
.formatAsList(listComments(rsrc));
}
- private Iterable<RobotComment> listComments(RevisionResource rsrc) throws OrmException {
+ private Iterable<RobotComment> listComments(RevisionResource rsrc) {
return commentsUtil.robotCommentsByPatchSet(rsrc.getNotes(), rsrc.getPatchSet().getId());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java b/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
index 7c9ba73c62..4c942d2d6f 100644
--- a/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
@@ -15,19 +15,17 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
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.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
@@ -35,16 +33,11 @@ public class MarkAsReviewed
implements RestModifyView<ChangeResource, Input>, UiAction<ChangeResource> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
private final StarredChangesUtil stars;
@Inject
- MarkAsReviewed(
- Provider<ReviewDb> dbProvider,
- ChangeData.Factory changeDataFactory,
- StarredChangesUtil stars) {
- this.dbProvider = dbProvider;
+ MarkAsReviewed(ChangeData.Factory changeDataFactory, StarredChangesUtil stars) {
this.changeDataFactory = changeDataFactory;
this.stars = stars;
}
@@ -59,7 +52,7 @@ public class MarkAsReviewed
@Override
public Response<String> apply(ChangeResource rsrc, Input input)
- throws RestApiException, OrmException, IllegalLabelException {
+ throws RestApiException, IllegalLabelException {
stars.markAsReviewed(rsrc);
return Response.ok("");
}
@@ -67,9 +60,9 @@ public class MarkAsReviewed
private boolean isReviewed(ChangeResource rsrc) {
try {
return changeDataFactory
- .create(dbProvider.get(), rsrc.getNotes())
+ .create(rsrc.getNotes())
.isReviewedBy(rsrc.getUser().asIdentifiedUser().getAccountId());
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("failed to check if change is reviewed");
}
return false;
diff --git a/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java b/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
index 6e15dcc486..5945b1485c 100644
--- a/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
@@ -15,18 +15,16 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
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.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
@@ -34,16 +32,11 @@ public class MarkAsUnreviewed
implements RestModifyView<ChangeResource, Input>, UiAction<ChangeResource> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
private final StarredChangesUtil stars;
@Inject
- MarkAsUnreviewed(
- Provider<ReviewDb> dbProvider,
- ChangeData.Factory changeDataFactory,
- StarredChangesUtil stars) {
- this.dbProvider = dbProvider;
+ MarkAsUnreviewed(ChangeData.Factory changeDataFactory, StarredChangesUtil stars) {
this.changeDataFactory = changeDataFactory;
this.stars = stars;
}
@@ -57,8 +50,7 @@ public class MarkAsUnreviewed
}
@Override
- public Response<String> apply(ChangeResource rsrc, Input input)
- throws OrmException, IllegalLabelException {
+ public Response<String> apply(ChangeResource rsrc, Input input) throws IllegalLabelException {
stars.markAsUnreviewed(rsrc);
return Response.ok("");
}
@@ -66,9 +58,9 @@ public class MarkAsUnreviewed
private boolean isReviewed(ChangeResource rsrc) {
try {
return changeDataFactory
- .create(dbProvider.get(), rsrc.getNotes())
+ .create(rsrc.getNotes())
.isReviewedBy(rsrc.getUser().asIdentifiedUser().getAccountId());
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("failed to check if change is reviewed");
}
return false;
diff --git a/java/com/google/gerrit/server/restapi/change/Mergeable.java b/java/com/google/gerrit/server/restapi/change/Mergeable.java
index b1963474f5..ece8938379 100644
--- a/java/com/google/gerrit/server/restapi/change/Mergeable.java
+++ b/java/com/google/gerrit/server/restapi/change/Mergeable.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitTypeRecord;
+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;
@@ -24,8 +25,6 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
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.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.MergeabilityCache;
import com.google.gerrit.server.change.RevisionResource;
@@ -38,12 +37,9 @@ 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.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
@@ -66,7 +62,6 @@ public class Mergeable implements RestReadView<RevisionResource> {
private final ProjectCache projectCache;
private final MergeUtil.Factory mergeUtilFactory;
private final ChangeData.Factory changeDataFactory;
- private final Provider<ReviewDb> db;
private final ChangeIndexer indexer;
private final MergeabilityCache cache;
private final SubmitRuleEvaluator submitRuleEvaluator;
@@ -77,7 +72,6 @@ public class Mergeable implements RestReadView<RevisionResource> {
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeData.Factory changeDataFactory,
- Provider<ReviewDb> db,
ChangeIndexer indexer,
MergeabilityCache cache,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
@@ -85,7 +79,6 @@ public class Mergeable implements RestReadView<RevisionResource> {
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.changeDataFactory = changeDataFactory;
- this.db = db;
this.indexer = indexer;
this.cache = cache;
submitRuleEvaluator = submitRuleEvaluatorFactory.create(SubmitRuleOptions.defaults());
@@ -97,20 +90,19 @@ public class Mergeable implements RestReadView<RevisionResource> {
@Override
public MergeableInfo apply(RevisionResource resource)
- throws AuthException, ResourceConflictException, BadRequestException, OrmException,
- IOException {
+ throws AuthException, ResourceConflictException, BadRequestException, IOException {
Change change = resource.getChange();
PatchSet ps = resource.getPatchSet();
MergeableInfo result = new MergeableInfo();
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
} else if (!ps.getId().equals(change.currentPatchSetId())) {
// Only the current revision is mergeable. Others always fail.
return result;
}
- ChangeData cd = changeDataFactory.create(db.get(), resource.getNotes());
+ ChangeData cd = changeDataFactory.create(resource.getNotes());
result.submitType = getSubmitType(cd);
try (Repository git = gitManager.openRepository(change.getProject())) {
@@ -143,10 +135,10 @@ public class Mergeable implements RestReadView<RevisionResource> {
return result;
}
- private SubmitType getSubmitType(ChangeData cd) throws OrmException {
+ private SubmitType getSubmitType(ChangeData cd) {
SubmitTypeRecord rec = submitRuleEvaluator.getSubmitType(cd);
if (rec.status != SubmitTypeRecord.Status.OK) {
- throw new OrmException("Submit type rule failed: " + rec);
+ throw new StorageException("Submit type rule failed: " + rec);
}
return rec.type;
}
@@ -157,8 +149,7 @@ public class Mergeable implements RestReadView<RevisionResource> {
ObjectId commit,
Ref ref,
SubmitType submitType,
- String strategy)
- throws OrmException {
+ String strategy) {
if (commit == null) {
return false;
}
@@ -186,25 +177,14 @@ public class Mergeable implements RestReadView<RevisionResource> {
SubmitType type,
String strategy,
Repository git,
- Boolean old)
- throws OrmException {
- final boolean mergeable = cache.get(commit, ref, type, strategy, change.getDest(), git);
+ Boolean old) {
+ boolean mergeable = cache.get(commit, ref, type, strategy, change.getDest(), git);
+ // TODO(dborowitz): Include something else in the change ETag that it's possible to bump here,
+ // such as cache or secondary index update time.
if (!Objects.equals(mergeable, old)) {
- invalidateETag(change.getId(), db.get());
-
@SuppressWarnings("unused")
Future<?> possiblyIgnoredError = indexer.indexAsync(change.getProject(), change.getId());
}
return mergeable;
}
-
- private static void invalidateETag(Change.Id id, ReviewDb db) throws OrmException {
- // Empty update of Change to bump rowVersion, changing its ETag.
- // TODO(dborowitz): Include cache info in ETag somehow instead.
- db = ReviewDbUtil.unwrapDb(db);
- Change c = db.changes().get(id);
- if (c != null) {
- db.changes().update(Collections.singleton(c));
- }
- }
}
diff --git a/java/com/google/gerrit/server/restapi/change/Module.java b/java/com/google/gerrit/server/restapi/change/Module.java
index 88242478d2..a57bd64ba8 100644
--- a/java/com/google/gerrit/server/restapi/change/Module.java
+++ b/java/com/google/gerrit/server/restapi/change/Module.java
@@ -32,6 +32,7 @@ import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.AddReviewersOp;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.DeleteChangeOp;
import com.google.gerrit.server.change.DeleteReviewerByEmailOp;
import com.google.gerrit.server.change.DeleteReviewerOp;
import com.google.gerrit.server.change.EmailReviewComments;
@@ -99,7 +100,6 @@ public class Module extends RestApiModule {
get(CHANGE_KIND, "submitted_together").to(SubmittedTogether.class);
post(CHANGE_KIND, "rebase").to(Rebase.CurrentRevision.class);
post(CHANGE_KIND, "index").to(Index.class);
- post(CHANGE_KIND, "rebuild.notedb").to(Rebuild.class);
post(CHANGE_KIND, "move").to(Move.class);
post(CHANGE_KIND, "private").to(PostPrivate.class);
post(CHANGE_KIND, "private.delete").to(DeletePrivateByPost.class);
@@ -190,6 +190,7 @@ public class Module extends RestApiModule {
factory(AccountLoader.Factory.class);
factory(ChangeInserter.Factory.class);
factory(ChangeResource.Factory.class);
+ factory(DeleteChangeOp.Factory.class);
factory(DeleteReviewerByEmailOp.Factory.class);
factory(DeleteReviewerOp.Factory.class);
factory(EmailReviewComments.Factory.class);
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index 013d3e9050..f2335b155d 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -23,23 +23,23 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.MoveInput;
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.MethodNotAllowedException;
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.Branch;
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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -47,6 +47,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -61,13 +62,13 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -79,7 +80,6 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final PermissionBackend permissionBackend;
- private final Provider<ReviewDb> dbProvider;
private final ChangeJson.Factory json;
private final GitRepositoryManager repoManager;
private final Provider<InternalChangeQuery> queryProvider;
@@ -87,11 +87,11 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
private final PatchSetUtil psUtil;
private final ApprovalsUtil approvalsUtil;
private final ProjectCache projectCache;
+ private final boolean moveEnabled;
@Inject
Move(
PermissionBackend permissionBackend,
- Provider<ReviewDb> dbProvider,
ChangeJson.Factory json,
GitRepositoryManager repoManager,
Provider<InternalChangeQuery> queryProvider,
@@ -99,10 +99,10 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
RetryHelper retryHelper,
PatchSetUtil psUtil,
ApprovalsUtil approvalsUtil,
- ProjectCache projectCache) {
+ ProjectCache projectCache,
+ @GerritServerConfig Config gerritConfig) {
super(retryHelper);
this.permissionBackend = permissionBackend;
- this.dbProvider = dbProvider;
this.json = json;
this.repoManager = repoManager;
this.queryProvider = queryProvider;
@@ -110,13 +110,19 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
this.psUtil = psUtil;
this.approvalsUtil = approvalsUtil;
this.projectCache = projectCache;
+ this.moveEnabled = gerritConfig.getBoolean("change", null, "move", true);
}
@Override
protected ChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, MoveInput input)
- throws RestApiException, OrmException, UpdateException, PermissionBackendException,
- IOException {
+ throws RestApiException, UpdateException, PermissionBackendException, IOException {
+ if (!moveEnabled) {
+ // This will be removed with the above config once we reach consensus for the move change
+ // behavior. See: https://bugs.chromium.org/p/gerrit/issues/detail?id=9877
+ throw new MethodNotAllowedException("move changes endpoint is disabled");
+ }
+
Change change = rsrc.getChange();
Project.NameKey project = rsrc.getProject();
IdentifiedUser caller = rsrc.getUser().asIdentifiedUser();
@@ -125,7 +131,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
}
input.destinationBranch = RefNames.fullName(input.destinationBranch);
- if (change.getStatus().isClosed()) {
+ if (!change.isNew()) {
throw new ResourceConflictException("Change is " + ChangeUtil.status(change));
}
@@ -139,16 +145,15 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
// Move requires abandoning this change, and creating a new change.
try {
- rsrc.permissions().database(dbProvider).check(ABANDON);
- permissionBackend.user(caller).database(dbProvider).ref(newDest).check(CREATE_CHANGE);
+ rsrc.permissions().check(ABANDON);
+ permissionBackend.user(caller).ref(newDest).check(CREATE_CHANGE);
} catch (AuthException denied) {
throw new AuthException("move not permitted", denied);
}
projectCache.checkedGet(project).checkStatePermitsWrite();
Op op = new Op(input);
- try (BatchUpdate u =
- updateFactory.create(dbProvider.get(), project, caller, TimeUtil.nowTs())) {
+ try (BatchUpdate u = updateFactory.create(project, caller, TimeUtil.nowTs())) {
u.addOp(change.getId(), op);
u.execute();
}
@@ -171,10 +176,9 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
}
@Override
- public boolean updateChange(ChangeContext ctx)
- throws OrmException, ResourceConflictException, IOException {
+ public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, IOException {
change = ctx.getChange();
- if (change.getStatus() != Status.NEW) {
+ if (!change.isNew()) {
throw new ResourceConflictException("Change is " + ChangeUtil.status(change));
}
@@ -190,8 +194,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
RevWalk revWalk = new RevWalk(repo)) {
RevCommit currPatchsetRevCommit =
revWalk.parseCommit(
- ObjectId.fromString(
- psUtil.current(ctx.getDb(), ctx.getNotes()).getRevision().get()));
+ ObjectId.fromString(psUtil.current(ctx.getNotes()).getRevision().get()));
if (currPatchsetRevCommit.getParentCount() > 1) {
throw new ResourceConflictException("Merge commit cannot be moved");
}
@@ -240,7 +243,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
}
ChangeMessage cmsg =
ChangeMessagesUtil.newMessage(ctx, msgBuf.toString(), ChangeMessagesUtil.TAG_MOVE);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
return true;
}
@@ -253,11 +256,11 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
*/
private void updateApprovals(
ChangeContext ctx, ChangeUpdate update, PatchSet.Id psId, Project.NameKey project)
- throws IOException, OrmException {
+ throws IOException {
List<PatchSetApproval> approvals = new ArrayList<>();
for (PatchSetApproval psa :
approvalsUtil.byPatchSet(
- ctx.getDb(), ctx.getNotes(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
+ ctx.getNotes(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
ProjectState projectState = projectCache.checkedGet(project);
LabelType type = projectState.getLabelTypes(ctx.getNotes()).byLabel(psa.getLabelId());
// Only keep veto votes, defined as votes where:
@@ -275,8 +278,6 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
(short) 0,
ctx.getWhen()));
}
- // Remove votes from ReviewDb.
- ctx.getDb().patchSetApprovals().upsert(approvals);
}
}
@@ -289,7 +290,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
.setVisible(false);
Change change = rsrc.getChange();
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
return description;
}
@@ -307,7 +308,7 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
if (psUtil.isPatchSetLocked(rsrc.getNotes())) {
return description;
}
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
logger.atSevere().withCause(e).log(
"Failed to check if the current patch set of change %s is locked", change.getId());
return description;
@@ -316,6 +317,6 @@ public class Move extends RetryingRestModifyView<ChangeResource, MoveInput, Chan
return description.setVisible(
and(
permissionBackend.user(rsrc.getUser()).ref(change.getDest()).testCond(CREATE_CHANGE),
- rsrc.permissions().database(dbProvider).testCond(ABANDON)));
+ rsrc.permissions().testCond(ABANDON)));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostHashtags.java b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
index c67fb67979..da499a91ba 100644
--- a/java/com/google/gerrit/server/restapi/change/PostHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/PostHashtags.java
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.api.changes.HashtagsInput;
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.server.ReviewDb;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.SetHashtagsOp;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -30,7 +29,6 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
@@ -38,14 +36,11 @@ public class PostHashtags
extends RetryingRestModifyView<
ChangeResource, HashtagsInput, Response<ImmutableSortedSet<String>>>
implements UiAction<ChangeResource> {
- private final Provider<ReviewDb> db;
private final SetHashtagsOp.Factory hashtagsFactory;
@Inject
- PostHashtags(
- Provider<ReviewDb> db, RetryHelper retryHelper, SetHashtagsOp.Factory hashtagsFactory) {
+ PostHashtags(RetryHelper retryHelper, SetHashtagsOp.Factory hashtagsFactory) {
super(retryHelper);
- this.db = db;
this.hashtagsFactory = hashtagsFactory;
}
@@ -56,12 +51,11 @@ public class PostHashtags
req.permissions().check(ChangePermission.EDIT_HASHTAGS);
try (BatchUpdate bu =
- updateFactory.create(
- db.get(), req.getChange().getProject(), req.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(req.getChange().getProject(), req.getUser(), TimeUtil.nowTs())) {
SetHashtagsOp op = hashtagsFactory.create(input);
bu.addOp(req.getId(), op);
bu.execute();
- return Response.<ImmutableSortedSet<String>>ok(op.getUpdatedHashtags());
+ return Response.ok(op.getUpdatedHashtags());
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostPrivate.java b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
index a5066242ae..5aa2ecc07d 100644
--- a/java/com/google/gerrit/server/restapi/change/PostPrivate.java
+++ b/java/com/google/gerrit/server/restapi/change/PostPrivate.java
@@ -24,8 +24,6 @@ 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.server.ReviewDb;
-import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.SetPrivateOp;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -37,7 +35,6 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Config;
@@ -45,23 +42,17 @@ import org.eclipse.jgit.lib.Config;
public class PostPrivate
extends RetryingRestModifyView<ChangeResource, SetPrivateOp.Input, Response<String>>
implements UiAction<ChangeResource> {
- private final ChangeMessagesUtil cmUtil;
- private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend;
private final SetPrivateOp.Factory setPrivateOpFactory;
private final boolean disablePrivateChanges;
@Inject
PostPrivate(
- Provider<ReviewDb> dbProvider,
RetryHelper retryHelper,
- ChangeMessagesUtil cmUtil,
PermissionBackend permissionBackend,
SetPrivateOp.Factory setPrivateOpFactory,
@GerritServerConfig Config config) {
super(retryHelper);
- this.dbProvider = dbProvider;
- this.cmUtil = cmUtil;
this.permissionBackend = permissionBackend;
this.setPrivateOpFactory = setPrivateOpFactory;
this.disablePrivateChanges = config.getBoolean("change", null, "disablePrivateChanges", false);
@@ -83,10 +74,9 @@ public class PostPrivate
return Response.ok("");
}
- SetPrivateOp op = setPrivateOpFactory.create(cmUtil, true, input);
+ SetPrivateOp op = setPrivateOpFactory.create(true, input);
try (BatchUpdate u =
- updateFactory.create(
- dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
u.addOp(rsrc.getId(), op).execute();
}
@@ -99,13 +89,16 @@ public class PostPrivate
return new UiAction.Description()
.setLabel("Mark private")
.setTitle("Mark change as private")
- .setVisible(and(!disablePrivateChanges && !change.isPrivate(), canSetPrivate(rsrc)));
+ .setVisible(
+ and(
+ !disablePrivateChanges && !change.isPrivate() && change.isNew(),
+ canSetPrivate(rsrc)));
}
private BooleanCondition canSetPrivate(ChangeResource rsrc) {
PermissionBackend.WithUser user = permissionBackend.user(rsrc.getUser());
return or(
- rsrc.isUserOwner() && rsrc.getChange().getStatus() != Change.Status.MERGED,
+ rsrc.isUserOwner() && !rsrc.getChange().isMerged(),
user.testCond(GlobalPermission.ADMINISTRATE_SERVER));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index bed7cc0804..8c94ba322f 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -29,7 +29,6 @@ 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.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
@@ -42,7 +41,6 @@ import com.google.gerrit.common.data.LabelTypes;
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.api.changes.RecipientType;
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;
@@ -58,12 +56,12 @@ import com.google.gerrit.extensions.common.FixReplacementInfo;
import com.google.gerrit.extensions.common.FixSuggestionInfo;
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.UnprocessableEntityException;
import com.google.gerrit.extensions.restapi.Url;
+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;
@@ -77,14 +75,12 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.PublishCommentUtil;
import com.google.gerrit.server.ReviewerSet;
@@ -93,7 +89,7 @@ import com.google.gerrit.server.change.AddReviewersEmail;
import com.google.gerrit.server.change.AddReviewersOp.Result;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.EmailReviewComments;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.ReviewerAdder;
import com.google.gerrit.server.change.ReviewerAdder.ReviewerAddition;
import com.google.gerrit.server.change.RevisionResource;
@@ -102,7 +98,6 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.events.CommentAdded;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.DiffSummary;
import com.google.gerrit.server.patch.DiffSummaryKey;
import com.google.gerrit.server.patch.PatchListCache;
@@ -125,9 +120,7 @@ import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gson.Gson;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -161,7 +154,6 @@ public class PostReview
private static final Gson GSON = OutputFormat.JSON_COMPACT.newGson();
private static final int DEFAULT_ROBOT_COMMENT_SIZE_LIMIT_IN_BYTES = 1024 * 1024;
- private final Provider<ReviewDb> db;
private final ChangeResource.Factory changeResourceFactory;
private final ChangeData.Factory changeDataFactory;
private final ApprovalsUtil approvalsUtil;
@@ -175,8 +167,7 @@ public class PostReview
private final CommentAdded commentAdded;
private final ReviewerAdder reviewerAdder;
private final AddReviewersEmail addReviewersEmail;
- private final NotesMigration migration;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final Config gerritConfig;
private final WorkInProgressOp.Factory workInProgressOpFactory;
private final ProjectCache projectCache;
@@ -185,7 +176,6 @@ public class PostReview
@Inject
PostReview(
- Provider<ReviewDb> db,
RetryHelper retryHelper,
ChangeResource.Factory changeResourceFactory,
ChangeData.Factory changeDataFactory,
@@ -200,14 +190,12 @@ public class PostReview
CommentAdded commentAdded,
ReviewerAdder reviewerAdder,
AddReviewersEmail addReviewersEmail,
- NotesMigration migration,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
@GerritServerConfig Config gerritConfig,
WorkInProgressOp.Factory workInProgressOpFactory,
ProjectCache projectCache,
PermissionBackend permissionBackend) {
super(retryHelper);
- this.db = db;
this.changeResourceFactory = changeResourceFactory;
this.changeDataFactory = changeDataFactory;
this.commentsUtil = commentsUtil;
@@ -221,8 +209,7 @@ public class PostReview
this.commentAdded = commentAdded;
this.reviewerAdder = reviewerAdder;
this.addReviewersEmail = addReviewersEmail;
- this.migration = migration;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.gerritConfig = gerritConfig;
this.workInProgressOpFactory = workInProgressOpFactory;
this.projectCache = projectCache;
@@ -233,15 +220,15 @@ public class PostReview
@Override
protected Response<ReviewResult> applyImpl(
BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input)
- throws RestApiException, UpdateException, OrmException, IOException,
- PermissionBackendException, ConfigInvalidException, PatchListNotAvailableException {
+ throws RestApiException, UpdateException, IOException, PermissionBackendException,
+ ConfigInvalidException, PatchListNotAvailableException {
return apply(updateFactory, revision, input, TimeUtil.nowTs());
}
public Response<ReviewResult> apply(
BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input, Timestamp ts)
- throws RestApiException, UpdateException, OrmException, IOException,
- PermissionBackendException, ConfigInvalidException, PatchListNotAvailableException {
+ throws RestApiException, UpdateException, IOException, PermissionBackendException,
+ ConfigInvalidException, PatchListNotAvailableException {
// Respect timestamp, but truncate at change created-on time.
ts = Ordering.natural().max(ts, revision.getChange().getCreatedOn());
if (revision.getEdit().isPresent()) {
@@ -261,20 +248,13 @@ public class PostReview
checkComments(revision, input.comments);
}
if (input.robotComments != null) {
- if (!migration.readChanges()) {
- throw new MethodNotAllowedException("robot comments not supported");
- }
checkRobotComments(revision, input.robotComments);
}
- NotifyHandling reviewerNotify = input.notify;
if (input.notify == null) {
input.notify = defaultNotify(revision.getChange(), input);
}
- ListMultimap<RecipientType, Account.Id> accountsToNotify =
- notifyUtil.resolveAccounts(input.notifyDetails);
-
Map<String, AddReviewerResult> reviewerJsonResults = null;
List<ReviewerAddition> reviewerResults = Lists.newArrayList();
boolean hasError = false;
@@ -282,15 +262,8 @@ public class PostReview
if (input.reviewers != null) {
reviewerJsonResults = Maps.newHashMap();
for (AddReviewerInput reviewerInput : input.reviewers) {
- // Prevent individual AddReviewersOps from sending one email each. Instead, we call
- // batchEmailReviewers at the very end to send out a single email.
- // TODO(dborowitz): I think this still sends out separate emails if any of input.reviewers
- // specifies explicit accountsToNotify. Unclear whether that's a good thing.
- reviewerInput.notify = NotifyHandling.NONE;
-
ReviewerAddition result =
- reviewerAdder.prepare(
- db.get(), revision.getNotes(), revision.getUser(), reviewerInput, true);
+ reviewerAdder.prepare(revision.getNotes(), revision.getUser(), reviewerInput, true);
reviewerJsonResults.put(reviewerInput.reviewer, result.result);
if (result.result.error != null) {
hasError = true;
@@ -313,7 +286,7 @@ public class PostReview
output.labels = input.labels;
try (BatchUpdate bu =
- updateFactory.create(db.get(), revision.getChange().getProject(), revision.getUser(), ts)) {
+ updateFactory.create(revision.getChange().getProject(), revision.getUser(), ts)) {
Account.Id id = revision.getUser().getAccountId();
boolean ccOrReviewer = false;
if (input.labels != null && !input.labels.isEmpty()) {
@@ -323,7 +296,7 @@ public class PostReview
if (!ccOrReviewer) {
// Check if user was already CCed or reviewing prior to this review.
ReviewerSet currentReviewers =
- approvalsUtil.getReviewers(db.get(), revision.getChangeResource().getNotes());
+ approvalsUtil.getReviewers(revision.getChangeResource().getNotes());
ccOrReviewer = currentReviewers.all().contains(id);
}
@@ -331,6 +304,7 @@ public class PostReview
// updated set of reviewers. Also keep track of whether the user added
// themselves as a reviewer or to the CC list.
for (ReviewerAddition reviewerResult : reviewerResults) {
+ reviewerResult.op.suppressEmail(); // Send a single batch email below.
bu.addOp(revision.getChange().getId(), reviewerResult.op);
if (!ccOrReviewer && reviewerResult.result.reviewers != null) {
for (ReviewerInfo reviewerInfo : reviewerResult.result.reviewers) {
@@ -355,6 +329,7 @@ public class PostReview
// isn't being explicitly added, and isn't voting on any label.
// Automatically CC them on this change so they receive replies.
ReviewerAddition selfAddition = reviewerAdder.ccCurrentUser(revision.getUser(), revision);
+ selfAddition.op.suppressEmail();
bu.addOp(revision.getChange().getId(), selfAddition.op);
}
@@ -365,51 +340,57 @@ public class PostReview
return Response.withStatusCode(SC_BAD_REQUEST, output);
}
- WorkInProgressOp.checkPermissions(
- permissionBackend, revision.getUser(), revision.getChange());
+ revision
+ .getChangeResource()
+ .permissions()
+ .check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
if (input.ready) {
output.ready = true;
}
- // Suppress notifications in WorkInProgressOp, we'll take care of
- // them in this endpoint.
- WorkInProgressOp.Input wipIn = new WorkInProgressOp.Input();
- wipIn.notify = NotifyHandling.NONE;
- bu.addOp(
- revision.getChange().getId(),
- workInProgressOpFactory.create(input.workInProgress, wipIn));
+ WorkInProgressOp wipOp =
+ workInProgressOpFactory.create(input.workInProgress, new WorkInProgressOp.Input());
+ wipOp.suppressEmail();
+ bu.addOp(revision.getChange().getId(), wipOp);
}
// Add the review op.
bu.addOp(
revision.getChange().getId(),
- new Op(projectState, revision.getPatchSet().getId(), input, accountsToNotify));
+ new Op(projectState, revision.getPatchSet().getId(), input));
+
+ // Notify based on ReviewInput, ignoring the notify settings from any AddReviewerInputs.
+ NotifyResolver.Result notify =
+ notifyResolver.resolve(getNotifyHandling(input, output, revision), input.notifyDetails);
+ bu.setNotify(notify);
bu.execute();
// Re-read change to take into account results of the update.
- ChangeData cd =
- changeDataFactory.create(db.get(), revision.getProject(), revision.getChange().getId());
+ ChangeData cd = changeDataFactory.create(revision.getProject(), revision.getChange().getId());
for (ReviewerAddition reviewerResult : reviewerResults) {
reviewerResult.gatherResults(cd);
}
- boolean readyForReview =
- (output.ready != null && output.ready) || !revision.getChange().isWorkInProgress();
// Sending from AddReviewersOp was suppressed so we can send a single batch email here.
- batchEmailReviewers(
- revision.getUser(),
- revision.getChange(),
- reviewerResults,
- reviewerNotify,
- accountsToNotify,
- readyForReview);
+ batchEmailReviewers(revision.getUser(), revision.getChange(), reviewerResults, notify);
}
return Response.ok(output);
}
+ private NotifyHandling getNotifyHandling(
+ ReviewInput input, ReviewResult output, RevisionResource revision) {
+ if (input.notify != null) {
+ return input.notify;
+ }
+ if ((output.ready != null && output.ready) || !revision.getChange().isWorkInProgress()) {
+ return NotifyHandling.ALL;
+ }
+ return NotifyHandling.NONE;
+ }
+
private NotifyHandling defaultNotify(Change c, ReviewInput in) {
boolean workInProgress = c.isWorkInProgress();
if (in.workInProgress) {
@@ -425,11 +406,12 @@ public class PostReview
}
if (workInProgress && !c.hasReviewStarted()) {
- // If review hasn't started we want to minimize recipients, no matter who
- // the author is.
- return NotifyHandling.OWNER;
+ // If review hasn't started we want to eliminate notifications, no matter who the author is.
+ return NotifyHandling.NONE;
}
+ // Otherwise, it's either a non-WIP change, or a WIP change where review has started. Notify
+ // everyone.
return NotifyHandling.ALL;
}
@@ -437,9 +419,7 @@ public class PostReview
CurrentUser user,
Change change,
List<ReviewerAddition> reviewerAdditions,
- @Nullable NotifyHandling notify,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
- boolean readyForReview) {
+ NotifyResolver.Result notify) {
List<Account.Id> to = new ArrayList<>();
List<Account.Id> cc = new ArrayList<>();
List<Address> toByEmail = new ArrayList<>();
@@ -459,19 +439,11 @@ public class PostReview
}
}
addReviewersEmail.emailReviewers(
- user.asIdentifiedUser(),
- change,
- to,
- cc,
- toByEmail,
- ccByEmail,
- notify,
- accountsToNotify,
- readyForReview);
+ user.asIdentifiedUser(), change, to, cc, toByEmail, ccByEmail, notify);
}
private RevisionResource onBehalfOf(RevisionResource rev, LabelTypes labelTypes, ReviewInput in)
- throws BadRequestException, AuthException, UnprocessableEntityException, OrmException,
+ throws BadRequestException, AuthException, UnprocessableEntityException,
PermissionBackendException, IOException, ConfigInvalidException {
if (in.labels == null || in.labels.isEmpty()) {
throw new AuthException(
@@ -482,7 +454,7 @@ public class PostReview
}
CurrentUser caller = rev.getUser();
- PermissionBackend.ForChange perm = rev.permissions().database(db);
+ PermissionBackend.ForChange perm = rev.permissions();
Iterator<Map.Entry<String, Short>> itr = in.labels.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, Short> ent = itr.next();
@@ -513,13 +485,9 @@ public class PostReview
String.format("label required to post review on behalf of \"%s\"", in.onBehalfOf));
}
- IdentifiedUser reviewer = accountResolver.parseOnBehalfOf(caller, in.onBehalfOf);
+ IdentifiedUser reviewer = accountResolver.resolve(in.onBehalfOf).asUniqueUserOnBehalfOf(caller);
try {
- permissionBackend
- .user(reviewer)
- .database(db)
- .change(rev.getNotes())
- .check(ChangePermission.READ);
+ permissionBackend.user(reviewer).change(rev.getNotes()).check(ChangePermission.READ);
} catch (AuthException e) {
throw new UnprocessableEntityException(
String.format("on_behalf_of account %s cannot see change", reviewer.getAccountId()), e);
@@ -874,7 +842,6 @@ public class PostReview
private final ProjectState projectState;
private final PatchSet.Id psId;
private final ReviewInput in;
- private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
private IdentifiedUser user;
private ChangeNotes notes;
@@ -885,24 +852,19 @@ public class PostReview
private Map<String, Short> approvals = new HashMap<>();
private Map<String, Short> oldApprovals = new HashMap<>();
- private Op(
- ProjectState projectState,
- PatchSet.Id psId,
- ReviewInput in,
- ListMultimap<RecipientType, Account.Id> accountsToNotify) {
+ private Op(ProjectState projectState, PatchSet.Id psId, ReviewInput in) {
this.projectState = projectState;
this.psId = psId;
this.in = in;
- this.accountsToNotify = requireNonNull(accountsToNotify);
}
@Override
public boolean updateChange(ChangeContext ctx)
- throws OrmException, ResourceConflictException, UnprocessableEntityException, IOException,
+ throws ResourceConflictException, UnprocessableEntityException, IOException,
PatchListNotAvailableException {
user = ctx.getIdentifiedUser();
notes = ctx.getNotes();
- ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ ps = psUtil.get(ctx.getNotes(), psId);
boolean dirty = false;
dirty |= insertComments(ctx);
dirty |= insertRobotComments(ctx);
@@ -912,22 +874,14 @@ public class PostReview
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
if (message == null) {
return;
}
- if (in.notify.compareTo(NotifyHandling.NONE) > 0 || !accountsToNotify.isEmpty()) {
+ NotifyResolver.Result notify = ctx.getNotify(notes.getChangeId());
+ if (notify.shouldNotify()) {
email
- .create(
- in.notify,
- accountsToNotify,
- notes,
- ps,
- user,
- message,
- comments,
- in.message,
- labelDelta)
+ .create(notify, notes, ps, user, message, comments, in.message, labelDelta)
.sendAsync();
}
commentAdded.fire(
@@ -941,7 +895,7 @@ public class PostReview
}
private boolean insertComments(ChangeContext ctx)
- throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
+ throws UnprocessableEntityException, PatchListNotAvailableException {
Map<String, List<CommentInput>> map = in.comments;
if (map == null) {
map = Collections.emptyMap();
@@ -996,13 +950,12 @@ public class PostReview
break;
}
ChangeUpdate u = ctx.getUpdate(psId);
- commentsUtil.putComments(ctx.getDb(), u, Status.PUBLISHED, toPublish);
+ commentsUtil.putComments(u, Status.PUBLISHED, toPublish);
comments.addAll(toPublish);
return !toPublish.isEmpty();
}
- private boolean insertRobotComments(ChangeContext ctx)
- throws OrmException, PatchListNotAvailableException {
+ private boolean insertRobotComments(ChangeContext ctx) throws PatchListNotAvailableException {
if (in.robotComments == null) {
return false;
}
@@ -1014,7 +967,7 @@ public class PostReview
}
private List<RobotComment> getNewRobotComments(ChangeContext ctx)
- throws OrmException, PatchListNotAvailableException {
+ throws PatchListNotAvailableException {
List<RobotComment> toAdd = new ArrayList<>(in.robotComments.size());
Set<CommentSetEntry> existingIds =
@@ -1083,33 +1036,31 @@ public class PostReview
return new FixReplacement(fixReplacementInfo.path, range, fixReplacementInfo.replacement);
}
- private Set<CommentSetEntry> readExistingComments(ChangeContext ctx) throws OrmException {
- return commentsUtil.publishedByChange(ctx.getDb(), ctx.getNotes()).stream()
+ private Set<CommentSetEntry> readExistingComments(ChangeContext ctx) {
+ return commentsUtil.publishedByChange(ctx.getNotes()).stream()
.map(CommentSetEntry::create)
.collect(toSet());
}
- private Set<CommentSetEntry> readExistingRobotComments(ChangeContext ctx) throws OrmException {
+ private Set<CommentSetEntry> readExistingRobotComments(ChangeContext ctx) {
return commentsUtil.robotCommentsByChange(ctx.getNotes()).stream()
.map(CommentSetEntry::create)
.collect(toSet());
}
- private Map<String, Comment> changeDrafts(ChangeContext ctx) throws OrmException {
+ private Map<String, Comment> changeDrafts(ChangeContext ctx) {
Map<String, Comment> drafts = new HashMap<>();
- for (Comment c :
- commentsUtil.draftByChangeAuthor(ctx.getDb(), ctx.getNotes(), user.getAccountId())) {
+ for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), user.getAccountId())) {
c.tag = in.tag;
drafts.put(c.key.uuid, c);
}
return drafts;
}
- private Map<String, Comment> patchSetDrafts(ChangeContext ctx) throws OrmException {
+ private Map<String, Comment> patchSetDrafts(ChangeContext ctx) {
Map<String, Comment> drafts = new HashMap<>();
for (Comment c :
- commentsUtil.draftByPatchSetAuthor(
- ctx.getDb(), psId, user.getAccountId(), ctx.getNotes())) {
+ commentsUtil.draftByPatchSetAuthor(psId, user.getAccountId(), ctx.getNotes())) {
drafts.put(c.key.uuid, c);
}
return drafts;
@@ -1154,11 +1105,11 @@ public class PostReview
return previous;
}
- private boolean isReviewer(ChangeContext ctx) throws OrmException {
+ private boolean isReviewer(ChangeContext ctx) {
if (ctx.getAccountId().equals(ctx.getChange().getOwner())) {
return true;
}
- ChangeData cd = changeDataFactory.create(db.get(), ctx.getNotes());
+ ChangeData cd = changeDataFactory.create(ctx.getNotes());
ReviewerSet reviewers = cd.reviewers();
if (reviewers.byState(REVIEWER).contains(ctx.getAccountId())) {
return true;
@@ -1167,13 +1118,13 @@ public class PostReview
}
private boolean updateLabels(ProjectState projectState, ChangeContext ctx)
- throws OrmException, ResourceConflictException, IOException {
+ throws ResourceConflictException, IOException {
Map<String, Short> inLabels = firstNonNull(in.labels, Collections.emptyMap());
// If no labels were modified and change is closed, abort early.
// This avoids trying to record a modified label caused by a user
// losing access to a label after the change was submitted.
- if (inLabels.isEmpty() && ctx.getChange().getStatus().isClosed()) {
+ if (inLabels.isEmpty() && ctx.getChange().isClosed()) {
return false;
}
@@ -1242,14 +1193,6 @@ public class PostReview
forceCallerAsReviewer(projectState, ctx, current, ups, del);
- if (!del.isEmpty()) {
- ctx.getDb().patchSetApprovals().delete(del);
- }
-
- if (!ups.isEmpty()) {
- ctx.getDb().patchSetApprovals().upsert(ups);
- }
-
return !del.isEmpty() || !ups.isEmpty();
}
@@ -1260,11 +1203,11 @@ public class PostReview
List<PatchSetApproval> ups,
List<PatchSetApproval> del)
throws ResourceConflictException {
- if (ctx.getChange().getStatus().isOpen()) {
+ if (ctx.getChange().isNew()) {
return; // Not closed, nothing to validate.
} else if (del.isEmpty() && ups.isEmpty()) {
return; // No new votes.
- } else if (ctx.getChange().getStatus() != Change.Status.MERGED) {
+ } else if (!ctx.getChange().isMerged()) {
throw new ResourceConflictException("change is closed");
}
@@ -1300,11 +1243,8 @@ public class PostReview
checkState(prev != psa.getValue()); // Should be filtered out above.
if (prev > psa.getValue()) {
reduced.add(psa);
- } else {
- // Set postSubmit bit in ReviewDb; not required for NoteDb, which sets
- // it automatically.
- psa.setPostSubmit(true);
}
+ // No need to set postSubmit bit, which is set automatically when parsing from NoteDb.
}
if (!disallowed.isEmpty()) {
@@ -1362,13 +1302,12 @@ public class PostReview
private Map<String, PatchSetApproval> scanLabels(
ProjectState projectState, ChangeContext ctx, List<PatchSetApproval> del)
- throws OrmException, IOException {
+ throws IOException {
LabelTypes labelTypes = projectState.getLabelTypes(ctx.getNotes());
Map<String, PatchSetApproval> current = new HashMap<>();
for (PatchSetApproval a :
approvalsUtil.byPatchSetUser(
- ctx.getDb(),
ctx.getNotes(),
psId,
user.getAccountId(),
@@ -1388,7 +1327,7 @@ public class PostReview
return current;
}
- private boolean insertMessage(ChangeContext ctx) throws OrmException {
+ private boolean insertMessage(ChangeContext ctx) {
String msg = Strings.nullToEmpty(in.message).trim();
StringBuilder buf = new StringBuilder();
@@ -1412,7 +1351,7 @@ public class PostReview
message =
ChangeMessagesUtil.newMessage(
psId, user, ctx.getWhen(), "Patch Set " + psId.get() + ":" + buf, in.tag);
- cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), message);
+ cmUtil.addChangeMessage(ctx.getUpdate(psId), message);
return true;
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
index 3e6618931e..8abd96402f 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -16,11 +16,12 @@ package com.google.gerrit.server.restapi.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.RestApiException;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.ReviewerAdder;
import com.google.gerrit.server.change.ReviewerAdder.ReviewerAddition;
import com.google.gerrit.server.change.ReviewerResource;
@@ -31,9 +32,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestCollectionModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -43,47 +42,55 @@ public class PostReviewers
extends RetryingRestCollectionModifyView<
ChangeResource, ReviewerResource, AddReviewerInput, AddReviewerResult> {
- private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
+ private final NotifyResolver notifyResolver;
private final ReviewerAdder reviewerAdder;
@Inject
PostReviewers(
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
RetryHelper retryHelper,
+ NotifyResolver notifyResolver,
ReviewerAdder reviewerAdder) {
super(retryHelper);
- this.dbProvider = db;
this.changeDataFactory = changeDataFactory;
+ this.notifyResolver = notifyResolver;
this.reviewerAdder = reviewerAdder;
}
@Override
protected AddReviewerResult applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input)
- throws IOException, OrmException, RestApiException, UpdateException,
- PermissionBackendException, ConfigInvalidException {
+ throws IOException, RestApiException, UpdateException, PermissionBackendException,
+ ConfigInvalidException {
if (input.reviewer == null) {
throw new BadRequestException("missing reviewer field");
}
- ReviewerAddition addition =
- reviewerAdder.prepare(dbProvider.get(), rsrc.getNotes(), rsrc.getUser(), input, true);
+ ReviewerAddition addition = reviewerAdder.prepare(rsrc.getNotes(), rsrc.getUser(), input, true);
if (addition.op == null) {
return addition.result;
}
try (BatchUpdate bu =
- updateFactory.create(
- dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ bu.setNotify(resolveNotify(rsrc, input));
Change.Id id = rsrc.getChange().getId();
bu.addOp(id, addition.op);
bu.execute();
}
// Re-read change to take into account results of the update.
- addition.gatherResults(
- changeDataFactory.create(dbProvider.get(), rsrc.getProject(), rsrc.getId()));
+ addition.gatherResults(changeDataFactory.create(rsrc.getProject(), rsrc.getId()));
return addition.result;
}
+
+ private NotifyResolver.Result resolveNotify(ChangeResource rsrc, AddReviewerInput input)
+ throws BadRequestException, ConfigInvalidException, IOException {
+ NotifyHandling notifyHandling = input.notify;
+ if (notifyHandling == null) {
+ notifyHandling =
+ rsrc.getChange().isWorkInProgress() ? NotifyHandling.NONE : NotifyHandling.ALL;
+ }
+ return notifyResolver.resolve(notifyHandling, input.notifyDetails);
+ }
}
diff --git a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
index c0c434c087..c3cee0e2c4 100644
--- a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
+++ b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
@@ -26,7 +26,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ArchiveFormat;
@@ -40,7 +39,6 @@ import com.google.gerrit.server.submit.MergeOp;
import com.google.gerrit.server.submit.MergeOpRepoManager;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -61,7 +59,6 @@ import org.kohsuke.args4j.Option;
public class PreviewSubmit implements RestReadView<RevisionResource> {
private static final int MAX_DEFAULT_BUNDLE_SIZE = 100 * 1024 * 1024;
- private final Provider<ReviewDb> dbProvider;
private final Provider<MergeOp> mergeOpProvider;
private final AllowedFormats allowedFormats;
private int maxBundleSize;
@@ -74,11 +71,9 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
@Inject
PreviewSubmit(
- Provider<ReviewDb> dbProvider,
Provider<MergeOp> mergeOpProvider,
AllowedFormats allowedFormats,
@GerritServerConfig Config cfg) {
- this.dbProvider = dbProvider;
this.mergeOpProvider = mergeOpProvider;
this.allowedFormats = allowedFormats;
this.maxBundleSize = cfg.getInt("download", "maxBundleSize", MAX_DEFAULT_BUNDLE_SIZE);
@@ -86,7 +81,7 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
@Override
public BinaryResult apply(RevisionResource rsrc)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ throws RestApiException, UpdateException, IOException, ConfigInvalidException,
PermissionBackendException {
if (Strings.isNullOrEmpty(format)) {
throw new BadRequestException("format is not specified");
@@ -103,7 +98,7 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
}
Change change = rsrc.getChange();
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
throw new PreconditionFailedException("change is " + ChangeUtil.status(change));
}
if (!rsrc.getUser().isIdentifiedUser()) {
@@ -114,23 +109,21 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
}
private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ throws RestApiException, UpdateException, IOException, ConfigInvalidException,
PermissionBackendException {
- ReviewDb db = dbProvider.get();
IdentifiedUser caller = rsrc.getUser().asIdentifiedUser();
Change change = rsrc.getChange();
@SuppressWarnings("resource") // Returned BinaryResult takes ownership and handles closing.
MergeOp op = mergeOpProvider.get();
try {
- op.merge(db, change, caller, false, new SubmitInput(), true);
+ op.merge(change, caller, false, new SubmitInput(), true);
BinaryResult bin = new SubmitPreviewResult(op, f, maxBundleSize);
bin.disableGzip()
.setContentType(f.getMimeType())
.setAttachmentName("submit-preview-" + change.getChangeId() + "." + format);
return bin;
- } catch (OrmException
- | RestApiException
+ } catch (RestApiException
| UpdateException
| IOException
| ConfigInvalidException
diff --git a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
index 3d401c498a..a47037ca78 100644
--- a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
@@ -14,12 +14,15 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.common.base.MoreObjects.firstNonNull;
+
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
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.server.change.ChangeResource;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
@@ -28,7 +31,6 @@ import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -39,25 +41,25 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
public class PublishChangeEdit
extends RetryingRestModifyView<ChangeResource, PublishChangeEditInput, Response<?>> {
private final ChangeEditUtil editUtil;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final ContributorAgreementsChecker contributorAgreementsChecker;
@Inject
PublishChangeEdit(
RetryHelper retryHelper,
ChangeEditUtil editUtil,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
ContributorAgreementsChecker contributorAgreementsChecker) {
super(retryHelper);
this.editUtil = editUtil;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.contributorAgreementsChecker = contributorAgreementsChecker;
}
@Override
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
- throws IOException, OrmException, RestApiException, UpdateException, ConfigInvalidException,
+ throws IOException, RestApiException, UpdateException, ConfigInvalidException,
NoSuchProjectException {
contributorAgreementsChecker.check(rsrc.getProject(), rsrc.getUser());
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
@@ -73,8 +75,7 @@ public class PublishChangeEdit
rsrc.getNotes(),
rsrc.getUser(),
edit.get(),
- in.notify,
- notifyUtil.resolveAccounts(in.notifyDetails));
+ notifyResolver.resolve(firstNonNull(in.notify, NotifyHandling.ALL), in.notifyDetails));
return Response.none();
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index d247d5ed75..a9a6f12623 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -23,9 +23,7 @@ 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.RestApiException;
-import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.UiAction;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.AccountResolver;
@@ -41,9 +39,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -54,7 +50,6 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
private final AccountResolver accountResolver;
private final SetAssigneeOp.Factory assigneeFactory;
- private final Provider<ReviewDb> db;
private final ReviewerAdder reviewerAdder;
private final AccountLoader.Factory accountLoaderFactory;
private final PermissionBackend permissionBackend;
@@ -64,14 +59,12 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
AccountResolver accountResolver,
SetAssigneeOp.Factory assigneeFactory,
RetryHelper retryHelper,
- Provider<ReviewDb> db,
ReviewerAdder reviewerAdder,
AccountLoader.Factory accountLoaderFactory,
PermissionBackend permissionBackend) {
super(retryHelper);
this.accountResolver = accountResolver;
this.assigneeFactory = assigneeFactory;
- this.db = db;
this.reviewerAdder = reviewerAdder;
this.accountLoaderFactory = accountLoaderFactory;
this.permissionBackend = permissionBackend;
@@ -80,8 +73,8 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
@Override
protected AccountInfo applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AssigneeInput input)
- throws RestApiException, UpdateException, OrmException, IOException,
- PermissionBackendException, ConfigInvalidException {
+ throws RestApiException, UpdateException, IOException, PermissionBackendException,
+ ConfigInvalidException {
rsrc.permissions().check(ChangePermission.EDIT_ASSIGNEE);
input.assignee = Strings.nullToEmpty(input.assignee).trim();
@@ -89,14 +82,10 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
throw new BadRequestException("missing assignee field");
}
- IdentifiedUser assignee = accountResolver.parse(input.assignee);
- if (!assignee.getAccount().isActive()) {
- throw new UnprocessableEntityException(input.assignee + " is not active");
- }
+ IdentifiedUser assignee = accountResolver.resolve(input.assignee).asUniqueUser();
try {
permissionBackend
.absentUser(assignee.getAccountId())
- .database(db)
.change(rsrc.getNotes())
.check(ChangePermission.READ);
} catch (AuthException e) {
@@ -104,12 +93,12 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
}
try (BatchUpdate bu =
- updateFactory.create(
- db.get(), rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
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);
bu.execute();
@@ -118,13 +107,13 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
}
private ReviewerAddition addAssigneeAsCC(ChangeResource rsrc, String assignee)
- throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
+ throws IOException, PermissionBackendException, ConfigInvalidException {
AddReviewerInput reviewerInput = new AddReviewerInput();
reviewerInput.reviewer = assignee;
reviewerInput.state = ReviewerState.CC;
reviewerInput.confirmed = true;
reviewerInput.notify = NotifyHandling.NONE;
- return reviewerAdder.prepare(db.get(), rsrc.getNotes(), rsrc.getUser(), reviewerInput, false);
+ return reviewerAdder.prepare(rsrc.getNotes(), rsrc.getUser(), reviewerInput, false);
}
@Override
diff --git a/java/com/google/gerrit/server/restapi/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
index 3b5edb26ef..0ec38e15f0 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -21,7 +21,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.RevisionResource;
@@ -35,28 +34,19 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.util.Collections;
@Singleton
public class PutDescription
extends RetryingRestModifyView<RevisionResource, DescriptionInput, Response<String>>
implements UiAction<RevisionResource> {
- private final Provider<ReviewDb> dbProvider;
private final ChangeMessagesUtil cmUtil;
private final PatchSetUtil psUtil;
@Inject
- PutDescription(
- Provider<ReviewDb> dbProvider,
- ChangeMessagesUtil cmUtil,
- RetryHelper retryHelper,
- PatchSetUtil psUtil) {
+ PutDescription(ChangeMessagesUtil cmUtil, RetryHelper retryHelper, PatchSetUtil psUtil) {
super(retryHelper);
- this.dbProvider = dbProvider;
this.cmUtil = cmUtil;
this.psUtil = psUtil;
}
@@ -69,8 +59,7 @@ public class PutDescription
Op op = new Op(input != null ? input : new DescriptionInput(), rsrc.getPatchSet().getId());
try (BatchUpdate u =
- updateFactory.create(
- dbProvider.get(), rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
u.addOp(rsrc.getChange().getId(), op);
u.execute();
}
@@ -92,11 +81,10 @@ public class PutDescription
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
- PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ public boolean updateChange(ChangeContext ctx) {
ChangeUpdate update = ctx.getUpdate(psId);
newDescription = Strings.nullToEmpty(input.description);
- oldDescription = Strings.nullToEmpty(ps.getDescription());
+ oldDescription = Strings.nullToEmpty(psUtil.get(ctx.getNotes(), psId).getDescription());
if (oldDescription.equals(newDescription)) {
return false;
}
@@ -109,15 +97,12 @@ public class PutDescription
summary = "Description changed to \"" + newDescription + "\"";
}
- ps.setDescription(newDescription);
update.setPsDescription(newDescription);
- ctx.getDb().patchSets().update(Collections.singleton(ps));
-
ChangeMessage cmsg =
ChangeMessagesUtil.newMessage(
psId, ctx.getUser(), ctx.getWhen(), summary, ChangeMessagesUtil.TAG_SET_DESCRIPTION);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 72358bd6c8..f6c9abe65d 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -26,7 +26,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.DraftCommentResource;
@@ -41,7 +40,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -53,7 +51,6 @@ import java.util.Optional;
public class PutDraftComment
extends RetryingRestModifyView<DraftCommentResource, DraftInput, Response<CommentInfo>> {
- private final Provider<ReviewDb> db;
private final DeleteDraftComment delete;
private final CommentsUtil commentsUtil;
private final PatchSetUtil psUtil;
@@ -62,7 +59,6 @@ public class PutDraftComment
@Inject
PutDraftComment(
- Provider<ReviewDb> db,
DeleteDraftComment delete,
CommentsUtil commentsUtil,
PatchSetUtil psUtil,
@@ -70,7 +66,6 @@ public class PutDraftComment
Provider<CommentJson> commentJson,
PatchListCache patchListCache) {
super(retryHelper);
- this.db = db;
this.delete = delete;
this.commentsUtil = commentsUtil;
this.psUtil = psUtil;
@@ -81,7 +76,7 @@ public class PutDraftComment
@Override
protected Response<CommentInfo> applyImpl(
BatchUpdate.Factory updateFactory, DraftCommentResource rsrc, DraftInput in)
- throws RestApiException, UpdateException, OrmException, PermissionBackendException {
+ throws RestApiException, UpdateException, PermissionBackendException {
if (in == null || in.message == null || in.message.trim().isEmpty()) {
return delete.applyImpl(updateFactory, rsrc, null);
} else if (in.id != null && !rsrc.getId().equals(in.id)) {
@@ -93,8 +88,7 @@ public class PutDraftComment
}
try (BatchUpdate bu =
- updateFactory.create(
- db.get(), rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
Op op = new Op(rsrc.getComment().key, in);
bu.addOp(rsrc.getChange().getId(), op);
bu.execute();
@@ -116,9 +110,9 @@ public class PutDraftComment
@Override
public boolean updateChange(ChangeContext ctx)
- throws ResourceNotFoundException, OrmException, PatchListNotAvailableException {
+ throws ResourceNotFoundException, PatchListNotAvailableException {
Optional<Comment> maybeComment =
- commentsUtil.getDraft(ctx.getDb(), ctx.getNotes(), ctx.getIdentifiedUser(), key);
+ commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
if (!maybeComment.isPresent()) {
// Disappeared out from under us. Can't easily fall back to insert,
// because the input might be missing required fields. Just give up.
@@ -133,7 +127,7 @@ public class PutDraftComment
PatchSet.Id psId = new PatchSet.Id(ctx.getChange().getId(), origComment.key.patchSetId);
ChangeUpdate update = ctx.getUpdate(psId);
- PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ PatchSet ps = psUtil.get(ctx.getNotes(), psId);
if (ps == null) {
throw new ResourceNotFoundException("patch set not found: " + psId);
}
@@ -141,16 +135,12 @@ public class PutDraftComment
// Updating the path alters the primary key, which isn't possible.
// Delete then recreate the comment instead of an update.
- commentsUtil.deleteComments(ctx.getDb(), update, Collections.singleton(origComment));
+ commentsUtil.deleteComments(update, Collections.singleton(origComment));
comment.key.filename = in.path;
}
setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
commentsUtil.putComments(
- ctx.getDb(),
- update,
- Status.DRAFT,
- Collections.singleton(update(comment, in, ctx.getWhen())));
- ctx.dontBumpLastUpdatedOn();
+ update, 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 bcd0e9e455..c542164a32 100644
--- a/java/com/google/gerrit/server/restapi/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -24,13 +24,12 @@ 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.reviewdb.server.ReviewDb;
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.change.ChangeResource;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -44,7 +43,6 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -68,12 +66,11 @@ public class PutMessage
private final GitRepositoryManager repositoryManager;
private final Provider<CurrentUser> userProvider;
- private final Provider<ReviewDb> db;
private final TimeZone tz;
private final PatchSetInserter.Factory psInserterFactory;
private final PermissionBackend permissionBackend;
private final PatchSetUtil psUtil;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final ProjectCache projectCache;
@Inject
@@ -81,22 +78,20 @@ public class PutMessage
RetryHelper retryHelper,
GitRepositoryManager repositoryManager,
Provider<CurrentUser> userProvider,
- Provider<ReviewDb> db,
PatchSetInserter.Factory psInserterFactory,
PermissionBackend permissionBackend,
@GerritPersonIdent PersonIdent gerritIdent,
PatchSetUtil psUtil,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
ProjectCache projectCache) {
super(retryHelper);
this.repositoryManager = repositoryManager;
this.userProvider = userProvider;
- this.db = db;
this.psInserterFactory = psInserterFactory;
this.tz = gerritIdent.getTimeZone();
this.permissionBackend = permissionBackend;
this.psUtil = psUtil;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.projectCache = projectCache;
}
@@ -104,8 +99,8 @@ public class PutMessage
protected Response<String> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource resource, CommitMessageInput input)
throws IOException, RestApiException, UpdateException, PermissionBackendException,
- OrmException, ConfigInvalidException {
- PatchSet ps = psUtil.current(db.get(), resource.getNotes());
+ ConfigInvalidException {
+ PatchSet ps = psUtil.current(resource.getNotes());
if (ps == null) {
throw new ResourceConflictException("current revision is missing");
}
@@ -121,11 +116,6 @@ public class PutMessage
resource.getChange().getKey().get(),
sanitizedCommitMessage);
- NotifyHandling notify = input.notify;
- if (notify == null) {
- notify = resource.getChange().isWorkInProgress() ? NotifyHandling.OWNER : NotifyHandling.ALL;
- }
-
try (Repository repository = repositoryManager.openRepository(resource.getProject());
RevWalk revWalk = new RevWalk(repository);
ObjectInserter objectInserter = repository.newObjectInserter()) {
@@ -138,8 +128,7 @@ public class PutMessage
Timestamp ts = TimeUtil.nowTs();
try (BatchUpdate bu =
- updateFactory.create(
- db.get(), resource.getChange().getProject(), userProvider.get(), ts)) {
+ updateFactory.create(resource.getChange().getProject(), userProvider.get(), ts)) {
// Ensure that BatchUpdate will update the same repo
bu.setRepository(repository, new RevWalk(objectInserter.newReader()), objectInserter);
@@ -150,8 +139,7 @@ public class PutMessage
inserter.setMessage(
String.format("Patch Set %s: Commit message was updated.", psId.getId()));
inserter.setDescription("Edit commit message");
- inserter.setNotify(notify);
- inserter.setAccountsToNotify(notifyUtil.resolveAccounts(input.notifyDetails));
+ bu.setNotify(resolveNotify(input, resource));
bu.addOp(resource.getChange().getId(), inserter);
bu.execute();
}
@@ -159,6 +147,16 @@ public class PutMessage
return Response.ok("ok");
}
+ private NotifyResolver.Result resolveNotify(CommitMessageInput input, ChangeResource resource)
+ throws BadRequestException, ConfigInvalidException, IOException {
+ NotifyHandling notifyHandling = input.notify;
+ if (notifyHandling == null) {
+ notifyHandling =
+ resource.getChange().isWorkInProgress() ? NotifyHandling.OWNER : NotifyHandling.ALL;
+ }
+ return notifyResolver.resolve(notifyHandling, input.notifyDetails);
+ }
+
private ObjectId createCommit(
ObjectInserter objectInserter,
RevCommit basePatchSetCommit,
@@ -177,8 +175,7 @@ public class PutMessage
}
private void ensureCanEditCommitMessage(ChangeNotes changeNotes)
- throws AuthException, PermissionBackendException, IOException, ResourceConflictException,
- OrmException {
+ throws AuthException, PermissionBackendException, IOException, ResourceConflictException {
if (!userProvider.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
@@ -188,7 +185,6 @@ public class PutMessage
try {
permissionBackend
.user(userProvider.get())
- .database(db.get())
.change(changeNotes)
.check(ChangePermission.ADD_PATCH_SET);
projectCache.checkedGet(changeNotes.getProjectName()).checkStatePermitsWrite();
diff --git a/java/com/google/gerrit/server/restapi/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
index 7f56c91a46..abfa49ba92 100644
--- a/java/com/google/gerrit/server/restapi/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -22,7 +22,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.change.ChangeResource;
@@ -38,26 +37,18 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput, Response<String>>
implements UiAction<ChangeResource> {
- private final Provider<ReviewDb> dbProvider;
private final ChangeMessagesUtil cmUtil;
private final TopicEdited topicEdited;
@Inject
- PutTopic(
- Provider<ReviewDb> dbProvider,
- ChangeMessagesUtil cmUtil,
- RetryHelper retryHelper,
- TopicEdited topicEdited) {
+ PutTopic(ChangeMessagesUtil cmUtil, RetryHelper retryHelper, TopicEdited topicEdited) {
super(retryHelper);
- this.dbProvider = dbProvider;
this.cmUtil = cmUtil;
this.topicEdited = topicEdited;
}
@@ -82,8 +73,7 @@ public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput,
Op op = new Op(sanitizedInput);
try (BatchUpdate u =
- updateFactory.create(
- dbProvider.get(), req.getChange().getProject(), req.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(req.getChange().getProject(), req.getUser(), TimeUtil.nowTs())) {
u.addOp(req.getId(), op);
u.execute();
}
@@ -102,7 +92,7 @@ public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput,
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
change = ctx.getChange();
ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
newTopicName = Strings.nullToEmpty(input.topic);
@@ -123,7 +113,7 @@ public class PutTopic extends RetryingRestModifyView<ChangeResource, TopicInput,
ChangeMessage cmsg =
ChangeMessagesUtil.newMessage(ctx, summary, ChangeMessagesUtil.TAG_SET_TOPIC);
- cmUtil.addChangeMessage(ctx.getDb(), update, cmsg);
+ cmUtil.addChangeMessage(update, cmsg);
return true;
}
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 082d27de8d..e1ed8d64fa 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.client.ListChangesOption;
+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;
@@ -31,7 +32,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
@@ -70,7 +70,7 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
@Option(name = "-O", usage = "Output option flags, in hex")
void setOptionFlagsHex(String hex) {
- options.addAll(ListChangesOption.fromBits(Integer.parseInt(hex, 16)));
+ options.addAll(ListOption.fromBits(ListChangesOption.class, Integer.parseInt(hex, 16)));
}
@Option(
@@ -82,6 +82,11 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
imp.setStart(start);
}
+ @Option(name = "--no-limit", usage = "Return all results, overriding the default limit")
+ public void setNoLimit(boolean on) {
+ imp.setNoLimit(on);
+ }
+
@Override
public void setDynamicBean(String plugin, DynamicOptions.DynamicBean dynamicBean) {
imp.setDynamicBean(plugin, dynamicBean);
@@ -109,7 +114,7 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
@Override
public List<?> apply(TopLevelResource rsrc)
- throws BadRequestException, AuthException, OrmException, PermissionBackendException {
+ throws BadRequestException, AuthException, PermissionBackendException {
List<List<ChangeInfo>> out;
try {
out = query();
@@ -122,8 +127,7 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
return out.size() == 1 ? out.get(0) : out;
}
- private List<List<ChangeInfo>> query()
- throws OrmException, QueryParseException, PermissionBackendException {
+ private List<List<ChangeInfo>> query() throws QueryParseException, PermissionBackendException {
if (imp.isDisabled()) {
throw new QueryParseException("query disabled");
}
@@ -137,7 +141,8 @@ public class QueryChanges implements RestReadView<TopLevelResource>, DynamicOpti
int cnt = queries.size();
List<QueryResult<ChangeData>> results = imp.query(qb.parse(queries));
- List<List<ChangeInfo>> res = json.create(options, this.imp).format(results);
+ List<List<ChangeInfo>> res =
+ json.create(options, this.imp.getAttributesFactory()).format(results);
for (int n = 0; n < cnt; n++) {
List<ChangeInfo> info = res.get(n);
if (results.get(n).more() && !info.isEmpty()) {
diff --git a/java/com/google/gerrit/server/restapi/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
index 3165436480..782b91aa08 100644
--- a/java/com/google/gerrit/server/restapi/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -17,6 +17,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.RebaseInput;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -28,13 +29,12 @@ 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.Change.Status;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.RebaseChangeOp;
import com.google.gerrit.server.change.RebaseUtil;
import com.google.gerrit.server.change.RebaseUtil.Base;
@@ -50,9 +50,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.lib.ObjectId;
@@ -75,7 +73,6 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
private final RebaseChangeOp.Factory rebaseFactory;
private final RebaseUtil rebaseUtil;
private final ChangeJson.Factory json;
- private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend;
private final ProjectCache projectCache;
private final PatchSetUtil patchSetUtil;
@@ -87,7 +84,6 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
RebaseChangeOp.Factory rebaseFactory,
RebaseUtil rebaseUtil,
ChangeJson.Factory json,
- Provider<ReviewDb> dbProvider,
PermissionBackend permissionBackend,
ProjectCache projectCache,
PatchSetUtil patchSetUtil) {
@@ -96,7 +92,6 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
this.rebaseFactory = rebaseFactory;
this.rebaseUtil = rebaseUtil;
this.json = json;
- this.dbProvider = dbProvider;
this.permissionBackend = permissionBackend;
this.projectCache = projectCache;
this.patchSetUtil = patchSetUtil;
@@ -105,12 +100,11 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
@Override
protected ChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, RevisionResource rsrc, RebaseInput input)
- throws OrmException, UpdateException, RestApiException, IOException,
- PermissionBackendException {
+ throws UpdateException, RestApiException, IOException, PermissionBackendException {
// Not allowed to rebase if the current patch set is locked.
patchSetUtil.checkPatchSetNotLocked(rsrc.getNotes());
- rsrc.permissions().database(dbProvider).check(ChangePermission.REBASE);
+ rsrc.permissions().check(ChangePermission.REBASE);
projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
Change change = rsrc.getChange();
@@ -119,14 +113,15 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader);
BatchUpdate bu =
- updateFactory.create(
- dbProvider.get(), change.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
- if (!change.getStatus().isOpen()) {
+ updateFactory.create(change.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
} else if (!hasOneParent(rw, rsrc.getPatchSet())) {
throw new ResourceConflictException(
"cannot rebase merge commits or commit with no ancestor");
}
+ // TODO(dborowitz): Why no notification? This seems wrong; dig up blame.
+ bu.setNotify(NotifyResolver.Result.none());
bu.setRepository(repo, rw, oi);
bu.addOp(
change.getId(),
@@ -141,7 +136,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
private ObjectId findBaseRev(
Repository repo, RevWalk rw, RevisionResource rsrc, RebaseInput input)
- throws RestApiException, OrmException, IOException, NoSuchChangeException, AuthException,
+ throws RestApiException, IOException, NoSuchChangeException, AuthException,
PermissionBackendException {
Branch.NameKey destRefKey = rsrc.getChange().getDest();
if (input == null || input.base == null) {
@@ -164,7 +159,8 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
try {
base = rebaseUtil.parseBase(rsrc, str);
if (base == null) {
- throw new ResourceConflictException("base revision is missing: " + str);
+ throw new ResourceConflictException(
+ "base revision is missing from the destination branch: " + str);
}
} catch (NoSuchChangeException e) {
throw new UnprocessableEntityException(
@@ -176,11 +172,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
throw new ResourceConflictException("cannot rebase change onto itself");
}
- permissionBackend
- .user(rsrc.getUser())
- .database(dbProvider)
- .change(base.notes())
- .check(ChangePermission.READ);
+ permissionBackend.user(rsrc.getUser()).change(base.notes()).check(ChangePermission.READ);
Change baseChange = base.notes().getChange();
if (!baseChange.getProject().equals(change.getProject())) {
@@ -189,7 +181,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
} else if (!baseChange.getDest().equals(change.getDest())) {
throw new ResourceConflictException(
"base change is targeting wrong branch: " + baseChange.getDest());
- } else if (baseChange.getStatus() == Status.ABANDONED) {
+ } else if (baseChange.isAbandoned()) {
throw new ResourceConflictException("base change is abandoned: " + baseChange.getKey());
} else if (isMergedInto(rw, rsrc.getPatchSet(), base.patchSet())) {
throw new ResourceConflictException(
@@ -221,7 +213,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
.setVisible(false);
Change change = rsrc.getChange();
- if (!(change.getStatus().isOpen() && rsrc.isCurrent())) {
+ if (!(change.isNew() && rsrc.isCurrent())) {
return description;
}
@@ -239,7 +231,7 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
if (patchSetUtil.isPatchSetLocked(rsrc.getNotes())) {
return description;
}
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
logger.atSevere().withCause(e).log(
"Failed to check if the current patch set of change %s is locked", change.getId());
return description;
@@ -251,13 +243,17 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
if (hasOneParent(rw, rsrc.getPatchSet())) {
enabled = rebaseUtil.canRebase(rsrc.getPatchSet(), change.getDest(), repo, rw);
}
- } catch (IOException e) {
+ } catch (Exception e) {
+ // Be generous here with the exceptions that we log and swallow. RebaseUtil#canRebase uses the
+ // change index and this UI action is on the critical path of rendering a change details page.
+ // If the index is broken, we log and disable the UI action, but still show the page to the
+ // user.
logger.atSevere().withCause(e).log(
"Failed to check if patch set can be rebased: %s", rsrc.getPatchSet());
return description;
}
- if (rsrc.permissions().database(dbProvider).testOrFalse(ChangePermission.REBASE)) {
+ if (rsrc.permissions().testOrFalse(ChangePermission.REBASE)) {
return description.setVisible(true).setEnabled(enabled);
}
return description;
@@ -278,9 +274,8 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
@Override
protected ChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RebaseInput input)
- throws OrmException, UpdateException, RestApiException, IOException,
- PermissionBackendException {
- PatchSet ps = psUtil.current(rebase.dbProvider.get(), rsrc.getNotes());
+ throws UpdateException, RestApiException, IOException, PermissionBackendException {
+ PatchSet ps = psUtil.current(rsrc.getNotes());
if (ps == null) {
throw new ResourceConflictException("current revision is missing");
}
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
index 6020e957ba..81294edf5c 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
@@ -27,7 +27,6 @@ import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -50,8 +49,7 @@ public class RebaseChangeEdit extends RetryingRestModifyView<ChangeResource, Inp
@Override
protected Response<?> applyImpl(BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
- throws AuthException, ResourceConflictException, IOException, OrmException,
- PermissionBackendException {
+ throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
Project.NameKey project = rsrc.getProject();
try (Repository repository = repositoryManager.openRepository(project)) {
editModifier.rebaseEdit(repository, rsrc.getNotes());
diff --git a/java/com/google/gerrit/server/restapi/change/Rebuild.java b/java/com/google/gerrit/server/restapi/change/Rebuild.java
deleted file mode 100644
index ec17598789..0000000000
--- a/java/com/google/gerrit/server/restapi/change/Rebuild.java
+++ /dev/null
@@ -1,109 +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.server.restapi.change;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.gerrit.extensions.common.Input;
-import com.google.gerrit.extensions.restapi.BinaryResult;
-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.RestModifyView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.change.ChangeResource;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
-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;
-
-@Singleton
-public class Rebuild implements RestModifyView<ChangeResource, Input> {
-
- private final Provider<ReviewDb> db;
- private final NotesMigration migration;
- private final ChangeRebuilder rebuilder;
- private final ChangeBundleReader bundleReader;
- private final CommentsUtil commentsUtil;
- private final ChangeNotes.Factory notesFactory;
-
- @Inject
- Rebuild(
- Provider<ReviewDb> db,
- NotesMigration migration,
- ChangeRebuilder rebuilder,
- ChangeBundleReader bundleReader,
- CommentsUtil commentsUtil,
- ChangeNotes.Factory notesFactory) {
- this.db = db;
- this.migration = migration;
- this.rebuilder = rebuilder;
- this.bundleReader = bundleReader;
- this.commentsUtil = commentsUtil;
- this.notesFactory = notesFactory;
- }
-
- @Override
- public BinaryResult apply(ChangeResource rsrc, Input input)
- throws ResourceNotFoundException, IOException, OrmException, ConfigInvalidException,
- ResourceConflictException {
- if (!migration.commitChangeWrites()) {
- throw new ResourceNotFoundException();
- }
- if (!migration.readChanges()) {
- // ChangeBundle#fromNotes currently doesn't work if reading isn't enabled,
- // so don't attempt a diff.
- rebuild(rsrc);
- return BinaryResult.create("Rebuilt change successfully");
- }
-
- // Not the same transaction as the rebuild, so may result in spurious diffs
- // in the case of races. This should be easy enough to detect by rerunning.
- ChangeBundle reviewDbBundle =
- bundleReader.fromReviewDb(ReviewDbUtil.unwrapDb(db.get()), rsrc.getId());
- if (reviewDbBundle == null) {
- throw new ResourceConflictException("change is missing in ReviewDb");
- }
- rebuild(rsrc);
- ChangeNotes notes = notesFactory.create(db.get(), rsrc.getChange().getProject(), rsrc.getId());
- ChangeBundle noteDbBundle = ChangeBundle.fromNotes(commentsUtil, notes);
- List<String> diffs = reviewDbBundle.differencesFrom(noteDbBundle);
- if (diffs.isEmpty()) {
- return BinaryResult.create("No differences between ReviewDb and NoteDb");
- }
- return BinaryResult.create(
- diffs.stream().collect(joining("\n", "Differences between ReviewDb and NoteDb:\n", "\n")));
- }
-
- private void rebuild(ChangeResource rsrc)
- throws ResourceNotFoundException, OrmException, IOException {
- try {
- rebuilder.rebuild(db.get(), rsrc.getId());
- } catch (NoSuchChangeException e) {
- throw new ResourceNotFoundException(IdString.fromDecoded(rsrc.getId().toString()), e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
index 40359ea9db..1421ab6774 100644
--- a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
+++ b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
@@ -28,7 +28,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -36,9 +35,7 @@ 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.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayDeque;
@@ -61,23 +58,20 @@ import org.eclipse.jgit.revwalk.RevWalk;
class RelatedChangesSorter {
private final GitRepositoryManager repoManager;
private final PermissionBackend permissionBackend;
- private final Provider<ReviewDb> dbProvider;
private final ProjectCache projectCache;
@Inject
RelatedChangesSorter(
GitRepositoryManager repoManager,
PermissionBackend permissionBackend,
- Provider<ReviewDb> dbProvider,
ProjectCache projectCache) {
this.repoManager = repoManager;
this.permissionBackend = permissionBackend;
- this.dbProvider = dbProvider;
this.projectCache = projectCache;
}
public List<PatchSetData> sort(List<ChangeData> in, PatchSet startPs)
- throws OrmException, IOException, PermissionBackendException {
+ 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);
@@ -118,8 +112,7 @@ class RelatedChangesSorter {
return result;
}
- private Map<String, PatchSetData> collectById(List<ChangeData> in)
- throws OrmException, IOException {
+ private Map<String, PatchSetData> collectById(List<ChangeData> in) throws IOException {
Project.NameKey project = in.get(0).change().getProject();
Map<String, PatchSetData> result = Maps.newHashMapWithExpectedSize(in.size() * 3);
try (Repository repo = repoManager.openRepository(project);
@@ -235,7 +228,7 @@ class RelatedChangesSorter {
}
private boolean isVisible(PatchSetData psd) throws PermissionBackendException, IOException {
- PermissionBackend.WithUser perm = permissionBackend.currentUser().database(dbProvider);
+ PermissionBackend.WithUser perm = permissionBackend.currentUser();
try {
perm.change(psd.data()).check(ChangePermission.READ);
} catch (AuthException e) {
diff --git a/java/com/google/gerrit/server/restapi/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
index d6f9e2bdc6..5f56cdb1d4 100644
--- a/java/com/google/gerrit/server/restapi/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -16,6 +16,7 @@ package com.google.gerrit.server.restapi.change;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
+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;
@@ -25,7 +26,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PatchSetUtil;
@@ -46,9 +46,7 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -58,7 +56,6 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final RestoredSender.Factory restoredSenderFactory;
- private final Provider<ReviewDb> dbProvider;
private final ChangeJson.Factory json;
private final ChangeMessagesUtil cmUtil;
private final PatchSetUtil psUtil;
@@ -68,7 +65,6 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
@Inject
Restore(
RestoredSender.Factory restoredSenderFactory,
- Provider<ReviewDb> dbProvider,
ChangeJson.Factory json,
ChangeMessagesUtil cmUtil,
PatchSetUtil psUtil,
@@ -77,7 +73,6 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
ProjectCache projectCache) {
super(retryHelper);
this.restoredSenderFactory = restoredSenderFactory;
- this.dbProvider = dbProvider;
this.json = json;
this.cmUtil = cmUtil;
this.psUtil = psUtil;
@@ -88,18 +83,16 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
@Override
protected ChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RestoreInput input)
- throws RestApiException, UpdateException, OrmException, PermissionBackendException,
- IOException {
+ throws RestApiException, UpdateException, PermissionBackendException, IOException {
// Not allowed to restore if the current patch set is locked.
psUtil.checkPatchSetNotLocked(rsrc.getNotes());
- rsrc.permissions().database(dbProvider).check(ChangePermission.RESTORE);
+ rsrc.permissions().check(ChangePermission.RESTORE);
projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite();
Op op = new Op(input);
try (BatchUpdate u =
- updateFactory.create(
- dbProvider.get(), rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getChange().getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
u.addOp(rsrc.getId(), op).execute();
}
return json.noOptions().format(op.change);
@@ -117,20 +110,20 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException, ResourceConflictException {
+ public boolean updateChange(ChangeContext ctx) throws ResourceConflictException {
change = ctx.getChange();
- if (change == null || change.getStatus() != Status.ABANDONED) {
+ if (change == null || !change.isAbandoned()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
PatchSet.Id psId = change.currentPatchSetId();
ChangeUpdate update = ctx.getUpdate(psId);
- patchSet = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ patchSet = psUtil.get(ctx.getNotes(), psId);
change.setStatus(Status.NEW);
change.setLastUpdatedOn(ctx.getWhen());
update.setStatus(change.getStatus());
message = newMessage(ctx);
- cmUtil.addChangeMessage(ctx.getDb(), update, message);
+ cmUtil.addChangeMessage(update, message);
return true;
}
@@ -145,7 +138,7 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
}
@Override
- public void postUpdate(Context ctx) throws OrmException {
+ public void postUpdate(Context ctx) {
try {
ReplyToChangeSender cm = restoredSenderFactory.create(ctx.getProject(), change.getId());
cm.setFrom(ctx.getAccountId());
@@ -168,7 +161,7 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
.setVisible(false);
Change change = rsrc.getChange();
- if (change.getStatus() != Status.ABANDONED) {
+ if (!change.isAbandoned()) {
return description;
}
@@ -186,13 +179,13 @@ public class Restore extends RetryingRestModifyView<ChangeResource, RestoreInput
if (psUtil.isPatchSetLocked(rsrc.getNotes())) {
return description;
}
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
logger.atSevere().withCause(e).log(
"Failed to check if the current patch set of change %s is locked", change.getId());
return description;
}
- boolean visible = rsrc.permissions().database(dbProvider).testOrFalse(ChangePermission.RESTORE);
+ boolean visible = rsrc.permissions().testOrFalse(ChangePermission.RESTORE);
return description.setVisible(visible);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index 1040f43fc3..dd51e7f28b 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -14,14 +14,13 @@
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 static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
import com.google.common.base.Strings;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
-import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.api.changes.RevertInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -33,7 +32,6 @@ 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.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.ChangeUtil;
@@ -41,17 +39,17 @@ 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.Sequences;
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.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.extensions.events.ChangeReverted;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.mail.send.RevertedSender;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
@@ -67,7 +65,6 @@ import com.google.gerrit.server.update.RetryingRestModifyView;
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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -93,7 +90,6 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
implements UiAction<ChangeResource> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
private final GitRepositoryManager repoManager;
private final ChangeInserter.Factory changeInserterFactory;
@@ -107,11 +103,10 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
private final ChangeReverted changeReverted;
private final ContributorAgreementsChecker contributorAgreements;
private final ProjectCache projectCache;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
@Inject
Revert(
- Provider<ReviewDb> db,
PermissionBackend permissionBackend,
GitRepositoryManager repoManager,
ChangeInserter.Factory changeInserterFactory,
@@ -126,9 +121,8 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
ChangeReverted changeReverted,
ContributorAgreementsChecker contributorAgreements,
ProjectCache projectCache,
- NotifyUtil notifyUtil) {
+ NotifyResolver notifyResolver) {
super(retryHelper);
- this.db = db;
this.permissionBackend = permissionBackend;
this.repoManager = repoManager;
this.changeInserterFactory = changeInserterFactory;
@@ -142,16 +136,16 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
this.changeReverted = changeReverted;
this.contributorAgreements = contributorAgreements;
this.projectCache = projectCache;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
}
@Override
public ChangeInfo applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RevertInput input)
- throws IOException, OrmException, RestApiException, UpdateException, NoSuchChangeException,
+ throws IOException, RestApiException, UpdateException, NoSuchChangeException,
PermissionBackendException, NoSuchProjectException, ConfigInvalidException {
Change change = rsrc.getChange();
- if (change.getStatus() != Change.Status.MERGED) {
+ if (!change.isMerged()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
@@ -165,11 +159,11 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
private Change.Id revert(
BatchUpdate.Factory updateFactory, ChangeNotes notes, CurrentUser user, RevertInput input)
- throws OrmException, IOException, RestApiException, UpdateException, ConfigInvalidException {
+ 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(db.get(), notes, patchSetId);
+ PatchSet patch = psUtil.get(notes, patchSetId);
if (patch == null) {
throw new ResourceNotFoundException(changeIdToRevert.toString());
}
@@ -215,18 +209,17 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
ObjectId id = oi.insert(revertCommitBuilder);
RevCommit revertCommit = revWalk.parseCommit(id);
- ListMultimap<RecipientType, Account.Id> accountsToNotify =
- notifyUtil.resolveAccounts(input.notifyDetails);
+ 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());
ins.setMessage("Uploaded patch set 1.");
- ins.setNotify(input.notify);
- ins.setAccountsToNotify(accountsToNotify);
- ReviewerSet reviewerSet = approvalsUtil.getReviewers(db.get(), notes);
+ ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
Set<Account.Id> reviewers = new HashSet<>();
reviewers.add(changeToRevert.getOwner());
@@ -237,10 +230,11 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
ins.setReviewersAndCcs(reviewers, ccs);
ins.setRevertOf(changeIdToRevert);
- try (BatchUpdate bu = updateFactory.create(db.get(), project, user, now)) {
+ try (BatchUpdate bu = updateFactory.create(project, user, now)) {
bu.setRepository(git, revWalk, oi);
+ bu.setNotify(notify);
bu.insertChange(ins);
- bu.addOp(changeId, new NotifyOp(changeToRevert, ins, input.notify, accountsToNotify));
+ bu.addOp(changeId, new NotifyOp(changeToRevert, ins));
bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(generatedChangeId));
bu.execute();
}
@@ -265,7 +259,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
.setTitle("Revert the change")
.setVisible(
and(
- change.getStatus() == Change.Status.MERGED && projectStatePermitsWrite,
+ change.isMerged() && projectStatePermitsWrite,
permissionBackend
.user(rsrc.getUser())
.ref(change.getDest())
@@ -275,18 +269,10 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
private class NotifyOp implements BatchUpdateOp {
private final Change change;
private final ChangeInserter ins;
- private final NotifyHandling notifyHandling;
- private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
- NotifyOp(
- Change change,
- ChangeInserter ins,
- NotifyHandling notifyHandling,
- ListMultimap<RecipientType, Account.Id> accountsToNotify) {
+ NotifyOp(Change change, ChangeInserter ins) {
this.change = change;
this.ins = ins;
- this.notifyHandling = notifyHandling;
- this.accountsToNotify = accountsToNotify;
}
@Override
@@ -295,8 +281,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
try {
RevertedSender cm = revertedSenderFactory.create(ctx.getProject(), change.getId());
cm.setFrom(ctx.getAccountId());
- cm.setNotify(notifyHandling);
- cm.setAccountsToNotify(accountsToNotify);
+ cm.setNotify(ctx.getNotify(change.getId()));
cm.send();
} catch (Exception err) {
logger.atSevere().withCause(err).log(
@@ -313,7 +298,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
}
@Override
- public boolean updateChange(ChangeContext ctx) throws Exception {
+ public boolean updateChange(ChangeContext ctx) {
Change change = ctx.getChange();
PatchSet.Id patchSetId = change.currentPatchSetId();
ChangeMessage changeMessage =
@@ -321,7 +306,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
ctx,
"Created a revert of this change as I" + computedChangeId.name(),
ChangeMessagesUtil.TAG_REVERT);
- cmUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(patchSetId), changeMessage);
+ cmUtil.addChangeMessage(ctx.getUpdate(patchSetId), changeMessage);
return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewed.java b/java/com/google/gerrit/server/restapi/change/Reviewed.java
index accc3556ce..4594503520 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewed.java
@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.change.AccountPatchReviewStore;
import com.google.gerrit.server.change.FileResource;
import com.google.gerrit.server.plugincontext.PluginItemContext;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -36,15 +35,14 @@ public class Reviewed {
}
@Override
- public Response<String> apply(FileResource resource, Input input) throws OrmException {
+ public Response<String> apply(FileResource resource, Input input) {
boolean reviewFlagUpdated =
accountPatchReviewStore.call(
s ->
s.markReviewed(
resource.getPatchKey().getParentKey(),
resource.getAccountId(),
- resource.getPatchKey().getFileName()),
- OrmException.class);
+ resource.getPatchKey().getFileName()));
return reviewFlagUpdated ? Response.created("") : Response.ok("");
}
}
@@ -59,14 +57,13 @@ public class Reviewed {
}
@Override
- public Response<?> apply(FileResource resource, Input input) throws OrmException {
+ public Response<?> apply(FileResource resource, Input input) {
accountPatchReviewStore.run(
s ->
s.clearReviewed(
resource.getPatchKey().getParentKey(),
resource.getAccountId(),
- resource.getPatchKey().getFileName()),
- OrmException.class);
+ resource.getPatchKey().getFileName()));
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 ce1ecb8480..92f7185696 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -27,7 +27,6 @@ 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.PatchSetApproval;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.change.ReviewerSuggestion;
@@ -40,7 +39,6 @@ import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -51,7 +49,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@@ -80,7 +77,6 @@ public class ReviewerRecommender {
private final PluginMapContext<ReviewerSuggestion> reviewerSuggestionPluginMap;
private final Provider<InternalChangeQuery> queryProvider;
private final ExecutorService executor;
- private final Provider<ReviewDb> dbProvider;
private final ApprovalsUtil approvalsUtil;
@Inject
@@ -89,7 +85,6 @@ public class ReviewerRecommender {
PluginMapContext<ReviewerSuggestion> reviewerSuggestionPluginMap,
Provider<InternalChangeQuery> queryProvider,
@FanOutExecutor ExecutorService executor,
- Provider<ReviewDb> dbProvider,
ApprovalsUtil approvalsUtil,
@GerritServerConfig Config config) {
this.changeQueryBuilder = changeQueryBuilder;
@@ -97,7 +92,6 @@ public class ReviewerRecommender {
this.queryProvider = queryProvider;
this.reviewerSuggestionPluginMap = reviewerSuggestionPluginMap;
this.executor = executor;
- this.dbProvider = dbProvider;
this.approvalsUtil = approvalsUtil;
}
@@ -106,7 +100,7 @@ public class ReviewerRecommender {
SuggestReviewers suggestReviewers,
ProjectState projectState,
List<Account.Id> candidateList)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
logger.atFine().log("Candidates %s", candidateList);
String query = suggestReviewers.getQuery();
@@ -183,7 +177,7 @@ public class ReviewerRecommender {
// Remove existing reviewers
approvalsUtil
- .getReviewers(dbProvider.get(), changeNotes)
+ .getReviewers(changeNotes)
.byState(REVIEWER)
.forEach(
r -> {
@@ -194,7 +188,7 @@ public class ReviewerRecommender {
}
// Sort results
- Stream<Entry<Account.Id, MutableDouble>> sorted =
+ Stream<Map.Entry<Account.Id, MutableDouble>> sorted =
reviewerScores.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
List<Account.Id> sortedSuggestions = sorted.map(Map.Entry::getKey).collect(toList());
@@ -203,7 +197,7 @@ public class ReviewerRecommender {
}
private Map<Account.Id, MutableDouble> baseRankingForEmptyQuery(double baseWeight)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
// Get the user's last 25 changes, check approvals
try {
List<ChangeData> result =
@@ -233,7 +227,7 @@ public class ReviewerRecommender {
private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
List<Account.Id> candidates, ProjectState projectState, double baseWeight)
- throws OrmException, IOException, ConfigInvalidException {
+ 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).
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewers.java b/java/com/google/gerrit/server/restapi/change/Reviewers.java
index f0aef13923..546ca0168d 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewers.java
@@ -23,14 +23,11 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.restapi.account.AccountsCollection;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
@@ -39,7 +36,6 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
public class Reviewers implements ChildCollection<ChangeResource, ReviewerResource> {
private final DynamicMap<RestView<ReviewerResource>> views;
- private final Provider<ReviewDb> dbProvider;
private final ApprovalsUtil approvalsUtil;
private final AccountsCollection accounts;
private final ReviewerResource.Factory resourceFactory;
@@ -47,13 +43,11 @@ public class Reviewers implements ChildCollection<ChangeResource, ReviewerResour
@Inject
Reviewers(
- Provider<ReviewDb> dbProvider,
ApprovalsUtil approvalsUtil,
AccountsCollection accounts,
ReviewerResource.Factory resourceFactory,
DynamicMap<RestView<ReviewerResource>> views,
ListReviewers list) {
- this.dbProvider = dbProvider;
this.approvalsUtil = approvalsUtil;
this.accounts = accounts;
this.resourceFactory = resourceFactory;
@@ -73,8 +67,7 @@ public class Reviewers implements ChildCollection<ChangeResource, ReviewerResour
@Override
public ReviewerResource parse(ChangeResource rsrc, IdString id)
- throws OrmException, ResourceNotFoundException, AuthException, IOException,
- ConfigInvalidException {
+ throws ResourceNotFoundException, AuthException, IOException, ConfigInvalidException {
Address address = Address.tryParse(id.get());
Account.Id accountId = null;
@@ -98,7 +91,7 @@ public class Reviewers implements ChildCollection<ChangeResource, ReviewerResour
throw new ResourceNotFoundException(id);
}
- private Collection<Account.Id> fetchAccountIds(ChangeResource rsrc) throws OrmException {
- return approvalsUtil.getReviewers(dbProvider.get(), rsrc.getNotes()).all();
+ private Collection<Account.Id> fetchAccountIds(ChangeResource rsrc) {
+ return approvalsUtil.getReviewers(rsrc.getNotes()).all();
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index ff628dbe80..f5907e76e6 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -32,6 +32,7 @@ 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.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.MetricMaker;
@@ -54,8 +55,6 @@ import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.account.AccountPredicates;
import com.google.gerrit.server.query.account.AccountQueryBuilder;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -114,9 +113,9 @@ public class ReviewersUtil {
}
}
- // Generate a candidate list at 2x the size of what the user wants to see to
+ // 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 = 2;
+ private static final int CANDIDATE_LIST_MULTIPLIER = 3;
private final AccountLoader.Factory accountLoaderFactory;
private final AccountQueryBuilder accountQueryBuilder;
@@ -154,7 +153,7 @@ public class ReviewersUtil {
}
public interface VisibilityControl {
- boolean isVisibleTo(Account.Id account) throws OrmException;
+ boolean isVisibleTo(Account.Id account);
}
public List<SuggestedReviewerInfo> suggestReviewers(
@@ -163,7 +162,7 @@ public class ReviewersUtil {
ProjectState projectState,
VisibilityControl visibilityControl,
boolean excludeGroups)
- throws IOException, OrmException, ConfigInvalidException, PermissionBackendException {
+ throws IOException, ConfigInvalidException, PermissionBackendException {
CurrentUser currentUser = self.get();
if (changeNotes != null) {
logger.atFine().log(
@@ -222,7 +221,7 @@ public class ReviewersUtil {
return suggestedReviewers;
}
- private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) throws OrmException {
+ private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) {
try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
try {
// For performance reasons we don't use AccountQueryProvider as it would always load the
@@ -262,7 +261,7 @@ public class ReviewersUtil {
VisibilityControl visibilityControl,
boolean excludeGroups,
List<Account.Id> filteredRecommendations)
- throws OrmException, PermissionBackendException, IOException {
+ throws PermissionBackendException, IOException {
List<SuggestedReviewerInfo> suggestedReviewers = loadAccounts(filteredRecommendations);
int limit = suggestReviewers.getLimit();
@@ -291,7 +290,7 @@ public class ReviewersUtil {
SuggestReviewers suggestReviewers,
ProjectState projectState,
List<Account.Id> candidateList)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
try (Timer0.Context ctx = metrics.recommendAccountsLatency.start()) {
return reviewerRecommender.suggestReviewers(
changeNotes, suggestReviewers, projectState, candidateList);
@@ -327,7 +326,7 @@ public class ReviewersUtil {
ProjectState projectState,
VisibilityControl visibilityControl,
int limit)
- throws OrmException, IOException {
+ throws IOException {
try (Timer0.Context ctx = metrics.queryGroupsLatency.start()) {
List<SuggestedReviewerInfo> groups = new ArrayList<>();
for (GroupReference g : suggestAccountGroups(suggestReviewers, projectState)) {
@@ -372,7 +371,7 @@ public class ReviewersUtil {
Project project,
GroupReference group,
VisibilityControl visibilityControl)
- throws OrmException, IOException {
+ throws IOException {
GroupAsReviewer result = new GroupAsReviewer();
int maxAllowed = suggestReviewers.getMaxAllowed();
int maxAllowedWithoutConfirmation = suggestReviewers.getMaxAllowedWithoutConfirmation();
@@ -396,7 +395,8 @@ public class ReviewersUtil {
return result;
}
- boolean needsConfirmation = result.size > maxAllowedWithoutConfirmation;
+ boolean needsConfirmation =
+ maxAllowedWithoutConfirmation > 0 && result.size > maxAllowedWithoutConfirmation;
if (needsConfirmation) {
logger.atFine().log(
"group %s needs confirmation to be added as reviewer, it has %d members",
diff --git a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
index b9b7a4f273..a41143cdc6 100644
--- a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
@@ -24,14 +24,11 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.restapi.account.AccountsCollection;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
@@ -40,7 +37,6 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
public class RevisionReviewers implements ChildCollection<RevisionResource, ReviewerResource> {
private final DynamicMap<RestView<ReviewerResource>> views;
- private final Provider<ReviewDb> dbProvider;
private final ApprovalsUtil approvalsUtil;
private final AccountsCollection accounts;
private final ReviewerResource.Factory resourceFactory;
@@ -48,13 +44,11 @@ public class RevisionReviewers implements ChildCollection<RevisionResource, Revi
@Inject
RevisionReviewers(
- Provider<ReviewDb> dbProvider,
ApprovalsUtil approvalsUtil,
AccountsCollection accounts,
ReviewerResource.Factory resourceFactory,
DynamicMap<RestView<ReviewerResource>> views,
ListRevisionReviewers list) {
- this.dbProvider = dbProvider;
this.approvalsUtil = approvalsUtil;
this.accounts = accounts;
this.resourceFactory = resourceFactory;
@@ -74,8 +68,8 @@ public class RevisionReviewers implements ChildCollection<RevisionResource, Revi
@Override
public ReviewerResource parse(RevisionResource rsrc, IdString id)
- throws OrmException, ResourceNotFoundException, AuthException, MethodNotAllowedException,
- IOException, ConfigInvalidException {
+ throws ResourceNotFoundException, AuthException, MethodNotAllowedException, IOException,
+ ConfigInvalidException {
if (!rsrc.isCurrent()) {
throw new MethodNotAllowedException("Cannot access on non-current patch set");
}
@@ -89,8 +83,7 @@ public class RevisionReviewers implements ChildCollection<RevisionResource, Revi
throw e;
}
}
- Collection<Account.Id> reviewers =
- approvalsUtil.getReviewers(dbProvider.get(), rsrc.getNotes()).all();
+ Collection<Account.Id> reviewers = approvalsUtil.getReviewers(rsrc.getNotes()).all();
// See if the id exists as a reviewer for this change
if (reviewers.contains(accountId)) {
return resourceFactory.create(rsrc, accountId);
diff --git a/java/com/google/gerrit/server/restapi/change/Revisions.java b/java/com/google/gerrit/server/restapi/change/Revisions.java
index 557d77a7fd..c5cce4f3e5 100644
--- a/java/com/google/gerrit/server/restapi/change/Revisions.java
+++ b/java/com/google/gerrit/server/restapi/change/Revisions.java
@@ -24,7 +24,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
@@ -34,9 +33,7 @@ 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.ProjectCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
@@ -48,7 +45,6 @@ import org.eclipse.jgit.lib.ObjectId;
@Singleton
public class Revisions implements ChildCollection<ChangeResource, RevisionResource> {
private final DynamicMap<RestView<RevisionResource>> views;
- private final Provider<ReviewDb> dbProvider;
private final ChangeEditUtil editUtil;
private final PatchSetUtil psUtil;
private final PermissionBackend permissionBackend;
@@ -57,13 +53,11 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
@Inject
Revisions(
DynamicMap<RestView<RevisionResource>> views,
- Provider<ReviewDb> dbProvider,
ChangeEditUtil editUtil,
PatchSetUtil psUtil,
PermissionBackend permissionBackend,
ProjectCache projectCache) {
this.views = views;
- this.dbProvider = dbProvider;
this.editUtil = editUtil;
this.psUtil = psUtil;
this.permissionBackend = permissionBackend;
@@ -82,12 +76,11 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
@Override
public RevisionResource parse(ChangeResource change, IdString id)
- throws ResourceNotFoundException, AuthException, OrmException, IOException,
- PermissionBackendException {
+ throws ResourceNotFoundException, AuthException, IOException, PermissionBackendException {
if (id.get().equals("current")) {
- PatchSet ps = psUtil.current(dbProvider.get(), change.getNotes());
+ PatchSet ps = psUtil.current(change.getNotes());
if (ps != null && visible(change)) {
- return RevisionResource.createNonCachable(change, ps);
+ return RevisionResource.createNonCacheable(change, ps);
}
throw new ResourceNotFoundException(id);
}
@@ -114,7 +107,6 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
permissionBackend
.user(change.getUser())
.change(change.getNotes())
- .database(dbProvider)
.check(ChangePermission.READ);
return projectCache.checkedGet(change.getProject()).statePermitsRead();
} catch (AuthException e) {
@@ -123,7 +115,7 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
}
private List<RevisionResource> find(ChangeResource change, String id)
- throws OrmException, IOException, AuthException {
+ throws IOException, AuthException {
if (id.equals("0") || id.equals("edit")) {
return loadEdit(change, null);
} else if (id.length() < 6 && id.matches("^[1-9][0-9]{0,4}$")) {
@@ -135,7 +127,7 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
return Collections.emptyList();
} else {
List<RevisionResource> out = new ArrayList<>();
- for (PatchSet ps : psUtil.byChange(dbProvider.get(), change.getNotes())) {
+ for (PatchSet ps : psUtil.byChange(change.getNotes())) {
if (ps.getRevision() != null && ps.getRevision().get().startsWith(id)) {
out.add(new RevisionResource(change, ps));
}
@@ -148,13 +140,9 @@ public class Revisions implements ChildCollection<ChangeResource, RevisionResour
}
}
- private List<RevisionResource> byLegacyPatchSetId(ChangeResource change, String id)
- throws OrmException {
+ private List<RevisionResource> byLegacyPatchSetId(ChangeResource change, String id) {
PatchSet ps =
- psUtil.get(
- dbProvider.get(),
- change.getNotes(),
- new PatchSet.Id(change.getId(), Integer.parseInt(id)));
+ psUtil.get(change.getNotes(), new PatchSet.Id(change.getId(), Integer.parseInt(id)));
if (ps != null) {
return Collections.singletonList(new RevisionResource(change, ps));
}
diff --git a/java/com/google/gerrit/server/restapi/change/RobotComments.java b/java/com/google/gerrit/server/restapi/change/RobotComments.java
index 6570ae0d97..1aa8c2ac92 100644
--- a/java/com/google/gerrit/server/restapi/change/RobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/RobotComments.java
@@ -24,7 +24,6 @@ import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.RobotCommentResource;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -56,7 +55,7 @@ public class RobotComments implements ChildCollection<RevisionResource, RobotCom
@Override
public RobotCommentResource parse(RevisionResource rev, IdString id)
- throws ResourceNotFoundException, OrmException {
+ throws ResourceNotFoundException {
String uuid = id.get();
ChangeNotes notes = rev.getNotes();
diff --git a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
index f911147406..aacf58b2d6 100644
--- a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
+++ b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
@@ -14,64 +14,49 @@
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 static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+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.reviewdb.client.Change.Status;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.WorkInProgressOp;
import com.google.gerrit.server.change.WorkInProgressOp.Input;
-import com.google.gerrit.server.permissions.GlobalPermission;
-import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
implements UiAction<ChangeResource> {
private final WorkInProgressOp.Factory opFactory;
- private final Provider<ReviewDb> db;
- private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
@Inject
- SetReadyForReview(
- RetryHelper retryHelper,
- WorkInProgressOp.Factory opFactory,
- Provider<ReviewDb> db,
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user) {
+ SetReadyForReview(RetryHelper retryHelper, WorkInProgressOp.Factory opFactory) {
super(retryHelper);
this.opFactory = opFactory;
- this.db = db;
- this.permissionBackend = permissionBackend;
- this.user = user;
}
@Override
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
- WorkInProgressOp.checkPermissions(permissionBackend, user.get(), rsrc.getChange());
+ rsrc.permissions().check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
Change change = rsrc.getChange();
- if (change.getStatus() != Status.NEW) {
+ if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
@@ -80,7 +65,8 @@ public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, In
}
try (BatchUpdate bu =
- updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.ALL)));
bu.addOp(rsrc.getChange().getId(), opFactory.create(false, input));
bu.execute();
return Response.ok("");
@@ -94,16 +80,7 @@ public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, In
.setTitle("Set Ready For Review")
.setVisible(
and(
- rsrc.getChange().getStatus() == Status.NEW && rsrc.getChange().isWorkInProgress(),
- or(
- rsrc.isUserOwner(),
- or(
- permissionBackend
- .currentUser()
- .testCond(GlobalPermission.ADMINISTRATE_SERVER),
- permissionBackend
- .currentUser()
- .project(rsrc.getProject())
- .testCond(ProjectPermission.WRITE_CONFIG)))));
+ rsrc.getChange().isNew() && rsrc.getChange().isWorkInProgress(),
+ rsrc.permissions().testCond(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 da5d8bbe9e..852813e96d 100644
--- a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
+++ b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
@@ -14,64 +14,49 @@
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 static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+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.reviewdb.client.Change.Status;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.WorkInProgressOp;
import com.google.gerrit.server.change.WorkInProgressOp.Input;
-import com.google.gerrit.server.permissions.GlobalPermission;
-import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, Input, Response<?>>
implements UiAction<ChangeResource> {
private final WorkInProgressOp.Factory opFactory;
- private final Provider<ReviewDb> db;
- private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
@Inject
- SetWorkInProgress(
- WorkInProgressOp.Factory opFactory,
- RetryHelper retryHelper,
- Provider<ReviewDb> db,
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user) {
+ SetWorkInProgress(WorkInProgressOp.Factory opFactory, RetryHelper retryHelper) {
super(retryHelper);
this.opFactory = opFactory;
- this.db = db;
- this.permissionBackend = permissionBackend;
- this.user = user;
}
@Override
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
- WorkInProgressOp.checkPermissions(permissionBackend, user.get(), rsrc.getChange());
+ rsrc.permissions().check(ChangePermission.TOGGLE_WORK_IN_PROGRESS_STATE);
Change change = rsrc.getChange();
- if (change.getStatus() != Status.NEW) {
+ if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
@@ -80,7 +65,8 @@ public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, In
}
try (BatchUpdate bu =
- updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
+ bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.NONE)));
bu.addOp(rsrc.getChange().getId(), opFactory.create(true, input));
bu.execute();
return Response.ok("");
@@ -94,16 +80,7 @@ public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, In
.setTitle("Set Work In Progress")
.setVisible(
and(
- rsrc.getChange().getStatus() == Status.NEW && !rsrc.getChange().isWorkInProgress(),
- or(
- rsrc.isUserOwner(),
- or(
- permissionBackend
- .currentUser()
- .testCond(GlobalPermission.ADMINISTRATE_SERVER),
- permissionBackend
- .currentUser()
- .project(rsrc.getProject())
- .testCond(ProjectPermission.WRITE_CONFIG)))));
+ rsrc.getChange().isNew() && !rsrc.getChange().isWorkInProgress(),
+ rsrc.permissions().testCond(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 a775c9399f..51e2dfbe3c 100644
--- a/java/com/google/gerrit/server/restapi/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -23,6 +23,7 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.ParameterizedString;
+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;
@@ -36,7 +37,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -60,8 +60,6 @@ import com.google.gerrit.server.submit.ChangeSet;
import com.google.gerrit.server.submit.MergeOp;
import com.google.gerrit.server.submit.MergeSuperSet;
import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -70,7 +68,6 @@ import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -107,7 +104,6 @@ public class Submit
}
}
- private final Provider<ReviewDb> dbProvider;
private final GitRepositoryManager repoManager;
private final PermissionBackend permissionBackend;
private final ChangeData.Factory changeDataFactory;
@@ -128,7 +124,6 @@ public class Submit
@Inject
Submit(
- Provider<ReviewDb> dbProvider,
GitRepositoryManager repoManager,
PermissionBackend permissionBackend,
ChangeData.Factory changeDataFactory,
@@ -140,7 +135,6 @@ public class Submit
Provider<InternalChangeQuery> queryProvider,
PatchSetUtil psUtil,
ProjectCache projectCache) {
- this.dbProvider = dbProvider;
this.repoManager = repoManager;
this.permissionBackend = permissionBackend;
this.changeDataFactory = changeDataFactory;
@@ -180,8 +174,8 @@ public class Submit
@Override
public Output apply(RevisionResource rsrc, SubmitInput input)
- throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
- PermissionBackendException, UpdateException, ConfigInvalidException {
+ throws RestApiException, RepositoryNotFoundException, IOException, PermissionBackendException,
+ UpdateException, ConfigInvalidException {
input.onBehalfOf = Strings.emptyToNull(input.onBehalfOf);
IdentifiedUser submitter;
if (input.onBehalfOf != null) {
@@ -196,10 +190,10 @@ public class Submit
}
public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
- throws OrmException, RestApiException, IOException, UpdateException, ConfigInvalidException,
+ throws RestApiException, IOException, UpdateException, ConfigInvalidException,
PermissionBackendException {
Change change = rsrc.getChange();
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
} else if (!ProjectUtil.branchExists(repoManager, change.getDest())) {
throw new ResourceConflictException(
@@ -212,26 +206,24 @@ public class Submit
}
try (MergeOp op = mergeOpProvider.get()) {
- ReviewDb db = dbProvider.get();
- op.merge(db, change, submitter, true, input, false);
- try {
- change =
- changeNotesFactory.createChecked(db, change.getProject(), change.getId()).getChange();
- } catch (NoSuchChangeException e) {
- throw new ResourceConflictException("change is deleted", e);
- }
+ op.merge(change, submitter, true, input, false);
+ }
+
+ // 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);
}
- switch (change.getStatus()) {
- case MERGED:
- return change;
- case NEW:
- throw new RestApiException(
- "change unexpectedly had status " + change.getStatus() + " after submit attempt");
- case ABANDONED:
- default:
- throw new ResourceConflictException("change is " + ChangeUtil.status(change));
+ if (change.isMerged()) {
+ return change;
+ }
+ if (change.isNew()) {
+ throw new RestApiException("change unexpectedly had status NEW after submit attempt");
}
+ throw new ResourceConflictException("change is " + ChangeUtil.status(change));
}
/**
@@ -257,7 +249,6 @@ public class Submit
Set<ChangePermission> can =
permissionBackend
.user(user)
- .database(dbProvider)
.change(c)
.test(EnumSet.of(ChangePermission.READ, ChangePermission.SUBMIT));
if (!can.contains(ChangePermission.READ)) {
@@ -292,9 +283,9 @@ public class Submit
return "Problems with change(s): "
+ unmergeable.stream().map(c -> c.getId().toString()).collect(joining(", "));
}
- } catch (PermissionBackendException | OrmException | IOException e) {
+ } catch (PermissionBackendException | IOException e) {
logger.atSevere().withCause(e).log("Error checking if change is submittable");
- throw new OrmRuntimeException("Could not determine problems for the change", e);
+ throw new StorageException("Could not determine problems for the change", e);
}
return null;
}
@@ -302,7 +293,7 @@ public class Submit
@Override
public UiAction.Description getDescription(RevisionResource resource) {
Change change = resource.getChange();
- if (!change.getStatus().isOpen() || !resource.isCurrent()) {
+ if (!change.isNew() || !resource.isCurrent()) {
return null; // submit not visible
}
@@ -312,51 +303,39 @@ public class Submit
}
} catch (IOException e) {
logger.atSevere().withCause(e).log("Error checking if change is submittable");
- throw new OrmRuntimeException("Could not determine problems for the change", e);
+ throw new StorageException("Could not determine problems for the change", e);
}
- ReviewDb db = dbProvider.get();
- ChangeData cd = changeDataFactory.create(db, resource.getNotes());
+ ChangeData cd = changeDataFactory.create(resource.getNotes());
try {
MergeOp.checkSubmitRule(cd, false);
} catch (ResourceConflictException e) {
return null; // submit not visible
- } catch (OrmException e) {
- logger.atSevere().withCause(e).log("Error checking if change is submittable");
- throw new OrmRuntimeException("Could not determine problems for the change", e);
}
ChangeSet cs;
try {
- cs = mergeSuperSet.get().completeChangeSet(db, cd.change(), resource.getUser());
- } catch (OrmException | IOException | PermissionBackendException e) {
- throw new OrmRuntimeException(
- "Could not determine complete set of changes to be submitted", e);
+ cs = mergeSuperSet.get().completeChangeSet(cd.change(), resource.getUser());
+ } catch (IOException | PermissionBackendException e) {
+ throw new StorageException("Could not determine complete set of changes to be submitted", e);
}
String topic = change.getTopic();
int topicSize = 0;
if (!Strings.isNullOrEmpty(topic)) {
- topicSize = getChangesByTopic(topic).size();
+ topicSize = queryProvider.get().noFields().byTopicOpen(topic).size();
}
boolean treatWithTopic = submitWholeTopic && !Strings.isNullOrEmpty(topic) && topicSize > 1;
String submitProblems = problemsForSubmittingChangeset(cd, cs, resource.getUser());
- Boolean enabled;
- try {
- // Recheck mergeability rather than using value stored in the index,
- // which may be stale.
- // TODO(dborowitz): This is ugly; consider providing a way to not read
- // stored fields from the index in the first place.
- // cd.setMergeable(null);
- // That was done in unmergeableChanges which was called by
- // problemsForSubmittingChangeset, so now it is safe to read from
- // the cache, as it yields the same result.
- enabled = cd.isMergeable();
- } catch (OrmException e) {
- throw new OrmRuntimeException("Could not determine mergeability", e);
- }
+ // Recheck mergeability rather than using value stored in the index, which may be stale.
+ // TODO(dborowitz): This is ugly; consider providing a way to not read stored fields from the
+ // index in the first place.
+ // cd.setMergeable(null);
+ // That was done in unmergeableChanges which was called by problemsForSubmittingChangeset, so
+ // now it is safe to read from the cache, as it yields the same result.
+ Boolean enabled = cd.isMergeable();
if (submitProblems != null) {
return new UiAction.Description()
@@ -392,7 +371,7 @@ public class Submit
.setEnabled(Boolean.TRUE.equals(enabled));
}
- public Collection<ChangeData> unmergeableChanges(ChangeSet cs) throws OrmException, IOException {
+ public Collection<ChangeData> unmergeableChanges(ChangeSet cs) throws IOException {
Set<ChangeData> mergeabilityMap = new HashSet<>();
for (ChangeData change : cs.changes()) {
mergeabilityMap.add(change);
@@ -441,15 +420,14 @@ public class Submit
}
private HashMap<Change.Id, RevCommit> findCommits(
- Collection<ChangeData> changes, Project.NameKey project) throws IOException, OrmException {
+ Collection<ChangeData> changes, Project.NameKey project) throws IOException {
HashMap<Change.Id, RevCommit> commits = new HashMap<>();
try (Repository repo = repoManager.openRepository(project);
RevWalk walk = new RevWalk(repo)) {
for (ChangeData change : changes) {
RevCommit commit =
walk.parseCommit(
- ObjectId.fromString(
- psUtil.current(dbProvider.get(), change.notes()).getRevision().get()));
+ ObjectId.fromString(psUtil.current(change.notes()).getRevision().get()));
commits.put(change.getId(), commit);
}
}
@@ -457,20 +435,17 @@ public class Submit
}
private IdentifiedUser onBehalfOf(RevisionResource rsrc, SubmitInput in)
- throws AuthException, UnprocessableEntityException, OrmException, PermissionBackendException,
- IOException, ConfigInvalidException {
- PermissionBackend.ForChange perm = rsrc.permissions().database(dbProvider);
+ throws AuthException, UnprocessableEntityException, PermissionBackendException, IOException,
+ ConfigInvalidException {
+ PermissionBackend.ForChange perm = rsrc.permissions();
perm.check(ChangePermission.SUBMIT);
perm.check(ChangePermission.SUBMIT_AS);
CurrentUser caller = rsrc.getUser();
- IdentifiedUser submitter = accountResolver.parseOnBehalfOf(caller, in.onBehalfOf);
+ IdentifiedUser submitter =
+ accountResolver.resolve(in.onBehalfOf).asUniqueUserOnBehalfOf(caller);
try {
- permissionBackend
- .user(submitter)
- .database(dbProvider)
- .change(rsrc.getNotes())
- .check(ChangePermission.READ);
+ permissionBackend.user(submitter).change(rsrc.getNotes()).check(ChangePermission.READ);
} catch (AuthException e) {
throw new UnprocessableEntityException(
String.format("on_behalf_of account %s cannot see change", submitter.getAccountId()), e);
@@ -478,27 +453,13 @@ public class Submit
return submitter;
}
- private List<ChangeData> getChangesByTopic(String topic) {
- try {
- return queryProvider.get().byTopicOpen(topic);
- } catch (OrmException e) {
- throw new OrmRuntimeException(e);
- }
- }
-
public static class CurrentRevision implements RestModifyView<ChangeResource, SubmitInput> {
- private final Provider<ReviewDb> dbProvider;
private final Submit submit;
private final ChangeJson.Factory json;
private final PatchSetUtil psUtil;
@Inject
- CurrentRevision(
- Provider<ReviewDb> dbProvider,
- Submit submit,
- ChangeJson.Factory json,
- PatchSetUtil psUtil) {
- this.dbProvider = dbProvider;
+ CurrentRevision(Submit submit, ChangeJson.Factory json, PatchSetUtil psUtil) {
this.submit = submit;
this.json = json;
this.psUtil = psUtil;
@@ -506,9 +467,9 @@ public class Submit
@Override
public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
- throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
+ throws RestApiException, RepositoryNotFoundException, IOException,
PermissionBackendException, UpdateException, ConfigInvalidException {
- PatchSet ps = psUtil.current(dbProvider.get(), rsrc.getNotes());
+ PatchSet ps = psUtil.current(rsrc.getNotes());
if (ps == null) {
throw new ResourceConflictException("current revision is missing");
}
diff --git a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
index 25c4406e51..cefbfdb748 100644
--- a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
+++ b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
@@ -19,16 +19,15 @@ import static java.util.Collections.reverseOrder;
import static java.util.stream.Collectors.toList;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
import com.google.gerrit.extensions.api.changes.SubmittedTogetherOption;
-import com.google.gerrit.extensions.client.ChangeStatus;
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.RestReadView;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.WalkSorter;
@@ -38,7 +37,6 @@ import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.submit.ChangeSet;
import com.google.gerrit.server.submit.MergeSuperSet;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -63,7 +61,6 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
Comparator.comparing(ChangeData::project).thenComparing(cd -> cd.getId().id, reverseOrder());
private final ChangeJson.Factory json;
- private final Provider<ReviewDb> dbProvider;
private final Provider<InternalChangeQuery> queryProvider;
private final Provider<MergeSuperSet> mergeSuperSet;
private final Provider<WalkSorter> sorter;
@@ -90,12 +87,10 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
@Inject
SubmittedTogether(
ChangeJson.Factory json,
- Provider<ReviewDb> dbProvider,
Provider<InternalChangeQuery> queryProvider,
Provider<MergeSuperSet> mergeSuperSet,
Provider<WalkSorter> sorter) {
this.json = json;
- this.dbProvider = dbProvider;
this.queryProvider = queryProvider;
this.mergeSuperSet = mergeSuperSet;
this.sorter = sorter;
@@ -114,7 +109,7 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
@Override
public Object apply(ChangeResource resource)
throws AuthException, BadRequestException, ResourceConflictException, IOException,
- OrmException, PermissionBackendException {
+ PermissionBackendException {
SubmittedTogetherInfo info = applyInfo(resource);
if (options.isEmpty()) {
return info.changes;
@@ -123,18 +118,17 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
}
public SubmittedTogetherInfo applyInfo(ChangeResource resource)
- throws AuthException, IOException, OrmException, PermissionBackendException {
+ throws AuthException, IOException, PermissionBackendException {
Change c = resource.getChange();
try {
List<ChangeData> cds;
int hidden;
- if (c.getStatus().isOpen()) {
- ChangeSet cs =
- mergeSuperSet.get().completeChangeSet(dbProvider.get(), c, resource.getUser());
+ if (c.isNew()) {
+ ChangeSet cs = mergeSuperSet.get().completeChangeSet(c, resource.getUser());
cds = ensureRequiredDataIsLoaded(cs.changes().asList());
hidden = cs.nonVisibleChanges().size();
- } else if (c.getStatus().asChangeStatus() == ChangeStatus.MERGED) {
+ } else if (c.isMerged()) {
cds = queryProvider.get().bySubmissionId(c.getSubmissionId());
hidden = 0;
} else {
@@ -151,13 +145,13 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
info.changes = json.create(jsonOpt).format(cds);
info.nonVisibleChanges = hidden;
return info;
- } catch (OrmException | IOException e) {
+ } catch (StorageException | IOException e) {
logger.atSevere().withCause(e).log("Error on getting a ChangeSet");
throw e;
}
}
- private List<ChangeData> sort(List<ChangeData> cds, int hidden) throws OrmException, IOException {
+ private List<ChangeData> sort(List<ChangeData> cds, int hidden) throws IOException {
if (cds.size() <= 1 && hidden == 0) {
// Skip sorting for singleton lists, to avoid WalkSorter opening the
// repo just to fill out the commit field in PatchSetData.
@@ -182,8 +176,7 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
return sorted;
}
- private static List<ChangeData> ensureRequiredDataIsLoaded(List<ChangeData> cds)
- throws OrmException {
+ private static List<ChangeData> ensureRequiredDataIsLoaded(List<ChangeData> cds) {
// TODO(hiesel): Instead of calling these manually, either implement a helper that brings a
// database-backed change on-par with an index-backed change in terms of the populated fields in
// ChangeData or check if any of the ChangeDatas was loaded from the database and allow
diff --git a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
index 0ce57508a4..f5a27516bc 100644
--- a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
@@ -19,8 +19,6 @@ 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.RestReadView;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -29,7 +27,6 @@ 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.restapi.change.ReviewersUtil.VisibilityControl;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -54,13 +51,12 @@ public class SuggestChangeReviewers extends SuggestReviewers
@Inject
SuggestChangeReviewers(
AccountVisibility av,
- Provider<ReviewDb> dbProvider,
PermissionBackend permissionBackend,
Provider<CurrentUser> self,
@GerritServerConfig Config cfg,
ReviewersUtil reviewersUtil,
ProjectCache projectCache) {
- super(av, dbProvider, cfg, reviewersUtil);
+ super(av, cfg, reviewersUtil);
this.permissionBackend = permissionBackend;
this.self = self;
this.projectCache = projectCache;
@@ -68,7 +64,7 @@ public class SuggestChangeReviewers extends SuggestReviewers
@Override
public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
- throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
+ throws AuthException, BadRequestException, IOException, ConfigInvalidException,
PermissionBackendException {
if (!self.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
@@ -83,17 +79,13 @@ public class SuggestChangeReviewers extends SuggestReviewers
private VisibilityControl getVisibility(ChangeResource rsrc) {
- return new VisibilityControl() {
- @Override
- public boolean isVisibleTo(Account.Id account) {
- // Use the destination reference, not the change, as private changes deny anyone who is not
- // already a reviewer.
- return permissionBackend
- .absentUser(account)
- .database(dbProvider)
- .ref(rsrc.getChange().getDest())
- .testOrFalse(RefPermission.READ);
- }
+ return account -> {
+ // Use the destination reference, not the change, as private changes deny anyone who is not
+ // already a reviewer.
+ return permissionBackend
+ .absentUser(account)
+ .ref(rsrc.getChange().getDest())
+ .testOrFalse(RefPermission.READ);
};
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/SuggestReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestReviewers.java
index 6e94218d6e..e071c894fe 100644
--- a/java/com/google/gerrit/server/restapi/change/SuggestReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestReviewers.java
@@ -18,13 +18,11 @@ import static com.google.gerrit.server.config.GerritConfigListenerHelper.acceptI
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.common.AccountVisibility;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ReviewerAdder;
import com.google.gerrit.server.config.ConfigKey;
import com.google.gerrit.server.config.GerritConfigListener;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import org.eclipse.jgit.lib.Config;
import org.kohsuke.args4j.Option;
@@ -33,7 +31,6 @@ public class SuggestReviewers {
private static final int DEFAULT_MAX_SUGGESTED = 10;
- protected final Provider<ReviewDb> dbProvider;
protected final ReviewersUtil reviewersUtil;
private final boolean suggestAccounts;
@@ -83,11 +80,7 @@ public class SuggestReviewers {
@Inject
public SuggestReviewers(
- AccountVisibility av,
- Provider<ReviewDb> dbProvider,
- @GerritServerConfig Config cfg,
- ReviewersUtil reviewersUtil) {
- this.dbProvider = dbProvider;
+ AccountVisibility av, @GerritServerConfig Config cfg, ReviewersUtil reviewersUtil) {
this.reviewersUtil = reviewersUtil;
this.maxSuggestedReviewers =
cfg.getInt("suggest", "maxSuggestedReviewers", DEFAULT_MAX_SUGGESTED);
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
index 1875981c15..370bdb25e7 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
@@ -26,7 +26,6 @@ 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.RestModifyView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -37,9 +36,7 @@ import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.DefaultSubmitRule;
import com.google.gerrit.server.rules.PrologRule;
import com.google.gerrit.server.rules.RulesCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.util.LinkedHashMap;
import java.util.List;
import org.kohsuke.args4j.Option;
@@ -47,7 +44,6 @@ import org.kohsuke.args4j.Option;
public class TestSubmitRule implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final RulesCache rules;
private final AccountLoader.Factory accountInfoFactory;
@@ -60,14 +56,12 @@ public class TestSubmitRule implements RestModifyView<RevisionResource, TestSubm
@Inject
TestSubmitRule(
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
RulesCache rules,
AccountLoader.Factory infoFactory,
ProjectCache projectCache,
DefaultSubmitRule defaultSubmitRule,
PrologRule prologRule) {
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.rules = rules;
this.accountInfoFactory = infoFactory;
@@ -78,7 +72,7 @@ public class TestSubmitRule implements RestModifyView<RevisionResource, TestSubm
@Override
public List<TestSubmitRuleInfo> apply(RevisionResource rsrc, TestSubmitRuleInput input)
- throws AuthException, OrmException, PermissionBackendException, BadRequestException {
+ throws AuthException, PermissionBackendException, BadRequestException {
if (input == null) {
input = new TestSubmitRuleInput();
}
@@ -98,7 +92,7 @@ public class TestSubmitRule implements RestModifyView<RevisionResource, TestSubm
if (projectState == null) {
throw new BadRequestException("project not found");
}
- ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
+ ChangeData cd = changeDataFactory.create(rsrc.getNotes());
List<SubmitRecord> records;
if (projectState.hasPrologRules() || input.rule != null) {
records = ImmutableList.copyOf(prologRule.evaluate(cd, opts));
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
index e67a319bac..4c7fad2011 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
@@ -24,21 +24,17 @@ import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
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.RulesCache;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import org.kohsuke.args4j.Option;
public class TestSubmitType implements RestModifyView<RevisionResource, TestSubmitRuleInput> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final Provider<ReviewDb> db;
private final ChangeData.Factory changeDataFactory;
private final RulesCache rules;
private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
@@ -48,11 +44,9 @@ public class TestSubmitType implements RestModifyView<RevisionResource, TestSubm
@Inject
TestSubmitType(
- Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
RulesCache rules,
SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
- this.db = db;
this.changeDataFactory = changeDataFactory;
this.rules = rules;
this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
@@ -60,7 +54,7 @@ public class TestSubmitType implements RestModifyView<RevisionResource, TestSubm
@Override
public SubmitType apply(RevisionResource rsrc, TestSubmitRuleInput input)
- throws AuthException, BadRequestException, OrmException {
+ throws AuthException, BadRequestException {
if (input == null) {
input = new TestSubmitRuleInput();
}
@@ -77,7 +71,7 @@ public class TestSubmitType implements RestModifyView<RevisionResource, TestSubm
.build();
SubmitRuleEvaluator evaluator = submitRuleEvaluatorFactory.create(opts);
- ChangeData cd = changeDataFactory.create(db.get(), rsrc.getNotes());
+ ChangeData cd = changeDataFactory.create(rsrc.getNotes());
SubmitTypeRecord rec = evaluator.getSubmitType(cd);
if (rec.status != SubmitTypeRecord.Status.OK) {
@@ -96,8 +90,7 @@ public class TestSubmitType implements RestModifyView<RevisionResource, TestSubm
}
@Override
- public SubmitType apply(RevisionResource resource)
- throws AuthException, BadRequestException, OrmException {
+ public SubmitType apply(RevisionResource resource) throws AuthException, BadRequestException {
return test.apply(resource, null);
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Unignore.java b/java/com/google/gerrit/server/restapi/change/Unignore.java
index 6f2144ad1a..26d3233e1a 100644
--- a/java/com/google/gerrit/server/restapi/change/Unignore.java
+++ b/java/com/google/gerrit/server/restapi/change/Unignore.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -22,7 +23,6 @@ import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.change.ChangeResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -46,8 +46,7 @@ public class Unignore implements RestModifyView<ChangeResource, Input>, UiAction
}
@Override
- public Response<String> apply(ChangeResource rsrc, Input input)
- throws OrmException, IllegalLabelException {
+ public Response<String> apply(ChangeResource rsrc, Input input) throws IllegalLabelException {
if (isIgnored(rsrc)) {
stars.unignore(rsrc);
}
@@ -57,7 +56,7 @@ public class Unignore implements RestModifyView<ChangeResource, Input>, UiAction
private boolean isIgnored(ChangeResource rsrc) {
try {
return stars.isIgnored(rsrc);
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("failed to check ignored star");
}
return false;
diff --git a/java/com/google/gerrit/server/restapi/change/Votes.java b/java/com/google/gerrit/server/restapi/change/Votes.java
index 3b2548c87f..31efe545fd 100644
--- a/java/com/google/gerrit/server/restapi/change/Votes.java
+++ b/java/com/google/gerrit/server/restapi/change/Votes.java
@@ -23,13 +23,10 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.change.VoteResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Map;
import java.util.TreeMap;
@@ -57,7 +54,7 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
@Override
public VoteResource parse(ReviewerResource reviewer, IdString id)
- throws ResourceNotFoundException, OrmException, AuthException, MethodNotAllowedException {
+ throws ResourceNotFoundException, AuthException, MethodNotAllowedException {
if (reviewer.getRevisionResource() != null && !reviewer.getRevisionResource().isCurrent()) {
throw new MethodNotAllowedException("Cannot access on non-current patch set");
}
@@ -66,18 +63,15 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
@Singleton
public static class List implements RestReadView<ReviewerResource> {
- private final Provider<ReviewDb> db;
private final ApprovalsUtil approvalsUtil;
@Inject
- List(Provider<ReviewDb> db, ApprovalsUtil approvalsUtil) {
- this.db = db;
+ List(ApprovalsUtil approvalsUtil) {
this.approvalsUtil = approvalsUtil;
}
@Override
- public Map<String, Short> apply(ReviewerResource rsrc)
- throws OrmException, MethodNotAllowedException {
+ public 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,7 +79,6 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
Map<String, Short> votes = new TreeMap<>();
Iterable<PatchSetApproval> byPatchSetUser =
approvalsUtil.byPatchSetUser(
- db.get(),
rsrc.getChangeResource().getNotes(),
rsrc.getChange().currentPatchSetId(),
rsrc.getReviewerUser().getAccountId(),
diff --git a/java/com/google/gerrit/server/restapi/config/AgreementJson.java b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
index 02e5f68b43..d5c085b98a 100644
--- a/java/com/google/gerrit/server/restapi/config/AgreementJson.java
+++ b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
@@ -17,7 +17,8 @@ package com.google.gerrit.server.restapi.config;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.AgreementInfo;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -25,7 +26,6 @@ import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.group.GroupJson;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -61,7 +61,7 @@ public class AgreementJson {
GroupControl gc = genericGroupControlFactory.controlFor(user, autoVerifyGroup.getUUID());
GroupResource group = new GroupResource(gc);
info.autoVerifyGroup = groupJson.format(group);
- } catch (NoSuchGroupException | OrmException e) {
+ } catch (NoSuchGroupException | StorageException e) {
logger.atWarning().log(
"autoverify group \"%s\" does not exist, referenced in CLA \"%s\"",
autoVerifyGroup.getName(), ca.getName());
diff --git a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
index a16736bc16..61d5c796b2 100644
--- a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
+++ b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.group.db.GroupsConsistencyChecker;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -56,8 +55,7 @@ public class CheckConsistency implements RestModifyView<ConfigResource, Consiste
@Override
public ConsistencyCheckInfo apply(ConfigResource resource, ConsistencyCheckInput input)
- throws RestApiException, IOException, OrmException, PermissionBackendException,
- ConfigInvalidException {
+ throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
if (input == null
diff --git a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
index d16a379005..b6dcd35cfb 100644
--- a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
+++ b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
@@ -26,7 +26,6 @@ import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.mail.EmailTokenVerifier;
import com.google.gerrit.server.restapi.config.ConfirmEmail.Input;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -55,8 +54,7 @@ public class ConfirmEmail implements RestModifyView<ConfigResource, Input> {
@Override
public Response<?> apply(ConfigResource rsrc, Input input)
- throws AuthException, UnprocessableEntityException, OrmException, IOException,
- ConfigInvalidException {
+ throws AuthException, UnprocessableEntityException, IOException, ConfigInvalidException {
CurrentUser user = self.get();
if (!user.isIdentifiedUser()) {
throw new AuthException("Authentication required");
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index 04f4f8a824..aa0e350481 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -20,14 +20,12 @@ import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.extensions.client.UiType;
import com.google.gerrit.extensions.common.AccountsInfo;
import com.google.gerrit.extensions.common.AuthInfo;
import com.google.gerrit.extensions.common.ChangeConfigInfo;
import com.google.gerrit.extensions.common.DownloadInfo;
import com.google.gerrit.extensions.common.DownloadSchemeInfo;
import com.google.gerrit.extensions.common.GerritInfo;
-import com.google.gerrit.extensions.common.MessageOfTheDayInfo;
import com.google.gerrit.extensions.common.PluginConfigInfo;
import com.google.gerrit.extensions.common.ReceiveInfo;
import com.google.gerrit.extensions.common.ServerInfo;
@@ -37,9 +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.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.extensions.systemstatus.MessageOfTheDay;
import com.google.gerrit.extensions.webui.WebUiPlugin;
import com.google.gerrit.server.EnableSignedPush;
import com.google.gerrit.server.account.AccountVisibilityProvider;
@@ -52,13 +48,11 @@ import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritOptions;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.plugincontext.PluginMapContext;
@@ -70,19 +64,12 @@ import com.google.inject.Inject;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.EnumSet;
import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
public class GetServerInfo implements RestReadView<ConfigResource> {
- private static final String URL_ALIAS = "urlAlias";
- private static final String KEY_MATCH = "match";
- private static final String KEY_TOKEN = "token";
-
private final Config config;
private final AccountVisibilityProvider accountVisibilityProvider;
private final AuthConfig authConfig;
@@ -98,13 +85,10 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
private final PluginItemContext<AvatarProvider> avatar;
private final boolean enableSignedPush;
private final QueryDocumentationExecutor docSearcher;
- private final NotesMigration migration;
private final ProjectCache projectCache;
private final AgreementJson agreementJson;
- private final GerritOptions gerritOptions;
private final ChangeIndexCollection indexes;
private final SitePaths sitePaths;
- private final DynamicSet<MessageOfTheDay> messages;
@Inject
public GetServerInfo(
@@ -123,13 +107,10 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
PluginItemContext<AvatarProvider> avatar,
@EnableSignedPush boolean enableSignedPush,
QueryDocumentationExecutor docSearcher,
- NotesMigration migration,
ProjectCache projectCache,
AgreementJson agreementJson,
- GerritOptions gerritOptions,
ChangeIndexCollection indexes,
- SitePaths sitePaths,
- DynamicSet<MessageOfTheDay> motd) {
+ SitePaths sitePaths) {
this.config = config;
this.accountVisibilityProvider = accountVisibilityProvider;
this.authConfig = authConfig;
@@ -145,13 +126,10 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
this.avatar = avatar;
this.enableSignedPush = enableSignedPush;
this.docSearcher = docSearcher;
- this.migration = migration;
this.projectCache = projectCache;
this.agreementJson = agreementJson;
- this.gerritOptions = gerritOptions;
this.indexes = indexes;
this.sitePaths = sitePaths;
- this.messages = motd;
}
@Override
@@ -162,16 +140,12 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
info.change = getChangeInfo();
info.download = getDownloadInfo();
info.gerrit = getGerritInfo();
- info.messages = getMessages();
- info.noteDbEnabled = toBoolean(isNoteDbEnabled());
+ info.noteDbEnabled = true;
info.plugin = getPluginInfo();
info.defaultTheme = getDefaultTheme();
info.sshd = getSshdInfo();
info.suggest = getSuggestInfo();
- Map<String, String> urlAliases = getUrlAliasesInfo();
- info.urlAliases = !urlAliases.isEmpty() ? urlAliases : null;
-
info.user = getUserInfo();
info.receive = getReceiveInfo();
return info;
@@ -311,16 +285,10 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
info.allProjects = allProjectsName.get();
info.allUsers = allUsersName.get();
info.reportBugUrl = config.getString("gerrit", null, "reportBugUrl");
- info.reportBugText = config.getString("gerrit", null, "reportBugText");
info.docUrl = getDocUrl();
info.docSearch = docSearcher.isAvailable();
info.editGpgKeys =
toBoolean(enableSignedPush && config.getBoolean("gerrit", null, "editGpgKeys", true));
- info.webUis = EnumSet.noneOf(UiType.class);
- info.webUis.add(UiType.POLYGERRIT);
- if (gerritOptions.enableGwtUi()) {
- info.webUis.add(UiType.GWT);
- }
info.primaryWeblinkName = config.getString("gerrit", null, "primaryWeblinkName");
return info;
}
@@ -333,24 +301,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
return CharMatcher.is('/').trimTrailingFrom(docUrl) + '/';
}
- private List<MessageOfTheDayInfo> getMessages() {
- return this.messages.stream()
- .filter(motd -> !Strings.isNullOrEmpty(motd.getHtmlMessage()))
- .map(
- motd -> {
- MessageOfTheDayInfo m = new MessageOfTheDayInfo();
- m.id = motd.getMessageId();
- m.redisplay = motd.getRedisplay();
- m.html = motd.getHtmlMessage();
- return m;
- })
- .collect(toList());
- }
-
- private boolean isNoteDbEnabled() {
- return migration.readChanges();
- }
-
private PluginConfigInfo getPluginInfo() {
PluginConfigInfo info = new PluginConfigInfo();
info.hasAvatars = toBoolean(avatar.hasImplementation());
@@ -386,16 +336,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
return null;
}
- private Map<String, String> getUrlAliasesInfo() {
- Map<String, String> urlAliases = new HashMap<>();
- for (String subsection : config.getSubsections(URL_ALIAS)) {
- urlAliases.put(
- config.getString(URL_ALIAS, subsection, KEY_MATCH),
- config.getString(URL_ALIAS, subsection, KEY_TOKEN));
- }
- return urlAliases;
- }
-
private SshdInfo getSshdInfo() {
String[] addr = config.getStringList("sshd", null, "listenAddress");
if (addr.length == 1 && isOff(addr[0])) {
diff --git a/java/com/google/gerrit/server/restapi/config/GetVersion.java b/java/com/google/gerrit/server/restapi/config/GetVersion.java
index 8135719848..ee206d63ec 100644
--- a/java/com/google/gerrit/server/restapi/config/GetVersion.java
+++ b/java/com/google/gerrit/server/restapi/config/GetVersion.java
@@ -15,19 +15,22 @@
package com.google.gerrit.server.restapi.config;
import com.google.gerrit.common.Version;
+import com.google.gerrit.extensions.restapi.CacheControl;
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.ConfigResource;
import com.google.inject.Singleton;
+import java.util.concurrent.TimeUnit;
@Singleton
public class GetVersion implements RestReadView<ConfigResource> {
@Override
- public String apply(ConfigResource resource) throws ResourceNotFoundException {
+ public Response<String> apply(ConfigResource resource) throws ResourceNotFoundException {
String version = Version.getVersion();
if (version == null) {
throw new ResourceNotFoundException();
}
- return version;
+ return Response.ok(version).caching(CacheControl.PRIVATE(30, TimeUnit.SECONDS));
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/IndexChanges.java b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
index b178118368..6ba93e8436 100644
--- a/java/com/google/gerrit/server/restapi/config/IndexChanges.java
+++ b/java/com/google/gerrit/server/restapi/config/IndexChanges.java
@@ -19,18 +19,14 @@ 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.RestModifyView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.restapi.config.IndexChanges.Input;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
-import java.io.IOException;
import java.util.Set;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@@ -43,38 +39,27 @@ public class IndexChanges implements RestModifyView<ConfigResource, Input> {
}
private final ChangeFinder changeFinder;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ChangeData.Factory changeDataFactory;
private final ChangeIndexer indexer;
@Inject
IndexChanges(
- ChangeFinder changeFinder,
- SchemaFactory<ReviewDb> schemaFactory,
- ChangeData.Factory changeDataFactory,
- ChangeIndexer indexer) {
+ ChangeFinder changeFinder, ChangeData.Factory changeDataFactory, ChangeIndexer indexer) {
this.changeFinder = changeFinder;
- this.schemaFactory = schemaFactory;
this.changeDataFactory = changeDataFactory;
this.indexer = indexer;
}
@Override
- public Object apply(ConfigResource resource, Input input) throws OrmException {
+ public Object apply(ConfigResource resource, Input input) {
if (input == null || input.changes == null) {
return Response.ok("Nothing to index");
}
- try (ReviewDb db = schemaFactory.open()) {
- for (String id : input.changes) {
- for (ChangeNotes n : changeFinder.find(id)) {
- try {
- indexer.index(changeDataFactory.create(db, n));
- logger.atFine().log("Indexed change %s", id);
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Failed to index change %s", id);
- }
- }
+ for (String id : input.changes) {
+ for (ChangeNotes n : changeFinder.find(id)) {
+ indexer.index(changeDataFactory.create(n));
+ logger.atFine().log("Indexed change %s", id);
}
}
diff --git a/java/com/google/gerrit/server/restapi/config/ListCapabilities.java b/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
index fa9bfde4e0..cacbbf590a 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCapabilities.java
@@ -14,38 +14,32 @@
package com.google.gerrit.server.restapi.config;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+
import com.google.common.collect.ImmutableMap;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.config.CapabilityDefinition;
-import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.config.CapabilityConstants;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PluginPermissionsUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.HashMap;
import java.util.Map;
-import java.util.regex.Pattern;
/** List capabilities visible to the calling user. */
@Singleton
public class ListCapabilities implements RestReadView<ConfigResource> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final Pattern PLUGIN_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9-]+$");
-
private final PermissionBackend permissionBackend;
- private final DynamicMap<CapabilityDefinition> pluginCapabilities;
+ private final PluginPermissionsUtil pluginPermissionsUtil;
@Inject
public ListCapabilities(
- PermissionBackend permissionBackend, DynamicMap<CapabilityDefinition> pluginCapabilities) {
+ PermissionBackend permissionBackend, PluginPermissionsUtil pluginPermissionsUtil) {
this.permissionBackend = permissionBackend;
- this.pluginCapabilities = pluginCapabilities;
+ this.pluginPermissionsUtil = pluginPermissionsUtil;
}
@Override
@@ -59,21 +53,14 @@ public class ListCapabilities implements RestReadView<ConfigResource> {
}
public Map<String, CapabilityInfo> collectPluginCapabilities() {
- Map<String, CapabilityInfo> output = new HashMap<>();
- for (String pluginName : pluginCapabilities.plugins()) {
- if (!PLUGIN_NAME_PATTERN.matcher(pluginName).matches()) {
- logger.atWarning().log(
- "Plugin name '%s' must match '%s' to use capabilities; rename the plugin",
- pluginName, PLUGIN_NAME_PATTERN.pattern());
- continue;
- }
- for (Map.Entry<String, Provider<CapabilityDefinition>> entry :
- pluginCapabilities.byPlugin(pluginName).entrySet()) {
- String id = String.format("%s-%s", pluginName, entry.getKey());
- output.put(id, new CapabilityInfo(id, entry.getValue().get().getDescription()));
- }
- }
- return output;
+ return convertToPermissionInfos(pluginPermissionsUtil.collectPluginCapabilities());
+ }
+
+ private static ImmutableMap<String, CapabilityInfo> convertToPermissionInfos(
+ ImmutableMap<String, String> permissionIdNames) {
+ return permissionIdNames.entrySet().stream()
+ .collect(
+ toImmutableMap(Map.Entry::getKey, e -> new CapabilityInfo(e.getKey(), e.getValue())));
}
private Map<String, CapabilityInfo> collectCoreCapabilities()
diff --git a/java/com/google/gerrit/server/restapi/config/ListTopMenus.java b/java/com/google/gerrit/server/restapi/config/ListTopMenus.java
index 7a85bcdbb9..01c273cc41 100644
--- a/java/com/google/gerrit/server/restapi/config/ListTopMenus.java
+++ b/java/com/google/gerrit/server/restapi/config/ListTopMenus.java
@@ -14,30 +14,30 @@
package com.google.gerrit.server.restapi.config;
-import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.webui.TopMenu;
+import com.google.gerrit.extensions.webui.TopMenu.MenuEntry;
import com.google.gerrit.server.config.ConfigResource;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
@Singleton
-class ListTopMenus implements RestReadView<ConfigResource> {
- private final DynamicSet<TopMenu> extensions;
+public class ListTopMenus implements RestReadView<ConfigResource> {
+ private final PluginSetContext<TopMenu> extensions;
@Inject
- ListTopMenus(DynamicSet<TopMenu> extensions) {
+ ListTopMenus(PluginSetContext<TopMenu> extensions) {
this.extensions = extensions;
}
@Override
- public List<TopMenu.MenuEntry> apply(ConfigResource resource) {
+ public Response<List<MenuEntry>> apply(ConfigResource resource) {
List<TopMenu.MenuEntry> entries = new ArrayList<>();
- for (TopMenu extension : extensions) {
- entries.addAll(extension.getEntries());
- }
- return entries;
+ extensions.runEach(extension -> entries.addAll(extension.getEntries()));
+ return Response.ok(entries).caching(ConfigResource.DEFAULT_CACHE_CONTROL);
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index 52835180b2..b2b14a1a5b 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -18,7 +18,7 @@ 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.common.errors.NoSuchGroupException;
+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;
@@ -37,6 +37,7 @@ import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.GroupControl;
@@ -48,7 +49,6 @@ import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.group.AddMembers.Input;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -112,9 +112,8 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
@Override
public List<AccountInfo> apply(GroupResource resource, Input input)
- throws AuthException, NotInternalGroupException, UnprocessableEntityException, OrmException,
- IOException, ConfigInvalidException, ResourceNotFoundException,
- PermissionBackendException {
+ throws AuthException, NotInternalGroupException, UnprocessableEntityException, IOException,
+ ConfigInvalidException, ResourceNotFoundException, PermissionBackendException {
GroupDescription.Internal internalGroup =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
input = Input.init(input);
@@ -144,19 +143,18 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
}
Account findAccount(String nameOrEmailOrId)
- throws AuthException, UnprocessableEntityException, OrmException, IOException,
- ConfigInvalidException {
+ throws UnprocessableEntityException, IOException, ConfigInvalidException {
+ AccountResolver.Result result = accountResolver.resolve(nameOrEmailOrId);
try {
- return accountResolver.parse(nameOrEmailOrId).getAccount();
- } catch (UnprocessableEntityException e) {
- // might be because the account does not exist or because the account is
- // not visible
+ return result.asUnique().getAccount();
+ } catch (UnresolvableAccountException e) {
switch (authType) {
case HTTP_LDAP:
case CLIENT_SSL_CERT_LDAP:
case LDAP:
- if (accountResolver.find(nameOrEmailOrId) == null) {
- // account does not exist, try to create it
+ if (!e.isSelf() && result.asList().isEmpty()) {
+ // Account does not exist, try to create it. This may leak account existence, since we
+ // can't distinguish between a nonexistent account and one that the caller can't see.
Optional<Account> a = createAccountByLdap(nameOrEmailOrId);
if (a.isPresent()) {
return a.get();
@@ -177,7 +175,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
}
public void addMembers(AccountGroup.UUID groupUuid, Set<Account.Id> newMemberIds)
- throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+ throws IOException, NoSuchGroupException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.union(memberIds, newMemberIds))
@@ -224,8 +222,8 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
@Override
public AccountInfo apply(GroupResource resource, IdString id, Input input)
- throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
- IOException, ConfigInvalidException, PermissionBackendException {
+ throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
+ ConfigInvalidException, PermissionBackendException {
AddMembers.Input in = new AddMembers.Input();
in._oneMember = id.get();
try {
@@ -251,7 +249,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
@Override
public AccountInfo apply(MemberResource resource, Input input)
- throws OrmException, PermissionBackendException {
+ 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 02c4a290c2..5c879e721d 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -19,7 +19,7 @@ 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.common.errors.NoSuchGroupException;
+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;
@@ -39,7 +39,6 @@ import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.group.AddSubgroups.Input;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -93,7 +92,7 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
@Override
public List<GroupInfo> apply(GroupResource resource, Input input)
- throws NotInternalGroupException, AuthException, UnprocessableEntityException, OrmException,
+ throws NotInternalGroupException, AuthException, UnprocessableEntityException,
ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
GroupDescription.Internal group =
@@ -124,7 +123,7 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
private void addSubgroups(
AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> newSubgroupUuids)
- throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+ throws NoSuchGroupException, IOException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setSubgroupModification(subgroupUuids -> Sets.union(subgroupUuids, newSubgroupUuids))
@@ -144,8 +143,8 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
@Override
public GroupInfo apply(GroupResource resource, IdString id, Input input)
- throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
- IOException, ConfigInvalidException, PermissionBackendException {
+ throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
+ ConfigInvalidException, PermissionBackendException {
AddSubgroups.Input in = new AddSubgroups.Input();
in.groups = ImmutableList.of(id.get());
try {
@@ -171,7 +170,7 @@ public class AddSubgroups implements RestModifyView<GroupResource, Input> {
@Override
public GroupInfo apply(SubgroupResource resource, Input input)
- throws OrmException, PermissionBackendException {
+ 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 4a2375b6bc..eaedcd7a18 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -19,6 +19,7 @@ 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.exceptions.DuplicateKeyException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.client.ListGroupsOption;
@@ -36,7 +37,6 @@ 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.Sequences;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.CreateGroupArgs;
import com.google.gerrit.server.account.GroupCache;
@@ -50,12 +50,11 @@ import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.gerrit.server.validators.GroupCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -125,8 +124,8 @@ public class CreateGroup
@Override
public GroupInfo apply(TopLevelResource resource, IdString id, GroupInput input)
throws AuthException, BadRequestException, UnprocessableEntityException,
- ResourceConflictException, OrmException, IOException, ConfigInvalidException,
- ResourceNotFoundException, PermissionBackendException {
+ ResourceConflictException, IOException, ConfigInvalidException, ResourceNotFoundException,
+ PermissionBackendException {
String name = id.get();
if (input == null) {
input = new GroupInput();
@@ -156,7 +155,7 @@ public class CreateGroup
args.initialMembers =
ownerUuid == null
? Collections.singleton(self.get().getAccountId())
- : Collections.<Account.Id>emptySet();
+ : Collections.emptySet();
}
try {
@@ -178,7 +177,7 @@ public class CreateGroup
}
private InternalGroup createGroup(CreateGroupArgs createGroupArgs)
- throws OrmException, ResourceConflictException, IOException, ConfigInvalidException {
+ throws ResourceConflictException, IOException, ConfigInvalidException {
String nameLower = createGroupArgs.getGroupName().toLowerCase(Locale.US);
@@ -218,7 +217,7 @@ public class CreateGroup
members -> ImmutableSet.copyOf(createGroupArgs.initialMembers));
try {
return groupsUpdateProvider.get().createGroup(groupCreation, groupUpdateBuilder.build());
- } catch (OrmDuplicateKeyException e) {
+ } catch (DuplicateKeyException e) {
throw new ResourceConflictException(
"group '" + createGroupArgs.getGroupName() + "' already exists", e);
}
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index 82dc7d9ac7..b5771a34c3 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -33,7 +33,6 @@ import com.google.gerrit.server.group.MemberResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.restapi.group.AddMembers.Input;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -56,8 +55,8 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
@Override
public Response<?> apply(GroupResource resource, Input input)
- throws AuthException, NotInternalGroupException, UnprocessableEntityException, OrmException,
- IOException, ConfigInvalidException, ResourceNotFoundException {
+ throws AuthException, NotInternalGroupException, UnprocessableEntityException, IOException,
+ ConfigInvalidException, ResourceNotFoundException {
GroupDescription.Internal internalGroup =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
input = Input.init(input);
@@ -69,8 +68,7 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
Set<Account.Id> membersToRemove = new HashSet<>();
for (String nameOrEmail : input.members) {
- Account a = accountResolver.parse(nameOrEmail).getAccount();
- membersToRemove.add(a.getId());
+ membersToRemove.add(accountResolver.resolve(nameOrEmail).asUnique().getAccount().getId());
}
AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
try {
@@ -83,7 +81,7 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
}
private void removeGroupMembers(AccountGroup.UUID groupUuid, Set<Account.Id> accountIds)
- throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+ throws IOException, NoSuchGroupException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.difference(memberIds, accountIds))
@@ -103,8 +101,8 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
@Override
public Response<?> apply(MemberResource resource, Input input)
- throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
- IOException, ConfigInvalidException, ResourceNotFoundException {
+ throws AuthException, MethodNotAllowedException, UnprocessableEntityException, IOException,
+ ConfigInvalidException, ResourceNotFoundException {
AddMembers.Input in = new AddMembers.Input();
in._oneMember = resource.getMember().getAccountId().toString();
return delete.get().apply(resource, in);
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index 307f874a72..9ecfa1f696 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -17,7 +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.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -33,7 +33,6 @@ import com.google.gerrit.server.group.SubgroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.restapi.group.AddSubgroups.Input;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -56,7 +55,7 @@ public class DeleteSubgroups implements RestModifyView<GroupResource, Input> {
@Override
public Response<?> apply(GroupResource resource, Input input)
- throws AuthException, NotInternalGroupException, UnprocessableEntityException, OrmException,
+ throws AuthException, NotInternalGroupException, UnprocessableEntityException,
ResourceNotFoundException, IOException, ConfigInvalidException {
GroupDescription.Internal internalGroup =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
@@ -86,7 +85,7 @@ public class DeleteSubgroups implements RestModifyView<GroupResource, Input> {
private void removeSubgroups(
AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> removedSubgroupUuids)
- throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+ throws NoSuchGroupException, IOException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setSubgroupModification(
@@ -107,7 +106,7 @@ public class DeleteSubgroups implements RestModifyView<GroupResource, Input> {
@Override
public Response<?> apply(SubgroupResource resource, Input input)
- throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
+ throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
ResourceNotFoundException, IOException, ConfigInvalidException {
AddSubgroups.Input in = new AddSubgroups.Input();
in.groups = ImmutableList.of(resource.getMember().get());
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index dcdd8a8802..1a781d989d 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -36,7 +36,6 @@ import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -76,8 +75,8 @@ public class GetAuditLog implements RestReadView<GroupResource> {
@Override
public List<? extends GroupAuditEventInfo> apply(GroupResource rsrc)
- throws AuthException, NotInternalGroupException, OrmException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ throws AuthException, NotInternalGroupException, IOException, ConfigInvalidException,
+ PermissionBackendException {
GroupDescription.Internal group =
rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
if (!rsrc.getControl().isOwner()) {
diff --git a/java/com/google/gerrit/server/restapi/group/GetDetail.java b/java/com/google/gerrit/server/restapi/group/GetDetail.java
index 75d1e34c84..c7573836a2 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDetail.java
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -33,7 +32,7 @@ public class GetDetail implements RestReadView<GroupResource> {
}
@Override
- public GroupInfo apply(GroupResource rsrc) throws OrmException, PermissionBackendException {
+ public GroupInfo apply(GroupResource rsrc) throws PermissionBackendException {
return 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 c6cddb6196..3ae447bf6e 100644
--- a/java/com/google/gerrit/server/restapi/group/GetGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetGroup.java
@@ -18,7 +18,6 @@ import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -32,7 +31,7 @@ public class GetGroup implements RestReadView<GroupResource> {
}
@Override
- public GroupInfo apply(GroupResource resource) throws OrmException, PermissionBackendException {
+ public GroupInfo apply(GroupResource resource) throws PermissionBackendException {
return 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 95063de867..63a8a1bba3 100644
--- a/java/com/google/gerrit/server/restapi/group/GetMember.java
+++ b/java/com/google/gerrit/server/restapi/group/GetMember.java
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.group.MemberResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -33,7 +32,7 @@ public class GetMember implements RestReadView<MemberResource> {
}
@Override
- public AccountInfo apply(MemberResource rsrc) throws OrmException, PermissionBackendException {
+ public AccountInfo apply(MemberResource rsrc) throws PermissionBackendException {
AccountLoader loader = infoFactory.create(true);
AccountInfo info = loader.get(rsrc.getMember().getAccountId());
loader.fill();
diff --git a/java/com/google/gerrit/server/restapi/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
index 6a97d01c15..10e1b23dc1 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -15,14 +15,13 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+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.RestReadView;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -40,8 +39,7 @@ public class GetOwner implements RestReadView<GroupResource> {
@Override
public GroupInfo apply(GroupResource resource)
- throws NotInternalGroupException, ResourceNotFoundException, OrmException,
- PermissionBackendException {
+ throws NotInternalGroupException, ResourceNotFoundException, PermissionBackendException {
GroupDescription.Internal group =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
try {
diff --git a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
index 16e27396a2..446618009c 100644
--- a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
@@ -18,7 +18,6 @@ import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.group.SubgroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -32,7 +31,7 @@ public class GetSubgroup implements RestReadView<SubgroupResource> {
}
@Override
- public GroupInfo apply(SubgroupResource rsrc) throws OrmException, PermissionBackendException {
+ public GroupInfo apply(SubgroupResource rsrc) throws PermissionBackendException {
return 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 a51fad24bb..12b9d613a7 100644
--- a/java/com/google/gerrit/server/restapi/group/GroupJson.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupJson.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collection;
@@ -76,18 +75,17 @@ public class GroupJson {
return this;
}
- public GroupInfo format(GroupResource rsrc) throws OrmException, PermissionBackendException {
+ public GroupInfo format(GroupResource rsrc) throws PermissionBackendException {
return createGroupInfo(rsrc.getGroup(), rsrc::getControl);
}
- public GroupInfo format(GroupDescription.Basic group)
- throws OrmException, PermissionBackendException {
+ public GroupInfo format(GroupDescription.Basic group) throws PermissionBackendException {
return createGroupInfo(group, Suppliers.memoize(() -> groupControlFactory.controlFor(group)));
}
private GroupInfo createGroupInfo(
GroupDescription.Basic group, Supplier<GroupControl> groupControlSupplier)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
GroupInfo info = createBasicGroupInfo(group);
if (group instanceof GroupDescription.Internal) {
@@ -110,7 +108,7 @@ public class GroupJson {
GroupInfo info,
GroupDescription.Internal internalGroup,
Supplier<GroupControl> groupControlSupplier)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
info.description = Strings.emptyToNull(internalGroup.getDescription());
info.groupId = internalGroup.getId().get();
diff --git a/java/com/google/gerrit/server/restapi/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
index 7fe9e90106..7c52300e12 100644
--- a/java/com/google/gerrit/server/restapi/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -23,8 +23,9 @@ 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.common.errors.NoSuchGroupException;
+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.RestApiException;
@@ -45,7 +46,6 @@ import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.restapi.account.GetGroups;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -202,7 +202,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
@Option(name = "-O", usage = "Output option flags, in hex")
void setOptionFlagsHex(String hex) {
- options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
+ options.addAll(ListOption.fromBits(ListGroupsOption.class, Integer.parseInt(hex, 16)));
}
@Option(
@@ -251,8 +251,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
@Override
public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
- throws OrmException, RestApiException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
SortedMap<String, GroupInfo> output = new TreeMap<>();
for (GroupInfo info : get()) {
output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
@@ -262,8 +261,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
}
public List<GroupInfo> get()
- throws OrmException, RestApiException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (!Strings.isNullOrEmpty(suggest)) {
return suggestGroups();
}
@@ -288,7 +286,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
}
private List<GroupInfo> getAllGroups()
- throws OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+ throws IOException, ConfigInvalidException, PermissionBackendException {
Pattern pattern = getRegexPattern();
Stream<GroupDescription.Internal> existingGroups =
getAllExistingGroups()
@@ -319,8 +317,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
return groups.getAllGroupReferences();
}
- private List<GroupInfo> suggestGroups()
- throws OrmException, BadRequestException, PermissionBackendException {
+ private List<GroupInfo> suggestGroups() throws BadRequestException, PermissionBackendException {
if (conflictingSuggestParameters()) {
throw new BadRequestException(
"You should only have no more than one --project and -n with --suggest");
@@ -375,7 +372,7 @@ public class ListGroups implements RestReadView<TopLevelResource> {
}
private List<GroupInfo> filterGroupsOwnedBy(Predicate<GroupDescription.Internal> filter)
- throws OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+ throws IOException, ConfigInvalidException, PermissionBackendException {
Pattern pattern = getRegexPattern();
Stream<? extends GroupDescription.Internal> foundGroups =
groups
@@ -403,14 +400,13 @@ public class ListGroups implements RestReadView<TopLevelResource> {
}
private List<GroupInfo> getGroupsOwnedBy(String id)
- throws OrmException, RestApiException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
String uuid = groupResolver.parse(id).getGroupUUID().get();
return filterGroupsOwnedBy(group -> group.getOwnerGroupUUID().get().equals(uuid));
}
private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user)
- throws OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+ throws IOException, ConfigInvalidException, PermissionBackendException {
return filterGroupsOwnedBy(group -> isOwner(user, group));
}
diff --git a/java/com/google/gerrit/server/restapi/group/ListMembers.java b/java/com/google/gerrit/server/restapi/group/ListMembers.java
index 40db2b7423..75be44cbee 100644
--- a/java/com/google/gerrit/server/restapi/group/ListMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/ListMembers.java
@@ -33,7 +33,6 @@ import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.HashSet;
@@ -67,7 +66,7 @@ public class ListMembers implements RestReadView<GroupResource> {
@Override
public List<AccountInfo> apply(GroupResource resource)
- throws NotInternalGroupException, OrmException, PermissionBackendException {
+ throws NotInternalGroupException, PermissionBackendException {
GroupDescription.Internal group =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
if (recursive) {
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index 864b01bd9b..bb72a10d0b 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -19,14 +19,13 @@ import static java.util.Comparator.comparing;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.GroupInfo;
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;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
@@ -47,7 +46,7 @@ public class ListSubgroups implements RestReadView<GroupResource> {
@Override
public List<GroupInfo> apply(GroupResource rsrc)
- throws NotInternalGroupException, OrmException, PermissionBackendException {
+ throws NotInternalGroupException, PermissionBackendException {
GroupDescription.Internal group =
rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
@@ -56,7 +55,7 @@ public class ListSubgroups implements RestReadView<GroupResource> {
public List<GroupInfo> getDirectSubgroups(
GroupDescription.Internal group, GroupControl groupControl)
- throws OrmException, PermissionBackendException {
+ throws PermissionBackendException {
boolean ownerOfParent = groupControl.isOwner();
List<GroupInfo> included = new ArrayList<>();
for (AccountGroup.UUID subgroupUuid : group.getSubgroups()) {
diff --git a/java/com/google/gerrit/server/restapi/group/MembersCollection.java b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
index fec14436fe..6dfb2b6ec3 100644
--- a/java/com/google/gerrit/server/restapi/group/MembersCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
@@ -26,7 +26,6 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.MemberResource;
import com.google.gerrit.server.restapi.account.AccountsCollection;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -56,8 +55,8 @@ public class MembersCollection implements ChildCollection<GroupResource, MemberR
@Override
public MemberResource parse(GroupResource parent, IdString id)
- throws NotInternalGroupException, AuthException, ResourceNotFoundException, OrmException,
- IOException, ConfigInvalidException {
+ throws NotInternalGroupException, AuthException, ResourceNotFoundException, IOException,
+ ConfigInvalidException {
GroupDescription.Internal group =
parent.asInternalGroup().orElseThrow(NotInternalGroupException::new);
diff --git a/java/com/google/gerrit/server/restapi/group/Module.java b/java/com/google/gerrit/server/restapi/group/Module.java
index 741c3da774..45ac411cf4 100644
--- a/java/com/google/gerrit/server/restapi/group/Module.java
+++ b/java/com/google/gerrit/server/restapi/group/Module.java
@@ -82,7 +82,7 @@ public class Module extends RestApiModule {
@Provides
@ServerInitiated
GroupsUpdate provideServerInitiatedGroupsUpdate(GroupsUpdate.Factory groupsUpdateFactory) {
- return groupsUpdateFactory.create(null);
+ return groupsUpdateFactory.createWithServerIdent();
}
@Provides
diff --git a/java/com/google/gerrit/server/restapi/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
index 9ce180f4c3..c4e6f09cfa 100644
--- a/java/com/google/gerrit/server/restapi/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -16,7 +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.common.errors.NoSuchGroupException;
+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;
@@ -27,7 +27,6 @@ import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -46,8 +45,8 @@ public class PutDescription implements RestModifyView<GroupResource, Description
@Override
public Response<String> apply(GroupResource resource, DescriptionInput input)
- throws AuthException, NotInternalGroupException, ResourceNotFoundException, OrmException,
- IOException, ConfigInvalidException {
+ throws AuthException, NotInternalGroupException, ResourceNotFoundException, IOException,
+ ConfigInvalidException {
if (input == null) {
input = new DescriptionInput(); // Delete would set description to null.
}
@@ -72,7 +71,7 @@ public class PutDescription implements RestModifyView<GroupResource, Description
}
return Strings.isNullOrEmpty(input.description)
- ? Response.<String>none()
+ ? Response.none()
: Response.ok(input.description);
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index 83757d01ec..adc20d523f 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -16,7 +16,8 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
import com.google.gerrit.extensions.common.NameInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -28,7 +29,6 @@ import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -79,7 +79,7 @@ public class PutName implements RestModifyView<GroupResource, NameInput> {
groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid), e);
- } catch (OrmDuplicateKeyException e) {
+ } catch (DuplicateKeyException e) {
throw new ResourceConflictException("group with name " + newName + " already exists", e);
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index 8698ee6da3..747d899a9b 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.restapi.group;
import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+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;
@@ -26,7 +26,6 @@ import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -45,7 +44,7 @@ public class PutOptions implements RestModifyView<GroupResource, GroupOptionsInf
@Override
public GroupOptionsInfo apply(GroupResource resource, GroupOptionsInfo input)
throws NotInternalGroupException, AuthException, BadRequestException,
- ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
+ ResourceNotFoundException, IOException, ConfigInvalidException {
GroupDescription.Internal internalGroup =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
if (!resource.getControl().isOwner()) {
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index 0b007daa7d..f766a84c1d 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -16,7 +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.common.errors.NoSuchGroupException;
+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;
@@ -31,7 +31,6 @@ import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -57,8 +56,8 @@ public class PutOwner implements RestModifyView<GroupResource, OwnerInput> {
@Override
public GroupInfo apply(GroupResource resource, OwnerInput input)
throws ResourceNotFoundException, NotInternalGroupException, AuthException,
- BadRequestException, UnprocessableEntityException, OrmException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ BadRequestException, UnprocessableEntityException, IOException, ConfigInvalidException,
+ PermissionBackendException {
GroupDescription.Internal internalGroup =
resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
if (!resource.getControl().isOwner()) {
diff --git a/java/com/google/gerrit/server/restapi/group/QueryGroups.java b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
index b11664636a..b8dd28d7f3 100644
--- a/java/com/google/gerrit/server/restapi/group/QueryGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.restapi.group;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
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.MethodNotAllowedException;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.group.GroupQueryBuilder;
import com.google.gerrit.server.query.group.GroupQueryProcessor;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.ArrayList;
@@ -83,7 +83,7 @@ public class QueryGroups implements RestReadView<TopLevelResource> {
@Option(name = "-O", usage = "Output option flags, in hex")
public void setOptionFlagsHex(String hex) {
- options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
+ options.addAll(ListOption.fromBits(ListGroupsOption.class, Integer.parseInt(hex, 16)));
}
@Inject
@@ -98,8 +98,7 @@ public class QueryGroups implements RestReadView<TopLevelResource> {
@Override
public List<GroupInfo> apply(TopLevelResource resource)
- throws BadRequestException, MethodNotAllowedException, OrmException,
- PermissionBackendException {
+ throws BadRequestException, MethodNotAllowedException, PermissionBackendException {
if (Strings.isNullOrEmpty(query)) {
throw new BadRequestException("missing query field");
}
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
index 1664635579..67b68b578f 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -23,7 +23,6 @@ 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.extensions.restapi.RestModifyView;
-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.server.account.AccountResolver;
@@ -35,7 +34,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -62,8 +60,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
@Override
public AccessCheckInfo apply(ProjectResource rsrc, AccessCheckInput input)
- throws OrmException, PermissionBackendException, RestApiException, IOException,
- ConfigInvalidException {
+ throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS);
rsrc.getProjectState().checkStatePermitsRead();
@@ -75,20 +72,16 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
throw new BadRequestException("input requires 'account'");
}
- Account match = accountResolver.find(input.account);
- if (match == null) {
- throw new UnprocessableEntityException(
- String.format("cannot find account %s", input.account));
- }
+ Account.Id match = accountResolver.resolve(input.account).asUnique().getAccount().getId();
AccessCheckInfo info = new AccessCheckInfo();
try {
permissionBackend
- .absentUser(match.getId())
+ .absentUser(match)
.project(rsrc.getNameKey())
.check(ProjectPermission.ACCESS);
} catch (AuthException e) {
- info.message = String.format("user %s cannot see project %s", match.getId(), rsrc.getName());
+ info.message = String.format("user %s cannot see project %s", match, rsrc.getName());
info.status = HttpServletResponse.SC_FORBIDDEN;
return info;
}
@@ -112,7 +105,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
if (!Strings.isNullOrEmpty(input.ref)) {
try {
permissionBackend
- .absentUser(match.getId())
+ .absentUser(match)
.ref(new Branch.NameKey(rsrc.getNameKey(), input.ref))
.check(refPerm);
} catch (AuthException e) {
@@ -120,7 +113,7 @@ public class CheckAccess implements RestModifyView<ProjectResource, AccessCheckI
info.message =
String.format(
"user %s lacks permission %s for %s in project %s",
- match.getId(), input.permission, input.ref, rsrc.getName());
+ match, input.permission, input.ref, rsrc.getName());
return info;
}
} else {
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
index 0aab0e1f54..770e8c3e97 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
@@ -20,7 +20,6 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -50,8 +49,7 @@ public class CheckAccessReadView implements RestReadView<ProjectResource> {
@Override
public AccessCheckInfo apply(ProjectResource rsrc)
- throws OrmException, PermissionBackendException, RestApiException, IOException,
- ConfigInvalidException {
+ throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
AccessCheckInput input = new AccessCheckInput();
input.ref = refName;
diff --git a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
index 3855b7842a..8cc8298a02 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
@@ -20,7 +20,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -36,8 +35,7 @@ public class CommitIncludedIn implements RestReadView<CommitResource> {
}
@Override
- public IncludedInInfo apply(CommitResource rsrc)
- throws RestApiException, OrmException, IOException {
+ public IncludedInInfo apply(CommitResource rsrc) throws RestApiException, IOException {
RevCommit commit = rsrc.getCommit();
Project.NameKey project = rsrc.getProjectState().getNameKey();
return 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 dc53923dd1..d6c44691cc 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -14,7 +14,8 @@
package com.google.gerrit.server.restapi.project;
-import com.google.common.flogger.FluentLogger;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
@@ -22,6 +23,7 @@ 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;
@@ -30,7 +32,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -39,14 +40,13 @@ import java.util.List;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@Singleton
public class CommitsCollection implements ChildCollection<ProjectResource, CommitResource> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
private final DynamicMap<RestView<CommitResource>> views;
private final GitRepositoryManager repoManager;
private final ChangeIndexCollection indexes;
@@ -105,23 +105,27 @@ public class CommitsCollection implements ChildCollection<ProjectResource, Commi
}
/** @return true if {@code commit} is visible to the caller. */
- public boolean canRead(ProjectState state, Repository repo, RevCommit commit) {
+ public boolean canRead(ProjectState state, Repository repo, RevCommit commit) throws IOException {
Project.NameKey project = state.getNameKey();
+ if (indexes.getSearchIndex() == null) {
+ // No index in slaves, fall back to scanning refs.
+ return reachable.fromRefs(project, repo, commit, repo.getRefDatabase().getRefs());
+ }
- // Look for changes associated with the commit.
- if (indexes.getSearchIndex() != null) {
- try {
- List<ChangeData> changes =
- queryProvider.get().enforceVisibility(true).byProjectCommit(project, commit);
- if (!changes.isEmpty()) {
- return true;
- }
- } catch (OrmException e) {
- logger.atSevere().withCause(e).log(
- "Cannot look up change for commit %s in %s", commit.name(), project);
- }
+ // 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);
+ if (!changes.isEmpty()) {
+ return true;
}
- return reachable.fromRefs(project, repo, commit, repo.getAllRefs());
+ // If we have already checked change refs using the change index, spare any further checks for
+ // changes.
+ List<Ref> refs =
+ repo.getRefDatabase().getRefs().stream()
+ .filter(r -> !r.getName().startsWith(RefNames.REFS_CHANGES))
+ .collect(toImmutableList());
+ return reachable.fromRefs(project, repo, commit, refs);
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
index e1798962fb..37bc265dee 100644
--- a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
+++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
@@ -101,7 +101,6 @@ public class ConfigInfoImpl extends ConfigInfo {
for (UiAction.Description d : uiActions.from(views, new ProjectResource(projectState, user))) {
actions.put(d.getId(), new ActionInfo(d));
}
- this.theme = projectState.getTheme();
this.extensionPanelNames = projectState.getConfig().getExtensionPanelSections();
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index 66a8de2edd..0741377759 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -16,7 +16,7 @@ package com.google.gerrit.server.restapi.project;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -28,12 +28,11 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
-import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
@@ -44,7 +43,6 @@ import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -64,10 +62,10 @@ public class CreateAccessChange implements RestModifyView<ProjectResource, Proje
private final ChangeInserter.Factory changeInserterFactory;
private final BatchUpdate.Factory updateFactory;
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
- private final Provider<ReviewDb> db;
private final SetAccessUtil setAccess;
private final ChangeJson.Factory jsonFactory;
private final ProjectCache projectCache;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
CreateAccessChange(
@@ -76,25 +74,25 @@ public class CreateAccessChange implements RestModifyView<ProjectResource, Proje
BatchUpdate.Factory updateFactory,
Sequences seq,
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
- Provider<ReviewDb> db,
SetAccessUtil accessUtil,
ChangeJson.Factory jsonFactory,
- ProjectCache projectCache) {
+ ProjectCache projectCache,
+ ProjectConfig.Factory projectConfigFactory) {
this.permissionBackend = permissionBackend;
this.seq = seq;
this.changeInserterFactory = changeInserterFactory;
this.updateFactory = updateFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory;
- this.db = db;
this.setAccess = accessUtil;
this.jsonFactory = jsonFactory;
this.projectCache = projectCache;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
public Response<ChangeInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
throws PermissionBackendException, AuthException, IOException, ConfigInvalidException,
- OrmException, InvalidNameException, UpdateException, RestApiException {
+ InvalidNameException, UpdateException, RestApiException {
PermissionBackend.ForProject forProject =
permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey());
if (!check(forProject, ProjectPermission.READ_CONFIG)) {
@@ -117,7 +115,7 @@ public class CreateAccessChange implements RestModifyView<ProjectResource, Proje
input.parent == null ? null : new Project.NameKey(input.parent);
try (MetaDataUpdate md = metaDataUpdateUser.create(rsrc.getNameKey())) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
ObjectId oldCommit = config.getRevision();
String oldCommitSha1 = oldCommit == null ? null : oldCommit.getName();
@@ -150,7 +148,7 @@ public class CreateAccessChange implements RestModifyView<ProjectResource, Proje
ObjectReader objReader = objInserter.newReader();
RevWalk rw = new RevWalk(objReader);
BatchUpdate bu =
- updateFactory.create(db.get(), rsrc.getNameKey(), rsrc.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(rsrc.getNameKey(), rsrc.getUser(), TimeUtil.nowTs())) {
bu.setRepository(md.getRepository(), rw, objInserter);
ChangeInserter ins = newInserter(changeId, commit);
bu.insertChange(ins);
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index eacce18c86..8b81f10286 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -19,21 +19,13 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.ProjectUtil;
-import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
-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.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.projects.ConfigInput;
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.common.ProjectInfo;
-import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -41,30 +33,18 @@ 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.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.UsedAt;
-import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.ProjectUtil;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.ProjectOwnerGroupsProvider;
-import com.google.gerrit.server.config.RepositoryConfig;
-import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.RepositoryCaseMismatchException;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.GroupResolver;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.plugincontext.PluginSetContext;
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.gerrit.server.project.ProjectJson;
import com.google.gerrit.server.project.ProjectNameLockManager;
import com.google.gerrit.server.project.ProjectResource;
@@ -80,77 +60,43 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-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.PersonIdent;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.ReceiveCommand;
@RequiresCapability(GlobalCapability.CREATE_PROJECT)
@Singleton
public class CreateProject
implements RestCollectionCreateView<TopLevelResource, ProjectResource, ProjectInput> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
private final Provider<ProjectsCollection> projectsCollection;
private final Provider<GroupResolver> groupResolver;
private final PluginSetContext<ProjectCreationValidationListener>
projectCreationValidationListeners;
private final ProjectJson json;
- private final GitRepositoryManager repoManager;
- private final PluginSetContext<NewProjectCreatedListener> createdListeners;
- private final ProjectCache projectCache;
- private final GroupBackend groupBackend;
private final ProjectOwnerGroupsProvider.Factory projectOwnerGroups;
- private final MetaDataUpdate.User metaDataUpdateFactory;
- private final GitReferenceUpdated referenceUpdated;
- private final RepositoryConfig repositoryCfg;
- private final Provider<PersonIdent> serverIdent;
- private final Provider<IdentifiedUser> identifiedUser;
private final Provider<PutConfig> putConfig;
private final AllProjectsName allProjects;
private final AllUsersName allUsers;
private final PluginItemContext<ProjectNameLockManager> lockManager;
+ private final ProjectCreator projectCreator;
@Inject
CreateProject(
+ ProjectCreator projectCreator,
Provider<ProjectsCollection> projectsCollection,
Provider<GroupResolver> groupResolver,
ProjectJson json,
PluginSetContext<ProjectCreationValidationListener> projectCreationValidationListeners,
- GitRepositoryManager repoManager,
- PluginSetContext<NewProjectCreatedListener> createdListeners,
- ProjectCache projectCache,
- GroupBackend groupBackend,
ProjectOwnerGroupsProvider.Factory projectOwnerGroups,
- MetaDataUpdate.User metaDataUpdateFactory,
- GitReferenceUpdated referenceUpdated,
- RepositoryConfig repositoryCfg,
- @GerritPersonIdent Provider<PersonIdent> serverIdent,
- Provider<IdentifiedUser> identifiedUser,
Provider<PutConfig> putConfig,
AllProjectsName allProjects,
AllUsersName allUsers,
PluginItemContext<ProjectNameLockManager> lockManager) {
this.projectsCollection = projectsCollection;
+ this.projectCreator = projectCreator;
this.groupResolver = groupResolver;
this.projectCreationValidationListeners = projectCreationValidationListeners;
this.json = json;
- this.repoManager = repoManager;
- this.createdListeners = createdListeners;
- this.projectCache = projectCache;
- this.groupBackend = groupBackend;
this.projectOwnerGroups = projectOwnerGroups;
- this.metaDataUpdateFactory = metaDataUpdateFactory;
- this.referenceUpdated = referenceUpdated;
- this.repositoryCfg = repositoryCfg;
- this.serverIdent = serverIdent;
- this.identifiedUser = identifiedUser;
this.putConfig = putConfig;
this.allProjects = allProjects;
this.allUsers = allUsers;
@@ -225,7 +171,7 @@ public class CreateProject
throw new ResourceConflictException(e.getMessage(), e);
}
- ProjectState projectState = createProject(args);
+ ProjectState projectState = projectCreator.createProject(args);
requireNonNull(
projectState,
() -> String.format("failed to create project %s", args.getProject().get()));
@@ -233,6 +179,7 @@ public class CreateProject
if (input.pluginConfigValues != null) {
ConfigInput in = new ConfigInput();
in.pluginConfigValues = input.pluginConfigValues;
+ in.description = args.projectDescription;
putConfig.get().apply(projectState, in);
}
return Response.created(json.format(projectState));
@@ -241,95 +188,6 @@ public class CreateProject
}
}
- @UsedAt(UsedAt.Project.COLLABNET)
- public ProjectState createProject(CreateProjectArgs args)
- throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
- final Project.NameKey nameKey = args.getProject();
- try {
- final String head = args.permissionsOnly ? RefNames.REFS_CONFIG : args.branch.get(0);
- try (Repository repo = repoManager.openRepository(nameKey)) {
- if (repo.getObjectDatabase().exists()) {
- throw new ResourceConflictException("project \"" + nameKey + "\" exists");
- }
- } catch (RepositoryNotFoundException e) {
- // It does not exist, safe to ignore.
- }
- try (Repository repo = repoManager.createRepository(nameKey)) {
- RefUpdate u = repo.updateRef(Constants.HEAD);
- u.disableRefLog();
- u.link(head);
-
- createProjectConfig(args);
-
- if (!args.permissionsOnly && args.createEmptyCommit) {
- createEmptyCommits(repo, nameKey, args.branch);
- }
-
- fire(nameKey, head);
-
- return projectCache.get(nameKey);
- }
- } catch (RepositoryCaseMismatchException e) {
- throw new ResourceConflictException(
- "Cannot create "
- + nameKey.get()
- + " because the name is already occupied by another project."
- + " The other project has the same name, only spelled in a"
- + " different case.",
- e);
- } catch (RepositoryNotFoundException badName) {
- throw new BadRequestException("invalid project name: " + nameKey, badName);
- } catch (ConfigInvalidException e) {
- String msg = "Cannot create " + nameKey;
- logger.atSevere().withCause(e).log(msg);
- throw e;
- }
- }
-
- private void createProjectConfig(CreateProjectArgs args)
- throws IOException, ConfigInvalidException {
- try (MetaDataUpdate md = metaDataUpdateFactory.create(args.getProject())) {
- ProjectConfig config = ProjectConfig.read(md);
-
- Project newProject = config.getProject();
- newProject.setDescription(args.projectDescription);
- newProject.setSubmitType(
- MoreObjects.firstNonNull(
- args.submitType, repositoryCfg.getDefaultSubmitType(args.getProject())));
- newProject.setBooleanConfig(
- BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS, args.contributorAgreements);
- newProject.setBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY, args.signedOffBy);
- newProject.setBooleanConfig(BooleanProjectConfig.USE_CONTENT_MERGE, args.contentMerge);
- newProject.setBooleanConfig(
- BooleanProjectConfig.CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET,
- args.newChangeForAllNotInTarget);
- newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, args.changeIdRequired);
- newProject.setBooleanConfig(BooleanProjectConfig.REJECT_EMPTY_COMMIT, args.rejectEmptyCommit);
- newProject.setMaxObjectSizeLimit(args.maxObjectSizeLimit);
- newProject.setBooleanConfig(BooleanProjectConfig.ENABLE_SIGNED_PUSH, args.enableSignedPush);
- newProject.setBooleanConfig(BooleanProjectConfig.REQUIRE_SIGNED_PUSH, args.requireSignedPush);
- if (args.newParent != null) {
- newProject.setParentName(args.newParent);
- }
-
- if (!args.ownerIds.isEmpty()) {
- AccessSection all = config.getAccessSection(AccessSection.ALL, true);
- for (AccountGroup.UUID ownerId : args.ownerIds) {
- GroupDescription.Basic g = groupBackend.get(ownerId);
- if (g != null) {
- GroupReference group = config.resolve(GroupReference.forGroup(g));
- all.getPermission(Permission.OWNER, true).add(new PermissionRule(group));
- }
- }
- }
-
- md.setMessage("Created project\n");
- config.commit(md);
- md.getRepository().setGitwebDescription(args.projectDescription);
- }
- projectCache.onCreateProject(args.getProject());
- }
-
private List<String> normalizeBranchNames(List<String> branches) throws BadRequestException {
if (branches == null || branches.isEmpty()) {
return Collections.singletonList(Constants.R_HEADS + Constants.MASTER);
@@ -350,78 +208,4 @@ public class CreateProject
}
return normalizedBranches;
}
-
- private void createEmptyCommits(Repository repo, Project.NameKey project, List<String> refs)
- throws IOException {
- try (ObjectInserter oi = repo.newObjectInserter()) {
- CommitBuilder cb = new CommitBuilder();
- cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {}));
- cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent());
- cb.setCommitter(serverIdent.get());
- cb.setMessage("Initial empty repository\n");
-
- ObjectId id = oi.insert(cb);
- oi.flush();
-
- for (String ref : refs) {
- RefUpdate ru = repo.updateRef(ref);
- ru.setNewObjectId(id);
- Result result = ru.update();
- switch (result) {
- case NEW:
- referenceUpdated.fire(
- project, ru, ReceiveCommand.Type.CREATE, identifiedUser.get().state());
- break;
- case FAST_FORWARD:
- case FORCED:
- case IO_FAILURE:
- case LOCK_FAILURE:
- case NOT_ATTEMPTED:
- case NO_CHANGE:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case RENAMED:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- {
- throw new IOException(
- String.format("Failed to create ref \"%s\": %s", ref, result.name()));
- }
- }
- }
- } catch (IOException e) {
- logger.atSevere().withCause(e).log("Cannot create empty commit for %s", project.get());
- throw e;
- }
- }
-
- private void fire(Project.NameKey name, String head) {
- if (createdListeners.isEmpty()) {
- return;
- }
-
- Event event = new Event(name, head);
- createdListeners.runEach(l -> l.onNewProjectCreated(event));
- }
-
- static class Event extends AbstractNoNotifyEvent implements NewProjectCreatedListener.Event {
- private final Project.NameKey name;
- private final String head;
-
- Event(Project.NameKey name, String head) {
- this.name = name;
- this.head = head;
- }
-
- @Override
- public String getProjectName() {
- return name.get();
- }
-
- @Override
- public String getHeadName() {
- return head;
- }
- }
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
index 1d327ca1f8..b94fb51bf7 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
@@ -27,7 +27,6 @@ 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;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -47,7 +46,7 @@ public class DeleteBranch implements RestModifyView<BranchResource, Input> {
@Override
public Response<?> apply(BranchResource rsrc, Input input)
- throws RestApiException, OrmException, IOException, PermissionBackendException {
+ throws RestApiException, IOException, PermissionBackendException {
if (RefNames.HEAD.equals(rsrc.getBranchKey().get())) {
throw new MethodNotAllowedException("not allowed to delete HEAD");
} else if (isConfigRef(rsrc.getBranchKey().get())) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
index 28c05017aa..ba25e3394e 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
@@ -26,7 +26,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -42,7 +41,7 @@ public class DeleteBranches implements RestModifyView<ProjectResource, DeleteBra
@Override
public Response<?> apply(ProjectResource project, DeleteBranchesInput input)
- throws OrmException, IOException, RestApiException, PermissionBackendException {
+ throws IOException, RestApiException, PermissionBackendException {
if (input == null || input.branches == null || input.branches.isEmpty()) {
throw new BadRequestException("branches must be specified");
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
index 5df102c074..dae759ae79 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -25,6 +25,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Branch;
@@ -38,7 +39,6 @@ import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefValidationHelper;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -157,7 +157,7 @@ public class DeleteRef {
break;
case REJECTED_CURRENT_BRANCH:
- logger.atSevere().log("Cannot delete %s: %s", ref, result.name());
+ logger.atFine().log("Cannot delete current branch %s: %s", ref, result.name());
throw new ResourceConflictException("cannot delete current branch");
case IO_FAILURE:
@@ -168,8 +168,7 @@ public class DeleteRef {
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
- logger.atSevere().log("Cannot delete %s: %s", ref, result.name());
- throw new ResourceConflictException("cannot delete: " + result.name());
+ throw new StorageException(String.format("Cannot delete %s: %s", ref, result.name()));
}
}
}
@@ -180,15 +179,13 @@ public class DeleteRef {
* @param projectState the {@code ProjectState} of the project whose refs are to be deleted.
* @param refsToDelete the refs to be deleted.
* @param prefix the prefix of the refs.
- * @throws OrmException
* @throws IOException
* @throws ResourceConflictException
* @throws PermissionBackendException
*/
public void deleteMultipleRefs(
ProjectState projectState, ImmutableSet<String> refsToDelete, String prefix)
- throws OrmException, IOException, ResourceConflictException, PermissionBackendException,
- AuthException {
+ throws IOException, ResourceConflictException, PermissionBackendException, AuthException {
if (refsToDelete.isEmpty()) {
return;
}
@@ -229,8 +226,8 @@ public class DeleteRef {
private ReceiveCommand createDeleteCommand(
ProjectState projectState, Repository r, String refName)
- throws OrmException, IOException, ResourceConflictException, PermissionBackendException {
- Ref ref = r.getRefDatabase().getRef(refName);
+ throws IOException, ResourceConflictException, PermissionBackendException {
+ Ref ref = r.getRefDatabase().exactRef(refName);
ReceiveCommand command;
if (ref == null) {
command = new ReceiveCommand(ObjectId.zeroId(), ObjectId.zeroId(), refName);
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTag.java b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
index f7cce11024..33955eec04 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTag.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
@@ -24,7 +24,6 @@ import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.RefUtil;
import com.google.gerrit.server.project.TagResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -41,7 +40,7 @@ public class DeleteTag implements RestModifyView<TagResource, Input> {
@Override
public Response<?> apply(TagResource resource, Input input)
- throws OrmException, RestApiException, IOException, PermissionBackendException {
+ throws RestApiException, IOException, PermissionBackendException {
String tag = RefUtil.normalizeTagRef(resource.getTagInfo().ref);
if (isConfigRef(tag)) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTags.java b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
index bf2c5242c5..6e8ec378cb 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTags.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
@@ -24,7 +24,6 @@ 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.ProjectResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -40,7 +39,7 @@ public class DeleteTags implements RestModifyView<ProjectResource, DeleteTagsInp
@Override
public Response<?> apply(ProjectResource project, DeleteTagsInput input)
- throws OrmException, RestApiException, IOException, PermissionBackendException {
+ throws RestApiException, IOException, PermissionBackendException {
if (input == null || input.tags == null || input.tags.isEmpty()) {
throw new BadRequestException("tags must be specified");
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index 4ec187a68e..d6c07ddf0e 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -29,14 +29,11 @@ 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.common.data.RefConfigSection;
-import com.google.gerrit.common.data.WebLinkInfoCommon;
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.common.GroupInfo;
-import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -95,6 +92,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
private final Provider<MetaDataUpdate.Server> metaDataUpdateFactory;
private final GroupBackend groupBackend;
private final WebLinks webLinks;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
public GetAccess(
@@ -105,7 +103,8 @@ public class GetAccess implements RestReadView<ProjectResource> {
Provider<MetaDataUpdate.Server> metaDataUpdateFactory,
ProjectJson projectJson,
GroupBackend groupBackend,
- WebLinks webLinks) {
+ WebLinks webLinks,
+ ProjectConfig.Factory projectConfigFactory) {
this.user = self;
this.permissionBackend = permissionBackend;
this.allProjectsName = allProjectsName;
@@ -114,6 +113,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.groupBackend = groupBackend;
this.webLinks = webLinks;
+ this.projectConfigFactory = projectConfigFactory;
}
public ProjectAccessInfo apply(Project.NameKey nameKey)
@@ -141,18 +141,14 @@ public class GetAccess implements RestReadView<ProjectResource> {
ProjectConfig config;
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(projectName)) {
- config = ProjectConfig.read(md);
+ config = projectConfigFactory.read(md);
info.configWebLinks = new ArrayList<>();
// config may have a null revision if the repo doesn't have its own refs/meta/config.
if (config.getRevision() != null) {
- // WebLinks operates in terms of the data types used in the GWT UI. Once the GWT UI is
- // gone, WebLinks should be fixed to use the extension data types.
- for (WebLinkInfoCommon wl :
+ info.configWebLinks.addAll(
webLinks.getFileHistoryLinks(
- projectName.get(), config.getRevision().getName(), ProjectConfig.PROJECT_CONFIG)) {
- info.configWebLinks.add(new WebLinkInfo(wl.name, wl.imageUrl, wl.url, wl.target));
- }
+ projectName.get(), config.getRevision().getName(), ProjectConfig.PROJECT_CONFIG));
}
if (config.updateGroupNames(groupBackend)) {
@@ -199,7 +195,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
info.local.put(section.getName(), createAccessSection(groups, section));
}
- } else if (RefConfigSection.isValid(name)) {
+ } else if (AccessSection.isValidRefSectionName(name)) {
if (check(perm, name, WRITE_CONFIG)) {
info.local.put(name, createAccessSection(groups, section));
info.ownerOf.add(name);
diff --git a/java/com/google/gerrit/server/restapi/project/Index.java b/java/com/google/gerrit/server/restapi/project/Index.java
index a346aedb0e..bc58b23db0 100644
--- a/java/com/google/gerrit/server/restapi/project/Index.java
+++ b/java/com/google/gerrit/server/restapi/project/Index.java
@@ -16,7 +16,6 @@ package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
-import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -30,7 +29,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -40,8 +38,6 @@ import java.util.concurrent.Future;
@RequiresCapability(GlobalCapability.MAINTAIN_SERVER)
@Singleton
public class Index implements RestModifyView<ProjectResource, IndexProjectInput> {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
private final ProjectIndexer indexer;
private final ListeningExecutorService executor;
private final Provider<ListChildProjects> listChildProjectsProvider;
@@ -58,7 +54,7 @@ public class Index implements RestModifyView<ProjectResource, IndexProjectInput>
@Override
public Response.Accepted apply(ProjectResource rsrc, IndexProjectInput input)
- throws IOException, OrmException, PermissionBackendException, RestApiException {
+ throws IOException, PermissionBackendException, RestApiException {
String response = "Project " + rsrc.getName() + " submitted for reindexing";
reindex(rsrc.getNameKey(), input.async);
@@ -72,18 +68,10 @@ public class Index implements RestModifyView<ProjectResource, IndexProjectInput>
return Response.accepted(response);
}
- private void reindex(Project.NameKey project, Boolean async) throws IOException {
+ private void reindex(Project.NameKey project, Boolean async) {
if (Boolean.TRUE.equals(async)) {
@SuppressWarnings("unused")
- Future<?> possiblyIgnoredError =
- executor.submit(
- () -> {
- try {
- indexer.index(project);
- } catch (IOException e) {
- logger.atWarning().withCause(e).log("reindexing project %s failed", project);
- }
- });
+ Future<?> possiblyIgnoredError = executor.submit(() -> indexer.index(project));
} else {
indexer.index(project);
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
index ea54a4a217..387972032e 100644
--- a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
@@ -27,7 +27,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.ChildProjects;
import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.List;
@@ -67,7 +66,7 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
@Override
public List<ProjectInfo> apply(ProjectResource rsrc)
- throws PermissionBackendException, OrmException, RestApiException {
+ throws PermissionBackendException, RestApiException {
if (limit < 0) {
throw new BadRequestException("limit must be a positive number");
}
@@ -82,8 +81,7 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
return directChildProjects(rsrc.getNameKey());
}
- private List<ProjectInfo> directChildProjects(Project.NameKey parent)
- throws OrmException, RestApiException {
+ 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(
diff --git a/java/com/google/gerrit/server/restapi/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
index 377a793444..c583923e87 100644
--- a/java/com/google/gerrit/server/restapi/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -27,7 +27,8 @@ 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.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -37,11 +38,11 @@ import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
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.OutputFormat;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -57,7 +58,6 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.TreeFormatter;
import com.google.gson.reflect.TypeToken;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.BufferedWriter;
@@ -105,17 +105,6 @@ public class ListProjects implements RestReadView<TopLevelResource> {
return true;
}
},
- PARENT_CANDIDATES {
- @Override
- boolean matches(Repository git) {
- return true;
- }
-
- @Override
- boolean useMatch() {
- return false;
- }
- },
PERMISSIONS {
@Override
boolean matches(Repository git) throws IOException {
@@ -369,7 +358,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
.collect(
ImmutableSortedMap.toImmutableSortedMap(
natural(), p -> p.name, p -> showDescription ? p : nullifyDescription(p)));
- } catch (OrmException | MethodNotAllowedException e) {
+ } catch (StorageException | MethodNotAllowedException e) {
logger.atWarning().withCause(e).log(
"Internal error while processing the query '%s' request", query);
throw new BadRequestException("Internal error while processing the query request");
@@ -389,7 +378,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
newProjectsNamesStream(query).forEach(out::println);
}
out.flush();
- } catch (OrmException | MethodNotAllowedException e) {
+ } catch (StorageException | MethodNotAllowedException e) {
logger.atWarning().withCause(e).log(
"Internal error while processing the query '%s' request", query);
throw new BadRequestException("Internal error while processing the query request");
@@ -397,7 +386,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
}
private Stream<String> newProjectsNamesStream(String query)
- throws OrmException, MethodNotAllowedException, BadRequestException {
+ throws MethodNotAllowedException, BadRequestException {
Stream<String> projects =
queryProjectsProvider.get().withQuery(query).apply().stream().map(p -> p.name).skip(start);
if (limit > 0) {
@@ -436,11 +425,6 @@ public class ListProjects implements RestReadView<TopLevelResource> {
}
}
- if (type == FilterType.PARENT_CANDIDATES) {
- // Historically, PARENT_CANDIDATES implied showDescription.
- showDescription = true;
- }
-
int foundIndex = 0;
int found = 0;
TreeMap<String, ProjectInfo> output = new TreeMap<>();
@@ -449,10 +433,10 @@ public class ListProjects implements RestReadView<TopLevelResource> {
PermissionBackend.WithUser perm = permissionBackend.user(currentUser);
final TreeMap<Project.NameKey, ProjectNode> treeMap = new TreeMap<>();
try {
- Iterable<Project.NameKey> projectNames = filter(perm)::iterator;
- for (Project.NameKey projectName : projectNames) {
- final ProjectState e = projectCache.get(projectName);
- if (e == null || (e.getProject().getState() == HIDDEN && !all && state != HIDDEN)) {
+ Iterable<ProjectState> projectStatesIt = filter(perm)::iterator;
+ for (ProjectState e : projectStatesIt) {
+ Project.NameKey projectName = e.getNameKey();
+ if (e.getProject().getState() == HIDDEN && !all && state != HIDDEN) {
// If we can't get it from the cache, pretend it's not present.
// If all wasn't selected, and it's HIDDEN, pretend it's not present.
// If state HIDDEN wasn't selected, and it's HIDDEN, pretend it's not present.
@@ -469,26 +453,22 @@ public class ListProjects implements RestReadView<TopLevelResource> {
continue;
}
- ProjectInfo info = new ProjectInfo();
if (showTree && !format.isJson()) {
treeMap.put(projectName, projectNodeFactory.create(e.getProject(), true));
continue;
}
+ if (foundIndex++ < start) {
+ continue;
+ }
+ if (limit > 0 && ++found > limit) {
+ break;
+ }
+
+ ProjectInfo info = new ProjectInfo();
info.name = projectName.get();
if (showTree && format.isJson()) {
- ProjectState parent = Iterables.getFirst(e.parents(), null);
- if (parent != null) {
- if (isParentAccessible(accessibleParents, perm, parent)) {
- info.parent = parent.getName();
- } else {
- info.parent = hiddenNames.get(parent.getName());
- if (info.parent == null) {
- info.parent = "?-" + (hiddenNames.size() + 1);
- hiddenNames.put(parent.getName(), info.parent);
- }
- }
- }
+ addParentProjectInfo(hiddenNames, accessibleParents, perm, e, info);
}
if (showDescription) {
@@ -503,32 +483,12 @@ public class ListProjects implements RestReadView<TopLevelResource> {
continue;
}
- boolean canReadAllRefs = e.statePermitsRead();
- if (canReadAllRefs) {
- try {
- permissionBackend
- .user(currentUser)
- .project(e.getNameKey())
- .check(ProjectPermission.READ);
- } catch (AuthException exp) {
- canReadAllRefs = false;
- }
- }
-
- List<Ref> refs = getBranchRefs(projectName, canReadAllRefs);
+ List<Ref> refs = retieveBranchRefs(e);
if (!hasValidRef(refs)) {
continue;
}
- for (int i = 0; i < showBranch.size(); i++) {
- Ref ref = refs.get(i);
- if (ref != null && ref.getObjectId() != null) {
- if (info.branches == null) {
- info.branches = new LinkedHashMap<>();
- }
- info.branches.put(showBranch.get(i), ref.getObjectId().name());
- }
- }
+ addProjectBranchesInfo(info, refs);
}
} else if (!showTree && type.useMatch()) {
try (Repository git = repoManager.openRepository(projectName)) {
@@ -545,17 +505,8 @@ public class ListProjects implements RestReadView<TopLevelResource> {
continue;
}
- if (type != FilterType.PARENT_CANDIDATES) {
- List<WebLinkInfo> links = webLinks.getProjectLinks(projectName.get());
- info.webLinks = links.isEmpty() ? null : links;
- }
-
- if (foundIndex++ < start) {
- continue;
- }
- if (limit > 0 && ++found > limit) {
- break;
- }
+ List<WebLinkInfo> links = webLinks.getProjectLinks(projectName.get());
+ info.webLinks = links.isEmpty() ? null : links;
if (stdout == null || format.isJson()) {
output.put(info.name, info);
@@ -563,15 +514,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
}
if (!showBranch.isEmpty()) {
- for (String name : showBranch) {
- String ref = info.branches != null ? info.branches.get(name) : null;
- if (ref == null) {
- // Print stub (forty '-' symbols)
- ref = "----------------------------------------";
- }
- stdout.print(ref);
- stdout.print(' ');
- }
+ printProjectBranches(stdout, info);
}
stdout.print(info.name);
@@ -604,28 +547,79 @@ public class ListProjects implements RestReadView<TopLevelResource> {
}
}
- private Stream<Project.NameKey> filter(PermissionBackend.WithUser perm)
- throws BadRequestException {
- Stream<Project.NameKey> matches = StreamSupport.stream(scan().spliterator(), false);
- if (type == FilterType.PARENT_CANDIDATES) {
- matches =
- matches.map(projectCache::get).map(this::parentOf).filter(Objects::nonNull).sorted();
+ private void printProjectBranches(PrintWriter stdout, ProjectInfo info) {
+ for (String name : showBranch) {
+ String ref = info.branches != null ? info.branches.get(name) : null;
+ if (ref == null) {
+ // Print stub (forty '-' symbols)
+ ref = "----------------------------------------";
+ }
+ stdout.print(ref);
+ stdout.print(' ');
}
- return matches.filter(p -> perm.project(p).testOrFalse(ProjectPermission.ACCESS));
}
- private Project.NameKey parentOf(ProjectState ps) {
- if (ps == null) {
- return null;
+ private void addProjectBranchesInfo(ProjectInfo info, List<Ref> refs) {
+ for (int i = 0; i < showBranch.size(); i++) {
+ Ref ref = refs.get(i);
+ if (ref != null && ref.getObjectId() != null) {
+ if (info.branches == null) {
+ info.branches = new LinkedHashMap<>();
+ }
+ info.branches.put(showBranch.get(i), ref.getObjectId().name());
+ }
+ }
+ }
+
+ private List<Ref> retieveBranchRefs(ProjectState e) throws PermissionBackendException {
+ boolean canReadAllRefs = e.statePermitsRead();
+ if (canReadAllRefs) {
+ try {
+ permissionBackend.user(currentUser).project(e.getNameKey()).check(ProjectPermission.READ);
+ } catch (AuthException exp) {
+ canReadAllRefs = false;
+ }
}
- Project.NameKey parent = ps.getProject().getParent();
+
+ return getBranchRefs(e.getNameKey(), canReadAllRefs);
+ }
+
+ private void addParentProjectInfo(
+ Map<String, String> hiddenNames,
+ Map<Project.NameKey, Boolean> accessibleParents,
+ PermissionBackend.WithUser perm,
+ ProjectState e,
+ ProjectInfo info)
+ throws PermissionBackendException {
+ ProjectState parent = Iterables.getFirst(e.parents(), null);
if (parent != null) {
- if (projectCache.get(parent) != null) {
- return parent;
+ if (isParentAccessible(accessibleParents, perm, parent)) {
+ info.parent = parent.getName();
+ } else {
+ info.parent = hiddenNames.get(parent.getName());
+ if (info.parent == null) {
+ info.parent = "?-" + (hiddenNames.size() + 1);
+ hiddenNames.put(parent.getName(), info.parent);
+ }
}
- logger.atWarning().log("parent project %s of project %s not found", ps.getName());
}
- return null;
+ }
+
+ private Stream<ProjectState> filter(PermissionBackend.WithUser perm) throws BadRequestException {
+ return StreamSupport.stream(scan().spliterator(), false)
+ .map(projectCache::get)
+ .filter(Objects::nonNull)
+ .filter(p -> permissionCheck(p, perm));
+ }
+
+ private boolean permissionCheck(ProjectState state, PermissionBackend.WithUser perm) {
+ // Hidden projects(permitsRead = false) should only be accessible by the project owners.
+ // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
+ // be allowed for other users). Allowing project owners to access here will help them to view
+ // and update the config of hidden projects easily.
+ return perm.project(state.getNameKey())
+ .testOrFalse(
+ state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG);
}
private boolean isParentAccessible(
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index 5fbfcf6f83..08b9b84a5e 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -17,7 +17,7 @@ package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
import static java.util.Comparator.comparing;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
@@ -127,7 +127,8 @@ public class ListTags implements RestReadView<ProjectResource> {
try (Repository repo = getRepository(resource.getNameKey());
RevWalk rw = new RevWalk(repo)) {
Map<String, Ref> all =
- visibleTags(resource.getNameKey(), repo, repo.getRefDatabase().getRefs(Constants.R_TAGS));
+ visibleTags(
+ resource.getNameKey(), repo, repo.getRefDatabase().getRefsByPrefix(Constants.R_TAGS));
for (Ref ref : all.values()) {
tags.add(
createTagInfo(perm.ref(ref.getName()), ref, rw, resource.getProjectState(), links));
@@ -154,8 +155,7 @@ public class ListTags implements RestReadView<ProjectResource> {
}
Ref ref = repo.getRefDatabase().exactRef(tagName);
if (ref != null
- && !visibleTags(resource.getNameKey(), repo, ImmutableMap.of(ref.getName(), ref))
- .isEmpty()) {
+ && !visibleTags(resource.getNameKey(), repo, ImmutableList.of(ref)).isEmpty()) {
return createTagInfo(
permissionBackend
.user(resource.getUser())
@@ -221,15 +221,11 @@ public class ListTags implements RestReadView<ProjectResource> {
}
}
- private Map<String, Ref> visibleTags(
- Project.NameKey project, Repository repo, Map<String, Ref> tags)
+ private Map<String, Ref> visibleTags(Project.NameKey project, Repository repo, List<Ref> tags)
throws PermissionBackendException {
return permissionBackend
.currentUser()
.project(project)
- .filter(
- tags,
- repo,
- RefFilterOptions.builder().setFilterMeta(true).setFilterTagsSeparately(true).build());
+ .filter(tags, repo, RefFilterOptions.builder().setFilterMeta(true).build());
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
index 8c8ab495f4..de5661d357 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -55,7 +55,6 @@ public class Module extends RestApiModule {
get(PROJECT_KIND, "access").to(GetAccess.class);
post(PROJECT_KIND, "access").to(SetAccess.class);
put(PROJECT_KIND, "access:review").to(CreateAccessChange.class);
- post(PROJECT_KIND, "check.access").to(CheckAccess.class);
get(PROJECT_KIND, "check.access").to(CheckAccessReadView.class);
post(PROJECT_KIND, "check").to(Check.class);
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
index ed317aadc9..31c90e5afa 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
@@ -17,7 +17,6 @@ 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.common.ProjectUtil;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -30,9 +29,10 @@ 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.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.OutputFormat;
+import com.google.gerrit.server.ProjectUtil;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
index e0b443b211..56ddbb4a99 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -47,6 +47,7 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.ProjectState.Factory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -54,7 +55,6 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.regex.Pattern;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -77,20 +77,22 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
private final DynamicMap<RestView<ProjectResource>> views;
private final Provider<CurrentUser> user;
private final PermissionBackend permissionBackend;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
PutConfig(
@EnableSignedPush boolean serverEnableSignedPush,
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
ProjectCache projectCache,
- ProjectState.Factory projectStateFactory,
+ Factory projectStateFactory,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginConfigFactory cfgFactory,
AllProjectsName allProjects,
UiActions uiActions,
DynamicMap<RestView<ProjectResource>> views,
Provider<CurrentUser> user,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ProjectConfig.Factory projectConfigFactory) {
this.serverEnableSignedPush = serverEnableSignedPush;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.projectCache = projectCache;
@@ -102,6 +104,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
this.views = views;
this.user = user;
this.permissionBackend = permissionBackend;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
@@ -122,7 +125,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
}
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(projectName)) {
- ProjectConfig projectConfig = ProjectConfig.read(md);
+ ProjectConfig projectConfig = projectConfigFactory.read(md);
Project p = projectConfig.getProject();
p.setDescription(Strings.emptyToNull(input.description));
@@ -164,7 +167,7 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
throw new ResourceConflictException("Cannot update " + projectName);
}
- ProjectState state = projectStateFactory.create(ProjectConfig.read(md));
+ ProjectState state = projectStateFactory.create(projectConfigFactory.read(md));
return new ConfigInfoImpl(
serverEnableSignedPush,
state,
@@ -188,10 +191,10 @@ public class PutConfig implements RestModifyView<ProjectResource, ConfigInput> {
ProjectConfig projectConfig,
Map<String, Map<String, ConfigValue>> pluginConfigValues)
throws BadRequestException {
- for (Entry<String, Map<String, ConfigValue>> e : pluginConfigValues.entrySet()) {
+ for (Map.Entry<String, Map<String, ConfigValue>> e : pluginConfigValues.entrySet()) {
String pluginName = e.getKey();
PluginConfig cfg = projectConfig.getPluginConfig(pluginName);
- for (Entry<String, ConfigValue> v : e.getValue().entrySet()) {
+ for (Map.Entry<String, ConfigValue> v : e.getValue().entrySet()) {
ProjectConfigEntry projectConfigEntry = pluginConfigEntries.get(pluginName, v.getKey());
if (projectConfigEntry != null) {
if (!PARAMETER_NAME_PATTERN.matcher(v.getKey()).matches()) {
diff --git a/java/com/google/gerrit/server/restapi/project/PutDescription.java b/java/com/google/gerrit/server/restapi/project/PutDescription.java
index f3366e5324..c3a063de68 100644
--- a/java/com/google/gerrit/server/restapi/project/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/project/PutDescription.java
@@ -43,15 +43,18 @@ public class PutDescription implements RestModifyView<ProjectResource, Descripti
private final ProjectCache cache;
private final Provider<MetaDataUpdate.Server> updateFactory;
private final PermissionBackend permissionBackend;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
PutDescription(
ProjectCache cache,
Provider<MetaDataUpdate.Server> updateFactory,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ProjectConfig.Factory projectConfigFactory) {
this.cache = cache;
this.updateFactory = updateFactory;
this.permissionBackend = permissionBackend;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
@@ -69,7 +72,7 @@ public class PutDescription implements RestModifyView<ProjectResource, Descripti
.check(ProjectPermission.WRITE_CONFIG);
try (MetaDataUpdate md = updateFactory.get().create(resource.getNameKey())) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
Project project = config.getProject();
project.setDescription(Strings.emptyToNull(input.description));
@@ -86,7 +89,7 @@ public class PutDescription implements RestModifyView<ProjectResource, Descripti
md.getRepository().setGitwebDescription(project.getDescription());
return Strings.isNullOrEmpty(project.getDescription())
- ? Response.<String>none()
+ ? Response.none()
: Response.ok(project.getDescription());
} catch (RepositoryNotFoundException notFound) {
throw new ResourceNotFoundException(resource.getName(), notFound);
diff --git a/java/com/google/gerrit/server/restapi/project/QueryProjects.java b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
index 44432aaf51..8727df33e3 100644
--- a/java/com/google/gerrit/server/restapi/project/QueryProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
@@ -29,7 +29,6 @@ import com.google.gerrit.index.query.QueryResult;
import com.google.gerrit.server.project.ProjectJson;
import com.google.gerrit.server.query.project.ProjectQueryBuilder;
import com.google.gerrit.server.query.project.ProjectQueryProcessor;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
@@ -88,12 +87,11 @@ public class QueryProjects implements RestReadView<TopLevelResource> {
@Override
public List<ProjectInfo> apply(TopLevelResource resource)
- throws BadRequestException, MethodNotAllowedException, OrmException {
+ throws BadRequestException, MethodNotAllowedException {
return apply();
}
- public List<ProjectInfo> apply()
- throws BadRequestException, MethodNotAllowedException, OrmException {
+ public List<ProjectInfo> apply() throws BadRequestException, MethodNotAllowedException {
if (Strings.isNullOrEmpty(query)) {
throw new BadRequestException("missing query field");
}
diff --git a/java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java b/java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java
index 2a2fc866f8..9f686eb4b7 100644
--- a/java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java
+++ b/java/com/google/gerrit/server/restapi/project/RepositoryStatistics.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.restapi.project;
import com.google.common.base.CaseFormat;
-import java.util.Map.Entry;
+import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
@@ -23,7 +23,7 @@ public class RepositoryStatistics extends TreeMap<String, Object> {
private static final long serialVersionUID = 1L;
RepositoryStatistics(Properties p) {
- for (Entry<Object, Object> e : p.entrySet()) {
+ for (Map.Entry<Object, Object> e : p.entrySet()) {
put(
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, e.getKey().toString()),
e.getValue());
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
index 4cefa4bddb..95bc75f7f0 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -17,7 +17,7 @@ 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.common.errors.InvalidNameException;
+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;
@@ -38,7 +38,6 @@ import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -56,6 +55,7 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
private final Provider<IdentifiedUser> identifiedUser;
private final SetAccessUtil accessUtil;
private final CreateGroupPermissionSyncer createGroupPermissionSyncer;
+ private final ProjectConfig.Factory projectConfigFactory;
@Inject
private SetAccess(
@@ -66,7 +66,8 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
GetAccess getAccess,
Provider<IdentifiedUser> identifiedUser,
SetAccessUtil accessUtil,
- CreateGroupPermissionSyncer createGroupPermissionSyncer) {
+ CreateGroupPermissionSyncer createGroupPermissionSyncer,
+ ProjectConfig.Factory projectConfigFactory) {
this.groupBackend = groupBackend;
this.permissionBackend = permissionBackend;
this.metaDataUpdateFactory = metaDataUpdateFactory;
@@ -75,13 +76,13 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
this.identifiedUser = identifiedUser;
this.accessUtil = accessUtil;
this.createGroupPermissionSyncer = createGroupPermissionSyncer;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
public ProjectAccessInfo apply(ProjectResource rsrc, ProjectAccessInput input)
throws ResourceNotFoundException, ResourceConflictException, IOException, AuthException,
- BadRequestException, UnprocessableEntityException, OrmException,
- PermissionBackendException {
+ BadRequestException, UnprocessableEntityException, PermissionBackendException {
MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
ProjectConfig config;
@@ -89,7 +90,7 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
List<AccessSection> removals = accessUtil.getAccessSections(input.remove);
List<AccessSection> additions = accessUtil.getAccessSections(input.add);
try (MetaDataUpdate md = metaDataUpdateUser.create(rsrc.getNameKey())) {
- config = ProjectConfig.read(md);
+ config = projectConfigFactory.read(md);
// Check that the user has the right permissions.
boolean checkedAdmin = false;
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
index c8857a23e0..e206319191 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
@@ -21,7 +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.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
@@ -146,7 +146,7 @@ public class SetAccessUtil {
boolean isGlobalCapabilities = AccessSection.GLOBAL_CAPABILITIES.equals(name);
if (!isGlobalCapabilities) {
- if (!AccessSection.isValid(name)) {
+ if (!AccessSection.isValidRefSectionName(name)) {
throw new BadRequestException("invalid section name");
}
RefPattern.validate(name);
diff --git a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
index 374f3b9e8e..3917bee62b 100644
--- a/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
+++ b/java/com/google/gerrit/server/restapi/project/SetDefaultDashboard.java
@@ -47,6 +47,7 @@ class SetDefaultDashboard implements RestModifyView<DashboardResource, SetDashbo
private final DashboardsCollection dashboards;
private final Provider<GetDashboard> get;
private final PermissionBackend permissionBackend;
+ private final ProjectConfig.Factory projectConfigFactory;
@Option(name = "--inherited", usage = "set dashboard inherited by children")
boolean inherited;
@@ -57,12 +58,14 @@ class SetDefaultDashboard implements RestModifyView<DashboardResource, SetDashbo
MetaDataUpdate.Server updateFactory,
DashboardsCollection dashboards,
Provider<GetDashboard> get,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ProjectConfig.Factory projectConfigFactory) {
this.cache = cache;
this.updateFactory = updateFactory;
this.dashboards = dashboards;
this.get = get;
this.permissionBackend = permissionBackend;
+ this.projectConfigFactory = projectConfigFactory;
}
@Override
@@ -93,7 +96,7 @@ class SetDefaultDashboard implements RestModifyView<DashboardResource, SetDashbo
}
try (MetaDataUpdate md = updateFactory.create(rsrc.getProjectState().getNameKey())) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
Project project = config.getProject();
if (inherited) {
project.setDefaultDashboard(input.id);
diff --git a/java/com/google/gerrit/server/restapi/project/SetParent.java b/java/com/google/gerrit/server/restapi/project/SetParent.java
index e778799f99..8e0363fbb0 100644
--- a/java/com/google/gerrit/server/restapi/project/SetParent.java
+++ b/java/com/google/gerrit/server/restapi/project/SetParent.java
@@ -62,6 +62,7 @@ public class SetParent
private final Provider<MetaDataUpdate.Server> updateFactory;
private final AllProjectsName allProjects;
private final AllUsersName allUsers;
+ private final ProjectConfig.Factory projectConfigFactory;
private volatile boolean allowProjectOwnersToChangeParent;
@Inject
@@ -71,12 +72,14 @@ public class SetParent
Provider<MetaDataUpdate.Server> updateFactory,
AllProjectsName allProjects,
AllUsersName allUsers,
+ ProjectConfig.Factory projectConfigFactory,
@GerritServerConfig Config config) {
this.cache = cache;
this.permissionBackend = permissionBackend;
this.updateFactory = updateFactory;
this.allProjects = allProjects;
this.allUsers = allUsers;
+ this.projectConfigFactory = projectConfigFactory;
this.allowProjectOwnersToChangeParent =
config.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
}
@@ -98,7 +101,7 @@ public class SetParent
MoreObjects.firstNonNull(Strings.emptyToNull(input.parent), allProjects.get());
validateParentUpdate(rsrc.getProjectState().getNameKey(), user, parentName, checkIfAdmin);
try (MetaDataUpdate md = updateFactory.get().create(rsrc.getNameKey())) {
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
Project project = config.getProject();
project.setParentName(parentName);
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
index b70858d2c7..2be6c19b47 100644
--- a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -20,6 +20,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
@@ -27,7 +28,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
@@ -80,7 +80,7 @@ public final class DefaultSubmitRule implements SubmitRule {
try {
labelTypes = cd.getLabelTypes().getLabelTypes();
approvals = cd.currentApprovals();
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log(
"Unable to fetch labels and approvals for change %s", cd.getId());
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
index f7c25a488f..759be39f44 100644
--- a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -23,14 +23,13 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.extensions.config.FactoryModule;
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.gwtorm.server.OrmException;
-import com.google.inject.Inject;
+import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collection;
@@ -47,7 +46,7 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
private static final String E_UNABLE_TO_FETCH_LABELS =
"Unable to fetch labels and approvals for the change";
- public static class Module extends FactoryModule {
+ public static class Module extends AbstractModule {
@Override
public void configure() {
bind(SubmitRule.class)
@@ -56,9 +55,6 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
}
}
- @Inject
- IgnoreSelfApprovalRule() {}
-
@Override
public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
List<LabelType> labelTypes;
@@ -66,12 +62,12 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
try {
labelTypes = cd.getLabelTypes().getLabelTypes();
approvals = cd.currentApprovals();
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_LABELS);
return singletonRuleError(E_UNABLE_TO_FETCH_LABELS);
}
- boolean shouldIgnoreSelfApproval = labelTypes.stream().anyMatch(l -> l.ignoreSelfApproval());
+ boolean shouldIgnoreSelfApproval = labelTypes.stream().anyMatch(LabelType::ignoreSelfApproval);
if (!shouldIgnoreSelfApproval) {
// Shortcut to avoid further processing if no label should ignore uploader approvals
return ImmutableList.of();
@@ -80,7 +76,7 @@ public class IgnoreSelfApprovalRule implements SubmitRule {
Account.Id uploader;
try {
uploader = cd.currentPatchSet().getUploader();
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_UPLOADER);
return singletonRuleError(E_UNABLE_TO_FETCH_UPLOADER);
}
diff --git a/java/com/google/gerrit/server/rules/PrologEnvironment.java b/java/com/google/gerrit/server/rules/PrologEnvironment.java
index 0b702c7aa6..ba78724bfa 100644
--- a/java/com/google/gerrit/server/rules/PrologEnvironment.java
+++ b/java/com/google/gerrit/server/rules/PrologEnvironment.java
@@ -20,6 +20,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
@@ -171,6 +172,7 @@ public class PrologEnvironment extends BufferingPrologControl {
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
private final GitRepositoryManager repositoryManager;
+ private final PluginConfigFactory pluginConfigFactory;
private final PatchListCache patchListCache;
private final PatchSetInfoFactory patchSetInfoFactory;
private final IdentifiedUser.GenericFactory userFactory;
@@ -185,6 +187,7 @@ public class PrologEnvironment extends BufferingPrologControl {
ProjectCache projectCache,
PermissionBackend permissionBackend,
GitRepositoryManager repositoryManager,
+ PluginConfigFactory pluginConfigFactory,
PatchListCache patchListCache,
PatchSetInfoFactory patchSetInfoFactory,
IdentifiedUser.GenericFactory userFactory,
@@ -195,6 +198,7 @@ public class PrologEnvironment extends BufferingPrologControl {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.repositoryManager = repositoryManager;
+ this.pluginConfigFactory = pluginConfigFactory;
this.patchListCache = patchListCache;
this.patchSetInfoFactory = patchSetInfoFactory;
this.userFactory = userFactory;
@@ -238,6 +242,10 @@ public class PrologEnvironment extends BufferingPrologControl {
return repositoryManager;
}
+ public PluginConfigFactory getPluginConfigFactory() {
+ return pluginConfigFactory;
+ }
+
public PatchListCache getPatchListCache() {
return patchListCache;
}
diff --git a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
index e61fc90935..9cde54c9a9 100644
--- a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
+++ b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
@@ -24,6 +24,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
@@ -36,7 +37,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.googlecode.prolog_cafe.exceptions.CompileException;
@@ -149,17 +149,17 @@ public class PrologRuleEvaluator {
try {
change = cd.change();
if (change == null) {
- throw new OrmException("No change found");
+ throw new StorageException("No change found");
}
if (projectState == null) {
throw new NoSuchProjectException(cd.project());
}
- } catch (OrmException | NoSuchProjectException e) {
+ } catch (StorageException | NoSuchProjectException e) {
return ruleError("Error looking up change " + cd.getId(), e);
}
- if (!opts.allowClosed() && change.getStatus().isClosed()) {
+ if (!opts.allowClosed() && change.isClosed()) {
SubmitRecord rec = new SubmitRecord();
rec.status = SubmitRecord.Status.CLOSED;
return Collections.singletonList(rec);
@@ -487,7 +487,6 @@ public class PrologRuleEvaluator {
env.set(StoredValues.ACCOUNTS, accounts);
env.set(StoredValues.ACCOUNT_CACHE, accountCache);
env.set(StoredValues.EMAILS, emails);
- env.set(StoredValues.REVIEW_DB, cd.db());
env.set(StoredValues.CHANGE_DATA, cd);
env.set(StoredValues.PROJECT_STATE, projectState);
return env;
diff --git a/java/com/google/gerrit/server/rules/StoredValues.java b/java/com/google/gerrit/server/rules/StoredValues.java
index ed70629937..30d329dca1 100644
--- a/java/com/google/gerrit/server/rules/StoredValues.java
+++ b/java/com/google/gerrit/server/rules/StoredValues.java
@@ -16,18 +16,19 @@ package com.google.gerrit.server.rules;
import static com.google.gerrit.server.rules.StoredValue.create;
+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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.Emails;
+import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
@@ -36,7 +37,6 @@ import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
import com.googlecode.prolog_cafe.exceptions.SystemException;
import com.googlecode.prolog_cafe.lang.Prolog;
import java.io.IOException;
@@ -50,7 +50,6 @@ public final class StoredValues {
public static final StoredValue<Accounts> ACCOUNTS = create(Accounts.class);
public static final StoredValue<AccountCache> ACCOUNT_CACHE = create(AccountCache.class);
public static final StoredValue<Emails> EMAILS = create(Emails.class);
- public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class);
public static final StoredValue<ChangeData> CHANGE_DATA = create(ChangeData.class);
public static final StoredValue<ProjectState> PROJECT_STATE = create(ProjectState.class);
@@ -58,7 +57,7 @@ public final class StoredValues {
ChangeData cd = CHANGE_DATA.get(engine);
try {
return cd.change();
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new SystemException(
String.format("Cannot load change %s: %s", cd.getId(), e.getMessage()));
}
@@ -68,7 +67,7 @@ public final class StoredValues {
ChangeData cd = CHANGE_DATA.get(engine);
try {
return cd.currentPatchSet();
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new SystemException(e.getMessage());
}
}
@@ -111,6 +110,27 @@ public final class StoredValues {
}
};
+ // Accessing GitRepositoryManager could be slow.
+ // It should be minimized or cached to reduce pause time
+ // when evaluating Prolog submit rules.
+ public static final StoredValue<GitRepositoryManager> REPO_MANAGER =
+ new StoredValue<GitRepositoryManager>() {
+ @Override
+ public GitRepositoryManager createValue(Prolog engine) {
+ PrologEnvironment env = (PrologEnvironment) engine.control;
+ return env.getArgs().getGitRepositoryManager();
+ }
+ };
+
+ public static final StoredValue<PluginConfigFactory> PLUGIN_CONFIG_FACTORY =
+ new StoredValue<PluginConfigFactory>() {
+ @Override
+ public PluginConfigFactory createValue(Prolog engine) {
+ PrologEnvironment env = (PrologEnvironment) engine.control;
+ return env.getArgs().getPluginConfigFactory();
+ }
+ };
+
public static final StoredValue<Repository> REPOSITORY =
new StoredValue<Repository>() {
@Override
diff --git a/java/com/google/gerrit/server/schema/AbstractDisabledAccess.java b/java/com/google/gerrit/server/schema/AbstractDisabledAccess.java
deleted file mode 100644
index 17eb56e11b..0000000000
--- a/java/com/google/gerrit/server/schema/AbstractDisabledAccess.java
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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.schema;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.Futures;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gwtorm.client.Key;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.AtomicUpdate;
-import com.google.gwtorm.server.ListResultSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-import java.util.Map;
-import java.util.function.Function;
-
-abstract class AbstractDisabledAccess<T, K extends Key<?>> implements Access<T, K> {
- private static <T> ResultSet<T> empty() {
- return new ListResultSet<>(ImmutableList.of());
- }
-
- @SuppressWarnings("deprecation")
- private static <T>
- com.google.common.util.concurrent.CheckedFuture<T, OrmException> emptyFuture() {
- return Futures.immediateCheckedFuture(null);
- }
-
- // Don't even hold a reference to delegate, so it's not possible to use it
- // accidentally.
- private final ReviewDbWrapper wrapper;
- private final String relationName;
- private final int relationId;
- private final Function<T, K> primaryKey;
- private final Function<Iterable<T>, Map<K, T>> toMap;
-
- AbstractDisabledAccess(ReviewDbWrapper wrapper, Access<T, K> delegate) {
- this.wrapper = wrapper;
- this.relationName = delegate.getRelationName();
- this.relationId = delegate.getRelationID();
- this.primaryKey = delegate::primaryKey;
- this.toMap = delegate::toMap;
- }
-
- @Override
- public final int getRelationID() {
- return relationId;
- }
-
- @Override
- public final String getRelationName() {
- return relationName;
- }
-
- @Override
- public final K primaryKey(T entity) {
- return primaryKey.apply(entity);
- }
-
- @Override
- public final Map<K, T> toMap(Iterable<T> iterable) {
- return toMap.apply(iterable);
- }
-
- @Override
- public final ResultSet<T> iterateAllEntities() {
- return empty();
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public final com.google.common.util.concurrent.CheckedFuture<T, OrmException> getAsync(K key) {
- return emptyFuture();
- }
-
- @Override
- public final ResultSet<T> get(Iterable<K> keys) {
- return empty();
- }
-
- @Override
- public final void insert(Iterable<T> instances) {
- // Do nothing.
- }
-
- @Override
- public final void update(Iterable<T> instances) {
- // Do nothing.
- }
-
- @Override
- public final void upsert(Iterable<T> instances) {
- // Do nothing.
- }
-
- @Override
- public final void deleteKeys(Iterable<K> keys) {
- // Do nothing.
- }
-
- @Override
- public final void delete(Iterable<T> instances) {
- // Do nothing.
- }
-
- @Override
- public final void beginTransaction(K key) {
- // Keep track of when we've started a transaction so that we can avoid calling commit/rollback
- // on the underlying ReviewDb. This is just a simple arm's-length approach, and may produce
- // slightly different results from a native ReviewDb in corner cases like:
- // * beginning transactions on different tables simultaneously
- // * doing work between commit and rollback
- // These kinds of things are already misuses of ReviewDb, and shouldn't be happening in current
- // code anyway.
- checkState(!wrapper.inTransaction(), "already in transaction");
- wrapper.beginTransaction();
- }
-
- @Override
- public final T atomicUpdate(K key, AtomicUpdate<T> update) {
- return null;
- }
-
- @Override
- public final T get(K id) {
- return null;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 348f88c6f4..9446b7c908 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -22,39 +22,27 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS
import static com.google.gerrit.server.schema.AclUtil.grant;
import static com.google.gerrit.server.schema.AclUtil.rule;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.Version;
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.LabelType;
-import com.google.gerrit.common.data.LabelValue;
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.extensions.client.InheritableBoolean;
-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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.UsedAt;
import com.google.gerrit.server.config.AllProjectsName;
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.server.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.RepoSequence;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.inject.Inject;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.BatchRefUpdate;
@@ -72,84 +60,39 @@ public class AllProjectsCreator {
private final GitRepositoryManager repositoryManager;
private final AllProjectsName allProjectsName;
private final PersonIdent serverUser;
- private final NotesMigration notesMigration;
+ private final NoteDbSchemaVersionManager versionManager;
+ private final ProjectConfig.Factory projectConfigFactory;
private final GroupReference anonymous;
private final GroupReference registered;
private final GroupReference owners;
- @Nullable private GroupReference admin;
- @Nullable private GroupReference batch;
- private String message;
- private int firstChangeId = ReviewDb.FIRST_CHANGE_ID;
- private LabelType codeReviewLabel;
- private List<LabelType> additionalLabelType;
-
@Inject
AllProjectsCreator(
GitRepositoryManager repositoryManager,
AllProjectsName allProjectsName,
@GerritPersonIdent PersonIdent serverUser,
- NotesMigration notesMigration,
- SystemGroupBackend systemGroupBackend) {
+ NoteDbSchemaVersionManager versionManager,
+ SystemGroupBackend systemGroupBackend,
+ ProjectConfig.Factory projectConfigFactory) {
this.repositoryManager = repositoryManager;
this.allProjectsName = allProjectsName;
this.serverUser = serverUser;
- this.notesMigration = notesMigration;
+ this.versionManager = versionManager;
+ this.projectConfigFactory = projectConfigFactory;
this.anonymous = systemGroupBackend.getGroup(ANONYMOUS_USERS);
this.registered = systemGroupBackend.getGroup(REGISTERED_USERS);
this.owners = systemGroupBackend.getGroup(PROJECT_OWNERS);
- this.codeReviewLabel = getDefaultCodeReviewLabel();
- this.additionalLabelType = new ArrayList<>();
- }
-
- /** If called, grant default permissions to this admin group */
- public AllProjectsCreator setAdministrators(GroupReference admin) {
- this.admin = admin;
- return this;
- }
-
- /** If called, grant stream-events permission and set appropriate priority for this group */
- public AllProjectsCreator setBatchUsers(GroupReference batch) {
- this.batch = batch;
- return this;
- }
-
- public AllProjectsCreator setCommitMessage(String message) {
- this.message = message;
- return this;
- }
-
- @UsedAt(UsedAt.Project.GOOGLE)
- public AllProjectsCreator setFirstChangeIdForNoteDb(int id) {
- checkArgument(id > 0, "id must be positive: %s", id);
- firstChangeId = id;
- return this;
- }
-
- /** If called, the provided "Code-Review" label will be used rather than the default. */
- @UsedAt(UsedAt.Project.GOOGLE)
- public AllProjectsCreator setCodeReviewLabel(LabelType labelType) {
- checkArgument(
- labelType.getName().equals("Code-Review"), "label should have 'Code-Review' as its name");
- this.codeReviewLabel = labelType;
- return this;
- }
-
- @UsedAt(UsedAt.Project.GOOGLE)
- public AllProjectsCreator addAdditionalLabel(LabelType labelType) {
- additionalLabelType.add(labelType);
- return this;
}
- public void create() throws IOException, ConfigInvalidException {
+ public void create(AllProjectsInput input) throws IOException, ConfigInvalidException {
try (Repository git = repositoryManager.openRepository(allProjectsName)) {
- initAllProjects(git);
+ initAllProjects(git, input);
} catch (RepositoryNotFoundException notFound) {
// A repository may be missing if this project existed only to store
// inheritable permissions. For example 'All-Projects'.
try (Repository git = repositoryManager.createRepository(allProjectsName)) {
- initAllProjects(git);
+ initAllProjects(git, input);
RefUpdate u = git.updateRef(Constants.HEAD);
u.link(RefNames.REFS_CONFIG);
} catch (RepositoryNotFoundException err) {
@@ -159,103 +102,131 @@ public class AllProjectsCreator {
}
}
- private void initAllProjects(Repository git) throws IOException, ConfigInvalidException {
+ private void initAllProjects(Repository git, AllProjectsInput input)
+ throws ConfigInvalidException, IOException {
BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
try (MetaDataUpdate md =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git, bru)) {
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
md.setMessage(
- MoreObjects.firstNonNull(
- Strings.emptyToNull(message),
- "Initialized Gerrit Code Review " + Version.getVersion()));
+ input.commitMessage().isPresent()
+ ? input.commitMessage().get()
+ : "Initialized Gerrit Code Review " + Version.getVersion());
- ProjectConfig config = ProjectConfig.read(md);
+ // init basic project configs.
+ ProjectConfig config = projectConfigFactory.read(md);
Project p = config.getProject();
- p.setDescription("Access inherited by all other projects.");
- p.setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, InheritableBoolean.TRUE);
- p.setBooleanConfig(BooleanProjectConfig.USE_CONTENT_MERGE, InheritableBoolean.TRUE);
- p.setBooleanConfig(BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS, InheritableBoolean.FALSE);
- p.setBooleanConfig(BooleanProjectConfig.USE_SIGNED_OFF_BY, InheritableBoolean.FALSE);
- p.setBooleanConfig(BooleanProjectConfig.ENABLE_SIGNED_PUSH, InheritableBoolean.FALSE);
+ p.setDescription(
+ input.projectDescription().orElse("Access inherited by all other projects."));
+
+ // init boolean project configs.
+ input.booleanProjectConfigs().forEach(p::setBooleanConfig);
+
+ // init labels.
+ input
+ .codeReviewLabel()
+ .ifPresent(
+ codeReviewLabel ->
+ config.getLabelSections().put(codeReviewLabel.getName(), codeReviewLabel));
+
+ if (input.initDefaultAcls()) {
+ // init access sections.
+ initDefaultAcls(config, input);
+ }
- AccessSection cap = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true);
- AccessSection all = config.getAccessSection(AccessSection.ALL, true);
- AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
- AccessSection tags = config.getAccessSection("refs/tags/*", true);
- AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
- AccessSection refsFor = config.getAccessSection("refs/for/*", true);
- AccessSection magic = config.getAccessSection("refs/for/" + AccessSection.ALL, true);
+ // commit all the above configs as a commit in "refs/meta/config" branch of the All-Projects.
+ config.commitToNewRef(md, RefNames.REFS_CONFIG);
- grant(config, cap, GlobalCapability.ADMINISTRATE_SERVER, admin);
- grant(config, all, Permission.READ, admin, anonymous);
- grant(config, refsFor, Permission.ADD_PATCH_SET, registered);
+ // init sequence number.
+ initSequences(git, bru, input.firstChangeIdForNoteDb());
- if (batch != null) {
- Permission priority = cap.getPermission(GlobalCapability.PRIORITY, true);
- PermissionRule r = rule(config, batch);
- r.setAction(Action.BATCH);
- priority.add(r);
+ // init schema
+ versionManager.init();
- Permission stream = cap.getPermission(GlobalCapability.STREAM_EVENTS, true);
- stream.add(rule(config, batch));
- }
+ execute(git, bru);
+ }
+ }
- initLabels(config);
- grant(config, heads, codeReviewLabel, -1, 1, registered);
+ private void initDefaultAcls(ProjectConfig config, AllProjectsInput input) {
+ AccessSection capabilities = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true);
+ AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
- grant(config, heads, codeReviewLabel, -2, 2, admin, owners);
- grant(config, heads, Permission.CREATE, admin, owners);
- grant(config, heads, Permission.PUSH, admin, owners);
- grant(config, heads, Permission.SUBMIT, admin, owners);
- grant(config, heads, Permission.FORGE_AUTHOR, registered);
- grant(config, heads, Permission.FORGE_COMMITTER, admin, owners);
- grant(config, heads, Permission.EDIT_TOPIC_NAME, true, admin, owners);
+ checkArgument(input.codeReviewLabel().isPresent());
+ LabelType codeReviewLabel = input.codeReviewLabel().get();
- grant(config, tags, Permission.CREATE, admin, owners);
- grant(config, tags, Permission.CREATE_TAG, admin, owners);
- grant(config, tags, Permission.CREATE_SIGNED_TAG, admin, owners);
+ initDefaultAclsForRegisteredUsers(heads, codeReviewLabel, config);
- grant(config, magic, Permission.PUSH, registered);
- grant(config, magic, Permission.PUSH_MERGE, registered);
+ input
+ .batchUsersGroup()
+ .ifPresent(
+ batchUsersGroup -> initDefaultAclsForBatchUsers(capabilities, config, batchUsersGroup));
- meta.getPermission(Permission.READ, true).setExclusiveGroup(true);
- grant(config, meta, Permission.READ, admin, owners);
- grant(config, meta, codeReviewLabel, -2, 2, admin, owners);
- grant(config, meta, Permission.CREATE, admin, owners);
- grant(config, meta, Permission.PUSH, admin, owners);
- grant(config, meta, Permission.SUBMIT, admin, owners);
+ input
+ .administratorsGroup()
+ .ifPresent(
+ adminsGroup ->
+ initDefaultAclsForAdmins(
+ capabilities, config, heads, codeReviewLabel, adminsGroup));
+ }
- config.commitToNewRef(md, RefNames.REFS_CONFIG);
- initSequences(git, bru);
- execute(git, bru);
- }
+ private void initDefaultAclsForRegisteredUsers(
+ AccessSection heads, LabelType codeReviewLabel, ProjectConfig config) {
+ AccessSection refsFor = config.getAccessSection("refs/for/*", true);
+ AccessSection magic = config.getAccessSection("refs/for/" + AccessSection.ALL, true);
+
+ grant(config, refsFor, Permission.ADD_PATCH_SET, registered);
+ grant(config, heads, codeReviewLabel, -1, 1, registered);
+ grant(config, heads, Permission.FORGE_AUTHOR, registered);
+ grant(config, magic, Permission.PUSH, registered);
+ grant(config, magic, Permission.PUSH_MERGE, registered);
}
- @UsedAt(UsedAt.Project.GOOGLE)
- public static LabelType getDefaultCodeReviewLabel() {
- LabelType type =
- new LabelType(
- "Code-Review",
- ImmutableList.of(
- new LabelValue((short) 2, "Looks good to me, approved"),
- new LabelValue((short) 1, "Looks good to me, but someone else must approve"),
- new LabelValue((short) 0, "No score"),
- new LabelValue((short) -1, "I would prefer this is not merged as is"),
- new LabelValue((short) -2, "This shall not be merged")));
- type.setCopyMinScore(true);
- type.setCopyAllScoresOnTrivialRebase(true);
- return type;
+ private void initDefaultAclsForBatchUsers(
+ AccessSection capabilities, ProjectConfig config, GroupReference batchUsersGroup) {
+ Permission priority = capabilities.getPermission(GlobalCapability.PRIORITY, true);
+ PermissionRule r = rule(config, batchUsersGroup);
+ r.setAction(Action.BATCH);
+ priority.add(r);
+
+ Permission stream = capabilities.getPermission(GlobalCapability.STREAM_EVENTS, true);
+ stream.add(rule(config, batchUsersGroup));
}
- private void initLabels(ProjectConfig projectConfig) {
- projectConfig.getLabelSections().put(codeReviewLabel.getName(), codeReviewLabel);
- additionalLabelType.forEach(t -> projectConfig.getLabelSections().put(t.getName(), t));
+ private void initDefaultAclsForAdmins(
+ AccessSection capabilities,
+ ProjectConfig config,
+ AccessSection heads,
+ LabelType codeReviewLabel,
+ GroupReference adminsGroup) {
+ AccessSection all = config.getAccessSection(AccessSection.ALL, true);
+ AccessSection tags = config.getAccessSection("refs/tags/*", true);
+ AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
+
+ grant(config, capabilities, GlobalCapability.ADMINISTRATE_SERVER, adminsGroup);
+ grant(config, all, Permission.READ, adminsGroup, anonymous);
+ grant(config, heads, codeReviewLabel, -2, 2, adminsGroup, owners);
+ grant(config, heads, Permission.CREATE, adminsGroup, owners);
+ grant(config, heads, Permission.PUSH, adminsGroup, owners);
+ grant(config, heads, Permission.SUBMIT, adminsGroup, owners);
+ grant(config, heads, Permission.FORGE_COMMITTER, adminsGroup, owners);
+ grant(config, heads, Permission.EDIT_TOPIC_NAME, true, adminsGroup, owners);
+
+ grant(config, tags, Permission.CREATE, adminsGroup, owners);
+ grant(config, tags, Permission.CREATE_TAG, adminsGroup, owners);
+ grant(config, tags, Permission.CREATE_SIGNED_TAG, adminsGroup, owners);
+
+ meta.getPermission(Permission.READ, true).setExclusiveGroup(true);
+ grant(config, meta, Permission.READ, adminsGroup, owners);
+ grant(config, meta, codeReviewLabel, -2, 2, adminsGroup, owners);
+ grant(config, meta, Permission.CREATE, adminsGroup, owners);
+ grant(config, meta, Permission.PUSH, adminsGroup, owners);
+ grant(config, meta, Permission.SUBMIT, adminsGroup, owners);
}
- private void initSequences(Repository git, BatchRefUpdate bru) throws IOException {
- if (notesMigration.readChangeSequence()
- && git.exactRef(REFS_SEQUENCES + Sequences.NAME_CHANGES) == null) {
+ private void initSequences(Repository git, BatchRefUpdate bru, int firstChangeId)
+ throws IOException {
+ if (git.exactRef(REFS_SEQUENCES + Sequences.NAME_CHANGES) == null) {
// Can't easily reuse the inserter from MetaDataUpdate, but this shouldn't slow down site
// initialization unduly.
try (ObjectInserter ins = git.newObjectInserter()) {
diff --git a/java/com/google/gerrit/server/schema/AllProjectsInput.java b/java/com/google/gerrit/server/schema/AllProjectsInput.java
new file mode 100644
index 0000000000..7231b1858a
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/AllProjectsInput.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.server.schema;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+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.extensions.client.InheritableBoolean;
+import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
+import com.google.gerrit.server.notedb.Sequences;
+import java.util.Optional;
+
+@AutoValue
+public abstract class AllProjectsInput {
+
+ /** Default boolean configs set when initializing All-Projects. */
+ public static final ImmutableMap<BooleanProjectConfig, InheritableBoolean>
+ DEFAULT_BOOLEAN_PROJECT_CONFIGS =
+ ImmutableMap.of(
+ BooleanProjectConfig.REQUIRE_CHANGE_ID,
+ InheritableBoolean.TRUE,
+ BooleanProjectConfig.USE_CONTENT_MERGE,
+ InheritableBoolean.TRUE,
+ BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS,
+ InheritableBoolean.FALSE,
+ BooleanProjectConfig.USE_SIGNED_OFF_BY,
+ InheritableBoolean.FALSE,
+ BooleanProjectConfig.ENABLE_SIGNED_PUSH,
+ InheritableBoolean.FALSE);
+
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public static LabelType getDefaultCodeReviewLabel() {
+ LabelType type =
+ new LabelType(
+ "Code-Review",
+ ImmutableList.of(
+ new LabelValue((short) 2, "Looks good to me, approved"),
+ new LabelValue((short) 1, "Looks good to me, but someone else must approve"),
+ new LabelValue((short) 0, "No score"),
+ new LabelValue((short) -1, "I would prefer this is not merged as is"),
+ new LabelValue((short) -2, "This shall not be merged")));
+ type.setCopyMinScore(true);
+ type.setCopyAllScoresOnTrivialRebase(true);
+ return type;
+ }
+
+ /** The administrator group which gets default permissions granted. */
+ public abstract Optional<GroupReference> administratorsGroup();
+
+ /** The group which gets stream-events permission granted and appropriate properties set. */
+ public abstract Optional<GroupReference> batchUsersGroup();
+
+ /** The commit message used when commit the project config change. */
+ public abstract Optional<String> commitMessage();
+
+ /** The first change-id used in this host. */
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public abstract int firstChangeIdForNoteDb();
+
+ /** The "Code-Review" label to be defined in All-Projects. */
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public abstract Optional<LabelType> codeReviewLabel();
+
+ /** Description for the All-Projects. */
+ public abstract Optional<String> projectDescription();
+
+ /** Boolean project configs to be set in All-Projects. */
+ public abstract ImmutableMap<BooleanProjectConfig, InheritableBoolean> booleanProjectConfigs();
+
+ /** Whether initializing default access sections in All-Projects. */
+ public abstract boolean initDefaultAcls();
+
+ public abstract Builder toBuilder();
+
+ public static Builder builder() {
+ Builder builder =
+ new AutoValue_AllProjectsInput.Builder()
+ .codeReviewLabel(getDefaultCodeReviewLabel())
+ .firstChangeIdForNoteDb(Sequences.FIRST_CHANGE_ID)
+ .initDefaultAcls(true);
+ DEFAULT_BOOLEAN_PROJECT_CONFIGS.forEach(builder::addBooleanProjectConfig);
+
+ return builder;
+ }
+
+ public static Builder builderWithNoDefault() {
+ return new AutoValue_AllProjectsInput.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder administratorsGroup(GroupReference adminGroup);
+
+ public abstract Builder batchUsersGroup(GroupReference batchGroup);
+
+ public abstract Builder commitMessage(String commitMessage);
+
+ public abstract Builder firstChangeIdForNoteDb(int firstChangeId);
+
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public abstract Builder codeReviewLabel(LabelType codeReviewLabel);
+
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public abstract Builder projectDescription(String projectDescription);
+
+ public abstract ImmutableMap.Builder<BooleanProjectConfig, InheritableBoolean>
+ booleanProjectConfigsBuilder();
+
+ public Builder addBooleanProjectConfig(
+ BooleanProjectConfig booleanProjectConfig, InheritableBoolean inheritableBoolean) {
+ booleanProjectConfigsBuilder().put(booleanProjectConfig, inheritableBoolean);
+ return this;
+ }
+
+ @UsedAt(UsedAt.Project.GOOGLE)
+ public abstract Builder initDefaultAcls(boolean initDefaultACLs);
+
+ public abstract AllProjectsInput build();
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/AllUsersCreator.java b/java/com/google/gerrit/server/schema/AllUsersCreator.java
index 3779d0d548..d2f5ef183c 100644
--- a/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -17,9 +17,10 @@ package com.google.gerrit.server.schema;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.schema.AclUtil.grant;
-import static com.google.gerrit.server.schema.AllProjectsCreator.getDefaultCodeReviewLabel;
+import static com.google.gerrit.server.schema.AllProjectsInput.getDefaultCodeReviewLabel;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupReference;
@@ -28,13 +29,13 @@ 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.server.GerritPersonIdent;
-import com.google.gerrit.server.UsedAt;
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.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectConfig;
+import com.google.gerrit.server.project.ProjectConfig.Factory;
import com.google.gerrit.server.project.RefPattern;
import com.google.inject.Inject;
import java.io.IOException;
@@ -50,6 +51,7 @@ public class AllUsersCreator {
private final GitRepositoryManager mgr;
private final AllUsersName allUsersName;
private final PersonIdent serverUser;
+ private final ProjectConfig.Factory projectConfigFactory;
private final GroupReference registered;
@Nullable private GroupReference admin;
@@ -60,11 +62,13 @@ public class AllUsersCreator {
GitRepositoryManager mgr,
AllUsersName allUsersName,
SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
+ @GerritPersonIdent PersonIdent serverUser,
+ Factory projectConfigFactory) {
this.mgr = mgr;
this.allUsersName = allUsersName;
this.serverUser = serverUser;
this.registered = systemGroupBackend.getGroup(REGISTERED_USERS);
+ this.projectConfigFactory = projectConfigFactory;
this.codeReviewLabel = getDefaultCodeReviewLabel();
}
@@ -107,7 +111,7 @@ public class AllUsersCreator {
md.getCommitBuilder().setCommitter(serverUser);
md.setMessage("Initialized Gerrit Code Review " + Version.getVersion());
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
Project project = config.getProject();
project.setDescription("Individual user settings and preferences.");
diff --git a/java/com/google/gerrit/server/schema/BUILD b/java/com/google/gerrit/server/schema/BUILD
index 41016b0d95..ee99c67c71 100644
--- a/java/com/google/gerrit/server/schema/BUILD
+++ b/java/com/google/gerrit/server/schema/BUILD
@@ -9,14 +9,16 @@ java_library(
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/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",
"//lib:guava",
- "//lib:gwtorm",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/commons:dbcp",
diff --git a/java/com/google/gerrit/server/schema/BaseDataSourceType.java b/java/com/google/gerrit/server/schema/BaseDataSourceType.java
deleted file mode 100644
index 4b3a5700d2..0000000000
--- a/java/com/google/gerrit/server/schema/BaseDataSourceType.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2012 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import java.io.IOException;
-import java.io.InputStream;
-
-public abstract class BaseDataSourceType implements DataSourceType {
-
- private static final String DEFAULT_VALIDATION_QUERY = "select 1";
- private final String driver;
-
- protected BaseDataSourceType(String driver) {
- this.driver = driver;
- }
-
- @Override
- public final String getDriver() {
- return driver;
- }
-
- @Override
- public boolean usePool() {
- return true;
- }
-
- @Override
- public String getValidationQuery() {
- return DEFAULT_VALIDATION_QUERY;
- }
-
- @Override
- public ScriptRunner getIndexScript() throws IOException {
- return getScriptRunner("index_generic.sql");
- }
-
- protected static final ScriptRunner getScriptRunner(String path) throws IOException {
- if (path == null) {
- return ScriptRunner.NOOP;
- }
- ScriptRunner runner;
- try (InputStream in = ReviewDb.class.getResourceAsStream(path)) {
- if (in == null) {
- throw new IllegalStateException("SQL script " + path + " not found");
- }
- runner = new ScriptRunner(path, in);
- }
- return runner;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/DB2.java b/java/com/google/gerrit/server/schema/DB2.java
deleted file mode 100644
index fcf8c1f8f3..0000000000
--- a/java/com/google/gerrit/server/schema/DB2.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 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.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-public class DB2 extends BaseDataSourceType {
- private Config cfg;
-
- @Inject
- public DB2(@GerritServerConfig Config cfg) {
- super("com.ibm.db2.jcc.DB2Driver");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- final StringBuilder b = new StringBuilder();
- final ConfigSection dbc = new ConfigSection(cfg, "database");
- b.append("jdbc:db2://");
- b.append(hostname(dbc.optional("hostname")));
- b.append(port(dbc.optional("port")));
- b.append("/");
- b.append(dbc.required("database"));
- return b.toString();
- }
-
- @Override
- public String getValidationQuery() {
- return "SELECT 1 FROM SYSIBM.SYSDUMMY1";
- }
-}
diff --git a/java/com/google/gerrit/server/schema/DataSourceModule.java b/java/com/google/gerrit/server/schema/DataSourceModule.java
deleted file mode 100644
index ee57c8bf75..0000000000
--- a/java/com/google/gerrit/server/schema/DataSourceModule.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2012 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 com.google.inject.AbstractModule;
-import com.google.inject.name.Names;
-
-public class DataSourceModule extends AbstractModule {
-
- @Override
- protected void configure() {
- bind(DataSourceType.class).annotatedWith(Names.named("db2")).to(DB2.class);
- bind(DataSourceType.class).annotatedWith(Names.named("derby")).to(Derby.class);
- bind(DataSourceType.class).annotatedWith(Names.named("h2")).to(H2.class);
- bind(DataSourceType.class).annotatedWith(Names.named("jdbc")).to(JDBC.class);
- bind(DataSourceType.class).annotatedWith(Names.named("mariadb")).to(MariaDb.class);
- bind(DataSourceType.class).annotatedWith(Names.named("mysql")).to(MySql.class);
- bind(DataSourceType.class).annotatedWith(Names.named("oracle")).to(Oracle.class);
- bind(DataSourceType.class).annotatedWith(Names.named("postgresql")).to(PostgreSQL.class);
- /*
- * DatabaseMetaData.getDatabaseProductName() returns "sap db" for MaxDB.
- * For auto-detection of the DB type (com.google.gerrit.pgm.util.SiteProgram#getDbType)
- * we have to map "sap db" additionally to "maxdb", which is used for explicit configuration.
- */
- bind(DataSourceType.class).annotatedWith(Names.named("maxdb")).to(MaxDb.class);
- bind(DataSourceType.class).annotatedWith(Names.named("sap db")).to(MaxDb.class);
- bind(DataSourceType.class).annotatedWith(Names.named("hana")).to(HANA.class);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/DataSourceProvider.java b/java/com/google/gerrit/server/schema/DataSourceProvider.java
deleted file mode 100644
index d4cfaa6cac..0000000000
--- a/java/com/google/gerrit/server/schema/DataSourceProvider.java
+++ /dev/null
@@ -1,206 +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.server.schema;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.extensions.persistence.DataSourceInterceptor;
-import com.google.gerrit.metrics.CallbackMetric1;
-import com.google.gerrit.metrics.Description;
-import com.google.gerrit.metrics.Field;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.jdbc.SimpleDataSource;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.sql.SQLException;
-import java.util.Properties;
-import javax.sql.DataSource;
-import org.apache.commons.dbcp.BasicDataSource;
-import org.eclipse.jgit.lib.Config;
-
-/** Provides access to the DataSource. */
-@Singleton
-public class DataSourceProvider implements Provider<DataSource>, LifecycleListener {
- private static final String DATABASE_KEY = "database";
-
- private final Config cfg;
- private final MetricMaker metrics;
- private final Context ctx;
- private final DataSourceType dst;
- private final ThreadSettingsConfig threadSettingsConfig;
- private DataSource ds;
-
- @Inject
- protected DataSourceProvider(
- @GerritServerConfig Config cfg,
- MetricMaker metrics,
- ThreadSettingsConfig threadSettingsConfig,
- Context ctx,
- DataSourceType dst) {
- this.cfg = cfg;
- this.metrics = metrics;
- this.threadSettingsConfig = threadSettingsConfig;
- this.ctx = ctx;
- this.dst = dst;
- }
-
- @Override
- public synchronized DataSource get() {
- if (ds == null) {
- ds = open(cfg, ctx, dst);
- }
- return ds;
- }
-
- @Override
- public void start() {}
-
- @Override
- public synchronized void stop() {
- if (ds instanceof BasicDataSource) {
- try {
- ((BasicDataSource) ds).close();
- } catch (SQLException e) {
- // Ignore the close failure.
- }
- }
- }
-
- public enum Context {
- SINGLE_USER,
- MULTI_USER
- }
-
- private DataSource open(Config cfg, Context context, DataSourceType dst) {
- ConfigSection dbs = new ConfigSection(cfg, DATABASE_KEY);
- String driver = dbs.optional("driver");
- if (Strings.isNullOrEmpty(driver)) {
- driver = dst.getDriver();
- }
-
- String url = dbs.optional("url");
- if (Strings.isNullOrEmpty(url)) {
- url = dst.getUrl();
- }
-
- String username = dbs.optional("username");
- String password = dbs.optional("password");
- String interceptor = dbs.optional("dataSourceInterceptorClass");
-
- boolean usePool;
- if (context == Context.SINGLE_USER) {
- usePool = false;
- } else {
- usePool = cfg.getBoolean(DATABASE_KEY, "connectionpool", dst.usePool());
- }
-
- if (usePool) {
- final BasicDataSource lds = new BasicDataSource();
- lds.setDriverClassName(driver);
- lds.setUrl(url);
- if (username != null && !username.isEmpty()) {
- lds.setUsername(username);
- }
- if (password != null && !password.isEmpty()) {
- lds.setPassword(password);
- }
- int poolLimit = threadSettingsConfig.getDatabasePoolLimit();
- lds.setMaxActive(poolLimit);
- lds.setMinIdle(cfg.getInt(DATABASE_KEY, "poolminidle", 4));
- lds.setMaxIdle(cfg.getInt(DATABASE_KEY, "poolmaxidle", Math.min(poolLimit, 16)));
- lds.setMaxWait(
- ConfigUtil.getTimeUnit(
- cfg,
- DATABASE_KEY,
- null,
- "poolmaxwait",
- MILLISECONDS.convert(30, SECONDS),
- MILLISECONDS));
- lds.setInitialSize(lds.getMinIdle());
- long evictIdleTimeMs = 1000L * 60;
- lds.setMinEvictableIdleTimeMillis(evictIdleTimeMs);
- lds.setTimeBetweenEvictionRunsMillis(evictIdleTimeMs / 2);
- lds.setTestOnBorrow(true);
- lds.setTestOnReturn(true);
- lds.setValidationQuery(dst.getValidationQuery());
- lds.setValidationQueryTimeout(5);
- exportPoolMetrics(lds);
- return intercept(interceptor, lds);
- }
- // Don't use the connection pool.
- //
- try {
- final Properties p = new Properties();
- p.setProperty("driver", driver);
- p.setProperty("url", url);
- if (username != null) {
- p.setProperty("user", username);
- }
- if (password != null) {
- p.setProperty("password", password);
- }
- return intercept(interceptor, new SimpleDataSource(p));
- } catch (SQLException se) {
- throw new ProvisionException("Database unavailable", se);
- }
- }
-
- private void exportPoolMetrics(BasicDataSource pool) {
- CallbackMetric1<Boolean, Integer> cnt =
- metrics.newCallbackMetric(
- "sql/connection_pool/connections",
- Integer.class,
- new Description("SQL database connections").setGauge().setUnit("connections"),
- Field.ofBoolean("active"));
- metrics.newTrigger(
- cnt,
- () -> {
- synchronized (pool) {
- cnt.set(true, pool.getNumActive());
- cnt.set(false, pool.getNumIdle());
- }
- });
- }
-
- private DataSource intercept(String interceptor, DataSource ds) {
- if (interceptor == null) {
- return ds;
- }
- try {
- Constructor<?> c = Class.forName(interceptor).getConstructor();
- DataSourceInterceptor datasourceInterceptor = (DataSourceInterceptor) c.newInstance();
- return datasourceInterceptor.intercept("reviewDb", ds);
- } catch (ClassNotFoundException
- | SecurityException
- | NoSuchMethodException
- | IllegalArgumentException
- | InstantiationException
- | IllegalAccessException
- | InvocationTargetException e) {
- throw new ProvisionException("Cannot intercept datasource", e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/DataSourceType.java b/java/com/google/gerrit/server/schema/DataSourceType.java
deleted file mode 100644
index cbdcf0f65a..0000000000
--- a/java/com/google/gerrit/server/schema/DataSourceType.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2012 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 java.io.IOException;
-
-/** Abstraction of a supported database platform */
-public interface DataSourceType {
-
- String getDriver();
-
- String getUrl();
-
- String getValidationQuery();
-
- boolean usePool();
-
- /**
- * Return a ScriptRunner that runs the index script. Must not return {@code null}, but may return
- * a ScriptRunner that does nothing.
- *
- * @throws IOException
- */
- ScriptRunner getIndexScript() throws IOException;
-}
diff --git a/java/com/google/gerrit/server/schema/DatabaseModule.java b/java/com/google/gerrit/server/schema/DatabaseModule.java
deleted file mode 100644
index 38a7751a55..0000000000
--- a/java/com/google/gerrit/server/schema/DatabaseModule.java
+++ /dev/null
@@ -1,41 +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.server.schema;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
-import com.google.gwtorm.jdbc.Database;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-
-/** Loads the database with standard dependencies. */
-public class DatabaseModule extends FactoryModule {
- @Override
- protected void configure() {
- TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
- new TypeLiteral<SchemaFactory<ReviewDb>>() {};
- TypeLiteral<Database<ReviewDb>> database = new TypeLiteral<Database<ReviewDb>>() {};
-
- bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
- bind(Key.get(schemaFactory, ReviewDbFactory.class)).to(database).in(SINGLETON);
- bind(database).toProvider(ReviewDbDatabaseProvider.class);
- bind(ChangeBundleReader.class).to(GwtormChangeBundleReader.class);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Derby.java b/java/com/google/gerrit/server/schema/Derby.java
deleted file mode 100644
index 9fb761d3c0..0000000000
--- a/java/com/google/gerrit/server/schema/Derby.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2015 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 com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class Derby extends BaseDataSourceType {
-
- protected final Config cfg;
- private final SitePaths site;
-
- @Inject
- Derby(@GerritServerConfig Config cfg, SitePaths site) {
- super("org.apache.derby.jdbc.EmbeddedDriver");
- this.cfg = cfg;
- this.site = site;
- }
-
- @Override
- public String getUrl() {
- String database = cfg.getString("database", null, "database");
- if (database == null || database.isEmpty()) {
- database = "db/ReviewDB";
- }
- return "jdbc:derby:" + site.resolve(database).toString() + ";create=true";
- }
-
- @Override
- public String getValidationQuery() {
- return "values 1";
- }
-}
diff --git a/java/com/google/gerrit/server/schema/GroupBundle.java b/java/com/google/gerrit/server/schema/GroupBundle.java
deleted file mode 100644
index e5fd7b7be5..0000000000
--- a/java/com/google/gerrit/server/schema/GroupBundle.java
+++ /dev/null
@@ -1,768 +0,0 @@
-// 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.schema;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
-import static java.util.Comparator.naturalOrder;
-import static java.util.Comparator.nullsLast;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Strings;
-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.common.collect.Multimaps;
-import com.google.common.collect.Streams;
-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.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.db.AuditLogReader;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * A bundle of all entities rooted at a single {@link AccountGroup} entity.
- *
- * <p>Used primarily during the migration process. Most callers should prefer {@link InternalGroup}
- * instead.
- */
-@AutoValue
-abstract class GroupBundle {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- static {
- // Initialization-time checks that the column set hasn't changed since the
- // last time this file was updated.
- checkColumns(AccountGroup.NameKey.class, 1);
- checkColumns(AccountGroup.UUID.class, 1);
- checkColumns(AccountGroup.Id.class, 1);
- checkColumns(AccountGroup.class, 1, 2, 4, 7, 9, 10, 11);
-
- checkColumns(AccountGroupById.Key.class, 1, 2);
- checkColumns(AccountGroupById.class, 1);
-
- checkColumns(AccountGroupByIdAud.Key.class, 1, 2, 3);
- checkColumns(AccountGroupByIdAud.class, 1, 2, 3, 4);
-
- checkColumns(AccountGroupMember.Key.class, 1, 2);
- checkColumns(AccountGroupMember.class, 1);
-
- checkColumns(AccountGroupMemberAudit.Key.class, 1, 2, 3);
- checkColumns(AccountGroupMemberAudit.class, 1, 2, 3, 4);
- }
-
- public enum Source {
- REVIEW_DB("ReviewDb"),
- NOTE_DB("NoteDb");
-
- private final String name;
-
- private Source(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- @Singleton
- public static class Factory {
- private final AuditLogReader auditLogReader;
-
- @Inject
- Factory(AuditLogReader auditLogReader) {
- this.auditLogReader = auditLogReader;
- }
-
- public GroupBundle fromNoteDb(
- Project.NameKey projectName, Repository repo, AccountGroup.UUID uuid)
- throws ConfigInvalidException, IOException {
- GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repo, uuid);
- InternalGroup internalGroup = groupConfig.getLoadedGroup().get();
- AccountGroup.Id groupId = internalGroup.getId();
-
- AccountGroup accountGroup =
- new AccountGroup(
- internalGroup.getNameKey(),
- internalGroup.getId(),
- internalGroup.getGroupUUID(),
- internalGroup.getCreatedOn());
- accountGroup.setDescription(internalGroup.getDescription());
- accountGroup.setOwnerGroupUUID(internalGroup.getOwnerGroupUUID());
- accountGroup.setVisibleToAll(internalGroup.isVisibleToAll());
-
- return create(
- Source.NOTE_DB,
- accountGroup,
- internalGroup.getMembers().stream()
- .map(
- accountId ->
- new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)))
- .collect(toImmutableSet()),
- auditLogReader.getMembersAudit(repo, uuid),
- internalGroup.getSubgroups().stream()
- .map(
- subgroupUuid ->
- new AccountGroupById(new AccountGroupById.Key(groupId, subgroupUuid)))
- .collect(toImmutableSet()),
- auditLogReader.getSubgroupsAudit(repo, uuid));
- }
-
- public static GroupBundle fromReviewDb(ReviewDb db, AccountGroup.UUID groupUuid)
- throws OrmException {
- JdbcSchema jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
- AccountGroup group = readAccountGroupFromReviewDb(jdbcSchema, groupUuid);
- AccountGroup.Id groupId = group.getId();
-
- return create(
- Source.REVIEW_DB,
- group,
- readAccountGroupMembersFromReviewDb(jdbcSchema, groupId),
- readAccountGroupMemberAuditsFromReviewDb(jdbcSchema, groupId),
- readAccountGroupSubgroupsFromReviewDb(jdbcSchema, groupId),
- readAccountGroupSubgroupAuditsFromReviewDb(jdbcSchema, groupId));
- }
-
- private static AccountGroup readAccountGroupFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.UUID groupUuid) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT group_id,"
- + " name,"
- + " created_on,"
- + " description,"
- + " owner_group_uuid,"
- + " visible_to_all"
- + " FROM account_groups"
- + " WHERE group_uuid = '"
- + groupUuid.get()
- + "'")) {
- if (!rs.next()) {
- throw new OrmException(String.format("Group %s not found", groupUuid));
- }
-
- AccountGroup.Id groupId = new AccountGroup.Id(rs.getInt(1));
- AccountGroup.NameKey groupName = new AccountGroup.NameKey(rs.getString(2));
- Timestamp createdOn = rs.getTimestamp(3);
- String description = rs.getString(4);
- AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID(rs.getString(5));
- boolean visibleToAll = "Y".equals(rs.getString(6));
-
- AccountGroup group = new AccountGroup(groupName, groupId, groupUuid, createdOn);
- group.setDescription(description);
- group.setOwnerGroupUUID(ownerGroupUuid);
- group.setVisibleToAll(visibleToAll);
-
- if (rs.next()) {
- throw new OrmException(String.format("Group UUID %s is ambiguous", groupUuid));
- }
-
- return group;
- } catch (SQLException e) {
- throw new OrmException(
- String.format("Failed to read account group %s from ReviewDb", groupUuid.get()), e);
- }
- }
-
- private static List<AccountGroupMember> readAccountGroupMembersFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT account_id"
- + " FROM account_group_members"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupMember> members = new ArrayList<>();
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
- members.add(new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)));
- }
- return members;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read members of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
-
- private static List<AccountGroupMemberAudit> readAccountGroupMemberAuditsFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT account_id, added_by, added_on, removed_by, removed_on"
- + " FROM account_group_members_audit"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupMemberAudit> audits = new ArrayList<>();
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
-
- Account.Id addedBy = new Account.Id(rs.getInt(2));
- Timestamp addedOn = rs.getTimestamp(3);
-
- Timestamp removedOn = rs.getTimestamp(5);
- Account.Id removedBy = removedOn != null ? new Account.Id(rs.getInt(4)) : null;
-
- AccountGroupMemberAudit.Key key =
- new AccountGroupMemberAudit.Key(accountId, groupId, addedOn);
- AccountGroupMemberAudit audit = new AccountGroupMemberAudit(key, addedBy);
- audit.removed(removedBy, removedOn);
- audits.add(audit);
- }
- return audits;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read member audits of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
-
- private static List<AccountGroupById> readAccountGroupSubgroupsFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT include_uuid"
- + " FROM account_group_by_id"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupById> subgroups = new ArrayList<>();
- while (rs.next()) {
- AccountGroup.UUID includedGroupUuid = new AccountGroup.UUID(rs.getString(1));
- subgroups.add(new AccountGroupById(new AccountGroupById.Key(groupId, includedGroupUuid)));
- }
- return subgroups;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read subgroups of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
-
- private static List<AccountGroupByIdAud> readAccountGroupSubgroupAuditsFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT include_uuid, added_by, added_on, removed_by, removed_on"
- + " FROM account_group_by_id_aud"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupByIdAud> audits = new ArrayList<>();
- while (rs.next()) {
- AccountGroup.UUID includedGroupUuid = new AccountGroup.UUID(rs.getString(1));
-
- Account.Id addedBy = new Account.Id(rs.getInt(2));
- Timestamp addedOn = rs.getTimestamp(3);
-
- Timestamp removedOn = rs.getTimestamp(5);
- Account.Id removedBy = removedOn != null ? new Account.Id(rs.getInt(4)) : null;
-
- AccountGroupByIdAud.Key key =
- new AccountGroupByIdAud.Key(groupId, includedGroupUuid, addedOn);
- AccountGroupByIdAud audit = new AccountGroupByIdAud(key, addedBy);
- audit.removed(removedBy, removedOn);
- audits.add(audit);
- }
- return audits;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read subgroup audits of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
- }
-
- private static final Comparator<AccountGroupMember> ACCOUNT_GROUP_MEMBER_COMPARATOR =
- Comparator.comparingInt((AccountGroupMember m) -> m.getAccountGroupId().get())
- .thenComparingInt(m -> m.getAccountId().get());
-
- private static final Comparator<AccountGroupMemberAudit> ACCOUNT_GROUP_MEMBER_AUDIT_COMPARATOR =
- Comparator.comparingInt((AccountGroupMemberAudit a) -> a.getGroupId().get())
- .thenComparing(AccountGroupMemberAudit::getAddedOn)
- .thenComparingInt(a -> a.getAddedBy().get())
- .thenComparingInt(a -> a.getMemberId().get())
- .thenComparing(
- a -> a.getRemovedBy() != null ? a.getRemovedBy().get() : null,
- nullsLast(naturalOrder()))
- .thenComparing(AccountGroupMemberAudit::getRemovedOn, nullsLast(naturalOrder()));
-
- private static final Comparator<AccountGroupById> ACCOUNT_GROUP_BY_ID_COMPARATOR =
- Comparator.comparingInt((AccountGroupById m) -> m.getGroupId().get())
- .thenComparing(AccountGroupById::getIncludeUUID);
-
- private static final Comparator<AccountGroupByIdAud> ACCOUNT_GROUP_BY_ID_AUD_COMPARATOR =
- Comparator.comparingInt((AccountGroupByIdAud a) -> a.getGroupId().get())
- .thenComparing(AccountGroupByIdAud::getAddedOn)
- .thenComparingInt(a -> a.getAddedBy().get())
- .thenComparing(AccountGroupByIdAud::getIncludeUUID)
- .thenComparing(
- a -> a.getRemovedBy() != null ? a.getRemovedBy().get() : null,
- nullsLast(naturalOrder()))
- .thenComparing(AccountGroupByIdAud::getRemovedOn, nullsLast(naturalOrder()));
-
- private static final Comparator<AuditEntry> AUDIT_ENTRY_COMPARATOR =
- Comparator.comparing(AuditEntry::getTimestamp)
- .thenComparing(AuditEntry::getAction, Comparator.comparingInt(Action::getOrder));
-
- public static GroupBundle create(
- Source source,
- AccountGroup group,
- Iterable<AccountGroupMember> members,
- Iterable<AccountGroupMemberAudit> memberAudit,
- Iterable<AccountGroupById> byId,
- Iterable<AccountGroupByIdAud> byIdAudit) {
- AccountGroup.UUID uuid = group.getGroupUUID();
- return new AutoValue_GroupBundle.Builder()
- .source(source)
- .group(group)
- .members(
- logIfNotUnique(
- source, uuid, members, ACCOUNT_GROUP_MEMBER_COMPARATOR, AccountGroupMember.class))
- .memberAudit(
- logIfNotUnique(
- source,
- uuid,
- memberAudit,
- ACCOUNT_GROUP_MEMBER_AUDIT_COMPARATOR,
- AccountGroupMemberAudit.class))
- .byId(
- logIfNotUnique(
- source, uuid, byId, ACCOUNT_GROUP_BY_ID_COMPARATOR, AccountGroupById.class))
- .byIdAudit(
- logIfNotUnique(
- source,
- uuid,
- byIdAudit,
- ACCOUNT_GROUP_BY_ID_AUD_COMPARATOR,
- AccountGroupByIdAud.class))
- .build();
- }
-
- private static <T> ImmutableSet<T> logIfNotUnique(
- Source source,
- AccountGroup.UUID uuid,
- Iterable<T> iterable,
- Comparator<T> comparator,
- Class<T> clazz) {
- List<T> list = Streams.stream(iterable).sorted(comparator).collect(toList());
- ImmutableSet<T> set = ImmutableSet.copyOf(list);
- if (set.size() != list.size()) {
- // One way this can happen is that distinct audit entities can compare equal, because
- // AccountGroup{MemberAudit,ByIdAud}.Key does not include the addedOn timestamp in its
- // members() list. However, this particular issue only applies to pure adds, since removedOn
- // *is* included in equality. As a result, if this happens, it means the audit log is already
- // corrupt, and it's not clear if we can programmatically repair it. For migrating to NoteDb,
- // we'll try our best to recreate it, but no guarantees it will match the real sequence of
- // attempted operations, which is in any case lost in the mists of time.
- logger.atWarning().log(
- "group %s in %s has duplicate %s entities: %s",
- uuid, source, clazz.getSimpleName(), iterable);
- }
- return set;
- }
-
- static Builder builder() {
- return new AutoValue_GroupBundle.Builder().members().memberAudit().byId().byIdAudit();
- }
-
- public static ImmutableList<String> compareWithAudits(
- GroupBundle reviewDbBundle, GroupBundle noteDbBundle) {
- return compare(reviewDbBundle, noteDbBundle, true);
- }
-
- public static ImmutableList<String> compareWithoutAudits(
- GroupBundle reviewDbBundle, GroupBundle noteDbBundle) {
- return compare(reviewDbBundle, noteDbBundle, false);
- }
-
- private static ImmutableList<String> compare(
- GroupBundle reviewDbBundle, GroupBundle noteDbBundle, boolean compareAudits) {
- // Normalize the ReviewDb bundle to what we expect in NoteDb. This means that values in error
- // messages will not reflect the actual data in ReviewDb, but it will make it easier for humans
- // to see the difference.
- reviewDbBundle = reviewDbBundle.truncateToSecond();
- AccountGroup reviewDbGroup = new AccountGroup(reviewDbBundle.group());
- reviewDbGroup.setDescription(Strings.emptyToNull(reviewDbGroup.getDescription()));
- reviewDbBundle = reviewDbBundle.toBuilder().group(reviewDbGroup).build();
-
- checkArgument(
- reviewDbBundle.source() == Source.REVIEW_DB,
- "first bundle's source must be %s: %s",
- Source.REVIEW_DB,
- reviewDbBundle);
- checkArgument(
- noteDbBundle.source() == Source.NOTE_DB,
- "second bundle's source must be %s: %s",
- Source.NOTE_DB,
- noteDbBundle);
-
- ImmutableList.Builder<String> result = ImmutableList.builder();
- if (!reviewDbBundle.group().equals(noteDbBundle.group())) {
- result.add(
- "AccountGroups differ\n"
- + ("ReviewDb: " + reviewDbBundle.group() + "\n")
- + ("NoteDb : " + noteDbBundle.group()));
- }
- if (!reviewDbBundle.members().equals(noteDbBundle.members())) {
- result.add(
- "AccountGroupMembers differ\n"
- + ("ReviewDb: " + reviewDbBundle.members() + "\n")
- + ("NoteDb : " + noteDbBundle.members()));
- }
- if (compareAudits
- && !areMemberAuditsConsideredEqual(
- reviewDbBundle.memberAudit(), noteDbBundle.memberAudit())) {
- result.add(
- "AccountGroupMemberAudits differ\n"
- + ("ReviewDb: " + reviewDbBundle.memberAudit() + "\n")
- + ("NoteDb : " + noteDbBundle.memberAudit()));
- }
- if (!reviewDbBundle.byId().equals(noteDbBundle.byId())) {
- result.add(
- "AccountGroupByIds differ\n"
- + ("ReviewDb: " + reviewDbBundle.byId() + "\n")
- + ("NoteDb : " + noteDbBundle.byId()));
- }
- if (compareAudits
- && !areByIdAuditsConsideredEqual(reviewDbBundle.byIdAudit(), noteDbBundle.byIdAudit())) {
- result.add(
- "AccountGroupByIdAudits differ\n"
- + ("ReviewDb: " + reviewDbBundle.byIdAudit() + "\n")
- + ("NoteDb : " + noteDbBundle.byIdAudit()));
- }
- return result.build();
- }
-
- private static boolean areMemberAuditsConsideredEqual(
- ImmutableSet<AccountGroupMemberAudit> reviewDbMemberAudits,
- ImmutableSet<AccountGroupMemberAudit> noteDbMemberAudits) {
- ListMultimap<String, AuditEntry> reviewDbMemberAuditsByMemberId =
- toMemberAuditEntriesByMemberId(reviewDbMemberAudits);
- ListMultimap<String, AuditEntry> noteDbMemberAuditsByMemberId =
- toMemberAuditEntriesByMemberId(noteDbMemberAudits);
-
- return areConsideredEqual(reviewDbMemberAuditsByMemberId, noteDbMemberAuditsByMemberId);
- }
-
- private static boolean areByIdAuditsConsideredEqual(
- ImmutableSet<AccountGroupByIdAud> reviewDbByIdAudits,
- ImmutableSet<AccountGroupByIdAud> noteDbByIdAudits) {
- ListMultimap<String, AuditEntry> reviewDbByIdAuditsById =
- toByIdAuditEntriesById(reviewDbByIdAudits);
- ListMultimap<String, AuditEntry> noteDbByIdAuditsById =
- toByIdAuditEntriesById(noteDbByIdAudits);
-
- return areConsideredEqual(reviewDbByIdAuditsById, noteDbByIdAuditsById);
- }
-
- private static ListMultimap<String, AuditEntry> toMemberAuditEntriesByMemberId(
- ImmutableSet<AccountGroupMemberAudit> memberAudits) {
- return memberAudits.stream()
- .flatMap(GroupBundle::toAuditEntries)
- .collect(
- Multimaps.toMultimap(
- AuditEntry::getTarget,
- Function.identity(),
- MultimapBuilder.hashKeys().arrayListValues()::build));
- }
-
- private static Stream<AuditEntry> toAuditEntries(AccountGroupMemberAudit memberAudit) {
- AuditEntry additionAuditEntry =
- AuditEntry.create(
- Action.ADD,
- memberAudit.getAddedBy(),
- memberAudit.getMemberId(),
- memberAudit.getAddedOn());
- if (memberAudit.isActive()) {
- return Stream.of(additionAuditEntry);
- }
-
- AuditEntry removalAuditEntry =
- AuditEntry.create(
- Action.REMOVE,
- memberAudit.getRemovedBy(),
- memberAudit.getMemberId(),
- memberAudit.getRemovedOn());
- return Stream.of(additionAuditEntry, removalAuditEntry);
- }
-
- private static ListMultimap<String, AuditEntry> toByIdAuditEntriesById(
- ImmutableSet<AccountGroupByIdAud> byIdAudits) {
- return byIdAudits.stream()
- .flatMap(GroupBundle::toAuditEntries)
- .collect(
- Multimaps.toMultimap(
- AuditEntry::getTarget,
- Function.identity(),
- MultimapBuilder.hashKeys().arrayListValues()::build));
- }
-
- private static Stream<AuditEntry> toAuditEntries(AccountGroupByIdAud byIdAudit) {
- AuditEntry additionAuditEntry =
- AuditEntry.create(
- Action.ADD, byIdAudit.getAddedBy(), byIdAudit.getIncludeUUID(), byIdAudit.getAddedOn());
- if (byIdAudit.isActive()) {
- return Stream.of(additionAuditEntry);
- }
-
- AuditEntry removalAuditEntry =
- AuditEntry.create(
- Action.REMOVE,
- byIdAudit.getRemovedBy(),
- byIdAudit.getIncludeUUID(),
- byIdAudit.getRemovedOn());
- return Stream.of(additionAuditEntry, removalAuditEntry);
- }
-
- /**
- * Determines whether the audit log entries are equal except for redundant entries. Entries of the
- * same type (addition/removal) which follow directly on each other according to their timestamp
- * are considered redundant.
- */
- private static boolean areConsideredEqual(
- ListMultimap<String, AuditEntry> reviewDbMemberAuditsByTarget,
- ListMultimap<String, AuditEntry> noteDbMemberAuditsByTarget) {
- for (String target : reviewDbMemberAuditsByTarget.keySet()) {
- ImmutableList<AuditEntry> reviewDbAuditEntries =
- reviewDbMemberAuditsByTarget.get(target).stream()
- .sorted(AUDIT_ENTRY_COMPARATOR)
- .collect(toImmutableList());
- ImmutableSet<AuditEntry> noteDbAuditEntries =
- noteDbMemberAuditsByTarget.get(target).stream()
- .sorted(AUDIT_ENTRY_COMPARATOR)
- .collect(toImmutableSet());
-
- int reviewDbIndex = 0;
- for (AuditEntry noteDbAuditEntry : noteDbAuditEntries) {
- Set<AuditEntry> redundantReviewDbAuditEntries = new HashSet<>();
- while (reviewDbIndex < reviewDbAuditEntries.size()) {
- AuditEntry reviewDbAuditEntry = reviewDbAuditEntries.get(reviewDbIndex);
- if (!reviewDbAuditEntry.getAction().equals(noteDbAuditEntry.getAction())) {
- break;
- }
- redundantReviewDbAuditEntries.add(reviewDbAuditEntry);
- reviewDbIndex++;
- }
-
- // The order of the entries is not perfect as ReviewDb included milliseconds for timestamps
- // and we cut off everything below seconds due to NoteDb/git. Consequently, we don't have a
- // way to know in this method in which exact order additions/removals within the same second
- // happened. The best we can do is to group all additions within the same second as
- // redundant entries and the removals afterward. To compensate that we possibly group
- // non-redundant additions/removals, we also accept NoteDb audit entries which just occur
- // anywhere as ReviewDb audit entries.
- if (!redundantReviewDbAuditEntries.contains(noteDbAuditEntry)
- && !reviewDbAuditEntries.contains(noteDbAuditEntry)) {
- return false;
- }
- }
-
- if (reviewDbIndex < reviewDbAuditEntries.size()) {
- // Some of the ReviewDb audit log entries aren't matched by NoteDb audit log entries.
- return false;
- }
- }
- return true;
- }
-
- public AccountGroup.Id id() {
- return group().getId();
- }
-
- public AccountGroup.UUID uuid() {
- return group().getGroupUUID();
- }
-
- public abstract Source source();
-
- public abstract AccountGroup group();
-
- public abstract ImmutableSet<AccountGroupMember> members();
-
- public abstract ImmutableSet<AccountGroupMemberAudit> memberAudit();
-
- public abstract ImmutableSet<AccountGroupById> byId();
-
- public abstract ImmutableSet<AccountGroupByIdAud> byIdAudit();
-
- public abstract Builder toBuilder();
-
- public GroupBundle truncateToSecond() {
- AccountGroup newGroup = new AccountGroup(group());
- if (newGroup.getCreatedOn() != null) {
- newGroup.setCreatedOn(TimeUtil.truncateToSecond(newGroup.getCreatedOn()));
- }
- return toBuilder()
- .group(newGroup)
- .memberAudit(
- memberAudit().stream().map(GroupBundle::truncateToSecond).collect(toImmutableSet()))
- .byIdAudit(
- byIdAudit().stream().map(GroupBundle::truncateToSecond).collect(toImmutableSet()))
- .build();
- }
-
- private static AccountGroupMemberAudit truncateToSecond(AccountGroupMemberAudit a) {
- AccountGroupMemberAudit result =
- new AccountGroupMemberAudit(
- new AccountGroupMemberAudit.Key(
- a.getKey().getParentKey(),
- a.getKey().getGroupId(),
- TimeUtil.truncateToSecond(a.getKey().getAddedOn())),
- a.getAddedBy());
- if (a.getRemovedOn() != null) {
- result.removed(a.getRemovedBy(), TimeUtil.truncateToSecond(a.getRemovedOn()));
- }
- return result;
- }
-
- private static AccountGroupByIdAud truncateToSecond(AccountGroupByIdAud a) {
- AccountGroupByIdAud result =
- new AccountGroupByIdAud(
- new AccountGroupByIdAud.Key(
- a.getKey().getParentKey(),
- a.getKey().getIncludeUUID(),
- TimeUtil.truncateToSecond(a.getKey().getAddedOn())),
- a.getAddedBy());
- if (a.getRemovedOn() != null) {
- result.removed(a.getRemovedBy(), TimeUtil.truncateToSecond(a.getRemovedOn()));
- }
- return result;
- }
-
- public InternalGroup toInternalGroup() {
- return InternalGroup.create(
- group(),
- members().stream().map(AccountGroupMember::getAccountId).collect(toImmutableSet()),
- byId().stream().map(AccountGroupById::getIncludeUUID).collect(toImmutableSet()));
- }
-
- @Override
- public final int hashCode() {
- throw new UnsupportedOperationException(
- "hashCode is not supported because equals is not supported");
- }
-
- @Override
- public final boolean equals(Object o) {
- throw new UnsupportedOperationException("Use GroupBundle.compare(a, b) instead of equals");
- }
-
- @AutoValue
- abstract static class AuditEntry {
- private static AuditEntry create(
- Action action, Account.Id userId, Account.Id memberId, Timestamp timestamp) {
- return new AutoValue_GroupBundle_AuditEntry(
- action, userId, String.valueOf(memberId.get()), timestamp);
- }
-
- private static AuditEntry create(
- Action action, Account.Id userId, AccountGroup.UUID subgroupId, Timestamp timestamp) {
- return new AutoValue_GroupBundle_AuditEntry(action, userId, subgroupId.get(), timestamp);
- }
-
- abstract Action getAction();
-
- abstract Account.Id getUserId();
-
- abstract String getTarget();
-
- abstract Timestamp getTimestamp();
- }
-
- enum Action {
- ADD(1),
- REMOVE(2);
-
- private final int order;
-
- Action(int order) {
- this.order = order;
- }
-
- public int getOrder() {
- return order;
- }
- }
-
- @AutoValue.Builder
- abstract static class Builder {
- abstract Builder source(Source source);
-
- abstract Builder group(AccountGroup group);
-
- abstract Builder members(AccountGroupMember... member);
-
- abstract Builder members(Iterable<AccountGroupMember> member);
-
- abstract Builder memberAudit(AccountGroupMemberAudit... audit);
-
- abstract Builder memberAudit(Iterable<AccountGroupMemberAudit> audit);
-
- abstract Builder byId(AccountGroupById... byId);
-
- abstract Builder byId(Iterable<AccountGroupById> byId);
-
- abstract Builder byIdAudit(AccountGroupByIdAud... audit);
-
- abstract Builder byIdAudit(Iterable<AccountGroupByIdAud> audit);
-
- abstract GroupBundle build();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/GroupRebuilder.java b/java/com/google/gerrit/server/schema/GroupRebuilder.java
deleted file mode 100644
index 0b4fd0299a..0000000000
--- a/java/com/google/gerrit/server/schema/GroupRebuilder.java
+++ /dev/null
@@ -1,299 +0,0 @@
-// 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.schema;
-
-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 com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.ListMultimap;
-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.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.git.meta.VersionedMetaData.BatchMetaDataUpdate;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.group.db.InternalGroupCreation;
-import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gerrit.server.group.db.InternalGroupUpdate.MemberModification;
-import com.google.gerrit.server.group.db.InternalGroupUpdate.SubgroupModification;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Helper for rebuilding an entire group's NoteDb refs. */
-class GroupRebuilder {
- private final PersonIdent serverIdent;
- private final AllUsersName allUsers;
- private final AuditLogFormatter auditLogFormatter;
-
- public GroupRebuilder(
- PersonIdent serverIdent, AllUsersName allUsers, AuditLogFormatter auditLogFormatter) {
- this.serverIdent = serverIdent;
- this.allUsers = allUsers;
- this.auditLogFormatter = auditLogFormatter;
- }
-
- public void rebuild(Repository allUsersRepo, GroupBundle bundle, @Nullable BatchRefUpdate bru)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
- AccountGroup group = bundle.group();
- InternalGroupCreation groupCreation =
- InternalGroupCreation.builder()
- .setId(bundle.id())
- .setNameKey(group.getNameKey())
- .setGroupUUID(group.getGroupUUID())
- .build();
- GroupConfig groupConfig = GroupConfig.createForNewGroup(allUsers, allUsersRepo, groupCreation);
- groupConfig.setAllowSaveEmptyName();
-
- InternalGroupUpdate.Builder updateBuilder =
- InternalGroupUpdate.builder()
- .setOwnerGroupUUID(group.getOwnerGroupUUID())
- .setVisibleToAll(group.isVisibleToAll())
- .setUpdatedOn(group.getCreatedOn());
- if (bundle.group().getDescription() != null) {
- updateBuilder.setDescription(group.getDescription());
- }
- groupConfig.setGroupUpdate(updateBuilder.build(), auditLogFormatter);
-
- Map<Key, Collection<Event>> events = toEvents(bundle).asMap();
- PersonIdent nowServerIdent = getServerIdent(events);
-
- MetaDataUpdate md = createMetaDataUpdate(allUsers, allUsersRepo, bru);
-
- // Creation is done by the server (unlike later audit events).
- PersonIdent created = new PersonIdent(nowServerIdent, group.getCreatedOn());
- md.getCommitBuilder().setAuthor(created);
- md.getCommitBuilder().setCommitter(created);
-
- // Rebuild group ref.
- try (BatchMetaDataUpdate batch = groupConfig.openUpdate(md)) {
- batch.write(groupConfig, md.getCommitBuilder());
-
- for (Map.Entry<Key, Collection<Event>> e : events.entrySet()) {
- InternalGroupUpdate.Builder ub = InternalGroupUpdate.builder();
- e.getValue().forEach(event -> event.update().accept(ub));
- ub.setUpdatedOn(e.getKey().when());
- groupConfig.setGroupUpdate(ub.build(), auditLogFormatter);
-
- PersonIdent currServerIdent = new PersonIdent(nowServerIdent, e.getKey().when());
- CommitBuilder cb = new CommitBuilder();
- cb.setAuthor(
- e.getKey()
- .accountId()
- .map(id -> auditLogFormatter.getParsableAuthorIdent(id, currServerIdent))
- .orElse(currServerIdent));
- cb.setCommitter(currServerIdent);
- batch.write(groupConfig, cb);
- }
-
- batch.createRef(groupConfig.getRefName());
- }
- }
-
- private ListMultimap<Key, Event> toEvents(GroupBundle bundle) {
- ListMultimap<Key, Event> result =
- MultimapBuilder.treeKeys(Key.COMPARATOR).arrayListValues(1).build();
- Event e;
-
- for (AccountGroupMemberAudit a : bundle.memberAudit()) {
- checkArgument(
- a.getKey().getGroupId().equals(bundle.id()),
- "key %s does not match group %s",
- a.getKey(),
- bundle.id());
- Account.Id accountId = a.getKey().getParentKey();
- e = event(Type.ADD_MEMBER, a.getAddedBy(), a.getKey().getAddedOn(), addMember(accountId));
- result.put(e.key(), e);
- if (!a.isActive()) {
- e = event(Type.REMOVE_MEMBER, a.getRemovedBy(), a.getRemovedOn(), removeMember(accountId));
- result.put(e.key(), e);
- }
- }
-
- for (AccountGroupByIdAud a : bundle.byIdAudit()) {
- checkArgument(
- a.getKey().getParentKey().equals(bundle.id()),
- "key %s does not match group %s",
- a.getKey(),
- bundle.id());
- AccountGroup.UUID uuid = a.getKey().getIncludeUUID();
- e = event(Type.ADD_GROUP, a.getAddedBy(), a.getKey().getAddedOn(), addGroup(uuid));
- result.put(e.key(), e);
- if (!a.isActive()) {
- e = event(Type.REMOVE_GROUP, a.getRemovedBy(), a.getRemovedOn(), removeGroup(uuid));
- result.put(e.key(), e);
- }
- }
-
- // Due to clock skew, audit events may be in the future relative to this machine. Ensure the
- // fixup event happens after any other events, both for the purposes of sorting Keys correctly
- // and to avoid non-monotonic timestamps in the commit history.
- Timestamp maxTs =
- Stream.concat(result.keySet().stream().map(Key::when), Stream.of(TimeUtil.nowTs()))
- .max(Comparator.naturalOrder())
- .get();
- Timestamp fixupTs = new Timestamp(maxTs.getTime() + 1);
- e = serverEvent(Type.FIXUP, fixupTs, setCurrentMembership(bundle));
- result.put(e.key(), e);
-
- return result;
- }
-
- private PersonIdent getServerIdent(Map<Key, Collection<Event>> events) {
- // Created with MultimapBuilder.treeKeys, so the keySet is navigable.
- Key lastKey = ((NavigableSet<Key>) events.keySet()).last();
- checkState(lastKey.type() == Type.FIXUP);
- return new PersonIdent(
- serverIdent.getName(),
- serverIdent.getEmailAddress(),
- Iterables.getOnlyElement(events.get(lastKey)).when(),
- serverIdent.getTimeZone());
- }
-
- private static MetaDataUpdate createMetaDataUpdate(
- Project.NameKey projectName, Repository repository, @Nullable BatchRefUpdate batchRefUpdate) {
- return new MetaDataUpdate(
- GitReferenceUpdated.DISABLED, projectName, repository, batchRefUpdate);
- }
-
- private static Consumer<InternalGroupUpdate.Builder> addMember(Account.Id toAdd) {
- return b -> {
- MemberModification prev = b.getMemberModification();
- b.setMemberModification(in -> Sets.union(prev.apply(in), ImmutableSet.of(toAdd)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> removeMember(Account.Id toRemove) {
- return b -> {
- MemberModification prev = b.getMemberModification();
- b.setMemberModification(in -> Sets.difference(prev.apply(in), ImmutableSet.of(toRemove)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> addGroup(AccountGroup.UUID toAdd) {
- return b -> {
- SubgroupModification prev = b.getSubgroupModification();
- b.setSubgroupModification(in -> Sets.union(prev.apply(in), ImmutableSet.of(toAdd)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> removeGroup(AccountGroup.UUID toRemove) {
- return b -> {
- SubgroupModification prev = b.getSubgroupModification();
- b.setSubgroupModification(in -> Sets.difference(prev.apply(in), ImmutableSet.of(toRemove)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> setCurrentMembership(GroupBundle bundle) {
- // Overwrite members and subgroups with the current values. The storage layer will do the
- // set differences to compute the appropriate delta, if any.
- return b ->
- b.setMemberModification(
- in ->
- bundle.members().stream()
- .map(AccountGroupMember::getAccountId)
- .collect(toImmutableSet()))
- .setSubgroupModification(
- in ->
- bundle.byId().stream()
- .map(AccountGroupById::getIncludeUUID)
- .collect(toImmutableSet()));
- }
-
- private static Event event(
- Type type,
- Account.Id accountId,
- Timestamp when,
- Consumer<InternalGroupUpdate.Builder> update) {
- return new AutoValue_GroupRebuilder_Event(type, Optional.of(accountId), when, update);
- }
-
- private static Event serverEvent(
- Type type, Timestamp when, Consumer<InternalGroupUpdate.Builder> update) {
- return new AutoValue_GroupRebuilder_Event(type, Optional.empty(), when, update);
- }
-
- @AutoValue
- abstract static class Event {
- abstract Type type();
-
- abstract Optional<Account.Id> accountId();
-
- abstract Timestamp when();
-
- abstract Consumer<InternalGroupUpdate.Builder> update();
-
- Key key() {
- return new AutoValue_GroupRebuilder_Key(accountId(), when(), type());
- }
- }
-
- /**
- * Distinct event types.
- *
- * <p>Events at the same time by the same user are batched together by type. The types should
- * correspond to the possible batch operations supported by AuditService.
- */
- enum Type {
- ADD_MEMBER,
- REMOVE_MEMBER,
- ADD_GROUP,
- REMOVE_GROUP,
- FIXUP;
- }
-
- @AutoValue
- abstract static class Key {
- static final Comparator<Key> COMPARATOR =
- Comparator.comparing(Key::when)
- .thenComparing(
- k -> k.accountId().map(Account.Id::get).orElse(null),
- Comparator.nullsFirst(Comparator.naturalOrder()))
- .thenComparing(Key::type);
-
- abstract Optional<Account.Id> accountId();
-
- abstract Timestamp when();
-
- abstract Type type();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/H2.java b/java/com/google/gerrit/server/schema/H2.java
deleted file mode 100644
index 94720126f4..0000000000
--- a/java/com/google/gerrit/server/schema/H2.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2012 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 com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.nio.file.Path;
-import org.eclipse.jgit.lib.Config;
-
-class H2 extends BaseDataSourceType {
-
- protected final Config cfg;
- private final SitePaths site;
-
- @Inject
- H2(SitePaths site, @GerritServerConfig Config cfg) {
- super("org.h2.Driver");
- this.cfg = cfg;
- this.site = site;
- }
-
- @Override
- public String getUrl() {
- String database = cfg.getString("database", null, "database");
- if (database == null || database.isEmpty()) {
- database = "db/ReviewDB";
- }
- return appendUrlOptions(cfg, createUrl(site.resolve(database)));
- }
-
- public static String createUrl(Path path) {
- return new StringBuilder().append("jdbc:h2:").append(path.toUri().toString()).toString();
- }
-
- public static String appendUrlOptions(Config cfg, String url) {
- long h2CacheSize = cfg.getLong("database", "h2", "cacheSize", -1);
- long h2MaxQueryTimeout = cfg.getLong("database", "h2", "maxQueryTimeout", -1);
- boolean h2AutoServer = cfg.getBoolean("database", "h2", "autoServer", false);
-
- StringBuilder urlBuilder = new StringBuilder().append(url);
-
- if (h2CacheSize >= 0) {
- // H2 CACHE_SIZE is always given in KB
- urlBuilder.append(";CACHE_SIZE=").append(h2CacheSize / 1024);
- }
- if (h2MaxQueryTimeout >= 0) {
- urlBuilder.append(";MAX_QUERY_TIMEOUT=").append(h2MaxQueryTimeout);
- }
- if (h2AutoServer) {
- urlBuilder.append(";AUTO_SERVER=TRUE");
- }
- return urlBuilder.toString();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java
index 514614023f..3f74cda664 100644
--- a/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.SQLException;
@@ -36,17 +36,17 @@ public class H2AccountPatchReviewStore extends JdbcAccountPatchReviewStore {
}
@Override
- public OrmException convertError(String op, SQLException err) {
+ public StorageException convertError(String op, SQLException err) {
switch (getSQLStateInt(err)) {
case 23001: // UNIQUE CONSTRAINT VIOLATION
case 23505: // DUPLICATE_KEY_1
- return new OrmDuplicateKeyException("account_patch_reviews", err);
+ return new DuplicateKeyException("account_patch_reviews", err);
default:
if (err.getCause() == null && err.getNextException() != null) {
err.initCause(err.getNextException());
}
- return new OrmException(op + " failure on account_patch_reviews", err);
+ return new StorageException(op + " failure on account_patch_reviews", err);
}
}
}
diff --git a/java/com/google/gerrit/server/schema/HANA.java b/java/com/google/gerrit/server/schema/HANA.java
deleted file mode 100644
index f9811c6e74..0000000000
--- a/java/com/google/gerrit/server/schema/HANA.java
+++ /dev/null
@@ -1,56 +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.server.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.lib.Config;
-
-class HANA extends BaseDataSourceType {
-
- private Config cfg;
-
- @Inject
- HANA(@GerritServerConfig Config cfg) {
- super("com.sap.db.jdbc.Driver");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- final StringBuilder b = new StringBuilder();
- final ConfigSection dbs = new ConfigSection(cfg, "database");
- b.append("jdbc:sap://");
- b.append(hostname(dbs.required("hostname")));
- b.append(port(dbs.optional("port")));
- String database = dbs.optional("database");
- if (!Strings.isNullOrEmpty(database)) {
- b.append("?databaseName=").append(database);
- }
- return b.toString();
- }
-
- @Override
- public ScriptRunner getIndexScript() throws IOException {
- // HANA uses column tables and should not require additional indices
- return ScriptRunner.NOOP;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/InMemoryAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/InMemoryAccountPatchReviewStore.java
deleted file mode 100644
index 35e81b22dc..0000000000
--- a/java/com/google/gerrit/server/schema/InMemoryAccountPatchReviewStore.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.server.change.AccountPatchReviewStore;
-import com.google.gwtorm.jdbc.SimpleDataSource;
-import java.sql.SQLException;
-import java.util.Properties;
-import javax.sql.DataSource;
-
-public class InMemoryAccountPatchReviewStore extends JdbcAccountPatchReviewStore {
- @VisibleForTesting
- public static class Module extends LifecycleModule {
- @Override
- protected void configure() {
- InMemoryAccountPatchReviewStore inMemoryStore = new InMemoryAccountPatchReviewStore();
- DynamicItem.bind(binder(), AccountPatchReviewStore.class).toInstance(inMemoryStore);
- listener().toInstance(inMemoryStore);
- }
- }
-
- /**
- * Creates an in-memory H2 database to store the reviewed flags. This should be used for tests
- * only.
- */
- @VisibleForTesting
- private InMemoryAccountPatchReviewStore() {
- super(newDataSource());
- }
-
- private static synchronized DataSource newDataSource() {
- final Properties p = new Properties();
- p.setProperty("driver", "org.h2.Driver");
- // DB_CLOSE_DELAY=-1: By default the content of an in-memory H2 database is lost at the moment
- // the last connection is closed. This option keeps the content as long as the vm lives.
- p.setProperty("url", "jdbc:h2:mem:account_patch_reviews;DB_CLOSE_DELAY=-1");
- try {
- return new SimpleDataSource(p);
- } catch (SQLException e) {
- throw new RuntimeException("Unable to create test datasource", e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/JDBC.java b/java/com/google/gerrit/server/schema/JDBC.java
deleted file mode 100644
index d188df4e8a..0000000000
--- a/java/com/google/gerrit/server/schema/JDBC.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2012 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 com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class JDBC extends BaseDataSourceType {
-
- protected final Config cfg;
-
- @Inject
- JDBC(@GerritServerConfig Config cfg) {
- super(ConfigUtil.getRequired(cfg, "database", "driver"));
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- return ConfigUtil.getRequired(cfg, "database", "url");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index 83a0986bf5..0612bb983b 100644
--- a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -17,21 +17,24 @@ package com.google.gerrit.server.schema;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
+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.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.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
+import java.nio.file.Path;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -47,6 +50,12 @@ public abstract class JdbcAccountPatchReviewStore
implements AccountPatchReviewStore, LifecycleListener {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ // DB_CLOSE_DELAY=-1: By default the content of an in-memory H2 database is lost at the moment the
+ // last connection is closed. This option keeps the content as long as the VM lives.
+ @VisibleForTesting
+ public static final String TEST_IN_MEMORY_URL =
+ "jdbc:h2:mem:account_patch_reviews;DB_CLOSE_DELAY=-1";
+
private static final String ACCOUNT_PATCH_REVIEW_DB = "accountPatchReviewDb";
private static final String H2_DB = "h2";
private static final String MARIADB = "mariadb";
@@ -108,14 +117,10 @@ public abstract class JdbcAccountPatchReviewStore
this.ds = createDataSource(cfg, sitePaths, threadSettingsConfig);
}
- protected JdbcAccountPatchReviewStore(DataSource ds) {
- this.ds = ds;
- }
-
private static String getUrl(@GerritServerConfig Config cfg, SitePaths sitePaths) {
String url = cfg.getString(ACCOUNT_PATCH_REVIEW_DB, null, URL);
if (url == null) {
- return H2.createUrl(sitePaths.db_dir.resolve("account_patch_reviews"));
+ return createH2Url(sitePaths.db_dir.resolve("account_patch_reviews"));
}
return url;
}
@@ -163,7 +168,7 @@ public abstract class JdbcAccountPatchReviewStore
public void start() {
try {
createTableIfNotExists();
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atSevere().withCause(e).log("Failed to create table to store account patch reviews");
}
}
@@ -172,7 +177,7 @@ public abstract class JdbcAccountPatchReviewStore
return ds.getConnection();
}
- public void createTableIfNotExists() throws OrmException {
+ public void createTableIfNotExists() {
try (Connection con = ds.getConnection();
Statement stmt = con.createStatement()) {
doCreateTable(stmt);
@@ -193,7 +198,7 @@ public abstract class JdbcAccountPatchReviewStore
+ ")");
}
- public void dropTableIfExists() throws OrmException {
+ public void dropTableIfExists() {
try (Connection con = ds.getConnection();
Statement stmt = con.createStatement()) {
stmt.executeUpdate("DROP TABLE IF EXISTS account_patch_reviews");
@@ -206,8 +211,7 @@ public abstract class JdbcAccountPatchReviewStore
public void stop() {}
@Override
- public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path)
- throws OrmException {
+ public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
try (Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
@@ -221,8 +225,8 @@ public abstract class JdbcAccountPatchReviewStore
stmt.executeUpdate();
return true;
} catch (SQLException e) {
- OrmException ormException = convertError("insert", e);
- if (ormException instanceof OrmDuplicateKeyException) {
+ StorageException ormException = convertError("insert", e);
+ if (ormException instanceof DuplicateKeyException) {
return false;
}
throw ormException;
@@ -230,8 +234,7 @@ public abstract class JdbcAccountPatchReviewStore
}
@Override
- public void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths)
- throws OrmException {
+ public void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths) {
if (paths == null || paths.isEmpty()) {
return;
}
@@ -251,8 +254,8 @@ public abstract class JdbcAccountPatchReviewStore
}
stmt.executeBatch();
} catch (SQLException e) {
- OrmException ormException = convertError("insert", e);
- if (ormException instanceof OrmDuplicateKeyException) {
+ StorageException ormException = convertError("insert", e);
+ if (ormException instanceof DuplicateKeyException) {
return;
}
throw ormException;
@@ -260,8 +263,7 @@ public abstract class JdbcAccountPatchReviewStore
}
@Override
- public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path)
- throws OrmException {
+ public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
try (Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
@@ -279,7 +281,7 @@ public abstract class JdbcAccountPatchReviewStore
}
@Override
- public void clearReviewed(PatchSet.Id psId) throws OrmException {
+ public void clearReviewed(PatchSet.Id psId) {
try (Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
@@ -294,8 +296,19 @@ public abstract class JdbcAccountPatchReviewStore
}
@Override
- public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId)
- throws OrmException {
+ public void clearReviewed(Change.Id changeId) {
+ try (Connection con = ds.getConnection();
+ PreparedStatement stmt =
+ con.prepareStatement("DELETE FROM account_patch_reviews WHERE change_id = ?")) {
+ stmt.setInt(1, changeId.get());
+ stmt.executeUpdate();
+ } catch (SQLException e) {
+ throw convertError("delete", e);
+ }
+ }
+
+ @Override
+ public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId) {
try (Connection con = ds.getConnection();
PreparedStatement stmt =
con.prepareStatement(
@@ -327,11 +340,11 @@ public abstract class JdbcAccountPatchReviewStore
}
}
- public OrmException convertError(String op, SQLException err) {
+ public StorageException convertError(String op, SQLException err) {
if (err.getCause() == null && err.getNextException() != null) {
err.initCause(err.getNextException());
}
- return new OrmException(op + " failure on account_patch_reviews", err);
+ return new StorageException(op + " failure on account_patch_reviews", err);
}
private static String getSQLState(SQLException err) {
@@ -352,4 +365,8 @@ public abstract class JdbcAccountPatchReviewStore
}
return 0;
}
+
+ private static String createH2Url(Path path) {
+ return new StringBuilder().append("jdbc:h2:").append(path.toUri().toString()).toString();
+ }
}
diff --git a/java/com/google/gerrit/server/schema/JdbcUtil.java b/java/com/google/gerrit/server/schema/JdbcUtil.java
deleted file mode 100644
index dddf23a955..0000000000
--- a/java/com/google/gerrit/server/schema/JdbcUtil.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2012 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 com.google.gerrit.server.UsedAt;
-
-public class JdbcUtil {
-
- public static String hostname(String hostname) {
- if (hostname == null || hostname.isEmpty()) {
- hostname = "localhost";
-
- } else if (hostname.contains(":") && !hostname.startsWith("[")) {
- hostname = "[" + hostname + "]";
- }
- return hostname;
- }
-
- @UsedAt(UsedAt.Project.PLUGINS_ALL)
- public static String port(String port) {
- if (port != null && !port.isEmpty()) {
- return ":" + port;
- }
- return "";
- }
-}
diff --git a/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java
index aa05a0878f..b0a3370fe1 100644
--- a/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.SQLException;
@@ -36,18 +36,18 @@ public class MariaDBAccountPatchReviewStore extends JdbcAccountPatchReviewStore
}
@Override
- public OrmException convertError(String op, SQLException err) {
+ public StorageException convertError(String op, SQLException err) {
switch (getSQLStateInt(err)) {
case 1022: // ER_DUP_KEY
case 1062: // ER_DUP_ENTRY
case 1169: // ER_DUP_UNIQUE;
- return new OrmDuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
+ return new DuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
default:
if (err.getCause() == null && err.getNextException() != null) {
err.initCause(err.getNextException());
}
- return new OrmException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
+ return new StorageException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
}
}
}
diff --git a/java/com/google/gerrit/server/schema/MariaDb.java b/java/com/google/gerrit/server/schema/MariaDb.java
deleted file mode 100644
index 6c5dd355f4..0000000000
--- a/java/com/google/gerrit/server/schema/MariaDb.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class MariaDb extends BaseDataSourceType {
- private final Config cfg;
-
- @Inject
- MariaDb(@GerritServerConfig Config cfg) {
- super("org.mariadb.jdbc.Driver");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- StringBuilder b = new StringBuilder();
- ConfigSection dbs = new ConfigSection(cfg, "database");
- b.append("jdbc:mariadb://");
- b.append(hostname(dbs.optional("hostname")));
- b.append(port(dbs.optional("port")));
- b.append("/");
- b.append(dbs.required("database"));
- b.append("?useBulkStmts=false");
- return b.toString();
- }
-
- @Override
- public boolean usePool() {
- // MariaDB has given us trouble with the connection pool,
- // sometimes the backend disconnects and the pool winds
- // up with a stale connection. Fortunately opening up
- // a new MariaDB connection is usually very fast.
- return false;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/MaxDb.java b/java/com/google/gerrit/server/schema/MaxDb.java
deleted file mode 100644
index d552eb65c3..0000000000
--- a/java/com/google/gerrit/server/schema/MaxDb.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2014 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.gerrit.server.schema.JdbcUtil.hostname;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.lib.Config;
-
-class MaxDb extends BaseDataSourceType {
-
- private Config cfg;
-
- @Inject
- MaxDb(@GerritServerConfig Config cfg) {
- super("com.sap.dbtech.jdbc.DriverSapDB");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- final StringBuilder b = new StringBuilder();
- final ConfigSection dbs = new ConfigSection(cfg, "database");
- b.append("jdbc:sapdb://");
- b.append(hostname(dbs.optional("hostname")));
- b.append("/");
- b.append(dbs.required("database"));
- return b.toString();
- }
-
- @Override
- public ScriptRunner getIndexScript() throws IOException {
- return getScriptRunner("index_maxdb.sql");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/MySql.java b/java/com/google/gerrit/server/schema/MySql.java
deleted file mode 100644
index e5f59d7667..0000000000
--- a/java/com/google/gerrit/server/schema/MySql.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2012 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.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-class MySql extends BaseDataSourceType {
-
- private Config cfg;
-
- @Inject
- MySql(@GerritServerConfig Config cfg) {
- super("com.mysql.jdbc.Driver");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- final StringBuilder b = new StringBuilder();
- final ConfigSection dbs = new ConfigSection(cfg, "database");
- b.append("jdbc:mysql://");
- b.append(hostname(dbs.optional("hostname")));
- b.append(port(dbs.optional("port")));
- b.append("/");
- b.append(dbs.required("database"));
- // See
- // https://stackoverflow.com/questions/42084633/table-name-pattern-can-not-be-null-or-empty-in-java
- b.append("?nullNamePatternMatchesAll=true");
- return b.toString();
- }
-
- @Override
- public boolean usePool() {
- // MySQL has given us trouble with the connection pool,
- // sometimes the backend disconnects and the pool winds
- // up with a stale connection. Fortunately opening up
- // a new MySQL connection is usually very fast.
- return false;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java
index cb8c7071b8..677b2de9fe 100644
--- a/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.SQLException;
@@ -37,18 +37,18 @@ public class MysqlAccountPatchReviewStore extends JdbcAccountPatchReviewStore {
}
@Override
- public OrmException convertError(String op, SQLException err) {
+ public StorageException convertError(String op, SQLException err) {
switch (err.getErrorCode()) {
case 1022: // ER_DUP_KEY
case 1062: // ER_DUP_ENTRY
case 1169: // ER_DUP_UNIQUE;
- return new OrmDuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
+ return new DuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
default:
if (err.getCause() == null && err.getNextException() != null) {
err.initCause(err.getNextException());
}
- return new OrmException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
+ return new StorageException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
}
}
diff --git a/java/com/google/gerrit/server/schema/NoChangesReviewDbWrapper.java b/java/com/google/gerrit/server/schema/NoChangesReviewDbWrapper.java
deleted file mode 100644
index 7247490f70..0000000000
--- a/java/com/google/gerrit/server/schema/NoChangesReviewDbWrapper.java
+++ /dev/null
@@ -1,214 +0,0 @@
-// 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.schema;
-
-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.ChangeMessage;
-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.server.ChangeAccess;
-import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
-import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
-import com.google.gerrit.reviewdb.server.PatchSetAccess;
-import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gwtorm.server.ListResultSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-
-/**
- * Wrapper for ReviewDb that never calls the underlying change tables.
- *
- * <p>See {@link NotesMigrationSchemaFactory} for discussion.
- */
-class NoChangesReviewDbWrapper extends ReviewDbWrapper {
- private static <T> ResultSet<T> empty() {
- return new ListResultSet<>(ImmutableList.of());
- }
-
- private final ChangeAccess changes;
- private final PatchSetApprovalAccess patchSetApprovals;
- private final ChangeMessageAccess changeMessages;
- private final PatchSetAccess patchSets;
- private final PatchLineCommentAccess patchComments;
-
- NoChangesReviewDbWrapper(ReviewDb db) {
- super(db);
- changes = new Changes(this, delegate);
- patchSetApprovals = new PatchSetApprovals(this, delegate);
- changeMessages = new ChangeMessages(this, delegate);
- patchSets = new PatchSets(this, delegate);
- patchComments = new PatchLineComments(this, delegate);
- }
-
- @Override
- public ChangeAccess changes() {
- return changes;
- }
-
- @Override
- public PatchSetApprovalAccess patchSetApprovals() {
- return patchSetApprovals;
- }
-
- @Override
- public ChangeMessageAccess changeMessages() {
- return changeMessages;
- }
-
- @Override
- public PatchSetAccess patchSets() {
- return patchSets;
- }
-
- @Override
- public PatchLineCommentAccess patchComments() {
- return patchComments;
- }
-
- private static class Changes extends AbstractDisabledAccess<Change, Change.Id>
- implements ChangeAccess {
- private Changes(NoChangesReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.changes());
- }
-
- @Override
- public ResultSet<Change> all() {
- return empty();
- }
- }
-
- private static class ChangeMessages
- extends AbstractDisabledAccess<ChangeMessage, ChangeMessage.Key>
- implements ChangeMessageAccess {
- private ChangeMessages(NoChangesReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.changeMessages());
- }
-
- @Override
- public ResultSet<ChangeMessage> byChange(Change.Id id) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<ChangeMessage> byPatchSet(PatchSet.Id id) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<ChangeMessage> all() throws OrmException {
- return empty();
- }
- }
-
- private static class PatchSets extends AbstractDisabledAccess<PatchSet, PatchSet.Id>
- implements PatchSetAccess {
- private PatchSets(NoChangesReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.patchSets());
- }
-
- @Override
- public ResultSet<PatchSet> byChange(Change.Id id) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchSet> all() {
- return empty();
- }
- }
-
- private static class PatchSetApprovals
- extends AbstractDisabledAccess<PatchSetApproval, PatchSetApproval.Key>
- implements PatchSetApprovalAccess {
- private PatchSetApprovals(NoChangesReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.patchSetApprovals());
- }
-
- @Override
- public ResultSet<PatchSetApproval> byChange(Change.Id id) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchSetApproval> byPatchSet(PatchSet.Id id) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchSetApproval> byPatchSetUser(PatchSet.Id patchSet, Account.Id account) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchSetApproval> all() {
- return empty();
- }
- }
-
- private static class PatchLineComments
- extends AbstractDisabledAccess<PatchLineComment, PatchLineComment.Key>
- implements PatchLineCommentAccess {
- private PatchLineComments(NoChangesReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.patchComments());
- }
-
- @Override
- public ResultSet<PatchLineComment> byChange(Change.Id id) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> byPatchSet(PatchSet.Id id) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> publishedByChangeFile(Change.Id id, String file) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> publishedByPatchSet(PatchSet.Id patchset) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByPatchSetAuthor(
- PatchSet.Id patchset, Account.Id author) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByChangeFileAuthor(
- Change.Id id, String file, Account.Id author) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> draftByAuthor(Account.Id author) {
- return empty();
- }
-
- @Override
- public ResultSet<PatchLineComment> all() {
- return empty();
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
new file mode 100644
index 0000000000..df95ff7c12
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -0,0 +1,191 @@
+// 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.schema;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+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.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;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.stream.IntStream;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+
+public class NoteDbSchemaUpdater {
+ private final Config cfg;
+ private final AllUsersName allUsersName;
+ private final GitRepositoryManager repoManager;
+ private final SchemaCreator schemaCreator;
+ private final NoteDbSchemaVersionManager versionManager;
+ private final NoteDbSchemaVersion.Arguments args;
+ private final ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> schemaVersions;
+
+ @Inject
+ NoteDbSchemaUpdater(
+ @GerritServerConfig Config cfg,
+ AllUsersName allUsersName,
+ GitRepositoryManager repoManager,
+ SchemaCreator schemaCreator,
+ NoteDbSchemaVersionManager versionManager,
+ NoteDbSchemaVersion.Arguments args) {
+ this(
+ cfg,
+ allUsersName,
+ repoManager,
+ schemaCreator,
+ versionManager,
+ args,
+ NoteDbSchemaVersions.ALL);
+ }
+
+ NoteDbSchemaUpdater(
+ Config cfg,
+ AllUsersName allUsersName,
+ GitRepositoryManager repoManager,
+ SchemaCreator schemaCreator,
+ NoteDbSchemaVersionManager versionManager,
+ NoteDbSchemaVersion.Arguments args,
+ ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> schemaVersions) {
+ this.cfg = cfg;
+ this.allUsersName = allUsersName;
+ this.repoManager = repoManager;
+ this.schemaCreator = schemaCreator;
+ this.versionManager = versionManager;
+ this.args = args;
+ this.schemaVersions = schemaVersions;
+ }
+
+ public void update(UpdateUI ui) {
+ ensureSchemaCreated();
+
+ int currentVersion = versionManager.read();
+ if (currentVersion == 0) {
+ // The only valid case where there is no refs/meta/version is when running 3.x init for the
+ // first time on a site that previously ran init on 2.16. A freshly created 3.x site will have
+ // seeded refs/meta/version during AllProjectsCreator, so it won't hit this block.
+ checkNoteDbConfigFor216();
+ }
+
+ for (int nextVersion : requiredUpgrades(currentVersion, schemaVersions.keySet())) {
+ try {
+ ui.message(String.format("Migrating data to schema %d ...", nextVersion));
+ NoteDbSchemaVersions.get(schemaVersions, nextVersion).upgrade(args, ui);
+ versionManager.increment(nextVersion - 1);
+ } catch (Exception e) {
+ throw new StorageException(
+ String.format("Failed to upgrade to schema version %d", nextVersion), e);
+ }
+ }
+ }
+
+ private void ensureSchemaCreated() {
+ try {
+ schemaCreator.ensureCreated();
+ } catch (IOException | ConfigInvalidException e) {
+ throw new StorageException("Cannot initialize Gerrit site", e);
+ }
+ }
+
+ // Config#getEnum requires this to be public, so give it an off-putting name.
+ public enum PrimaryStorageFor216Compatibility {
+ REVIEW_DB,
+ NOTE_DB
+ }
+
+ private void checkNoteDbConfigFor216() {
+ // Check that the NoteDb migration config matches what we expect from a site that both:
+ // * Completed the change migration to NoteDB.
+ // * Ran schema upgrades from a 2.16 final release.
+
+ if (!cfg.getBoolean("noteDb", "changes", "write", false)
+ || !cfg.getBoolean("noteDb", "changes", "read", false)
+ || cfg.getEnum(
+ "noteDb", "changes", "primaryStorage", PrimaryStorageFor216Compatibility.REVIEW_DB)
+ != PrimaryStorageFor216Compatibility.NOTE_DB
+ || !cfg.getBoolean("noteDb", "changes", "disableReviewDb", false)) {
+ throw new StorageException(
+ "You appear to be upgrading from a 2.x site, but the NoteDb change migration was"
+ + " not completed. See documentation:\n"
+ + "https://gerrit-review.googlesource.com/Documentation/note-db.html#migration");
+ }
+
+ // We don't have a direct way to check that 2.16 init was run; the most obvious side effect
+ // would be upgrading the *ReviewDb* schema to the latest 2.16 schema version. But in 3.x we can
+ // no longer access ReviewDb, so we can't check that directly.
+ //
+ // Instead, check for a NoteDb-specific side effect of the migration process: the presence of
+ // the NoteDb group sequence ref. This is created by the schema 163 migration, which was part of
+ // 2.16 and not 2.15.
+ //
+ // There are a few corner cases where we will proceed even if the schema is not fully up to
+ // date:
+ // * If a user happened to run init from master after schema 163 was added but before 2.16
+ // final. We assume that someone savvy enough to do that has followed the documented
+ // requirement of upgrading to 2.16 final before 3.0.
+ // * If a user ran init in 2.16.x and the upgrade to 163 succeeded but a later update failed.
+ // In this case the server literally will not start under 2.16. We assume the user will fix
+ // this and get 2.16 running rather than abandoning 2.16 and jumping to 3.0 at this point.
+ try (Repository allUsers = repoManager.openRepository(allUsersName)) {
+ if (allUsers.exactRef(RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS) == null) {
+ throw new StorageException(
+ "You appear to be upgrading to 3.x from a version prior to 2.16; you must upgrade to"
+ + " 2.16.x first");
+ }
+ } catch (IOException e) {
+ throw new StorageException("Failed to check NoteDb migration state", e);
+ }
+ }
+
+ @VisibleForTesting
+ static ImmutableList<Integer> requiredUpgrades(
+ int currentVersion, ImmutableSortedSet<Integer> allVersions) {
+ int firstVersion = allVersions.first();
+ int latestVersion = allVersions.last();
+ if (currentVersion == latestVersion) {
+ return ImmutableList.of();
+ } else if (currentVersion > latestVersion) {
+ throw new StorageException(
+ String.format(
+ "Cannot downgrade NoteDb schema from version %d to %d",
+ currentVersion, latestVersion));
+ }
+
+ int firstUpgradeVersion;
+ if (currentVersion == 0) {
+ // Bootstrap NoteDb version to minimum supported schema number.
+ firstUpgradeVersion = firstVersion;
+ } else {
+ if (currentVersion < firstVersion - 1) {
+ throw new StorageException(
+ String.format(
+ "Cannot skip NoteDb schema from version %d to %d", currentVersion, firstVersion));
+ }
+ firstUpgradeVersion = currentVersion + 1;
+ }
+ return IntStream.rangeClosed(firstUpgradeVersion, latestVersion)
+ .boxed()
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersion.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersion.java
new file mode 100644
index 0000000000..b6a7a1ce36
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersion.java
@@ -0,0 +1,46 @@
+// 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.schema;
+
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * Schema upgrade implementation.
+ *
+ * <p>Implementations must have a single non-private constructor with no arguments (e.g. the default
+ * constructor).
+ */
+interface NoteDbSchemaVersion {
+ @Singleton
+ class Arguments {
+ final GitRepositoryManager repoManager;
+ final AllProjectsName allProjects;
+ final AllUsersName allUsers;
+
+ @Inject
+ Arguments(
+ GitRepositoryManager repoManager, AllProjectsName allProjects, AllUsersName allUsers) {
+ this.repoManager = repoManager;
+ this.allProjects = allProjects;
+ this.allUsers = allUsers;
+ }
+ }
+
+ void upgrade(Arguments args, UpdateUI ui) throws Exception;
+}
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
new file mode 100644
index 0000000000..33534fc0a6
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
@@ -0,0 +1,77 @@
+// 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.schema;
+
+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.SitePaths;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.ProvisionException;
+
+public class NoteDbSchemaVersionCheck implements LifecycleListener {
+ public static Module module() {
+ return new LifecycleModule() {
+ @Override
+ protected void configure() {
+ listener().to(NoteDbSchemaVersionCheck.class);
+ }
+ };
+ }
+
+ private final NoteDbSchemaVersionManager versionManager;
+ private final SitePaths sitePaths;
+
+ @Inject
+ NoteDbSchemaVersionCheck(NoteDbSchemaVersionManager versionManager, SitePaths sitePaths) {
+ this.versionManager = versionManager;
+ this.sitePaths = sitePaths;
+ }
+
+ @Override
+ public void start() {
+ try {
+ int current = versionManager.read();
+ if (current == 0) {
+ throw new ProvisionException(
+ String.format(
+ "Schema not yet initialized. Run init to initialize the schema:\n"
+ + "$ java -jar gerrit.war init -d %s",
+ sitePaths.site_path.toAbsolutePath()));
+ }
+ int expected = NoteDbSchemaVersions.LATEST;
+ if (current != expected) {
+ String advice =
+ current > expected
+ ? "Downgrade is not supported"
+ : String.format(
+ "Run init to upgrade:\n$ java -jar %s init -d %s",
+ sitePaths.gerrit_war.toAbsolutePath(), sitePaths.site_path.toAbsolutePath());
+ throw new ProvisionException(
+ String.format(
+ "Unsupported schema version %d; expected schema version %d. %s",
+ current, expected, advice));
+ }
+ } catch (StorageException e) {
+ throw new ProvisionException("Failed to read NoteDb schema version", e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // Do nothing.
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
new file mode 100644
index 0000000000..7ff0ea6c44
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.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.schema;
+
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_VERSION;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.notedb.IntBlob;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+public class NoteDbSchemaVersionManager {
+ private final AllProjectsName allProjectsName;
+ private final GitRepositoryManager repoManager;
+
+ @Inject
+ @VisibleForTesting
+ public NoteDbSchemaVersionManager(
+ AllProjectsName allProjectsName, GitRepositoryManager repoManager) {
+ // Can't inject GitReferenceUpdated here because it has dependencies that are not always
+ // available in this injector (e.g. during init). This is ok for now since no other ref updates
+ // during init are available to plugins, and there are not any other use cases for listening for
+ // updates to the version ref.
+ this.allProjectsName = allProjectsName;
+ this.repoManager = repoManager;
+ }
+
+ public int read() {
+ try (Repository repo = repoManager.openRepository(allProjectsName)) {
+ return IntBlob.parse(repo, REFS_VERSION).map(IntBlob::value).orElse(0);
+ } catch (IOException e) {
+ throw new StorageException("Failed to read " + REFS_VERSION, e);
+ }
+ }
+
+ public void init() throws IOException {
+ try (Repository repo = repoManager.openRepository(allProjectsName);
+ RevWalk rw = new RevWalk(repo)) {
+ Optional<IntBlob> old = IntBlob.parse(repo, REFS_VERSION, rw);
+ if (old.isPresent()) {
+ throw new StorageException(
+ String.format(
+ "Expected no old version for %s, found %s", REFS_VERSION, old.get().value()));
+ }
+ IntBlob.store(
+ repo,
+ rw,
+ allProjectsName,
+ REFS_VERSION,
+ old.map(IntBlob::id).orElse(ObjectId.zeroId()),
+ NoteDbSchemaVersions.LATEST,
+ GitReferenceUpdated.DISABLED);
+ }
+ }
+
+ public void increment(int expectedOldVersion) throws IOException {
+ try (Repository repo = repoManager.openRepository(allProjectsName);
+ RevWalk rw = new RevWalk(repo)) {
+ Optional<IntBlob> old = IntBlob.parse(repo, REFS_VERSION, rw);
+ if (old.isPresent() && old.get().value() != expectedOldVersion) {
+ throw new StorageException(
+ String.format(
+ "Expected old version %d for %s, found %d",
+ expectedOldVersion, REFS_VERSION, old.get().value()));
+ }
+ IntBlob.store(
+ repo,
+ rw,
+ allProjectsName,
+ REFS_VERSION,
+ old.map(IntBlob::id).orElse(ObjectId.zeroId()),
+ expectedOldVersion + 1,
+ GitReferenceUpdated.DISABLED);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersions.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersions.java
new file mode 100644
index 0000000000..02250f2ad5
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersions.java
@@ -0,0 +1,60 @@
+// 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.schema;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
+import static java.util.Comparator.naturalOrder;
+
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.primitives.Ints;
+import com.google.gerrit.common.UsedAt;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+public class NoteDbSchemaVersions {
+ static final ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> ALL =
+ // List all supported NoteDb schema versions here.
+ Stream.of(Schema_180.class, Schema_181.class)
+ .collect(toImmutableSortedMap(naturalOrder(), v -> guessVersion(v).get(), v -> v));
+
+ public static final int FIRST = ALL.firstKey();
+ public static final int LATEST = ALL.lastKey();
+
+ // TODO(dborowitz): Migrate delete-project plugin to use this implementation.
+ @UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
+ public static Optional<Integer> guessVersion(Class<?> c) {
+ String prefix = "Schema_";
+ if (!c.getSimpleName().startsWith(prefix)) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(Ints.tryParse(c.getSimpleName().substring(prefix.length())));
+ }
+
+ public static NoteDbSchemaVersion get(
+ ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> schemaVersions, int i) {
+ Class<? extends NoteDbSchemaVersion> clazz = schemaVersions.get(i);
+ checkArgument(clazz != null, "Schema version not found: %s", i);
+ try {
+ return clazz.getDeclaredConstructor().newInstance();
+ } catch (InstantiationException
+ | IllegalAccessException
+ | NoSuchMethodException
+ | InvocationTargetException e) {
+ throw new IllegalStateException("failed to invoke constructor on " + clazz.getName(), e);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java b/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
deleted file mode 100644
index 0d95610499..0000000000
--- a/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
+++ /dev/null
@@ -1,78 +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.server.schema;
-
-import com.google.gerrit.reviewdb.server.DisallowReadFromChangesReviewDbWrapper;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-@Singleton
-public class NotesMigrationSchemaFactory implements SchemaFactory<ReviewDb> {
- private final SchemaFactory<ReviewDb> delegate;
- private final NotesMigration migration;
-
- @Inject
- NotesMigrationSchemaFactory(
- @ReviewDbFactory SchemaFactory<ReviewDb> delegate, NotesMigration migration) {
- this.delegate = delegate;
- this.migration = migration;
- }
-
- @Override
- public ReviewDb open() throws OrmException {
- // There are two levels at which this class disables access to Changes and related tables,
- // corresponding to two phases of the NoteDb migration:
- //
- // 1. When changes are read from NoteDb but some changes might still have their primary storage
- // in ReviewDb, it is generally programmer error to read changes from ReviewDb. However,
- // since ReviewDb is still the primary storage for most or all changes, we still need to
- // support writing to ReviewDb. This behavior is accomplished by wrapping in a
- // DisallowReadFromChangesReviewDbWrapper.
- //
- // Some codepaths might need to be able to read from ReviewDb if they really need to,
- // because they need to operate on the underlying source of truth, for example when reading
- // a change to determine its primary storage. To support this, ReviewDbUtil#unwrapDb can
- // detect and unwrap databases of this type.
- //
- // 2. After all changes have their primary storage in NoteDb, we can completely shut off access
- // to the change tables. At this point in the migration, we are by definition not using the
- // ReviewDb tables at all; we could even delete the tables at this point, and Gerrit would
- // continue to function.
- //
- // This is accomplished by setting the delegate ReviewDb *underneath*
- // DisallowReadFromChanges to be a complete no-op, with NoChangesReviewDbWrapper. With this
- // wrapper, all read operations return no results, and write operations silently do nothing.
- // This wrapper is not a public class and nobody should ever attempt to unwrap it.
-
- // First create the wrappers which can not be removed by ReviewDbUtil#unwrapDb(ReviewDb).
- ReviewDb db = delegate.open();
- if (migration.readChanges() && migration.disableChangeReviewDb()) {
- // Disable writes to change tables in ReviewDb (ReviewDb access for changes are No-Ops).
- db = new NoChangesReviewDbWrapper(db);
- }
-
- // Second create the wrappers which can be removed by ReviewDbUtil#unwrapDb(ReviewDb).
- if (migration.readChanges()) {
- // If reading changes from NoteDb is configured, changes should not be read from ReviewDb.
- // Make sure that any attempt to read a change from ReviewDb anyway fails with an exception.
- db = new DisallowReadFromChangesReviewDbWrapper(db);
- }
- return db;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Oracle.java b/java/com/google/gerrit/server/schema/Oracle.java
deleted file mode 100644
index 4ff72432ea..0000000000
--- a/java/com/google/gerrit/server/schema/Oracle.java
+++ /dev/null
@@ -1,50 +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.schema;
-
-import static com.google.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import org.eclipse.jgit.lib.Config;
-
-public class Oracle extends BaseDataSourceType {
- private Config cfg;
-
- @Inject
- public Oracle(@GerritServerConfig Config cfg) {
- super("oracle.jdbc.driver.OracleDriver");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- final StringBuilder b = new StringBuilder();
- final ConfigSection dbc = new ConfigSection(cfg, "database");
- b.append("jdbc:oracle:thin:@");
- b.append(hostname(dbc.optional("hostname")));
- b.append(port(dbc.optional("port")));
- b.append(":");
- b.append(dbc.required("instance"));
- return b.toString();
- }
-
- @Override
- public String getValidationQuery() {
- return "select 1 from dual";
- }
-}
diff --git a/java/com/google/gerrit/server/schema/PostgreSQL.java b/java/com/google/gerrit/server/schema/PostgreSQL.java
deleted file mode 100644
index d6aee9415a..0000000000
--- a/java/com/google/gerrit/server/schema/PostgreSQL.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2012 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.gerrit.server.schema.JdbcUtil.hostname;
-import static com.google.gerrit.server.schema.JdbcUtil.port;
-
-import com.google.gerrit.server.config.ConfigSection;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.lib.Config;
-
-class PostgreSQL extends BaseDataSourceType {
-
- private Config cfg;
-
- @Inject
- PostgreSQL(@GerritServerConfig Config cfg) {
- super("org.postgresql.Driver");
- this.cfg = cfg;
- }
-
- @Override
- public String getUrl() {
- final StringBuilder b = new StringBuilder();
- final ConfigSection dbc = new ConfigSection(cfg, "database");
- b.append("jdbc:postgresql://");
- b.append(hostname(dbc.optional("hostname")));
- b.append(port(dbc.optional("port")));
- b.append("/");
- b.append(dbc.required("database"));
- return b.toString();
- }
-
- @Override
- public ScriptRunner getIndexScript() throws IOException {
- return getScriptRunner("index_postgres.sql");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java
index 34f7dba1c0..db68f2e8a2 100644
--- a/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.sql.SQLException;
@@ -36,10 +36,10 @@ public class PostgresqlAccountPatchReviewStore extends JdbcAccountPatchReviewSto
}
@Override
- public OrmException convertError(String op, SQLException err) {
+ public StorageException convertError(String op, SQLException err) {
switch (getSQLStateInt(err)) {
case 23505: // DUPLICATE_KEY_1
- return new OrmDuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
+ return new DuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
case 23514: // CHECK CONSTRAINT VIOLATION
case 23503: // FOREIGN KEY CONSTRAINT VIOLATION
@@ -49,7 +49,7 @@ public class PostgresqlAccountPatchReviewStore extends JdbcAccountPatchReviewSto
if (err.getCause() == null && err.getNextException() != null) {
err.initCause(err.getNextException());
}
- return new OrmException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
+ return new StorageException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
}
}
}
diff --git a/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java b/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
index c25b846ae1..9e12807cae 100644
--- a/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
+++ b/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
@@ -17,12 +17,17 @@ package com.google.gerrit.server.schema;
import static com.google.gerrit.server.project.ProjectConfig.ACCESS;
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.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;
import com.google.gerrit.server.git.meta.VersionedMetaData;
import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -31,22 +36,38 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.StoredConfig;
public class ProjectConfigSchemaUpdate extends VersionedMetaData {
+ public static class Factory {
+ private final SitePaths sitePaths;
+ private final AllProjectsName allProjectsName;
+
+ @Inject
+ Factory(SitePaths sitePaths, AllProjectsName allProjectsName) {
+ this.sitePaths = sitePaths;
+ this.allProjectsName = allProjectsName;
+ }
+
+ ProjectConfigSchemaUpdate read(MetaDataUpdate update)
+ throws IOException, ConfigInvalidException {
+ ProjectConfigSchemaUpdate r =
+ new ProjectConfigSchemaUpdate(
+ update,
+ ProjectConfig.Factory.getBaseConfig(sitePaths, allProjectsName, allProjectsName));
+ r.load(update);
+ return r;
+ }
+ }
private final MetaDataUpdate update;
+ @Nullable private final StoredConfig baseConfig;
private Config config;
private boolean updated;
- public static ProjectConfigSchemaUpdate read(MetaDataUpdate update)
- throws IOException, ConfigInvalidException {
- ProjectConfigSchemaUpdate r = new ProjectConfigSchemaUpdate(update);
- r.load(update);
- return r;
- }
-
- private ProjectConfigSchemaUpdate(MetaDataUpdate update) {
+ private ProjectConfigSchemaUpdate(MetaDataUpdate update, @Nullable StoredConfig baseConfig) {
this.update = update;
+ this.baseConfig = baseConfig;
}
@Override
@@ -56,7 +77,15 @@ public class ProjectConfigSchemaUpdate extends VersionedMetaData {
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
- config = readConfig(ProjectConfig.PROJECT_CONFIG);
+ if (baseConfig != null) {
+ baseConfig.load();
+ }
+ config = readConfig(ProjectConfig.PROJECT_CONFIG, baseConfig);
+ }
+
+ @VisibleForTesting
+ Config getConfig() {
+ return config;
}
public void removeForceFromPermission(String name) {
@@ -86,7 +115,7 @@ public class ProjectConfigSchemaUpdate extends VersionedMetaData {
return true;
}
- public void save(PersonIdent personIdent, String commitMessage) throws OrmException {
+ public void save(PersonIdent personIdent, String commitMessage) {
if (!updated) {
return;
}
@@ -97,7 +126,7 @@ public class ProjectConfigSchemaUpdate extends VersionedMetaData {
try {
commit(update);
} catch (IOException e) {
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
diff --git a/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java b/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java
deleted file mode 100644
index 0fbaeca63b..0000000000
--- a/java/com/google/gerrit/server/schema/ReviewDbDatabaseProvider.java
+++ /dev/null
@@ -1,43 +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.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.Database;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.name.Named;
-import javax.sql.DataSource;
-
-/** Provides the {@code Database<ReviewDb>} database handle. */
-final class ReviewDbDatabaseProvider implements Provider<Database<ReviewDb>> {
- private final DataSource datasource;
-
- @Inject
- ReviewDbDatabaseProvider(@Named("ReviewDb") final DataSource ds) {
- datasource = ds;
- }
-
- @Override
- public Database<ReviewDb> get() {
- try {
- return new Database<>(datasource, ReviewDb.class);
- } catch (OrmException e) {
- throw new ProvisionException("Cannot create ReviewDb", e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/ReviewDbFactory.java b/java/com/google/gerrit/server/schema/ReviewDbFactory.java
deleted file mode 100644
index 86f5d06eea..0000000000
--- a/java/com/google/gerrit/server/schema/ReviewDbFactory.java
+++ /dev/null
@@ -1,31 +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.server.schema;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.inject.BindingAnnotation;
-import java.lang.annotation.Retention;
-
-/**
- * Marker on {@link com.google.gwtorm.server.SchemaFactory} implementation that talks to the
- * underlying traditional {@link com.google.gerrit.reviewdb.server.ReviewDb} database.
- *
- * <p>During the migration to NoteDb, the actual {@code ReviewDb} will be a wrapper with certain
- * tables enabled/disabled; this marker goes on the low-level implementation that has all tables.
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface ReviewDbFactory {}
diff --git a/java/com/google/gerrit/server/schema/SchemaCreator.java b/java/com/google/gerrit/server/schema/SchemaCreator.java
index 13734c37ee..b78ce73b65 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// 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.
@@ -14,262 +14,29 @@
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.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.GroupUUID;
-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.config.GerritServerId;
-import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.config.SitePaths;
-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.server.group.InternalGroup;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.group.db.InternalGroupCreation;
-import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gerrit.server.index.group.GroupIndex;
-import com.google.gerrit.server.index.group.GroupIndexCollection;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.update.RefUpdateUtil;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collections;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-/** Creates the current database schema and populates initial code rows. */
-public class SchemaCreator {
- @SitePath private final Path site_path;
-
- private final GitRepositoryManager repoManager;
- private final AllProjectsCreator allProjectsCreator;
- private final AllUsersCreator allUsersCreator;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
- private final DataSourceType dataSourceType;
- private final GroupIndexCollection indexCollection;
- private final String serverId;
-
- private final Config config;
- private final MetricMaker metricMaker;
- private final NotesMigration migration;
- private final AllProjectsName allProjectsName;
-
- @Inject
- public SchemaCreator(
- SitePaths site,
- GitRepositoryManager repoManager,
- AllProjectsCreator ap,
- AllUsersCreator auc,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent au,
- DataSourceType dst,
- GroupIndexCollection ic,
- @GerritServerId String serverId,
- @GerritServerConfig Config config,
- MetricMaker metricMaker,
- NotesMigration migration,
- AllProjectsName apName) {
- this(
- site.site_path,
- repoManager,
- ap,
- auc,
- allUsersName,
- au,
- dst,
- ic,
- serverId,
- config,
- metricMaker,
- migration,
- apName);
- }
-
- public SchemaCreator(
- @SitePath Path site,
- GitRepositoryManager repoManager,
- AllProjectsCreator ap,
- AllUsersCreator auc,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent au,
- DataSourceType dst,
- GroupIndexCollection ic,
- String serverId,
- Config config,
- MetricMaker metricMaker,
- NotesMigration migration,
- AllProjectsName apName) {
- site_path = site;
- this.repoManager = repoManager;
- allProjectsCreator = ap;
- allUsersCreator = auc;
- this.allUsersName = allUsersName;
- serverUser = au;
- dataSourceType = dst;
- indexCollection = ic;
- this.serverId = serverId;
-
- this.config = config;
- this.allProjectsName = apName;
- this.migration = migration;
- this.metricMaker = metricMaker;
- }
-
- public void create(ReviewDb db) throws OrmException, IOException, ConfigInvalidException {
- final JdbcSchema jdbc = (JdbcSchema) db;
- try (JdbcExecutor e = new JdbcExecutor(jdbc)) {
- jdbc.updateSchema(e);
- }
-
- final CurrentSchemaVersion sVer = CurrentSchemaVersion.create();
- sVer.versionNbr = SchemaVersion.getBinaryVersion();
- db.schemaVersion().insert(Collections.singleton(sVer));
-
- GroupReference admins = createGroupReference("Administrators");
- GroupReference batchUsers = createGroupReference("Non-Interactive Users");
-
- allProjectsCreator.setAdministrators(admins).setBatchUsers(batchUsers).create();
- // We have to create the All-Users repository before we can use it to store the groups in it.
- allUsersCreator.setAdministrators(admins).create();
-
- // Don't rely on injection to construct Sequences, as it requires ReviewDb.
- Sequences seqs =
- new Sequences(
- config,
- () -> db,
- migration,
- repoManager,
- GitReferenceUpdated.DISABLED,
- allProjectsName,
- allUsersName,
- metricMaker);
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- createAdminsGroup(seqs, allUsersRepo, admins);
- createBatchUsersGroup(seqs, allUsersRepo, batchUsers, admins.getUUID());
- }
-
- dataSourceType.getIndexScript().run(db);
- }
-
- private void createAdminsGroup(
- Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
- throws OrmException, IOException, ConfigInvalidException {
- InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
- InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder().setDescription("Gerrit Site Administrators").build();
-
- createGroup(allUsersRepo, groupCreation, groupUpdate);
- }
-
- private void createBatchUsersGroup(
- Sequences seqs,
- Repository allUsersRepo,
- GroupReference groupReference,
- AccountGroup.UUID adminsGroupUuid)
- throws OrmException, IOException, ConfigInvalidException {
- InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
- InternalGroupUpdate groupUpdate =
- InternalGroupUpdate.builder()
- .setDescription("Users who perform batch actions on Gerrit")
- .setOwnerGroupUUID(adminsGroupUuid)
- .build();
-
- createGroup(allUsersRepo, groupCreation, groupUpdate);
- }
-
- private void createGroup(
- Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws OrmException, ConfigInvalidException, IOException {
- InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupUpdate);
- index(createdGroup);
- }
-
- private InternalGroup createGroupInNoteDb(
- Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws ConfigInvalidException, IOException, OrmDuplicateKeyException {
- // This method is only executed on a new server which doesn't have any accounts or groups.
- AuditLogFormatter auditLogFormatter =
- AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), serverId);
-
- GroupConfig groupConfig =
- GroupConfig.createForNewGroup(allUsersName, allUsersRepo, groupCreation);
- groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
-
- AccountGroup.NameKey groupName = groupUpdate.getName().orElseGet(groupCreation::getNameKey);
- GroupNameNotes groupNameNotes =
- GroupNameNotes.forNewGroup(
- allUsersName, allUsersRepo, groupCreation.getGroupUUID(), groupName);
-
- commit(allUsersRepo, groupConfig, groupNameNotes);
-
- return groupConfig
- .getLoadedGroup()
- .orElseThrow(() -> new IllegalStateException("Created group wasn't automatically loaded"));
- }
-
- private void commit(
- Repository allUsersRepo, GroupConfig groupConfig, GroupNameNotes groupNameNotes)
- throws IOException {
- BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
- groupConfig.commit(metaDataUpdate);
- }
- // MetaDataUpdates unfortunately can't be reused. -> Create a new one.
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
- groupNameNotes.commit(metaDataUpdate);
- }
- RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
- }
-
- private MetaDataUpdate createMetaDataUpdate(
- Repository allUsersRepo, @Nullable BatchRefUpdate batchRefUpdate) {
- MetaDataUpdate metaDataUpdate =
- new MetaDataUpdate(
- GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo, batchRefUpdate);
- metaDataUpdate.getCommitBuilder().setAuthor(serverUser);
- metaDataUpdate.getCommitBuilder().setCommitter(serverUser);
- return metaDataUpdate;
- }
-
- private void index(InternalGroup group) throws IOException {
- for (GroupIndex groupIndex : indexCollection.getWriteIndexes()) {
- groupIndex.replace(group);
- }
- }
-
- private GroupReference createGroupReference(String name) {
- AccountGroup.UUID groupUuid = GroupUUID.make(name, serverUser);
- return new GroupReference(groupUuid, name);
- }
-
- private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference)
- throws OrmException {
- int next = seqs.nextGroupId();
- return InternalGroupCreation.builder()
- .setNameKey(new AccountGroup.NameKey(groupReference.getName()))
- .setId(new AccountGroup.Id(next))
- .setGroupUUID(groupReference.getUUID())
- .build();
- }
+/** Populates initial NoteDb schema, {@code All-Projects} configuration, etc. */
+public interface SchemaCreator {
+
+ /**
+ * Create the schema, assuming it does not already exist.
+ *
+ * <p>Fails if the schema does exist.
+ *
+ * @throws IOException an error occurred.
+ * @throws ConfigInvalidException an error occurred.
+ */
+ void create() throws IOException, ConfigInvalidException;
+
+ /**
+ * Create the schema only if it does not already exist.
+ *
+ * <p>Succeeds if the schema does exist.
+ *
+ * @throws IOException an error occurred.
+ * @throws ConfigInvalidException an error occurred.
+ */
+ void ensureCreated() throws IOException, ConfigInvalidException;
}
diff --git a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
new file mode 100644
index 0000000000..0a5823a743
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -0,0 +1,226 @@
+// 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.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.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;
+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.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.db.AuditLogFormatter;
+import com.google.gerrit.server.group.db.GroupConfig;
+import com.google.gerrit.server.group.db.GroupNameNotes;
+import com.google.gerrit.server.group.db.InternalGroupCreation;
+import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.server.index.group.GroupIndex;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.inject.Inject;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+// TODO(dborowitz): The current NoteDb implementation mirrors the old ReviewDb code: this class is
+// called to create the site early on in NoteDbSchemaUpdater#update. This logic is a little
+// confusing and could stand to be reworked. Another smell is that this is an interface only for
+// testing purposes.
+public class SchemaCreatorImpl implements SchemaCreator {
+ private final GitRepositoryManager repoManager;
+ private final AllProjectsCreator allProjectsCreator;
+ private final AllUsersCreator allUsersCreator;
+ private final AllUsersName allUsersName;
+ private final PersonIdent serverUser;
+ private final GroupIndexCollection indexCollection;
+ private final String serverId;
+
+ private final Config config;
+ private final MetricMaker metricMaker;
+ private final AllProjectsName allProjectsName;
+
+ @Inject
+ public SchemaCreatorImpl(
+ GitRepositoryManager repoManager,
+ AllProjectsCreator ap,
+ AllUsersCreator auc,
+ AllUsersName allUsersName,
+ @GerritPersonIdent PersonIdent au,
+ GroupIndexCollection ic,
+ String serverId,
+ Config config,
+ MetricMaker metricMaker,
+ AllProjectsName apName) {
+ this.repoManager = repoManager;
+ allProjectsCreator = ap;
+ allUsersCreator = auc;
+ this.allUsersName = allUsersName;
+ serverUser = au;
+ indexCollection = ic;
+ this.serverId = serverId;
+
+ this.config = config;
+ this.allProjectsName = apName;
+ this.metricMaker = metricMaker;
+ }
+
+ @Override
+ public void create() throws IOException, ConfigInvalidException {
+ GroupReference admins = createGroupReference("Administrators");
+ GroupReference batchUsers = createGroupReference("Non-Interactive Users");
+
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builder().administratorsGroup(admins).batchUsersGroup(batchUsers).build();
+ allProjectsCreator.create(allProjectsInput);
+ // We have to create the All-Users repository before we can use it to store the groups in it.
+ allUsersCreator.setAdministrators(admins).create();
+
+ // Don't rely on injection to construct Sequences, as the default GitReferenceUpdated has a
+ // thick dependency stack which may not all be available at schema creation time.
+ Sequences seqs =
+ new Sequences(
+ config,
+ repoManager,
+ GitReferenceUpdated.DISABLED,
+ allProjectsName,
+ allUsersName,
+ metricMaker);
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ createAdminsGroup(seqs, allUsersRepo, admins);
+ createBatchUsersGroup(seqs, allUsersRepo, batchUsers, admins.getUUID());
+ }
+ }
+
+ @Override
+ public void ensureCreated() throws IOException, ConfigInvalidException {
+ try {
+ repoManager.openRepository(allProjectsName).close();
+ } catch (RepositoryNotFoundException e) {
+ create();
+ }
+ }
+
+ private void createAdminsGroup(
+ Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
+ throws IOException, ConfigInvalidException {
+ InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setDescription("Gerrit Site Administrators").build();
+
+ createGroup(allUsersRepo, groupCreation, groupUpdate);
+ }
+
+ private void createBatchUsersGroup(
+ Sequences seqs,
+ Repository allUsersRepo,
+ GroupReference groupReference,
+ AccountGroup.UUID adminsGroupUuid)
+ throws IOException, ConfigInvalidException {
+ InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setDescription("Users who perform batch actions on Gerrit")
+ .setOwnerGroupUUID(adminsGroupUuid)
+ .build();
+
+ createGroup(allUsersRepo, groupCreation, groupUpdate);
+ }
+
+ private void createGroup(
+ Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
+ throws ConfigInvalidException, IOException {
+ InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupUpdate);
+ index(createdGroup);
+ }
+
+ private InternalGroup createGroupInNoteDb(
+ Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
+ throws ConfigInvalidException, IOException, DuplicateKeyException {
+ // This method is only executed on a new server which doesn't have any accounts or groups.
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), serverId);
+
+ GroupConfig groupConfig =
+ GroupConfig.createForNewGroup(allUsersName, allUsersRepo, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+
+ AccountGroup.NameKey groupName = groupUpdate.getName().orElseGet(groupCreation::getNameKey);
+ GroupNameNotes groupNameNotes =
+ GroupNameNotes.forNewGroup(
+ allUsersName, allUsersRepo, groupCreation.getGroupUUID(), groupName);
+
+ commit(allUsersRepo, groupConfig, groupNameNotes);
+
+ return groupConfig
+ .getLoadedGroup()
+ .orElseThrow(() -> new IllegalStateException("Created group wasn't automatically loaded"));
+ }
+
+ private void commit(
+ Repository allUsersRepo, GroupConfig groupConfig, GroupNameNotes groupNameNotes)
+ throws IOException {
+ BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
+ groupConfig.commit(metaDataUpdate);
+ }
+ // MetaDataUpdates unfortunately can't be reused. -> Create a new one.
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
+ groupNameNotes.commit(metaDataUpdate);
+ }
+ RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
+ }
+
+ private MetaDataUpdate createMetaDataUpdate(
+ Repository allUsersRepo, @Nullable BatchRefUpdate batchRefUpdate) {
+ MetaDataUpdate metaDataUpdate =
+ new MetaDataUpdate(
+ GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo, batchRefUpdate);
+ metaDataUpdate.getCommitBuilder().setAuthor(serverUser);
+ metaDataUpdate.getCommitBuilder().setCommitter(serverUser);
+ return metaDataUpdate;
+ }
+
+ private void index(InternalGroup group) {
+ for (GroupIndex groupIndex : indexCollection.getWriteIndexes()) {
+ groupIndex.replace(group);
+ }
+ }
+
+ private GroupReference createGroupReference(String name) {
+ AccountGroup.UUID groupUuid = GroupUUID.make(name, serverUser);
+ return new GroupReference(groupUuid, name);
+ }
+
+ 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))
+ .setGroupUUID(groupReference.getUUID())
+ .build();
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/SchemaModule.java b/java/com/google/gerrit/server/schema/SchemaModule.java
index 9ce19fe03f..ff2073dddc 100644
--- a/java/com/google/gerrit/server/schema/SchemaModule.java
+++ b/java/com/google/gerrit/server/schema/SchemaModule.java
@@ -27,9 +27,10 @@ import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.config.GerritServerIdProvider;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
import org.eclipse.jgit.lib.PersonIdent;
-/** Validate the schema and connect to Git. */
+/** Bindings for low-level Gerrit schema data. */
public class SchemaModule extends FactoryModule {
@Override
protected void configure() {
@@ -49,5 +50,11 @@ public class SchemaModule extends FactoryModule {
.annotatedWith(GerritServerId.class)
.toProvider(GerritServerIdProvider.class)
.in(SINGLETON);
+
+ // It feels wrong to have this binding in a seemingly unrelated module, but it's a dependency of
+ // SchemaCreatorImpl, so it's needed.
+ // TODO(dborowitz): Is there any way to untangle this?
+ bind(GroupIndexCollection.class);
+ bind(SchemaCreator.class).to(SchemaCreatorImpl.class);
}
}
diff --git a/java/com/google/gerrit/server/schema/SchemaUpdater.java b/java/com/google/gerrit/server/schema/SchemaUpdater.java
deleted file mode 100644
index bbf7349725..0000000000
--- a/java/com/google/gerrit/server/schema/SchemaUpdater.java
+++ /dev/null
@@ -1,133 +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.server.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AnonymousCowardName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.Stage;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-
-/** Creates or updates the current database schema. */
-public class SchemaUpdater {
- private final SchemaFactory<ReviewDb> schema;
- private final SchemaCreator creator;
- private final Provider<SchemaVersion> updater;
-
- @Inject
- SchemaUpdater(
- @ReviewDbFactory SchemaFactory<ReviewDb> schema, SchemaCreator creator, Injector parent) {
- this.schema = schema;
- this.creator = creator;
- this.updater = buildInjector(parent).getProvider(SchemaVersion.class);
- }
-
- private static Injector buildInjector(Injector parent) {
- // Use DEVELOPMENT mode to allow lazy initialization of the
- // graph. This avoids touching ancient schema versions that
- // are behind this installation's current version.
- return Guice.createInjector(
- Stage.DEVELOPMENT,
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(SchemaVersion.class).to(SchemaVersion.C);
-
- for (Key<?> k :
- new Key<?>[] {
- Key.get(PersonIdent.class, GerritPersonIdent.class),
- Key.get(String.class, AnonymousCowardName.class),
- Key.get(Config.class, GerritServerConfig.class),
- }) {
- rebind(parent, k);
- }
-
- for (Class<?> c :
- new Class<?>[] {
- AllProjectsName.class,
- AllUsersCreator.class,
- AllUsersName.class,
- GitRepositoryManager.class,
- SitePaths.class,
- SystemGroupBackend.class,
- }) {
- rebind(parent, Key.get(c));
- }
- }
-
- private <T> void rebind(Injector parent, Key<T> c) {
- bind(c).toProvider(parent.getProvider(c));
- }
- });
- }
-
- public void update(UpdateUI ui) throws OrmException {
- CurrentSchemaVersion version;
- SchemaVersion u;
- try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
- version = getSchemaVersion(db);
- u = updater.get();
- if (version == null) {
- try {
- creator.create(db);
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Cannot initialize schema", e);
- }
- }
- }
-
- if (version != null) {
- try {
- u.check(ui, version, schema);
- } catch (SQLException e) {
- throw new OrmException("Cannot upgrade schema", e);
- }
- }
- }
-
- @VisibleForTesting
- public SchemaVersion getLatestSchemaVersion() {
- return updater.get();
- }
-
- private CurrentSchemaVersion getSchemaVersion(ReviewDb db) {
- try {
- return db.schemaVersion().get(new CurrentSchemaVersion.Key());
- } catch (OrmException e) {
- return null;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/SchemaVersion.java b/java/com/google/gerrit/server/schema/SchemaVersion.java
deleted file mode 100644
index 8ecf95d8ad..0000000000
--- a/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ /dev/null
@@ -1,231 +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.server.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Lists;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.UsedAt;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** A version of the database schema. */
-public abstract class SchemaVersion {
- /** The current schema version. */
- public static final Class<Schema_170> C = Schema_170.class;
-
- public static int getBinaryVersion() {
- return guessVersion(C);
- }
-
- private final Provider<? extends SchemaVersion> prior;
- private final int versionNbr;
-
- protected SchemaVersion(Provider<? extends SchemaVersion> prior) {
- this.prior = prior;
- this.versionNbr = guessVersion(getClass());
- }
-
- @UsedAt(UsedAt.Project.PLUGIN_DELETE_PROJECT)
- public static int guessVersion(Class<?> c) {
- String n = c.getName();
- n = n.substring(n.lastIndexOf('_') + 1);
- while (n.startsWith("0")) {
- n = n.substring(1);
- }
- return Integer.parseInt(n);
- }
-
- /** @return the {@link CurrentSchemaVersion#versionNbr} this step targets. */
- public final int getVersionNbr() {
- return versionNbr;
- }
-
- @VisibleForTesting
- public final SchemaVersion getPrior() {
- return prior.get();
- }
-
- public final void check(UpdateUI ui, CurrentSchemaVersion curr, SchemaFactory<ReviewDb> schema)
- throws OrmException, SQLException {
- if (curr.versionNbr == versionNbr) {
- // Nothing to do, we are at the correct schema.
- } else if (curr.versionNbr > versionNbr) {
- throw new OrmException(
- "Cannot downgrade database schema from version "
- + curr.versionNbr
- + " to "
- + versionNbr
- + ".");
- } else {
- upgradeFrom(ui, curr, schema);
- }
- }
-
- /** Runs check on the prior schema version, and then upgrades. */
- private void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr, SchemaFactory<ReviewDb> schema)
- throws OrmException, SQLException {
- List<SchemaVersion> pending = pending(curr.versionNbr);
-
- try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
- updateSchema(pending, ui, db);
- }
-
- migrateData(pending, ui, curr, schema);
-
- try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
- JdbcSchema s = (JdbcSchema) db;
- List<String> pruneList = new ArrayList<>();
- s.pruneSchema(
- new StatementExecutor() {
- @Override
- public void execute(String sql) {
- pruneList.add(sql);
- }
-
- @Override
- public void close() {
- // Do nothing.
- }
- });
-
- try (JdbcExecutor e = new JdbcExecutor(s)) {
- if (!pruneList.isEmpty()) {
- ui.pruneSchema(e, pruneList);
- }
- }
- }
- }
-
- private List<SchemaVersion> pending(int curr) {
- List<SchemaVersion> r = Lists.newArrayListWithCapacity(versionNbr - curr);
- for (SchemaVersion v = this; curr < v.getVersionNbr(); v = v.prior.get()) {
- r.add(v);
- }
- Collections.reverse(r);
- return r;
- }
-
- private void updateSchema(List<SchemaVersion> pending, UpdateUI ui, ReviewDb db)
- throws OrmException, SQLException {
- for (SchemaVersion v : pending) {
- ui.message(String.format("Upgrading schema to %d ...", v.getVersionNbr()));
- v.preUpdateSchema(db);
- }
-
- JdbcSchema s = (JdbcSchema) db;
- try (JdbcExecutor e = new JdbcExecutor(s)) {
- s.updateSchema(e);
- }
- }
-
- /**
- * Invoked before updateSchema adds new columns/tables.
- *
- * @param db open database handle.
- * @throws OrmException if a Gerrit-specific exception occurred.
- * @throws SQLException if an underlying SQL exception occurred.
- */
- protected void preUpdateSchema(ReviewDb db) throws OrmException, SQLException {}
-
- private void migrateData(
- List<SchemaVersion> pending,
- UpdateUI ui,
- CurrentSchemaVersion curr,
- SchemaFactory<ReviewDb> schema)
- throws OrmException, SQLException {
- for (SchemaVersion v : pending) {
- Stopwatch sw = Stopwatch.createStarted();
- ui.message(String.format("Migrating data to schema %d ...", v.getVersionNbr()));
- try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
- v.migrateData(db, ui);
- }
- try (ReviewDb db = ReviewDbUtil.unwrapDb(schema.open())) {
- v.finish(curr, db);
- }
- ui.message(String.format("\t> Done (%.3f s)", sw.elapsed(TimeUnit.MILLISECONDS) / 1000d));
- }
- }
-
- /**
- * Invoked between updateSchema (adds new columns/tables) and pruneSchema (removes deleted
- * columns/tables).
- *
- * @param db open database handle.
- * @param ui interface for interacting with the user.
- * @throws OrmException if a Gerrit-specific exception occurred.
- * @throws SQLException if an underlying SQL exception occurred.
- */
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {}
-
- /** Mark the current schema version. */
- protected void finish(CurrentSchemaVersion curr, ReviewDb db) throws OrmException {
- curr.versionNbr = versionNbr;
- db.schemaVersion().update(Collections.singleton(curr));
- }
-
- /** Rename an existing table. */
- protected static void renameTable(ReviewDb db, String from, String to) throws OrmException {
- JdbcSchema s = (JdbcSchema) db;
- try (JdbcExecutor e = new JdbcExecutor(s)) {
- s.renameTable(e, from, to);
- }
- }
-
- /** Rename an existing column. */
- protected static void renameColumn(ReviewDb db, String table, String from, String to)
- throws OrmException {
- JdbcSchema s = (JdbcSchema) db;
- try (JdbcExecutor e = new JdbcExecutor(s)) {
- s.renameColumn(e, table, from, to);
- }
- }
-
- /** Execute an SQL statement. */
- protected static void execute(ReviewDb db, String sql) throws SQLException {
- try (Statement s = newStatement(db)) {
- s.execute(sql);
- }
- }
-
- /** Open a new single statement. */
- protected static Statement newStatement(ReviewDb db) throws SQLException {
- return ((JdbcSchema) db).getConnection().createStatement();
- }
-
- /** Open a new prepared statement. */
- protected static PreparedStatement prepareStatement(ReviewDb db, String sql) throws SQLException {
- return ((JdbcSchema) db).getConnection().prepareStatement(sql);
- }
-
- /** Open a new statement executor. */
- protected static JdbcExecutor newExecutor(ReviewDb db) throws OrmException {
- return new JdbcExecutor(((JdbcSchema) db).getConnection());
- }
-}
diff --git a/java/com/google/gerrit/server/schema/SchemaVersionCheck.java b/java/com/google/gerrit/server/schema/SchemaVersionCheck.java
deleted file mode 100644
index bdc15f4cb0..0000000000
--- a/java/com/google/gerrit/server/schema/SchemaVersionCheck.java
+++ /dev/null
@@ -1,95 +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.server.schema;
-
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.ProvisionException;
-
-/** Validates the current schema version. */
-public class SchemaVersionCheck implements LifecycleListener {
- public static Module module() {
- return new LifecycleModule() {
- @Override
- protected void configure() {
- listener().to(SchemaVersionCheck.class);
- }
- };
- }
-
- private final SchemaFactory<ReviewDb> schema;
- private final SitePaths site;
-
- @Inject
- public SchemaVersionCheck(SchemaFactory<ReviewDb> schemaFactory, SitePaths site) {
- this.schema = schemaFactory;
- this.site = site;
- }
-
- @Override
- public void start() {
- try (ReviewDb db = schema.open()) {
- final CurrentSchemaVersion currentVer = getSchemaVersion(db);
- final int expectedVer = SchemaVersion.getBinaryVersion();
-
- if (currentVer == null) {
- throw new ProvisionException(
- "Schema not yet initialized."
- + " Run init to initialize the schema:\n"
- + "$ java -jar gerrit.war init -d "
- + site.site_path.toAbsolutePath());
- }
- if (currentVer.versionNbr < expectedVer) {
- throw new ProvisionException(
- "Unsupported schema version "
- + currentVer.versionNbr
- + "; expected schema version "
- + expectedVer
- + ". Run init to upgrade:\n"
- + "$ java -jar "
- + site.gerrit_war.toAbsolutePath()
- + " init -d "
- + site.site_path.toAbsolutePath());
- } else if (currentVer.versionNbr > expectedVer) {
- throw new ProvisionException(
- "Unsupported schema version "
- + currentVer.versionNbr
- + "; expected schema version "
- + expectedVer
- + ". Downgrade is not supported.");
- }
- } catch (OrmException e) {
- throw new ProvisionException("Cannot read schema_version", e);
- }
- }
-
- @Override
- public void stop() {}
-
- private CurrentSchemaVersion getSchemaVersion(ReviewDb db) {
- try {
- return db.schemaVersion().get(new CurrentSchemaVersion.Key());
- } catch (OrmException e) {
- return null;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_100.java b/java/com/google/gerrit/server/schema/Schema_100.java
deleted file mode 100644
index 090219498e..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_100.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_100 extends SchemaVersion {
- @Inject
- Schema_100(Provider<Schema_99> prior) {
- super(prior);
- }
-
- // No database migration; merges are rechecked on reindex.
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_101.java b/java/com/google/gerrit/server/schema/Schema_101.java
deleted file mode 100644
index ccbb2dee1f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_101.java
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (C) 2014 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 com.google.common.base.Joiner;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcExecutor;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.ColumnModel;
-import com.google.gwtorm.schema.RelationModel;
-import com.google.gwtorm.schema.java.JavaSchemaModel;
-import com.google.gwtorm.schema.sql.DialectPostgreSQL;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class Schema_101 extends SchemaVersion {
-
- private static class PrimaryKey {
- String oldNameInDb;
- List<String> cols;
- }
-
- private Connection conn;
- private SqlDialect dialect;
-
- @Inject
- Schema_101(Provider<Schema_100> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- conn = ((JdbcSchema) db).getConnection();
- dialect = ((JdbcSchema) db).getDialect();
- Map<String, PrimaryKey> corrections = findPKUpdates();
- if (corrections.isEmpty()) {
- return;
- }
-
- ui.message("Wrong Primary Key Column Order Detected");
- ui.message("The following tables are affected:");
- ui.message(Joiner.on(", ").join(corrections.keySet()));
- ui.message("fixing primary keys...");
- try (JdbcExecutor executor = new JdbcExecutor(conn)) {
- for (Map.Entry<String, PrimaryKey> c : corrections.entrySet()) {
- ui.message(String.format(" table: %s ... ", c.getKey()));
- recreatePK(executor, c.getKey(), c.getValue(), ui);
- ui.message("done");
- }
- ui.message("done");
- }
- }
-
- private Map<String, PrimaryKey> findPKUpdates() throws OrmException, SQLException {
- Map<String, PrimaryKey> corrections = new TreeMap<>();
- DatabaseMetaData meta = conn.getMetaData();
- JavaSchemaModel jsm = new JavaSchemaModel(ReviewDb.class);
- for (RelationModel rm : jsm.getRelations()) {
- String tableName = rm.getRelationName();
- List<String> expectedPKCols = relationPK(rm);
- PrimaryKey actualPK = dbTablePK(meta, tableName);
- if (!expectedPKCols.equals(actualPK.cols)) {
- actualPK.cols = expectedPKCols;
- corrections.put(tableName, actualPK);
- }
- }
- return corrections;
- }
-
- private List<String> relationPK(RelationModel rm) {
- Collection<ColumnModel> cols = rm.getPrimaryKeyColumns();
- List<String> pk = new ArrayList<>(cols.size());
- for (ColumnModel cm : cols) {
- pk.add(cm.getColumnName().toLowerCase(Locale.US));
- }
- return pk;
- }
-
- private PrimaryKey dbTablePK(DatabaseMetaData meta, String tableName) throws SQLException {
- if (meta.storesUpperCaseIdentifiers()) {
- tableName = tableName.toUpperCase();
- } else if (meta.storesLowerCaseIdentifiers()) {
- tableName = tableName.toLowerCase();
- }
-
- try (ResultSet cols = meta.getPrimaryKeys(null, null, tableName)) {
- PrimaryKey pk = new PrimaryKey();
- Map<Short, String> seqToName = new TreeMap<>();
- while (cols.next()) {
- seqToName.put(cols.getShort("KEY_SEQ"), cols.getString("COLUMN_NAME"));
- if (pk.oldNameInDb == null) {
- pk.oldNameInDb = cols.getString("PK_NAME");
- }
- }
-
- pk.cols = new ArrayList<>(seqToName.size());
- for (String name : seqToName.values()) {
- pk.cols.add(name.toLowerCase(Locale.US));
- }
- return pk;
- }
- }
-
- private void recreatePK(StatementExecutor executor, String tableName, PrimaryKey pk, UpdateUI ui)
- throws OrmException {
- if (pk.oldNameInDb == null) {
- ui.message(String.format("warning: primary key for table %s didn't exist ... ", tableName));
- } else {
- if (dialect instanceof DialectPostgreSQL) {
- // postgresql doesn't support the ALTER TABLE foo DROP PRIMARY KEY form
- executor.execute("ALTER TABLE " + tableName + " DROP CONSTRAINT " + pk.oldNameInDb);
- } else {
- executor.execute("ALTER TABLE " + tableName + " DROP PRIMARY KEY");
- }
- }
- executor.execute(
- "ALTER TABLE " + tableName + " ADD PRIMARY KEY(" + Joiner.on(",").join(pk.cols) + ")");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_102.java b/java/com/google/gerrit/server/schema/Schema_102.java
deleted file mode 100644
index 1c1aa55256..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_102.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2014 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectPostgreSQL;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-public class Schema_102 extends SchemaVersion {
- @Inject
- Schema_102(Provider<Schema_101> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- SqlDialect dialect = schema.getDialect();
- try (StatementExecutor e = newExecutor(db)) {
- // Drop left over indexes that were missed to be removed in schema 84.
- // See "Delete SQL index support" commit for more details:
- // d4ae3a16d5e1464574bd04f429a63eb9c02b3b43
- Pattern pattern =
- Pattern.compile("^changes_(allOpen|allClosed|byBranchClosed)$", Pattern.CASE_INSENSITIVE);
- String table = "changes";
- Set<String> listIndexes = dialect.listIndexes(schema.getConnection(), table);
- for (String index : listIndexes) {
- if (pattern.matcher(index).matches()) {
- dialect.dropIndex(e, table, index);
- }
- }
-
- dialect.dropIndex(e, table, "changes_byProjectOpen");
- if (dialect instanceof DialectPostgreSQL) {
- e.execute(
- "CREATE INDEX changes_byProjectOpen"
- + " ON "
- + table
- + " (dest_project_name, last_updated_on)"
- + " WHERE open = 'Y'");
- } else {
- e.execute(
- "CREATE INDEX changes_byProjectOpen"
- + " ON "
- + table
- + " (open, dest_project_name, last_updated_on)");
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_103.java b/java/com/google/gerrit/server/schema/Schema_103.java
deleted file mode 100644
index 60a5213cff..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_103.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_103 extends SchemaVersion {
- @Inject
- Schema_103(Provider<Schema_102> prior) {
- super(prior);
- }
-
- // Adds originalSubject column
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_104.java b/java/com/google/gerrit/server/schema/Schema_104.java
deleted file mode 100644
index bebdacaa65..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_104.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2015 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_104 extends SchemaVersion {
- @Inject
- Schema_104(Provider<Schema_103> prior) {
- super(prior);
- }
-
- // Remove old change screen
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_105.java b/java/com/google/gerrit/server/schema/Schema_105.java
deleted file mode 100644
index dd5e71a7fd..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_105.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2015 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 com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class Schema_105 extends SchemaVersion {
- private static final String TABLE = "changes";
-
- @Inject
- Schema_105(Provider<Schema_104> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException, OrmException {
- JdbcSchema schema = (JdbcSchema) db;
- SqlDialect dialect = schema.getDialect();
-
- Map<String, OrmException> errors = new HashMap<>();
- try (StatementExecutor e = newExecutor(db)) {
- for (String index : listChangesIndexes(schema)) {
- ui.message("Dropping index " + index + " on table " + TABLE);
- try {
- dialect.dropIndex(e, TABLE, index);
- } catch (OrmException err) {
- errors.put(index, err);
- }
- }
- }
-
- for (String index : listChangesIndexes(schema)) {
- String msg = "Failed to drop index " + index;
- OrmException err = errors.get(index);
- if (err != null) {
- msg += ": " + err.getMessage();
- }
- ui.message(msg);
- }
- }
-
- private Set<String> listChangesIndexes(JdbcSchema schema) throws SQLException {
- // List of all changes indexes ever created or dropped, found with the
- // following command:
- // find g* -name \*.sql | xargs git log -i -p -S' index changes_' | grep -io ' index
- // changes_\w*' | cut -d' ' -f3 | tr A-Z a-z | sort -u
- // Used rather than listIndexes as we're not sure whether it might include
- // primary key indexes.
- Set<String> allChanges =
- ImmutableSet.of(
- "changes_allclosed",
- "changes_allopen",
- "changes_bybranchclosed",
- "changes_byownerclosed",
- "changes_byowneropen",
- "changes_byproject",
- "changes_byprojectopen",
- "changes_key",
- "changes_submitted");
- return Sets.intersection(
- schema.getDialect().listIndexes(schema.getConnection(), TABLE), allChanges);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_106.java b/java/com/google/gerrit/server/schema/Schema_106.java
deleted file mode 100644
index 7b6a30a8b2..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_106.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (C) 2015 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 java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_106 extends SchemaVersion {
- // we can use multiple threads per CPU as we can expect that threads will be
- // waiting for IO
- private static final int THREADS_PER_CPU = 4;
- private final GitRepositoryManager repoManager;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_106(
- Provider<Schema_105> prior,
- GitRepositoryManager repoManager,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- if (!(repoManager instanceof LocalDiskRepositoryManager)) {
- return;
- }
-
- ui.message("listing all repositories ...");
- SortedSet<Project.NameKey> repoList = repoManager.list();
- ui.message("done");
-
- ui.message(String.format("creating reflog files for %s branches ...", RefNames.REFS_CONFIG));
-
- ExecutorService executorPool = createExecutor(ui, repoList.size());
- List<Future<Void>> futures = new ArrayList<>();
-
- for (Project.NameKey project : repoList) {
- Callable<Void> callable = new ReflogCreator(project);
- futures.add(executorPool.submit(callable));
- }
-
- executorPool.shutdown();
- try {
- for (Future<Void> future : futures) {
- try {
- future.get();
- } catch (ExecutionException e) {
- ui.message(e.getCause().getMessage());
- }
- }
- ui.message("done");
- } catch (InterruptedException ex) {
- String msg =
- String.format(
- "Migration step 106 was interrupted. "
- + "Reflog created in %d of %d repositories only.",
- countDone(futures), repoList.size());
- ui.message(msg);
- }
- }
-
- private static int countDone(List<Future<Void>> futures) {
- int count = 0;
- for (Future<Void> future : futures) {
- if (future.isDone()) {
- count++;
- }
- }
-
- return count;
- }
-
- private ExecutorService createExecutor(UpdateUI ui, int repoCount) {
- int procs = Runtime.getRuntime().availableProcessors();
- int threads = Math.min(procs * THREADS_PER_CPU, repoCount);
- ui.message(String.format("... using %d threads ...", threads));
- return Executors.newFixedThreadPool(threads);
- }
-
- private class ReflogCreator implements Callable<Void> {
- private final Project.NameKey project;
-
- ReflogCreator(Project.NameKey project) {
- this.project = project;
- }
-
- @Override
- public Void call() throws IOException {
- try (Repository repo = repoManager.openRepository(project)) {
- File metaConfigLog = new File(repo.getDirectory(), "logs/" + RefNames.REFS_CONFIG);
- if (metaConfigLog.exists()) {
- return null;
- }
-
- if (!metaConfigLog.getParentFile().mkdirs() || !metaConfigLog.createNewFile()) {
- throw new IOException();
- }
-
- ObjectId metaConfigId = repo.resolve(RefNames.REFS_CONFIG);
- if (metaConfigId != null) {
- try (PrintWriter writer = new PrintWriter(metaConfigLog, UTF_8.name())) {
- writer.print(ObjectId.zeroId().name());
- writer.print(" ");
- writer.print(metaConfigId.name());
- writer.print(" ");
- writer.print(serverUser.toExternalString());
- writer.print("\t");
- writer.print("create reflog");
- writer.println();
- }
- }
- return null;
- } catch (IOException e) {
- throw new IOException(
- String.format(
- "ERROR: Failed to create reflog file for the %s branch in repository %s",
- RefNames.REFS_CONFIG, project.get()),
- e);
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_107.java b/java/com/google/gerrit/server/schema/Schema_107.java
deleted file mode 100644
index dd8868f816..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_107.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2015 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_107 extends SchemaVersion {
-
- @Inject
- Schema_107(Provider<Schema_106> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement()) {
- stmt.executeUpdate("UPDATE accounts set mute_common_path_prefixes = 'Y'");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_108.java b/java/com/google/gerrit/server/schema/Schema_108.java
deleted file mode 100644
index 4e62460323..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_108.java
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright (C) 2015 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 com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-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.gerrit.reviewdb.client.Project.NameKey;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.GroupCollector;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefDatabase;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_108 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
-
- @Inject
- Schema_108(Provider<Schema_107> prior, GitRepositoryManager repoManager) {
- super(prior);
- this.repoManager = repoManager;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- ui.message("Listing all changes ...");
- SetMultimap<Project.NameKey, Change.Id> openByProject = getOpenChangesByProject(db, ui);
- ui.message("done");
-
- ui.message("Updating groups for open changes ...");
- int i = 0;
- for (Map.Entry<Project.NameKey, Collection<Change.Id>> e : openByProject.asMap().entrySet()) {
- try (Repository repo = repoManager.openRepository(e.getKey());
- RevWalk rw = new RevWalk(repo)) {
- updateProjectGroups(db, repo, rw, (Set<Change.Id>) e.getValue(), ui);
- } catch (IOException | NoSuchChangeException err) {
- throw new OrmException(err);
- }
- if (++i % 100 == 0) {
- ui.message(" done " + i + " projects ...");
- }
- }
- ui.message("done");
- }
-
- private void updateProjectGroups(
- ReviewDb db, Repository repo, RevWalk rw, Set<Change.Id> changes, UpdateUI ui)
- throws OrmException, IOException {
- // Match sorting in ReceiveCommits.
- rw.reset();
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.REVERSE, true);
-
- RefDatabase refdb = repo.getRefDatabase();
- for (Ref ref : refdb.getRefsByPrefix(Constants.R_HEADS)) {
- RevCommit c = maybeParseCommit(rw, ref.getObjectId(), ui);
- if (c != null) {
- rw.markUninteresting(c);
- }
- }
-
- ListMultimap<ObjectId, Ref> changeRefsBySha =
- MultimapBuilder.hashKeys().arrayListValues().build();
- ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha =
- MultimapBuilder.hashKeys().arrayListValues().build();
- for (Ref ref : refdb.getRefsByPrefix(RefNames.REFS_CHANGES)) {
- ObjectId id = ref.getObjectId();
- if (ref.getObjectId() == null) {
- continue;
- }
- id = id.copy();
- changeRefsBySha.put(id, ref);
- PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
- if (psId != null && changes.contains(psId.getParentKey())) {
- patchSetsBySha.put(id, psId);
- RevCommit c = maybeParseCommit(rw, id, ui);
- if (c != null) {
- rw.markStart(c);
- }
- }
- }
-
- GroupCollector collector = GroupCollector.createForSchemaUpgradeOnly(changeRefsBySha, db);
- RevCommit c;
- while ((c = rw.next()) != null) {
- collector.visit(c);
- }
-
- updateGroups(db, collector, patchSetsBySha);
- }
-
- private static void updateGroups(
- ReviewDb db, GroupCollector collector, ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha)
- throws OrmException {
- Map<PatchSet.Id, PatchSet> patchSets =
- db.patchSets().toMap(db.patchSets().get(patchSetsBySha.values()));
- for (Map.Entry<ObjectId, Collection<String>> e : collector.getGroups().asMap().entrySet()) {
- for (PatchSet.Id psId : patchSetsBySha.get(e.getKey())) {
- PatchSet ps = patchSets.get(psId);
- if (ps != null) {
- ps.setGroups(ImmutableList.copyOf(e.getValue()));
- }
- }
- }
-
- db.patchSets().update(patchSets.values());
- }
-
- private SetMultimap<Project.NameKey, Change.Id> getOpenChangesByProject(ReviewDb db, UpdateUI ui)
- throws OrmException {
- SortedSet<NameKey> projects = repoManager.list();
- SortedSet<NameKey> nonExistentProjects = Sets.newTreeSet();
- SetMultimap<Project.NameKey, Change.Id> openByProject =
- MultimapBuilder.hashKeys().hashSetValues().build();
- for (Change c : db.changes().all()) {
- Status status = c.getStatus();
- if (status != null && status.isClosed()) {
- continue;
- }
-
- NameKey projectKey = c.getProject();
- if (!projects.contains(projectKey)) {
- nonExistentProjects.add(projectKey);
- } else {
- // The old "submitted" state is not supported anymore
- // (thus status is null) but it was an opened state and needs
- // to be migrated as such
- openByProject.put(projectKey, c.getId());
- }
- }
-
- if (!nonExistentProjects.isEmpty()) {
- ui.message("Detected open changes referring to the following non-existent projects:");
- ui.message(Joiner.on(", ").join(nonExistentProjects));
- ui.message(
- "It is highly recommended to remove\n"
- + "the obsolete open changes, comments and patch-sets from your DB.\n");
- }
- return openByProject;
- }
-
- private static RevCommit maybeParseCommit(RevWalk rw, ObjectId id, UpdateUI ui)
- throws IOException {
- if (id != null) {
- try {
- RevObject obj = rw.parseAny(id);
- return (obj instanceof RevCommit) ? (RevCommit) obj : null;
- } catch (MissingObjectException moe) {
- ui.message("Missing object: " + id.getName() + "\n");
- }
- }
- return null;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_109.java b/java/com/google/gerrit/server/schema/Schema_109.java
deleted file mode 100644
index c5a601500f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_109.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2015 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_109 extends SchemaVersion {
- @Inject
- Schema_109(Provider<Schema_108> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (StatementExecutor e = newExecutor(db)) {
- e.execute("UPDATE changes SET status = 'n', created_on = created_on WHERE status = 's'");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_110.java b/java/com/google/gerrit/server/schema/Schema_110.java
deleted file mode 100644
index 9e0f112037..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_110.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_110 extends SchemaVersion {
- @Inject
- Schema_110(Provider<Schema_109> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_111.java b/java/com/google/gerrit/server/schema/Schema_111.java
deleted file mode 100644
index 223fdb602a..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_111.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_111 extends SchemaVersion {
- @Inject
- Schema_111(Provider<Schema_110> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_112.java b/java/com/google/gerrit/server/schema/Schema_112.java
deleted file mode 100644
index 3e879bd452..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_112.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_112 extends SchemaVersion {
- @Inject
- Schema_112(Provider<Schema_111> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_113.java b/java/com/google/gerrit/server/schema/Schema_113.java
deleted file mode 100644
index 32d655e63e..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_113.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_113 extends SchemaVersion {
- @Inject
- Schema_113(Provider<Schema_112> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_114.java b/java/com/google/gerrit/server/schema/Schema_114.java
deleted file mode 100644
index 85c93d2fee..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_114.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2015 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_114 extends SchemaVersion {
- @Inject
- Schema_114(Provider<Schema_113> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_115.java b/java/com/google/gerrit/server/schema/Schema_115.java
deleted file mode 100644
index 70bc9215f4..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_115.java
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright (C) 2015 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.base.Preconditions.checkState;
-import static com.google.gerrit.server.config.ConfigUtil.storeSection;
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.client.Theme;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.git.UserConfigSections;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.patch.PatchListKey;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_115 extends SchemaVersion {
- private final GitRepositoryManager mgr;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_115(
- Provider<Schema_114> prior,
- GitRepositoryManager mgr,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.mgr = mgr;
- this.allUsersName = allUsersName;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- Map<Account.Id, DiffPreferencesInfo> imports = new HashMap<>();
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs = stmt.executeQuery("SELECT * FROM account_diff_preferences")) {
- Set<String> availableColumns = getColumns(rs);
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt("id"));
- DiffPreferencesInfo prefs = new DiffPreferencesInfo();
- if (availableColumns.contains("context")) {
- prefs.context = (int) rs.getShort("context");
- }
- if (availableColumns.contains("expand_all_comments")) {
- prefs.expandAllComments = toBoolean(rs.getString("expand_all_comments"));
- }
- if (availableColumns.contains("hide_line_numbers")) {
- prefs.hideLineNumbers = toBoolean(rs.getString("hide_line_numbers"));
- }
- if (availableColumns.contains("hide_top_menu")) {
- prefs.hideTopMenu = toBoolean(rs.getString("hide_top_menu"));
- }
- if (availableColumns.contains("ignore_whitespace")) {
- // Enum with char as value
- prefs.ignoreWhitespace = toWhitespace(rs.getString("ignore_whitespace"));
- }
- if (availableColumns.contains("intraline_difference")) {
- prefs.intralineDifference = toBoolean(rs.getString("intraline_difference"));
- }
- if (availableColumns.contains("line_length")) {
- prefs.lineLength = rs.getInt("line_length");
- }
- if (availableColumns.contains("manual_review")) {
- prefs.manualReview = toBoolean(rs.getString("manual_review"));
- }
- if (availableColumns.contains("render_entire_file")) {
- prefs.renderEntireFile = toBoolean(rs.getString("render_entire_file"));
- }
- if (availableColumns.contains("retain_header")) {
- prefs.retainHeader = toBoolean(rs.getString("retain_header"));
- }
- if (availableColumns.contains("show_line_endings")) {
- prefs.showLineEndings = toBoolean(rs.getString("show_line_endings"));
- }
- if (availableColumns.contains("show_tabs")) {
- prefs.showTabs = toBoolean(rs.getString("show_tabs"));
- }
- if (availableColumns.contains("show_whitespace_errors")) {
- prefs.showWhitespaceErrors = toBoolean(rs.getString("show_whitespace_errors"));
- }
- if (availableColumns.contains("skip_deleted")) {
- prefs.skipDeleted = toBoolean(rs.getString("skip_deleted"));
- }
- if (availableColumns.contains("skip_uncommented")) {
- prefs.skipUncommented = toBoolean(rs.getString("skip_uncommented"));
- }
- if (availableColumns.contains("syntax_highlighting")) {
- prefs.syntaxHighlighting = toBoolean(rs.getString("syntax_highlighting"));
- }
- if (availableColumns.contains("tab_size")) {
- prefs.tabSize = rs.getInt("tab_size");
- }
- if (availableColumns.contains("theme")) {
- // Enum with name as values; can be null
- prefs.theme = toTheme(rs.getString("theme"));
- }
- if (availableColumns.contains("hide_empty_pane")) {
- prefs.hideEmptyPane = toBoolean(rs.getString("hide_empty_pane"));
- }
- if (availableColumns.contains("auto_hide_diff_table_header")) {
- prefs.autoHideDiffTableHeader = toBoolean(rs.getString("auto_hide_diff_table_header"));
- }
- imports.put(accountId, prefs);
- }
- }
-
- if (imports.isEmpty()) {
- return;
- }
-
- try (Repository git = mgr.openRepository(allUsersName);
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
- for (Map.Entry<Account.Id, DiffPreferencesInfo> e : imports.entrySet()) {
- try (MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- VersionedAccountPreferences p = VersionedAccountPreferences.forUser(e.getKey());
- p.load(md);
- storeSection(
- p.getConfig(),
- UserConfigSections.DIFF,
- null,
- e.getValue(),
- DiffPreferencesInfo.defaults());
- p.commit(md);
- }
- }
-
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-
- private Set<String> getColumns(ResultSet rs) throws SQLException {
- ResultSetMetaData metaData = rs.getMetaData();
- int columnCount = metaData.getColumnCount();
- Set<String> columns = new HashSet<>(columnCount);
- for (int i = 1; i <= columnCount; i++) {
- columns.add(metaData.getColumnLabel(i).toLowerCase());
- }
- return columns;
- }
-
- private static Theme toTheme(String v) {
- if (v == null) {
- return Theme.DEFAULT;
- }
- return Theme.valueOf(v);
- }
-
- private static Whitespace toWhitespace(String v) {
- requireNonNull(v);
- if (v.isEmpty()) {
- return Whitespace.IGNORE_NONE;
- }
- Whitespace r = PatchListKey.WHITESPACE_TYPES.inverse().get(v.charAt(0));
- if (r == null) {
- throw new IllegalArgumentException("Cannot find Whitespace type for: " + v);
- }
- return r;
- }
-
- private static boolean toBoolean(String v) {
- checkState(!Strings.isNullOrEmpty(v));
- return v.equals("Y");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_116.java b/java/com/google/gerrit/server/schema/Schema_116.java
deleted file mode 100644
index 5b018a2953..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_116.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_116 extends SchemaVersion {
- @Inject
- Schema_116(Provider<Schema_115> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_117.java b/java/com/google/gerrit/server/schema/Schema_117.java
deleted file mode 100644
index 35e6c8aaf8..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_117.java
+++ /dev/null
@@ -1,50 +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.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Set;
-
-public class Schema_117 extends SchemaVersion {
- @Inject
- Schema_117(Provider<Schema_116> prior) {
- super(prior);
- }
-
- @Override
- protected void preUpdateSchema(ReviewDb db) throws OrmException, SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- Connection connection = schema.getConnection();
- String tableName = "patch_sets";
- String oldColumnName = "push_certficate";
- String newColumnName = "push_certificate";
- Set<String> columns = schema.getDialect().listColumns(connection, tableName);
- if (columns.contains(oldColumnName)) {
- renameColumn(db, tableName, oldColumnName, newColumnName);
- }
- try (Statement stmt = schema.getConnection().createStatement()) {
- stmt.execute("ALTER TABLE " + tableName + " MODIFY " + newColumnName + " clob");
- } catch (SQLException e) {
- // Ignore. Type may have already been modified manually.
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_118.java b/java/com/google/gerrit/server/schema/Schema_118.java
deleted file mode 100644
index 8c2c740b16..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_118.java
+++ /dev/null
@@ -1,25 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_118 extends SchemaVersion {
- @Inject
- Schema_118(Provider<Schema_117> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_119.java b/java/com/google/gerrit/server/schema/Schema_119.java
deleted file mode 100644
index e5a6405957..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_119.java
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright (C) 2015 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.base.Preconditions.checkState;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.ANON_GIT;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.ANON_HTTP;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.HTTP;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.REPO_DOWNLOAD;
-import static com.google.gerrit.reviewdb.client.CoreDownloadSchemes.SSH;
-import static com.google.gerrit.server.config.ConfigUtil.storeSection;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DateFormat;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DownloadCommand;
-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.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.git.UserConfigSections;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_119 extends SchemaVersion {
- private static final ImmutableMap<String, String> LEGACY_DISPLAYNAME_MAP =
- ImmutableMap.<String, String>of(
- "ANON_GIT", ANON_GIT,
- "ANON_HTTP", ANON_HTTP,
- "HTTP", HTTP,
- "SSH", SSH,
- "REPO_DOWNLOAD", REPO_DOWNLOAD);
-
- private final GitRepositoryManager mgr;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_119(
- Provider<Schema_118> prior,
- GitRepositoryManager mgr,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.mgr = mgr;
- this.allUsersName = allUsersName;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- Connection connection = schema.getConnection();
- String tableName = "accounts";
- String emailStrategy = "email_strategy";
- Set<String> columns = schema.getDialect().listColumns(connection, tableName);
- Map<Account.Id, GeneralPreferencesInfo> imports = new HashMap<>();
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "select "
- + "account_id, "
- + "maximum_page_size, "
- + "show_site_header, "
- + "use_flash_clipboard, "
- + "download_url, "
- + "download_command, "
- + (columns.contains(emailStrategy)
- ? emailStrategy + ", "
- : "copy_self_on_email, ")
- + "date_format, "
- + "time_format, "
- + "relative_date_in_change_table, "
- + "diff_view, "
- + "size_bar_in_change_table, "
- + "legacycid_in_change_table, "
- + "review_category_strategy, "
- + "mute_common_path_prefixes "
- + "from "
- + tableName)) {
- while (rs.next()) {
- GeneralPreferencesInfo p = new GeneralPreferencesInfo();
- Account.Id accountId = new Account.Id(rs.getInt(1));
- p.changesPerPage = (int) rs.getShort(2);
- p.showSiteHeader = toBoolean(rs.getString(3));
- p.useFlashClipboard = toBoolean(rs.getString(4));
- p.downloadScheme = convertToModernNames(rs.getString(5));
- p.downloadCommand = toDownloadCommand(rs.getString(6));
- p.emailStrategy = toEmailStrategy(rs.getString(7), columns.contains(emailStrategy));
- p.dateFormat = toDateFormat(rs.getString(8));
- p.timeFormat = toTimeFormat(rs.getString(9));
- p.relativeDateInChangeTable = toBoolean(rs.getString(10));
- p.diffView = toDiffView(rs.getString(11));
- p.sizeBarInChangeTable = toBoolean(rs.getString(12));
- p.legacycidInChangeTable = toBoolean(rs.getString(13));
- p.reviewCategoryStrategy = toReviewCategoryStrategy(rs.getString(14));
- p.muteCommonPathPrefixes = toBoolean(rs.getString(15));
- p.defaultBaseForMerges = GeneralPreferencesInfo.defaults().defaultBaseForMerges;
- imports.put(accountId, p);
- }
- }
-
- if (imports.isEmpty()) {
- return;
- }
-
- try (Repository git = mgr.openRepository(allUsersName);
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
- for (Map.Entry<Account.Id, GeneralPreferencesInfo> e : imports.entrySet()) {
- try (MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- VersionedAccountPreferences p = VersionedAccountPreferences.forUser(e.getKey());
- p.load(md);
- storeSection(
- p.getConfig(),
- UserConfigSections.GENERAL,
- null,
- e.getValue(),
- GeneralPreferencesInfo.defaults());
- p.commit(md);
- }
- }
-
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-
- private String convertToModernNames(String s) {
- return !Strings.isNullOrEmpty(s) && LEGACY_DISPLAYNAME_MAP.containsKey(s)
- ? LEGACY_DISPLAYNAME_MAP.get(s)
- : s;
- }
-
- private static DownloadCommand toDownloadCommand(String v) {
- if (v == null) {
- return DownloadCommand.CHECKOUT;
- }
- return DownloadCommand.valueOf(v);
- }
-
- private static DateFormat toDateFormat(String v) {
- if (v == null) {
- return DateFormat.STD;
- }
- return DateFormat.valueOf(v);
- }
-
- private static TimeFormat toTimeFormat(String v) {
- if (v == null) {
- return TimeFormat.HHMM_12;
- }
- return TimeFormat.valueOf(v);
- }
-
- private static DiffView toDiffView(String v) {
- if (v == null) {
- return DiffView.SIDE_BY_SIDE;
- }
- return DiffView.valueOf(v);
- }
-
- private static EmailStrategy toEmailStrategy(String v, boolean emailStrategyColumnExists)
- throws OrmException {
- if (v == null) {
- return EmailStrategy.ENABLED;
- }
- if (emailStrategyColumnExists) {
- return EmailStrategy.valueOf(v);
- }
- if (v.equals("N")) {
- // EMAIL_STRATEGY='ENABLED' WHERE (COPY_SELF_ON_EMAIL='N')
- return EmailStrategy.ENABLED;
- } else if (v.equals("Y")) {
- // EMAIL_STRATEGY='CC_ON_OWN_COMMENTS' WHERE (COPY_SELF_ON_EMAIL='Y')
- return EmailStrategy.CC_ON_OWN_COMMENTS;
- } else {
- throw new OrmException("invalid value in accounts.copy_self_on_email: " + v);
- }
- }
-
- private static ReviewCategoryStrategy toReviewCategoryStrategy(String v) {
- if (v == null) {
- return ReviewCategoryStrategy.NONE;
- }
- return ReviewCategoryStrategy.valueOf(v);
- }
-
- private static boolean toBoolean(String v) {
- checkState(!Strings.isNullOrEmpty(v));
- return v.equals("Y");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_120.java b/java/com/google/gerrit/server/schema/Schema_120.java
deleted file mode 100644
index f2f3b99ceb..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_120.java
+++ /dev/null
@@ -1,119 +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.server.schema;
-
-import com.google.gerrit.common.data.SubscribeSection;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.server.project.ProjectConfig;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.RefSpec;
-
-public class Schema_120 extends SchemaVersion {
-
- private final GitRepositoryManager mgr;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_120(
- Provider<Schema_119> prior,
- GitRepositoryManager mgr,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.mgr = mgr;
- this.serverUser = serverUser;
- }
-
- private void allowSubmoduleSubscription(Branch.NameKey subbranch, Branch.NameKey superBranch)
- throws OrmException {
- try (Repository git = mgr.openRepository(subbranch.getParentKey());
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
- try (MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, subbranch.getParentKey(), git, bru)) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage("Added superproject subscription during upgrade");
- ProjectConfig pc = ProjectConfig.read(md);
-
- SubscribeSection s = null;
- for (SubscribeSection s1 : pc.getSubscribeSections(subbranch)) {
- if (s1.getProject().equals(superBranch.getParentKey())) {
- s = s1;
- }
- }
- if (s == null) {
- s = new SubscribeSection(superBranch.getParentKey());
- pc.addSubscribeSection(s);
- }
- RefSpec newRefSpec = new RefSpec(subbranch.get() + ":" + superBranch.get());
-
- if (!s.getMatchingRefSpecs().contains(newRefSpec)) {
- // For the migration we use only exact RefSpecs, we're not trying to
- // generalize it.
- s.addMatchingRefSpec(newRefSpec);
- }
-
- pc.commit(md);
- }
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- } catch (ConfigInvalidException | IOException e) {
- throw new OrmException(e);
- }
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- ui.message("Generating Superproject subscriptions table to submodule ACLs");
-
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT "
- + "super_project_project_name, "
- + "super_project_branch_name, "
- + "submodule_project_name, "
- + "submodule_branch_name "
- + "FROM submodule_subscriptions")) {
- while (rs.next()) {
- Project.NameKey superproject = new Project.NameKey(rs.getString(1));
- Branch.NameKey superbranch = new Branch.NameKey(superproject, rs.getString(2));
-
- Project.NameKey submodule = new Project.NameKey(rs.getString(3));
- Branch.NameKey subbranch = new Branch.NameKey(submodule, rs.getString(4));
-
- allowSubmoduleSubscription(subbranch, superbranch);
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_121.java b/java/com/google/gerrit/server/schema/Schema_121.java
deleted file mode 100644
index 31b42fb53f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_121.java
+++ /dev/null
@@ -1,25 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_121 extends SchemaVersion {
- @Inject
- Schema_121(Provider<Schema_120> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_122.java b/java/com/google/gerrit/server/schema/Schema_122.java
deleted file mode 100644
index b5b799d4e6..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_122.java
+++ /dev/null
@@ -1,27 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_122 extends SchemaVersion {
- @Inject
- Schema_122(Provider<Schema_121> prior) {
- super(prior);
- }
-
- // Adds tag column
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_123.java b/java/com/google/gerrit/server/schema/Schema_123.java
deleted file mode 100644
index 31cfd5d30d..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_123.java
+++ /dev/null
@@ -1,86 +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.server.schema;
-
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.StarredChangesUtil;
-import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Map;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-public class Schema_123 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
-
- @Inject
- Schema_123(
- Provider<Schema_122> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- ListMultimap<Account.Id, Change.Id> imports =
- MultimapBuilder.hashKeys().arrayListValues().build();
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs = stmt.executeQuery("SELECT account_id, change_id FROM starred_changes")) {
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
- Change.Id changeId = new Change.Id(rs.getInt(2));
- imports.put(accountId, changeId);
- }
- }
-
- if (imports.isEmpty()) {
- return;
- }
-
- try (Repository git = repoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
- ObjectId id = StarredChangesUtil.writeLabels(git, StarredChangesUtil.DEFAULT_LABELS);
- for (Map.Entry<Account.Id, Change.Id> e : imports.entries()) {
- bru.addCommand(
- new ReceiveCommand(
- ObjectId.zeroId(), id, RefNames.refsStarredChanges(e.getValue(), e.getKey())));
- }
- bru.execute(rw, new TextProgressMonitor());
- } catch (IOException | IllegalLabelException ex) {
- throw new OrmException(ex);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_124.java b/java/com/google/gerrit/server/schema/Schema_124.java
deleted file mode 100644
index 6164fd149c..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_124.java
+++ /dev/null
@@ -1,138 +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.server.schema;
-
-import static java.util.Comparator.comparing;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Ordering;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountSshKey;
-import com.google.gerrit.server.account.VersionedAuthorizedKeys;
-import com.google.gerrit.server.account.VersionedAuthorizedKeys.SimpleSshKeyCreator;
-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.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_124 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_124(
- Provider<Schema_123> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- ListMultimap<Account.Id, AccountSshKey> imports =
- MultimapBuilder.hashKeys().arrayListValues().build();
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT "
- + "account_id, "
- + "seq, "
- + "ssh_public_key, "
- + "valid "
- + "FROM account_ssh_keys")) {
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
- int seq = rs.getInt(2);
- String sshPublicKey = rs.getString(3);
- boolean valid = toBoolean(rs.getString(4));
- AccountSshKey key = AccountSshKey.create(accountId, seq, sshPublicKey, valid);
- imports.put(accountId, key);
- }
- }
-
- if (imports.isEmpty()) {
- return;
- }
-
- try (Repository git = repoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
-
- for (Map.Entry<Account.Id, Collection<AccountSshKey>> e : imports.asMap().entrySet()) {
- try (MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
-
- VersionedAuthorizedKeys authorizedKeys =
- new VersionedAuthorizedKeys(new SimpleSshKeyCreator(), e.getKey());
- authorizedKeys.load(md);
- authorizedKeys.setKeys(fixInvalidSequenceNumbers(e.getValue()));
- authorizedKeys.commit(md);
- }
- }
-
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-
- private Collection<AccountSshKey> fixInvalidSequenceNumbers(Collection<AccountSshKey> keys) {
- Ordering<AccountSshKey> o = Ordering.from(comparing(AccountSshKey::seq));
- List<AccountSshKey> fixedKeys = new ArrayList<>(keys);
- AccountSshKey minKey = o.min(keys);
- while (minKey.seq() <= 0) {
- AccountSshKey fixedKey =
- AccountSshKey.create(
- minKey.accountId(), Math.max(o.max(keys).seq() + 1, 1), minKey.sshPublicKey());
- Collections.replaceAll(fixedKeys, minKey, fixedKey);
- minKey = o.min(fixedKeys);
- }
- return fixedKeys;
- }
-
- private static boolean toBoolean(String v) {
- return !Strings.isNullOrEmpty(v) && v.equalsIgnoreCase("Y");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_125.java b/java/com/google/gerrit/server/schema/Schema_125.java
deleted file mode 100644
index 7aab7c7946..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_125.java
+++ /dev/null
@@ -1,128 +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.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-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.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_125 extends SchemaVersion {
- private static final String COMMIT_MSG =
- "Assign default permissions on user branches\n"
- + "\n"
- + "By default each user should be able to read and update the own user\n"
- + "branch. Also the user should be able to approve and submit changes for\n"
- + "the own user branch. Assign default permissions for this and remove the\n"
- + "old exclusive read protection from the user branches.\n";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final AllProjectsName allProjectsName;
- private final SystemGroupBackend systemGroupBackend;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_125(
- Provider<Schema_124> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- AllProjectsName allProjectsName,
- SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.allProjectsName = allProjectsName;
- this.systemGroupBackend = systemGroupBackend;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (Repository git = repoManager.openRepository(allUsersName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
- ProjectConfig config = ProjectConfig.read(md);
-
- config
- .getAccessSection(RefNames.REFS_USERS + "*", true)
- .remove(new Permission(Permission.READ));
- GroupReference registered = systemGroupBackend.getGroup(REGISTERED_USERS);
- AccessSection users =
- config.getAccessSection(
- RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true);
- grant(config, users, Permission.READ, true, registered);
- grant(config, users, Permission.PUSH, true, registered);
- grant(config, users, Permission.SUBMIT, true, registered);
-
- for (LabelType lt : getLabelTypes(config)) {
- if ("Code-Review".equals(lt.getName()) || "Verified".equals(lt.getName())) {
- grant(config, users, lt, lt.getMin().getValue(), lt.getMax().getValue(), registered);
- }
- }
-
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
- config.commit(md);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-
- private Collection<LabelType> getLabelTypes(ProjectConfig config)
- throws IOException, ConfigInvalidException {
- Map<String, LabelType> labelTypes = new HashMap<>(config.getLabelSections());
- Project.NameKey parent = config.getProject().getParent(allProjectsName);
- while (parent != null) {
- try (Repository git = repoManager.openRepository(parent);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, parent, git)) {
- ProjectConfig parentConfig = ProjectConfig.read(md);
- for (LabelType lt : parentConfig.getLabelSections().values()) {
- if (!labelTypes.containsKey(lt.getName())) {
- labelTypes.put(lt.getName(), lt);
- }
- }
- parent = parentConfig.getProject().getParent(allProjectsName);
- }
- }
- return labelTypes.values();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_126.java b/java/com/google/gerrit/server/schema/Schema_126.java
deleted file mode 100644
index 5dbda7279f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_126.java
+++ /dev/null
@@ -1,86 +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.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-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.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_126 extends SchemaVersion {
- private static final String COMMIT_MSG = "Fix default permissions on user branches";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final SystemGroupBackend systemGroupBackend;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_126(
- Provider<Schema_125> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.systemGroupBackend = systemGroupBackend;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (Repository git = repoManager.openRepository(allUsersName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
- ProjectConfig config = ProjectConfig.read(md);
-
- String refsUsersShardedId = RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}";
- config.remove(config.getAccessSection(refsUsersShardedId));
-
- GroupReference registered = systemGroupBackend.getGroup(REGISTERED_USERS);
- AccessSection users = config.getAccessSection(refsUsersShardedId, true);
- grant(config, users, Permission.READ, false, true, registered);
- grant(config, users, Permission.PUSH, false, true, registered);
- grant(config, users, Permission.SUBMIT, false, true, registered);
-
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
- config.commit(md);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_127.java b/java/com/google/gerrit/server/schema/Schema_127.java
deleted file mode 100644
index d246b7593c..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_127.java
+++ /dev/null
@@ -1,87 +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.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import org.eclipse.jgit.lib.Config;
-
-public class Schema_127 extends SchemaVersion {
- private static final int MAX_BATCH_SIZE = 1000;
-
- private final SitePaths sitePaths;
- private final Config cfg;
- private final ThreadSettingsConfig threadSettingsConfig;
-
- @Inject
- Schema_127(
- Provider<Schema_126> prior,
- SitePaths sitePaths,
- @GerritServerConfig Config cfg,
- ThreadSettingsConfig threadSettingsConfig) {
- super(prior);
- this.sitePaths = sitePaths;
- this.cfg = cfg;
- this.threadSettingsConfig = threadSettingsConfig;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- JdbcAccountPatchReviewStore jdbcAccountPatchReviewStore =
- JdbcAccountPatchReviewStore.createAccountPatchReviewStore(
- cfg, sitePaths, threadSettingsConfig);
- jdbcAccountPatchReviewStore.dropTableIfExists();
- jdbcAccountPatchReviewStore.createTableIfNotExists();
- try (Connection con = jdbcAccountPatchReviewStore.getConnection();
- PreparedStatement stmt =
- con.prepareStatement(
- "INSERT INTO account_patch_reviews "
- + "(account_id, change_id, patch_set_id, file_name) VALUES "
- + "(?, ?, ?, ?)")) {
- int batchCount = 0;
-
- try (Statement s = newStatement(db);
- ResultSet rs = s.executeQuery("SELECT * from account_patch_reviews")) {
- while (rs.next()) {
- stmt.setInt(1, rs.getInt("account_id"));
- stmt.setInt(2, rs.getInt("change_id"));
- stmt.setInt(3, rs.getInt("patch_set_id"));
- stmt.setString(4, rs.getString("file_name"));
- stmt.addBatch();
- batchCount++;
- if (batchCount >= MAX_BATCH_SIZE) {
- stmt.executeBatch();
- batchCount = 0;
- }
- }
- }
- if (batchCount > 0) {
- stmt.executeBatch();
- }
- } catch (SQLException e) {
- throw jdbcAccountPatchReviewStore.convertError("insert", e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_128.java b/java/com/google/gerrit/server/schema/Schema_128.java
deleted file mode 100644
index bd6b76a3c4..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_128.java
+++ /dev/null
@@ -1,80 +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.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-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.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_128 extends SchemaVersion {
- private static final String COMMIT_MSG = "Add addPatchSet permission to all projects";
-
- private final GitRepositoryManager repoManager;
- private final AllProjectsName allProjectsName;
- private final SystemGroupBackend systemGroupBackend;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_128(
- Provider<Schema_127> prior,
- GitRepositoryManager repoManager,
- AllProjectsName allProjectsName,
- SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allProjectsName = allProjectsName;
- this.systemGroupBackend = systemGroupBackend;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (Repository git = repoManager.openRepository(allProjectsName);
- MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git)) {
- ProjectConfig config = ProjectConfig.read(md);
-
- GroupReference registered = systemGroupBackend.getGroup(REGISTERED_USERS);
- AccessSection refsFor = config.getAccessSection("refs/for/*", true);
- grant(config, refsFor, Permission.ADD_PATCH_SET, false, false, registered);
-
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
- config.commit(md);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_129.java b/java/com/google/gerrit/server/schema/Schema_129.java
deleted file mode 100644
index 73ce3c3e48..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_129.java
+++ /dev/null
@@ -1,40 +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.server.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_129 extends SchemaVersion {
-
- @Inject
- Schema_129(Provider<Schema_128> prior) {
- super(prior);
- }
-
- @Override
- protected void preUpdateSchema(ReviewDb db) throws OrmException {
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement()) {
- stmt.execute("ALTER TABLE patch_sets MODIFY groups clob");
- } catch (SQLException e) {
- // Ignore. Type may have already been modified manually.
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_130.java b/java/com/google/gerrit/server/schema/Schema_130.java
deleted file mode 100644
index 91cdcdae4c..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_130.java
+++ /dev/null
@@ -1,74 +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.server.schema;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_130 extends SchemaVersion {
- private static final String COMMIT_MSG =
- "Remove force option from 'Push Annotated Tag' permission\n"
- + "\n"
- + "The force option on 'Push Annotated Tag' had no effect and is no longer\n"
- + "supported.";
-
- private final GitRepositoryManager repoManager;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_130(
- Provider<Schema_129> prior,
- GitRepositoryManager repoManager,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- SortedSet<Project.NameKey> repoList = repoManager.list();
- SortedSet<Project.NameKey> repoUpgraded = new TreeSet<>();
- ui.message("\tMigrating " + repoList.size() + " repositories ...");
- for (Project.NameKey projectName : repoList) {
- try (Repository git = repoManager.openRepository(projectName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, projectName, git)) {
- ProjectConfigSchemaUpdate cfg = ProjectConfigSchemaUpdate.read(md);
- cfg.removeForceFromPermission("pushTag");
- if (cfg.isUpdated()) {
- repoUpgraded.add(projectName);
- }
- cfg.save(serverUser, COMMIT_MSG);
- } catch (Exception ex) {
- throw new OrmException("Cannot migrate project " + projectName, ex);
- }
- }
- ui.message("\tMigration completed: " + repoUpgraded.size() + " repositories updated:");
- ui.message("\t" + repoUpgraded.stream().map(Project.NameKey::get).collect(joining(" ")));
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_131.java b/java/com/google/gerrit/server/schema/Schema_131.java
deleted file mode 100644
index 3755211664..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_131.java
+++ /dev/null
@@ -1,76 +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.server.schema;
-
-import static java.util.stream.Collectors.joining;
-
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_131 extends SchemaVersion {
- private static final String COMMIT_MSG =
- "Rename 'Push Annotated/Signed Tag' permission to 'Create Annotated/Signed Tag'";
-
- private final GitRepositoryManager repoManager;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_131(
- Provider<Schema_130> prior,
- GitRepositoryManager repoManager,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- SortedSet<Project.NameKey> repoList = repoManager.list();
- SortedSet<Project.NameKey> repoUpgraded = new TreeSet<>();
- ui.message("\tMigrating " + repoList.size() + " repositories ...");
- for (Project.NameKey projectName : repoList) {
- try (Repository git = repoManager.openRepository(projectName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, projectName, git)) {
- ProjectConfig config = ProjectConfig.read(md);
- if (config.hasLegacyPermissions()) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
- config.commit(md);
- repoUpgraded.add(projectName);
- }
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException("Cannot migrate project " + projectName, ex);
- }
- }
- ui.message("\tMigration completed: " + repoUpgraded.size() + " repositories updated:");
- ui.message("\t" + repoUpgraded.stream().map(Project.NameKey::get).collect(joining(" ")));
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_132.java b/java/com/google/gerrit/server/schema/Schema_132.java
deleted file mode 100644
index 7c1cde8fe2..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_132.java
+++ /dev/null
@@ -1,25 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_132 extends SchemaVersion {
- @Inject
- Schema_132(Provider<Schema_131> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_133.java b/java/com/google/gerrit/server/schema/Schema_133.java
deleted file mode 100644
index 31d330bc14..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_133.java
+++ /dev/null
@@ -1,25 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_133 extends SchemaVersion {
- @Inject
- Schema_133(Provider<Schema_132> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_134.java b/java/com/google/gerrit/server/schema/Schema_134.java
deleted file mode 100644
index fa01ff3667..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_134.java
+++ /dev/null
@@ -1,25 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_134 extends SchemaVersion {
- @Inject
- Schema_134(Provider<Schema_133> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_135.java b/java/com/google/gerrit/server/schema/Schema_135.java
deleted file mode 100644
index df3dcaf92b..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_135.java
+++ /dev/null
@@ -1,98 +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.server.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS;
-import static java.util.stream.Collectors.toSet;
-
-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.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-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.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.Set;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_135 extends SchemaVersion {
- private static final String COMMIT_MSG =
- "Allow admins and project owners to create refs/meta/config";
-
- private final GitRepositoryManager repoManager;
- private final AllProjectsName allProjectsName;
- private final SystemGroupBackend systemGroupBackend;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_135(
- Provider<Schema_134> prior,
- GitRepositoryManager repoManager,
- AllProjectsName allProjectsName,
- SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allProjectsName = allProjectsName;
- this.systemGroupBackend = systemGroupBackend;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (Repository git = repoManager.openRepository(allProjectsName);
- MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git)) {
- ProjectConfig config = ProjectConfig.read(md);
-
- AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
- Permission createRefsMetaConfigPermission = meta.getPermission(Permission.CREATE, true);
-
- Set<GroupReference> groups =
- Stream.concat(
- config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true)
- .getPermission(GlobalCapability.ADMINISTRATE_SERVER, true).getRules().stream()
- .map(PermissionRule::getGroup),
- Stream.of(systemGroupBackend.getGroup(PROJECT_OWNERS)))
- .filter(g -> createRefsMetaConfigPermission.getRule(g) == null)
- .collect(toSet());
-
- for (GroupReference group : groups) {
- createRefsMetaConfigPermission.add(new PermissionRule(config.resolve(group)));
- }
-
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
- config.commit(md);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_136.java b/java/com/google/gerrit/server/schema/Schema_136.java
deleted file mode 100644
index a4b1c82db9..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_136.java
+++ /dev/null
@@ -1,25 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_136 extends SchemaVersion {
- @Inject
- Schema_136(Provider<Schema_135> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_137.java b/java/com/google/gerrit/server/schema/Schema_137.java
deleted file mode 100644
index 1b4102fae0..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_137.java
+++ /dev/null
@@ -1,26 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/* change the type of SystemConfig#sitePath to CLOB */
-public class Schema_137 extends SchemaVersion {
- @Inject
- Schema_137(Provider<Schema_136> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_138.java b/java/com/google/gerrit/server/schema/Schema_138.java
deleted file mode 100644
index f824ee135a..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_138.java
+++ /dev/null
@@ -1,26 +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.server.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/* Add resolved field to PatchLineComment */
-public class Schema_138 extends SchemaVersion {
- @Inject
- Schema_138(Provider<Schema_137> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_139.java b/java/com/google/gerrit/server/schema/Schema_139.java
deleted file mode 100644
index cdde7e4b22..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_139.java
+++ /dev/null
@@ -1,212 +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.server.schema;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Strings;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountConfig;
-import com.google.gerrit.server.account.InternalAccountUpdate;
-import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
-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.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class Schema_139 extends SchemaVersion {
- private static final String MSG = "Migrate project watches to git";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_139(
- Provider<Schema_138> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- ListMultimap<Account.Id, ProjectWatch> imports =
- MultimapBuilder.hashKeys().arrayListValues().build();
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT "
- + "account_id, "
- + "project_name, "
- + "filter, "
- + "notify_abandoned_changes, "
- + "notify_all_comments, "
- + "notify_new_changes, "
- + "notify_new_patch_sets, "
- + "notify_submitted_changes "
- + "FROM account_project_watches")) {
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
- ProjectWatch.Builder b =
- ProjectWatch.builder()
- .project(new Project.NameKey(rs.getString(2)))
- .filter(rs.getString(3))
- .notifyAbandonedChanges(toBoolean(rs.getString(4)))
- .notifyAllComments(toBoolean(rs.getString(5)))
- .notifyNewChanges(toBoolean(rs.getString(6)))
- .notifyNewPatchSets(toBoolean(rs.getString(7)))
- .notifySubmittedChanges(toBoolean(rs.getString(8)));
- imports.put(accountId, b.build());
- }
- }
-
- if (imports.isEmpty()) {
- return;
- }
-
- try (Repository git = repoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
- bru.setRefLogIdent(serverUser);
- bru.setRefLogMessage(MSG, false);
-
- for (Map.Entry<Account.Id, Collection<ProjectWatch>> e : imports.asMap().entrySet()) {
- Map<ProjectWatchKey, Set<NotifyType>> projectWatches = new HashMap<>();
- for (ProjectWatch projectWatch : e.getValue()) {
- ProjectWatchKey key =
- ProjectWatchKey.create(projectWatch.project(), projectWatch.filter());
- if (projectWatches.containsKey(key)) {
- throw new OrmDuplicateKeyException(
- "Duplicate key for watched project: " + key.toString());
- }
- Set<NotifyType> notifyValues = EnumSet.noneOf(NotifyType.class);
- if (projectWatch.notifyAbandonedChanges()) {
- notifyValues.add(NotifyType.ABANDONED_CHANGES);
- }
- if (projectWatch.notifyAllComments()) {
- notifyValues.add(NotifyType.ALL_COMMENTS);
- }
- if (projectWatch.notifyNewChanges()) {
- notifyValues.add(NotifyType.NEW_CHANGES);
- }
- if (projectWatch.notifyNewPatchSets()) {
- notifyValues.add(NotifyType.NEW_PATCHSETS);
- }
- if (projectWatch.notifySubmittedChanges()) {
- notifyValues.add(NotifyType.SUBMITTED_CHANGES);
- }
- projectWatches.put(key, notifyValues);
- }
-
- try (MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git, bru)) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(MSG);
-
- AccountConfig accountConfig = new AccountConfig(e.getKey(), allUsersName, git);
- accountConfig.load(md);
- accountConfig.setAccountUpdate(
- InternalAccountUpdate.builder()
- .deleteProjectWatches(accountConfig.getProjectWatches().keySet())
- .updateProjectWatches(projectWatches)
- .build());
- accountConfig.commit(md);
- }
- }
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- } catch (IOException | ConfigInvalidException ex) {
- throw new OrmException(ex);
- }
- }
-
- @AutoValue
- abstract static class ProjectWatch {
- abstract Project.NameKey project();
-
- abstract @Nullable String filter();
-
- abstract boolean notifyAbandonedChanges();
-
- abstract boolean notifyAllComments();
-
- abstract boolean notifyNewChanges();
-
- abstract boolean notifyNewPatchSets();
-
- abstract boolean notifySubmittedChanges();
-
- static Builder builder() {
- return new AutoValue_Schema_139_ProjectWatch.Builder();
- }
-
- @AutoValue.Builder
- abstract static class Builder {
- abstract Builder project(Project.NameKey project);
-
- abstract Builder filter(@Nullable String filter);
-
- abstract Builder notifyAbandonedChanges(boolean notifyAbandonedChanges);
-
- abstract Builder notifyAllComments(boolean notifyAllComments);
-
- abstract Builder notifyNewChanges(boolean notifyNewChanges);
-
- abstract Builder notifyNewPatchSets(boolean notifyNewPatchSets);
-
- abstract Builder notifySubmittedChanges(boolean notifySubmittedChanges);
-
- abstract ProjectWatch build();
- }
- }
-
- private static boolean toBoolean(String v) {
- checkState(!Strings.isNullOrEmpty(v));
- return v.equals("Y");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_140.java b/java/com/google/gerrit/server/schema/Schema_140.java
deleted file mode 100644
index bdc5f5525c..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_140.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Remove ChangeMessage sequence. */
-public class Schema_140 extends SchemaVersion {
- @Inject
- Schema_140(Provider<Schema_139> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_141.java b/java/com/google/gerrit/server/schema/Schema_141.java
deleted file mode 100644
index c081ea99b7..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_141.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add status field to account. */
-public class Schema_141 extends SchemaVersion {
- @Inject
- Schema_141(Provider<Schema_140> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_142.java b/java/com/google/gerrit/server/schema/Schema_142.java
deleted file mode 100644
index e67ae2f986..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_142.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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.schema;
-
-import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.HashedPassword;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_142 extends SchemaVersion {
- private static final int MAX_BATCH_SIZE = 1000;
-
- @Inject
- Schema_142(Provider<Schema_141> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (PreparedStatement updateStmt =
- ((JdbcSchema) db)
- .getConnection()
- .prepareStatement(
- "UPDATE account_external_ids " + "SET password = ? " + "WHERE external_id = ?")) {
- int batchCount = 0;
-
- try (Statement stmt = newStatement(db);
- ResultSet rs =
- stmt.executeQuery("SELECT external_id, password FROM account_external_ids")) {
- while (rs.next()) {
- String externalId = rs.getString("external_id");
- String password = rs.getString("password");
- if (!ExternalId.Key.parse(externalId).isScheme(SCHEME_USERNAME)
- || Strings.isNullOrEmpty(password)) {
- continue;
- }
-
- HashedPassword hashed = HashedPassword.fromPassword(password);
- updateStmt.setString(1, hashed.encode());
- updateStmt.setString(2, externalId);
- updateStmt.addBatch();
- batchCount++;
- if (batchCount >= MAX_BATCH_SIZE) {
- updateStmt.executeBatch();
- batchCount = 0;
- }
- }
- }
-
- if (batchCount > 0) {
- updateStmt.executeBatch();
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_143.java b/java/com/google/gerrit/server/schema/Schema_143.java
deleted file mode 100644
index b190b29b5f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_143.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add isPrivate field to change. */
-public class Schema_143 extends SchemaVersion {
- @Inject
- Schema_143(Provider<Schema_142> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_144.java b/java/com/google/gerrit/server/schema/Schema_144.java
deleted file mode 100644
index bb0cbcafdb..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_144.java
+++ /dev/null
@@ -1,97 +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.server.schema;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.account.externalids.ExternalIdNotes;
-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.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashSet;
-import java.util.Set;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_144 extends SchemaVersion {
- private static final String COMMIT_MSG = "Import external IDs from ReviewDb";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final PersonIdent serverIdent;
-
- @Inject
- Schema_144(
- Provider<Schema_143> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverIdent) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverIdent = serverIdent;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- Set<ExternalId> toAdd = new HashSet<>();
- try (Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT "
- + "account_id, "
- + "email_address, "
- + "password, "
- + "external_id "
- + "FROM account_external_ids")) {
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
- String email = rs.getString(2);
- String password = rs.getString(3);
- String externalId = rs.getString(4);
-
- toAdd.add(ExternalId.create(ExternalId.Key.parse(externalId), accountId, email, password));
- }
- }
-
- try {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsersName, repo);
- extIdNotes.upsert(toAdd);
- try (MetaDataUpdate metaDataUpdate =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, repo)) {
- metaDataUpdate.getCommitBuilder().setAuthor(serverIdent);
- metaDataUpdate.getCommitBuilder().setCommitter(serverIdent);
- metaDataUpdate.getCommitBuilder().setMessage(COMMIT_MSG);
- extIdNotes.commit(metaDataUpdate);
- }
- }
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Failed to migrate external IDs to NoteDb", e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_145.java b/java/com/google/gerrit/server/schema/Schema_145.java
deleted file mode 100644
index 6ccb5d8b7a..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_145.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Create account_external_ids_byEmail index. */
-public class Schema_145 extends SchemaVersion {
-
- @Inject
- Schema_145(Provider<Schema_144> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- SqlDialect dialect = schema.getDialect();
- try (StatementExecutor e = newExecutor(db)) {
- try {
- dialect.dropIndex(e, "account_external_ids", "account_external_ids_byEmail");
- } catch (OrmException ex) {
- // Ignore. The index did not exist.
- }
- e.execute(
- "CREATE INDEX account_external_ids_byEmail"
- + " ON account_external_ids"
- + " (email_address)");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_146.java b/java/com/google/gerrit/server/schema/Schema_146.java
deleted file mode 100644
index cdb65c91fd..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_146.java
+++ /dev/null
@@ -1,327 +0,0 @@
-// 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.schema;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.text.ParseException;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.storage.file.GC;
-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.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.pack.PackConfig;
-
-/**
- * Make sure that for every account a user branch exists that has an initial empty commit with the
- * registration date as commit time.
- *
- * <p>For accounts that don't have a user branch yet the user branch is created with an initial
- * empty commit that has the registration date as commit time.
- *
- * <p>For accounts that already have a user branch the user branch is rewritten and an initial empty
- * commit with the registration date as commit time is inserted (if such a commit doesn't exist
- * yet).
- */
-public class Schema_146 extends SchemaVersion {
- private static final String CREATE_ACCOUNT_MSG = "Create Account";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final PersonIdent serverIdent;
- private AtomicInteger i = new AtomicInteger();
- private Stopwatch sw = Stopwatch.createStarted();
- ReentrantLock gcLock = new ReentrantLock();
- private int size;
-
- @Inject
- Schema_146(
- Provider<Schema_145> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverIdent) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverIdent = serverIdent;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- ui.message("Migrating accounts");
- Set<Entry<Account.Id, Timestamp>> accounts = scanAccounts(db, ui).entrySet();
- ui.message("Run full gc as preparation for the migration");
- gc(ui);
- ui.message(String.format("... (%.3f s) full gc completed", elapsed()));
- Set<List<Entry<Account.Id, Timestamp>>> batches =
- Sets.newHashSet(Iterables.partition(accounts, 500));
- ExecutorService pool = createExecutor(ui);
- try {
- batches.stream()
- .forEach(
- batch -> {
- @SuppressWarnings("unused")
- Future<?> unused = pool.submit(() -> processBatch(batch, ui));
- });
- pool.shutdown();
- pool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- ui.message(
- String.format("... (%.3f s) Migrated all %d accounts to schema 146", elapsed(), i.get()));
- ui.message("Run full gc");
- gc(ui);
- ui.message(String.format("... (%.3f s) full gc completed", elapsed()));
- }
-
- private ExecutorService createExecutor(UpdateUI ui) {
- int threads;
- try {
- threads = Integer.parseInt(System.getProperty("threadcount"));
- } catch (NumberFormatException e) {
- threads = Runtime.getRuntime().availableProcessors();
- }
- ui.message(String.format("... using %d threads ...", threads));
- return Executors.newFixedThreadPool(threads);
- }
-
- private void processBatch(List<Entry<Account.Id, Timestamp>> batch, UpdateUI ui) {
- try (Repository repo = repoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(repo);
- ObjectInserter oi = repo.newObjectInserter()) {
- ObjectId emptyTree = emptyTree(oi);
-
- for (Map.Entry<Account.Id, Timestamp> e : batch) {
- String refName = RefNames.refsUsers(e.getKey());
- Ref ref = repo.exactRef(refName);
- if (ref != null) {
- rewriteUserBranch(repo, rw, oi, emptyTree, ref, e.getValue());
- } else {
- createUserBranch(repo, oi, emptyTree, e.getKey(), e.getValue());
- }
- int count = i.incrementAndGet();
- showProgress(ui, count);
- if (count % 1000 == 0) {
- boolean runFullGc = count % 100000 == 0;
- if (runFullGc) {
- ui.message("Run full gc");
- }
- gc(repo, !runFullGc, ui);
- if (runFullGc) {
- ui.message(String.format("... (%.3f s) full gc completed", elapsed()));
- }
- }
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private double elapsed() {
- return sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
- }
-
- private void showProgress(UpdateUI ui, int count) {
- if (count % 100 == 0) {
- ui.message(
- String.format(
- "... (%.3f s) migrated %d%% (%d/%d) accounts",
- elapsed(), Math.round(100.0 * count / size), count, size));
- }
- }
-
- private void gc(UpdateUI ui) {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- gc(repo, false, ui);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private void gc(Repository repo, boolean refsOnly, UpdateUI ui) {
- if (repo instanceof FileRepository && gcLock.tryLock()) {
- ProgressMonitor pm = null;
- try {
- pm = new TextProgressMonitor();
- FileRepository r = (FileRepository) repo;
- GC gc = new GC(r);
- gc.setProgressMonitor(pm);
- pm.beginTask("gc", ProgressMonitor.UNKNOWN);
- if (refsOnly) {
- ui.message(String.format("... (%.3f s) pack refs", elapsed()));
- gc.packRefs();
- } else {
- // TODO(ms): Enable bitmap index when this JGit performance issue is fixed:
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=562740
- PackConfig pconfig = new PackConfig(repo);
- pconfig.setBuildBitmaps(false);
- gc.setPackConfig(pconfig);
- ui.message(String.format("... (%.3f s) gc --prune=now", elapsed()));
- gc.setExpire(new Date());
- gc.gc();
- }
- } catch (IOException | ParseException e) {
- throw new RuntimeException(e);
- } finally {
- gcLock.unlock();
- if (pm != null) {
- pm.endTask();
- }
- }
- }
- }
-
- private void rewriteUserBranch(
- Repository repo,
- RevWalk rw,
- ObjectInserter oi,
- ObjectId emptyTree,
- Ref ref,
- Timestamp registeredOn)
- throws IOException {
- ObjectId current = createInitialEmptyCommit(oi, emptyTree, registeredOn);
-
- rw.reset();
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.REVERSE, true);
- rw.markStart(rw.parseCommit(ref.getObjectId()));
-
- RevCommit c;
- while ((c = rw.next()) != null) {
- if (isInitialEmptyCommit(emptyTree, c)) {
- return;
- }
-
- CommitBuilder cb = new CommitBuilder();
- cb.setParentId(current);
- cb.setTreeId(c.getTree());
- cb.setAuthor(c.getAuthorIdent());
- cb.setCommitter(c.getCommitterIdent());
- cb.setMessage(c.getFullMessage());
- cb.setEncoding(c.getEncoding());
- current = oi.insert(cb);
- }
-
- oi.flush();
-
- RefUpdate ru = repo.updateRef(ref.getName());
- ru.setExpectedOldObjectId(ref.getObjectId());
- ru.setNewObjectId(current);
- ru.setForceUpdate(true);
- ru.setRefLogIdent(serverIdent);
- ru.setRefLogMessage(getClass().getSimpleName(), true);
- Result result = ru.update();
- if (result != Result.FORCED) {
- throw new IOException(
- String.format("Failed to update ref %s: %s", ref.getName(), result.name()));
- }
- }
-
- public void createUserBranch(
- Repository repo,
- ObjectInserter oi,
- ObjectId emptyTree,
- Account.Id accountId,
- Timestamp registeredOn)
- throws IOException {
- ObjectId id = createInitialEmptyCommit(oi, emptyTree, registeredOn);
-
- String refName = RefNames.refsUsers(accountId);
- RefUpdate ru = repo.updateRef(refName);
- ru.setExpectedOldObjectId(ObjectId.zeroId());
- ru.setNewObjectId(id);
- ru.setRefLogIdent(serverIdent);
- ru.setRefLogMessage(CREATE_ACCOUNT_MSG, false);
- Result result = ru.update();
- if (result != Result.NEW) {
- throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
- }
- }
-
- private ObjectId createInitialEmptyCommit(
- ObjectInserter oi, ObjectId emptyTree, Timestamp registrationDate) throws IOException {
- PersonIdent ident = new PersonIdent(serverIdent, registrationDate);
-
- CommitBuilder cb = new CommitBuilder();
- cb.setTreeId(emptyTree);
- cb.setCommitter(ident);
- cb.setAuthor(ident);
- cb.setMessage(CREATE_ACCOUNT_MSG);
- return oi.insert(cb);
- }
-
- private boolean isInitialEmptyCommit(ObjectId emptyTree, RevCommit c) {
- return c.getParentCount() == 0
- && c.getTree().equals(emptyTree)
- && c.getShortMessage().equals(CREATE_ACCOUNT_MSG);
- }
-
- private static ObjectId emptyTree(ObjectInserter oi) throws IOException {
- return oi.insert(Constants.OBJ_TREE, new byte[] {});
- }
-
- private Map<Account.Id, Timestamp> scanAccounts(ReviewDb db, UpdateUI ui) throws SQLException {
- ui.message(String.format("... (%.3f s) scan accounts", elapsed()));
- try (Statement stmt = newStatement(db);
- ResultSet rs = stmt.executeQuery("SELECT account_id, registered_on FROM accounts")) {
- HashMap<Account.Id, Timestamp> m = new HashMap<>();
- while (rs.next()) {
- m.put(new Account.Id(rs.getInt(1)), rs.getTimestamp(2));
- }
- size = m.size();
- return m;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_147.java b/java/com/google/gerrit/server/schema/Schema_147.java
deleted file mode 100644
index d93daa1ca5..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_147.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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.schema;
-
-import static java.util.stream.Collectors.toSet;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.Repository;
-
-/** Delete user branches for which no account exists. */
-public class Schema_147 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
-
- @Inject
- Schema_147(
- Provider<Schema_146> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- Set<Account.Id> accountIdsFromReviewDb = scanAccounts(db);
- Set<Account.Id> accountIdsFromUserBranches =
- repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_USERS).stream()
- .map(r -> Account.Id.fromRef(r.getName()))
- .filter(Objects::nonNull)
- .collect(toSet());
- accountIdsFromUserBranches.removeAll(accountIdsFromReviewDb);
- for (Account.Id accountId : accountIdsFromUserBranches) {
- deleteUserBranch(repo, accountId);
- }
- } catch (IOException e) {
- throw new OrmException("Failed to delete user branches for non-existing accounts.", e);
- }
- }
-
- private Set<Account.Id> scanAccounts(ReviewDb db) throws SQLException {
- try (Statement stmt = newStatement(db);
- ResultSet rs = stmt.executeQuery("SELECT account_id FROM accounts")) {
- Set<Account.Id> ids = new HashSet<>();
- while (rs.next()) {
- ids.add(new Account.Id(rs.getInt(1)));
- }
- return ids;
- }
- }
-
- private void deleteUserBranch(Repository allUsersRepo, Account.Id accountId) throws IOException {
- String refName = RefNames.refsUsers(accountId);
- Ref ref = allUsersRepo.exactRef(refName);
- if (ref == null) {
- return;
- }
-
- RefUpdate ru = allUsersRepo.updateRef(refName);
- ru.setExpectedOldObjectId(ref.getObjectId());
- ru.setNewObjectId(ObjectId.zeroId());
- ru.setForceUpdate(true);
- Result result = ru.delete();
- if (result != Result.FORCED) {
- throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_148.java b/java/com/google/gerrit/server/schema/Schema_148.java
deleted file mode 100644
index 9433da849f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_148.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.schema;
-
-import com.google.common.primitives.Ints;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gerrit.server.account.externalids.ExternalIdNotes;
-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.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_148 extends SchemaVersion {
- private static final String COMMIT_MSG = "Make account IDs of external IDs human-readable";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_148(
- Provider<Schema_147> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- ExternalIdNotes extIdNotes = ExternalIdNotes.loadNoCacheUpdate(allUsersName, repo);
- for (ExternalId extId : extIdNotes.all()) {
- if (needsUpdate(extId)) {
- extIdNotes.upsert(extId);
- }
- }
-
- try (MetaDataUpdate metaDataUpdate =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, repo)) {
- metaDataUpdate.getCommitBuilder().setAuthor(serverUser);
- metaDataUpdate.getCommitBuilder().setCommitter(serverUser);
- metaDataUpdate.getCommitBuilder().setMessage(COMMIT_MSG);
- extIdNotes.commit(metaDataUpdate);
- }
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Failed to update external IDs", e);
- }
- }
-
- private static boolean needsUpdate(ExternalId extId) {
- Config cfg = new Config();
- cfg.setInt("externalId", extId.key().get(), "accountId", extId.accountId().get());
- return Ints.tryParse(cfg.getString("externalId", extId.key().get(), "accountId")) == null;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_149.java b/java/com/google/gerrit/server/schema/Schema_149.java
deleted file mode 100644
index f1ccaa6bd7..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_149.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add workInProgress field to change. */
-public class Schema_149 extends SchemaVersion {
- @Inject
- Schema_149(Provider<Schema_148> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_150.java b/java/com/google/gerrit/server/schema/Schema_150.java
deleted file mode 100644
index 456a01ae14..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_150.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Drop ACCOUNT_EXTERNAL_IDS table. */
-public class Schema_150 extends SchemaVersion {
- @Inject
- Schema_150(Provider<Schema_149> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_151.java b/java/com/google/gerrit/server/schema/Schema_151.java
deleted file mode 100644
index bab9f414a4..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_151.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Optional;
-
-/** A schema which adds the 'created on' field to groups. */
-public class Schema_151 extends SchemaVersion {
- @Inject
- protected Schema_151(Provider<Schema_150> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- Connection connection = ((JdbcSchema) db).getConnection();
- if (!createdOnColumnExists(connection)) {
- try (Statement stmt = connection.createStatement()) {
- stmt.execute("ALTER TABLE account_groups ADD COLUMN created_on TIMESTAMP NULL");
- }
- }
-
- try (PreparedStatement groupUpdate =
- prepareStatement(db, "UPDATE account_groups SET created_on = ? WHERE group_id = ?");
- PreparedStatement addedOnRetrieval =
- prepareStatement(
- db,
- "SELECT added_on FROM account_group_members_audit WHERE group_id = ?"
- + " ORDER BY added_on ASC")) {
- List<AccountGroup.Id> accountGroups = getAllGroupIds(db);
- for (AccountGroup.Id groupId : accountGroups) {
- Optional<Timestamp> firstTimeMentioned = getFirstTimeMentioned(addedOnRetrieval, groupId);
- Timestamp createdOn = firstTimeMentioned.orElseGet(AccountGroup::auditCreationInstantTs);
-
- groupUpdate.setTimestamp(1, createdOn);
- groupUpdate.setInt(2, groupId.get());
- groupUpdate.executeUpdate();
- }
- }
- }
-
- @VisibleForTesting
- public static boolean createdOnColumnExists(Connection connection) throws SQLException {
- DatabaseMetaData metaData = connection.getMetaData();
- boolean toUpper = metaData.storesUpperCaseIdentifiers();
- return metaData
- .getColumns(
- null, null, convertCase(toUpper, "account_groups"), convertCase(toUpper, "created_on"))
- .next();
- }
-
- private static String convertCase(boolean toUpper, String input) {
- return toUpper ? input.toUpperCase(Locale.US) : input;
- }
-
- private static Optional<Timestamp> getFirstTimeMentioned(
- PreparedStatement addedOnRetrieval, AccountGroup.Id groupId) throws SQLException {
- addedOnRetrieval.setInt(1, groupId.get());
- try (ResultSet resultSet = addedOnRetrieval.executeQuery()) {
- if (resultSet.next()) {
- return Optional.of(resultSet.getTimestamp(1));
- }
- }
- return Optional.empty();
- }
-
- private static List<AccountGroup.Id> getAllGroupIds(ReviewDb db) throws SQLException {
- try (Statement stmt = newStatement(db);
- ResultSet rs = stmt.executeQuery("SELECT group_id FROM account_groups")) {
- List<AccountGroup.Id> groupIds = new ArrayList<>();
- while (rs.next()) {
- groupIds.add(new AccountGroup.Id(rs.getInt(1)));
- }
- return groupIds;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_152.java b/java/com/google/gerrit/server/schema/Schema_152.java
deleted file mode 100644
index c5150a0efb..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_152.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Drop unused indexes from accounts table. */
-public class Schema_152 extends SchemaVersion {
- @Inject
- Schema_152(Provider<Schema_151> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- SqlDialect dialect = schema.getDialect();
- try (StatementExecutor e = newExecutor(db)) {
- dialect.dropIndex(e, "accounts", "accounts_byFullName");
- } catch (OrmException ex) {
- // Ignore. The index did not exist.
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_153.java b/java/com/google/gerrit/server/schema/Schema_153.java
deleted file mode 100644
index 28aeb171e2..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_153.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add reviewStarted field to change. */
-public class Schema_153 extends SchemaVersion {
- @Inject
- Schema_153(Provider<Schema_152> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (StatementExecutor e = newExecutor(db)) {
- // Initialize review_started to a sensible default value according to
- // whether change is currently WIP. No migration is needed in NoteDb,
- // where the value of review_started is always derived from the history
- // of assignments to work_in_progress.
- e.execute(
- "UPDATE changes SET review_started = 'Y', created_on = created_on WHERE work_in_progress = 'N'");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_154.java b/java/com/google/gerrit/server/schema/Schema_154.java
deleted file mode 100644
index 8a39c0d544..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_154.java
+++ /dev/null
@@ -1,194 +0,0 @@
-// 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.schema;
-
-import static java.util.stream.Collectors.toMap;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountConfig;
-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.git.meta.MetaDataUpdate;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.storage.file.GC;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.storage.pack.PackConfig;
-
-/** Migrate accounts to NoteDb. */
-public class Schema_154 extends SchemaVersion {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private static final String TABLE = "accounts";
- private static final ImmutableMap<String, AccountSetter> ACCOUNT_FIELDS_MAP =
- ImmutableMap.<String, AccountSetter>builder()
- .put("full_name", (a, rs, field) -> a.setFullName(rs.getString(field)))
- .put("preferred_email", (a, rs, field) -> a.setPreferredEmail(rs.getString(field)))
- .put("status", (a, rs, field) -> a.setStatus(rs.getString(field)))
- .put("inactive", (a, rs, field) -> a.setActive(rs.getString(field).equals("N")))
- .build();
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final Provider<PersonIdent> serverIdent;
- private final Stopwatch sw = Stopwatch.createStarted();
-
- @Inject
- Schema_154(
- Provider<Schema_153> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent Provider<PersonIdent> serverIdent) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverIdent = serverIdent;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- ProgressMonitor pm = new TextProgressMonitor();
- pm.beginTask("Collecting accounts", ProgressMonitor.UNKNOWN);
- Set<Account> accounts = scanAccounts(db, pm);
- pm.endTask();
- pm.beginTask("Migrating accounts to NoteDb", accounts.size());
- int i = 0;
- for (Account account : accounts) {
- updateAccountInNoteDb(repo, account);
- pm.update(1);
- if (++i % 100000 == 0) {
- gc(repo, ui);
- }
- }
- pm.endTask();
- }
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Migrating accounts to NoteDb failed", e);
- }
- }
-
- private Set<Account> scanAccounts(ReviewDb db, ProgressMonitor pm) throws SQLException {
- Map<String, AccountSetter> fields = getFields(db);
- if (fields.isEmpty()) {
- logger.atWarning().log("Only account_id and registered_on fields are migrated for accounts");
- }
-
- List<String> queryFields = new ArrayList<>();
- queryFields.add("account_id");
- queryFields.add("registered_on");
- queryFields.addAll(fields.keySet());
- String query = "SELECT " + String.join(", ", queryFields) + String.format(" FROM %s", TABLE);
- try (Statement stmt = newStatement(db);
- ResultSet rs = stmt.executeQuery(query)) {
- Set<Account> s = new HashSet<>();
- while (rs.next()) {
- Account a = new Account(new Account.Id(rs.getInt(1)), rs.getTimestamp(2));
- for (Map.Entry<String, AccountSetter> field : fields.entrySet()) {
- field.getValue().set(a, rs, field.getKey());
- }
- s.add(a);
- pm.update(1);
- }
- return s;
- }
- }
-
- private Map<String, AccountSetter> getFields(ReviewDb db) throws SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- Connection connection = schema.getConnection();
- Set<String> columns = schema.getDialect().listColumns(connection, TABLE);
- return ACCOUNT_FIELDS_MAP.entrySet().stream()
- .filter(e -> columns.contains(e.getKey()))
- .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
- }
-
- private void updateAccountInNoteDb(Repository allUsersRepo, Account account)
- throws IOException, ConfigInvalidException {
- MetaDataUpdate md =
- new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo);
- PersonIdent ident = serverIdent.get();
- md.getCommitBuilder().setAuthor(ident);
- md.getCommitBuilder().setCommitter(ident);
- new AccountConfig(account.getId(), allUsersName, allUsersRepo)
- .load()
- .setAccount(account)
- .commit(md);
- }
-
- @FunctionalInterface
- private interface AccountSetter {
- void set(Account a, ResultSet rs, String field) throws SQLException;
- }
-
- private double elapsed() {
- return sw.elapsed(TimeUnit.MILLISECONDS) / 1000d;
- }
-
- private void gc(Repository repo, UpdateUI ui) {
- if (repo instanceof FileRepository) {
- ProgressMonitor pm = null;
- try {
- pm = new TextProgressMonitor();
- FileRepository r = (FileRepository) repo;
- GC gc = new GC(r);
- // TODO(davido): Enable bitmap index when this JGit performance issue is fixed:
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=562740
- PackConfig pconfig = new PackConfig(repo);
- pconfig.setBuildBitmaps(false);
- gc.setPackConfig(pconfig);
- gc.setProgressMonitor(pm);
- pm.beginTask("gc", ProgressMonitor.UNKNOWN);
- ui.message(String.format("... (%.3f s) gc --prune=now", elapsed()));
- gc.setExpire(new Date());
- gc.gc();
- ui.message(String.format("... (%.3f s) full gc completed", elapsed()));
- } catch (IOException | ParseException e) {
- throw new RuntimeException(e);
- } finally {
- if (pm != null) {
- pm.endTask();
- }
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_155.java b/java/com/google/gerrit/server/schema/Schema_155.java
deleted file mode 100644
index ec16e062da..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_155.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.Sequences;
-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.notedb.RepoSequence;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Create account sequence in NoteDb */
-public class Schema_155 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
-
- @Inject
- Schema_155(
- Provider<Schema_154> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- @SuppressWarnings("deprecation")
- RepoSequence.Seed accountSeed = db::nextAccountId;
- RepoSequence accountSeq =
- new RepoSequence(
- repoManager,
- GitReferenceUpdated.DISABLED,
- allUsersName,
- Sequences.NAME_ACCOUNTS,
- accountSeed,
- 1);
-
- // consume one account ID to ensure that the account sequence is initialized in NoteDb
- accountSeq.next();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_156.java b/java/com/google/gerrit/server/schema/Schema_156.java
deleted file mode 100644
index fd8fc003ce..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_156.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Add revertOf field to change. */
-public class Schema_156 extends SchemaVersion {
- @Inject
- Schema_156(Provider<Schema_155> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_157.java b/java/com/google/gerrit/server/schema/Schema_157.java
deleted file mode 100644
index f5c5b599b3..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_157.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Drop unused indexes from accounts table. */
-public class Schema_157 extends SchemaVersion {
- @Inject
- Schema_157(Provider<Schema_156> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- JdbcSchema schema = (JdbcSchema) db;
- SqlDialect dialect = schema.getDialect();
- try (StatementExecutor e = newExecutor(db)) {
- dialect.dropIndex(e, "accounts", "accounts_byPreferredEmail");
- } catch (OrmException ex) {
- // Ignore. The index did not exist.
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_158.java b/java/com/google/gerrit/server/schema/Schema_158.java
deleted file mode 100644
index ea85444cfe..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_158.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Drop ACCOUNTS table. */
-public class Schema_158 extends SchemaVersion {
- @Inject
- Schema_158(Provider<Schema_157> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_159.java b/java/com/google/gerrit/server/schema/Schema_159.java
deleted file mode 100644
index d6e37d7ae7..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_159.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Migrate draft changes to private or wip changes. */
-public class Schema_159 extends SchemaVersion {
-
- private enum DraftWorkflowMigrationStrategy {
- PRIVATE,
- WORK_IN_PROGRESS
- }
-
- @Inject
- Schema_159(Provider<Schema_158> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- DraftWorkflowMigrationStrategy strategy = DraftWorkflowMigrationStrategy.WORK_IN_PROGRESS;
- if (ui.yesno(false, "Migrate draft changes to private changes (default is work-in-progress)")) {
- strategy = DraftWorkflowMigrationStrategy.PRIVATE;
- }
- ui.message(
- String.format("Replace draft changes with %s changes ...", strategy.name().toLowerCase()));
- try (StatementExecutor e = newExecutor(db)) {
- String column =
- strategy == DraftWorkflowMigrationStrategy.PRIVATE ? "is_private" : "work_in_progress";
- // Mark changes private/WIP and NEW if either:
- // * they have status DRAFT
- // * they have status NEW and have any draft patch sets
- e.execute(
- String.format(
- "UPDATE changes "
- + "SET %s = 'Y', "
- + " status = 'n', "
- + " created_on = created_on "
- + "WHERE status = 'd' "
- + " OR (status = 'n' "
- + " AND EXISTS "
- + " (SELECT * "
- + " FROM patch_sets "
- + " WHERE patch_sets.change_id = changes.change_id "
- + " AND patch_sets.draft = 'Y')) ",
- column));
- }
- ui.message("done");
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_160.java b/java/com/google/gerrit/server/schema/Schema_160.java
deleted file mode 100644
index eb8b70f3d3..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_160.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// 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.schema;
-
-import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static com.google.gerrit.server.git.UserConfigSections.MY;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.Accounts;
-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.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-
-/**
- * Remove "My Drafts" menu items for all users and server-wide default preferences.
- *
- * <p>Since draft changes no longer exist, these menu items are obsolete.
- *
- * <p>Only matches menu items (with any name) where the URL exactly matches one of the following,
- * with or without leading {@code #}:
- *
- * <ul>
- * <li>/q/is:draft
- * <li>/q/owner:self+is:draft
- * </ul>
- *
- * In particular, this includes the <a
- * href="https://gerrit.googlesource.com/gerrit/+/v2.14.4/gerrit-server/src/main/java/com/google/gerrit/server/account/GeneralPreferencesLoader.java#144">default
- * from version 2.14 and earlier</a>.
- *
- * <p>Other menus containing {@code is:draft} in other positions are not affected; this is still a
- * valid predicate that matches no changes.
- */
-public class Schema_160 extends SchemaVersion {
- @VisibleForTesting static final ImmutableList<String> DEFAULT_DRAFT_ITEMS;
-
- static {
- String ownerSelfIsDraft = "/q/owner:self+is:draft";
- String isDraft = "/q/is:draft";
- DEFAULT_DRAFT_ITEMS =
- ImmutableList.of(ownerSelfIsDraft, '#' + ownerSelfIsDraft, isDraft, '#' + isDraft);
- }
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final Provider<PersonIdent> serverIdent;
-
- @Inject
- Schema_160(
- Provider<Schema_159> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritPersonIdent Provider<PersonIdent> serverIdent) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.serverIdent = serverIdent;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- ProgressMonitor pm = new TextProgressMonitor();
- pm.beginTask("Removing \"My Drafts\" menu items", ProgressMonitor.UNKNOWN);
- for (Account.Id id : (Iterable<Account.Id>) Accounts.readUserRefs(repo)::iterator) {
- removeMyDrafts(repo, RefNames.refsUsers(id), pm);
- }
- removeMyDrafts(repo, RefNames.REFS_USERS_DEFAULT, pm);
- pm.endTask();
- }
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Removing \"My Drafts\" menu items failed", e);
- }
- }
-
- private void removeMyDrafts(Repository repo, String ref, ProgressMonitor pm)
- throws IOException, ConfigInvalidException {
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, repo);
- PersonIdent ident = serverIdent.get();
- md.getCommitBuilder().setAuthor(ident);
- md.getCommitBuilder().setCommitter(ident);
- Prefs prefs = new Prefs(ref);
- prefs.load(allUsersName, repo);
- prefs.removeMyDrafts();
- prefs.commit(md);
- if (prefs.dirty()) {
- pm.update(1);
- }
- }
-
- private static class Prefs extends VersionedAccountPreferences {
- private boolean dirty;
-
- Prefs(String ref) {
- super(ref);
- }
-
- @Override
- protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
- if (!dirty) {
- return false;
- }
- commit.setMessage("Remove \"My Drafts\" menu items");
- return super.onSave(commit);
- }
-
- void removeMyDrafts() {
- Config cfg = getConfig();
- for (String item : cfg.getSubsections(MY)) {
- String value = cfg.getString(MY, item, KEY_URL);
- if (DEFAULT_DRAFT_ITEMS.contains(value)) {
- cfg.unsetSection(MY, item);
- dirty = true;
- }
- }
- }
-
- boolean dirty() {
- return dirty;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_161.java b/java/com/google/gerrit/server/schema/Schema_161.java
deleted file mode 100644
index 70010f830d..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_161.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// 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.schema;
-
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.primitives.Ints;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.StarredChangesUtil;
-import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
-import com.google.gerrit.server.StarredChangesUtil.StarRef;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-public class Schema_161 extends SchemaVersion {
- private static final String MUTE_LABEL = "mute";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
-
- @Inject
- Schema_161(
- Provider<Schema_160> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (Repository git = repoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(git)) {
- BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
- bru.setAllowNonFastForwards(true);
-
- for (Ref ref : git.getRefDatabase().getRefsByPrefix(RefNames.REFS_STARRED_CHANGES)) {
- StarRef starRef = StarredChangesUtil.readLabels(git, ref.getName());
-
- Set<Integer> mutedPatchSets =
- StarredChangesUtil.getStarredPatchSets(starRef.labels(), MUTE_LABEL);
- if (mutedPatchSets.isEmpty()) {
- continue;
- }
-
- Set<Integer> reviewedPatchSets =
- StarredChangesUtil.getStarredPatchSets(
- starRef.labels(), StarredChangesUtil.REVIEWED_LABEL);
- Set<Integer> unreviewedPatchSets =
- StarredChangesUtil.getStarredPatchSets(
- starRef.labels(), StarredChangesUtil.UNREVIEWED_LABEL);
-
- List<String> newLabels =
- starRef.labels().stream()
- .map(
- l -> {
- if (l.startsWith(MUTE_LABEL)) {
- Integer mutedPatchSet = Ints.tryParse(l.substring(MUTE_LABEL.length() + 1));
- if (mutedPatchSet == null) {
- // unexpected format of mute label, must be a label that was manually
- // set, just leave it alone
- return l;
- }
- if (!reviewedPatchSets.contains(mutedPatchSet)
- && !unreviewedPatchSets.contains(mutedPatchSet)) {
- // convert mute label to reviewed label
- return StarredChangesUtil.REVIEWED_LABEL + "/" + mutedPatchSet;
- }
- // else patch set is muted but has either reviewed or unreviewed label
- // -> just drop the mute label
- return null;
- }
- return l;
- })
- .filter(Objects::nonNull)
- .collect(toList());
-
- ObjectId id = StarredChangesUtil.writeLabels(git, newLabels);
- bru.addCommand(new ReceiveCommand(ref.getTarget().getObjectId(), id, ref.getName()));
- }
- bru.execute(rw, new TextProgressMonitor());
- } catch (IOException | IllegalLabelException ex) {
- throw new OrmException(ex);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_162.java b/java/com/google/gerrit/server/schema/Schema_162.java
deleted file mode 100644
index 7406bc61a1..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_162.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-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.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-public class Schema_162 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllProjectsName allProjectsName;
- private final AllUsersName allUsersName;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_162(
- Provider<Schema_161> prior,
- GitRepositoryManager repoManager,
- AllProjectsName allProjectsName,
- AllUsersName allUsersName,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allProjectsName = allProjectsName;
- this.allUsersName = allUsersName;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- try (Repository git = repoManager.openRepository(allUsersName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
- ProjectConfig cfg = ProjectConfig.read(md);
- if (allProjectsName.equals(cfg.getProject().getParent(allProjectsName))) {
- return;
- }
- cfg.getProject().setParentName(allProjectsName);
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(
- String.format("Make %s inherit from %s", allUsersName.get(), allProjectsName.get()));
- cfg.commit(md);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_163.java b/java/com/google/gerrit/server/schema/Schema_163.java
deleted file mode 100644
index ae057744db..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_163.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.Sequences;
-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.notedb.RepoSequence;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-/** Create group sequence in NoteDb */
-public class Schema_163 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
-
- @Inject
- Schema_163(
- Provider<Schema_162> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- @SuppressWarnings("deprecation")
- RepoSequence.Seed groupSeed = db::nextAccountGroupId;
- RepoSequence groupSeq =
- new RepoSequence(
- repoManager,
- GitReferenceUpdated.DISABLED,
- allUsersName,
- Sequences.NAME_GROUPS,
- groupSeed,
- 1);
-
- // consume one account ID to ensure that the group sequence is initialized in NoteDb
- groupSeq.next();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_164.java b/java/com/google/gerrit/server/schema/Schema_164.java
deleted file mode 100644
index 8525478d71..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_164.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.schema.AclUtil.grant;
-
-import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Grant read on group branches */
-public class Schema_164 extends SchemaVersion {
- private static final String COMMIT_MSG = "Grant read permissions on group branches";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final SystemGroupBackend systemGroupBackend;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_164(
- Provider<Schema_163> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.systemGroupBackend = systemGroupBackend;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (Repository git = repoManager.openRepository(allUsersName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
-
- ProjectConfig config = ProjectConfig.read(md);
- AccessSection groups = config.getAccessSection(RefNames.REFS_GROUPS + "*", true);
- grant(
- config,
- groups,
- Permission.READ,
- false,
- true,
- systemGroupBackend.getGroup(REGISTERED_USERS));
- config.commit(md);
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Failed to grant read permissions on group branches", e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_165.java b/java/com/google/gerrit/server/schema/Schema_165.java
deleted file mode 100644
index cd6da55327..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_165.java
+++ /dev/null
@@ -1,142 +0,0 @@
-// 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.schema;
-
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-
-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.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-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.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.server.project.RefPattern;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import java.util.Optional;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Make default Label-Code-Review permission on user branches exclusive. */
-public class Schema_165 extends SchemaVersion {
- private static final String COMMIT_MSG =
- "Make default Label-Code-Review permission on user branches exclusive";
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final SystemGroupBackend systemGroupBackend;
- private final PersonIdent serverUser;
-
- @Inject
- Schema_165(
- Provider<Schema_164> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- SystemGroupBackend systemGroupBackend,
- @GerritPersonIdent PersonIdent serverUser) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.systemGroupBackend = systemGroupBackend;
- this.serverUser = serverUser;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (Repository git = repoManager.openRepository(allUsersName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
- ProjectConfig config = ProjectConfig.read(md);
- Optional<Permission> permission = findDefaultPermission(config);
- if (!permission.isPresent()) {
- // the default permission was not found, hence it cannot be fixed
- return;
- }
-
- permission.get().setExclusiveGroup(true);
-
- md.getCommitBuilder().setAuthor(serverUser);
- md.getCommitBuilder().setCommitter(serverUser);
- md.setMessage(COMMIT_MSG);
- config.commit(md);
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException(
- "Failed to make default Label-Code-Review permission on user branches exclusive", e);
- }
- }
-
- /**
- * Searches for the default "Label-Code-Review" permission on the user branch and returns it if it
- * was found. If it was not found (e.g. because it was removed or modified) {@link
- * Optional#empty()} is returned.
- */
- private Optional<Permission> findDefaultPermission(ProjectConfig config) {
- AccessSection users =
- config.getAccessSection(
- RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", false);
- if (users == null) {
- // default permission was removed
- return Optional.empty();
- }
-
- Permission permission = users.getPermission(Permission.LABEL + "Code-Review", false);
- return isDefaultPermissionUntouched(permission) ? Optional.of(permission) : Optional.empty();
- }
-
- /**
- * Checks whether the given permission matches the default "Label-Code-Review" permission on the
- * user branch that was initially setup by {@link AllUsersCreator}.
- */
- private boolean isDefaultPermissionUntouched(Permission permission) {
- if (permission == null) {
- // default permission was removed
- return false;
- } else if (permission.getExclusiveGroup()) {
- // default permission was modified
- return false;
- }
-
- if (permission.getRules().size() != 1) {
- // default permission was modified
- return false;
- }
-
- PermissionRule rule = permission.getRule(systemGroupBackend.getGroup(REGISTERED_USERS));
- if (rule == null) {
- // default permission was removed
- return false;
- }
-
- if (rule.getAction() != Action.ALLOW
- || rule.getForce()
- || rule.getMin() != -2
- || rule.getMax() != 2) {
- // default permission was modified
- return false;
- }
-
- return true;
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_166.java b/java/com/google/gerrit/server/schema/Schema_166.java
deleted file mode 100644
index aa6f4e68b0..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_166.java
+++ /dev/null
@@ -1,52 +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.schema;
-
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-
-/** Set HEAD for All-Users to refs/meta/config. */
-public class Schema_166 extends SchemaVersion {
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
-
- @Inject
- Schema_166(
- Provider<Schema_165> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (Repository git = repoManager.openRepository(allUsersName)) {
- RefUpdate u = git.updateRef(Constants.HEAD);
- u.link(RefNames.REFS_CONFIG);
- } catch (IOException e) {
- throw new OrmException(String.format("Failed to update HEAD for %s", allUsersName.get()), e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_167.java b/java/com/google/gerrit/server/schema/Schema_167.java
deleted file mode 100644
index a5066cc988..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_167.java
+++ /dev/null
@@ -1,288 +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.schema;
-
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-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.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountConfig;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.update.RefUpdateUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Migrate groups from ReviewDb to NoteDb. */
-public class Schema_167 extends SchemaVersion {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private final GitRepositoryManager repoManager;
- private final AllUsersName allUsersName;
- private final Config gerritConfig;
- private final SitePaths sitePaths;
- private final PersonIdent serverIdent;
- private final SystemGroupBackend systemGroupBackend;
-
- @Inject
- protected Schema_167(
- Provider<Schema_166> prior,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- @GerritServerConfig Config gerritConfig,
- SitePaths sitePaths,
- @GerritPersonIdent PersonIdent serverIdent,
- SystemGroupBackend systemGroupBackend) {
- super(prior);
- this.repoManager = repoManager;
- this.allUsersName = allUsersName;
- this.gerritConfig = gerritConfig;
- this.sitePaths = sitePaths;
- this.serverIdent = serverIdent;
- this.systemGroupBackend = systemGroupBackend;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- if (gerritConfig.getBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false)) {
- // Groups in ReviewDb have already been disabled, nothing to do.
- return;
- }
-
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- List<GroupReference> allGroupReferences = readGroupReferencesFromReviewDb(db);
-
- BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
- writeAllGroupNamesToNoteDb(allUsersRepo, allGroupReferences, batchRefUpdate);
-
- GroupRebuilder groupRebuilder = createGroupRebuilder(db, allUsersRepo);
- for (GroupReference groupReference : allGroupReferences) {
- migrateOneGroupToNoteDb(
- db, allUsersRepo, groupRebuilder, groupReference.getUUID(), batchRefUpdate);
- }
-
- RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException(
- String.format("Failed to migrate groups to NoteDb for %s", allUsersName.get()), e);
- }
- }
-
- private List<GroupReference> readGroupReferencesFromReviewDb(ReviewDb db) throws SQLException {
- try (Statement stmt = ReviewDbWrapper.unwrapJbdcSchema(db).getConnection().createStatement();
- ResultSet rs = stmt.executeQuery("SELECT group_uuid, name FROM account_groups")) {
- List<GroupReference> allGroupReferences = new ArrayList<>();
- while (rs.next()) {
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(rs.getString(1));
- String groupName = rs.getString(2);
- allGroupReferences.add(new GroupReference(groupUuid, groupName));
- }
- return allGroupReferences;
- }
- }
-
- private void writeAllGroupNamesToNoteDb(
- Repository allUsersRepo,
- List<GroupReference> allGroupReferences,
- BatchRefUpdate batchRefUpdate)
- throws IOException {
- try (ObjectInserter inserter = allUsersRepo.newObjectInserter()) {
- GroupNameNotes.updateAllGroups(
- allUsersRepo, inserter, batchRefUpdate, allGroupReferences, serverIdent);
- inserter.flush();
- }
- }
-
- private GroupRebuilder createGroupRebuilder(ReviewDb db, Repository allUsersRepo)
- throws IOException, ConfigInvalidException {
- AuditLogFormatter auditLogFormatter =
- createAuditLogFormatter(db, allUsersRepo, gerritConfig, sitePaths);
- return new GroupRebuilder(serverIdent, allUsersName, auditLogFormatter);
- }
-
- private AuditLogFormatter createAuditLogFormatter(
- ReviewDb db, Repository allUsersRepo, Config gerritConfig, SitePaths sitePaths)
- throws IOException, ConfigInvalidException {
- String serverId = new GerritServerIdProvider(gerritConfig, sitePaths).get();
- SimpleInMemoryAccountCache accountCache =
- new SimpleInMemoryAccountCache(allUsersName, allUsersRepo);
- SimpleInMemoryGroupCache groupCache = new SimpleInMemoryGroupCache(db);
- return AuditLogFormatter.create(
- accountCache::get,
- uuid -> {
- if (systemGroupBackend.handles(uuid)) {
- return Optional.ofNullable(systemGroupBackend.get(uuid));
- }
- return groupCache.get(uuid);
- },
- serverId);
- }
-
- private static void migrateOneGroupToNoteDb(
- ReviewDb db,
- Repository allUsersRepo,
- GroupRebuilder rebuilder,
- AccountGroup.UUID uuid,
- BatchRefUpdate batchRefUpdate)
- throws ConfigInvalidException, IOException, OrmException {
- GroupBundle reviewDbBundle = GroupBundle.Factory.fromReviewDb(db, uuid);
- RefUpdateUtil.deleteChecked(allUsersRepo, RefNames.refsGroups(uuid));
- rebuilder.rebuild(allUsersRepo, reviewDbBundle, batchRefUpdate);
- }
-
- // The regular account cache isn't available during init. -> Use a simple replacement which tries
- // to load every account only once from disk.
- private static class SimpleInMemoryAccountCache {
- private final AllUsersName allUsersName;
- private final Repository allUsersRepo;
- private Map<Account.Id, Optional<Account>> accounts = new HashMap<>();
-
- public SimpleInMemoryAccountCache(AllUsersName allUsersName, Repository allUsersRepo) {
- this.allUsersName = allUsersName;
- this.allUsersRepo = allUsersRepo;
- }
-
- public Optional<Account> get(Account.Id accountId) {
- accounts.computeIfAbsent(accountId, this::load);
- return accounts.get(accountId);
- }
-
- private Optional<Account> load(Account.Id accountId) {
- try {
- AccountConfig accountConfig =
- new AccountConfig(accountId, allUsersName, allUsersRepo).load();
- return accountConfig.getLoadedAccount();
- } catch (IOException | ConfigInvalidException ignored) {
- logger.atWarning().withCause(ignored).log(
- "Failed to load account %s."
- + " Cannot get account name for group audit log commit messages.",
- accountId.get());
- return Optional.empty();
- }
- }
- }
-
- // The regular GroupBackends (especially external GroupBackends) and our internal group cache
- // aren't available during init. -> Use a simple replacement which tries to look up only internal
- // groups and which loads every internal group only once from disc. (There's no way we can look up
- // external groups during init. As we need those groups only for cosmetic aspects in
- // AuditLogFormatter, it's safe to exclude them.)
- private static class SimpleInMemoryGroupCache {
- private final ReviewDb db;
- private Map<AccountGroup.UUID, Optional<GroupDescription.Basic>> groups = new HashMap<>();
-
- public SimpleInMemoryGroupCache(ReviewDb db) {
- this.db = db;
- }
-
- public Optional<GroupDescription.Basic> get(AccountGroup.UUID groupUuid) {
- groups.computeIfAbsent(groupUuid, this::load);
- return groups.get(groupUuid);
- }
-
- private Optional<GroupDescription.Basic> load(AccountGroup.UUID groupUuid) {
- if (!AccountGroup.isInternalGroup(groupUuid)) {
- return Optional.empty();
- }
-
- List<GroupDescription.Basic> groupDescriptions = getGroupDescriptions(groupUuid);
- if (groupDescriptions.size() == 1) {
- return Optional.of(Iterables.getOnlyElement(groupDescriptions));
- }
- return Optional.empty();
- }
-
- private List<GroupDescription.Basic> getGroupDescriptions(AccountGroup.UUID groupUuid) {
- try (Statement stmt = ReviewDbWrapper.unwrapJbdcSchema(db).getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT name FROM account_groups where group_uuid = '" + groupUuid + "'")) {
- List<GroupDescription.Basic> groupDescriptions = new ArrayList<>();
- while (rs.next()) {
- String groupName = rs.getString(1);
- groupDescriptions.add(toGroupDescription(groupUuid, groupName));
- }
- return groupDescriptions;
- } catch (SQLException ignored) {
- logger.atWarning().withCause(ignored).log(
- "Failed to load group %s."
- + " Cannot get group name for group audit log commit messages.",
- groupUuid.get());
- return ImmutableList.of();
- }
- }
-
- private static GroupDescription.Basic toGroupDescription(
- AccountGroup.UUID groupUuid, String groupName) {
- return new GroupDescription.Basic() {
- @Override
- public AccountGroup.UUID getGroupUUID() {
- return groupUuid;
- }
-
- @Override
- public String getName() {
- return groupName;
- }
-
- @Nullable
- @Override
- public String getEmailAddress() {
- return null;
- }
-
- @Nullable
- @Override
- public String getUrl() {
- return null;
- }
- };
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_168.java b/java/com/google/gerrit/server/schema/Schema_168.java
deleted file mode 100644
index 3ea84688f2..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_168.java
+++ /dev/null
@@ -1,26 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Drop group tables. */
-public class Schema_168 extends SchemaVersion {
- @Inject
- Schema_168(Provider<Schema_167> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_169.java b/java/com/google/gerrit/server/schema/Schema_169.java
deleted file mode 100644
index d0d60f8011..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_169.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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.schema;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.CommentJsonMigrator;
-import com.google.gerrit.server.notedb.CommentJsonMigrator.ProjectMigrationResult;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.util.SortedSet;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.TextProgressMonitor;
-
-/** Migrate NoteDb inline comments to JSON format. */
-public class Schema_169 extends SchemaVersion {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private final CommentJsonMigrator migrator;
- private final GitRepositoryManager repoManager;
- private final NotesMigration notesMigration;
-
- @Inject
- Schema_169(
- Provider<Schema_168> prior,
- CommentJsonMigrator migrator,
- GitRepositoryManager repoManager,
- @GerritServerConfig Config config) {
- super(prior);
- this.migrator = migrator;
- this.repoManager = repoManager;
- this.notesMigration = MutableNotesMigration.fromConfig(config);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- migrateData(ui);
- }
-
- @VisibleForTesting
- protected void migrateData(UpdateUI ui) throws OrmException {
- // If the migration hasn't started, no need to look for non-JSON
- if (!notesMigration.commitChangeWrites()) {
- return;
- }
-
- boolean ok = true;
- ProgressMonitor pm = new TextProgressMonitor();
- SortedSet<Project.NameKey> projects = repoManager.list();
- pm.beginTask("Migrating projects", projects.size());
- int skipped = 0;
- for (Project.NameKey project : projects) {
- try (Repository repo = repoManager.openRepository(project)) {
- ProjectMigrationResult progress = migrator.migrateProject(project, repo, false);
- skipped += progress.skipped;
- } catch (IOException e) {
- ok = false;
- logger.atSevere().withCause(e).log("Error migrating project %s", project);
- }
- pm.update(1);
- }
-
- pm.endTask();
- ui.message(
- "Skipped " + skipped + " project" + (skipped == 1 ? "" : "s") + " with no legacy comments");
-
- if (!ok) {
- throw new OrmException("Migration failed");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_170.java b/java/com/google/gerrit/server/schema/Schema_170.java
deleted file mode 100644
index c87fa3e97f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_170.java
+++ /dev/null
@@ -1,25 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_170 extends SchemaVersion {
- @Inject
- Schema_170(Provider<Schema_169> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_180.java b/java/com/google/gerrit/server/schema/Schema_180.java
new file mode 100644
index 0000000000..6912b3e9ff
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_180.java
@@ -0,0 +1,22 @@
+// 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.schema;
+
+public class Schema_180 implements NoteDbSchemaVersion {
+ @Override
+ public void upgrade(Arguments args, UpdateUI ui) {
+ // Do nothing; only used to populate the version ref, which is done by the caller.
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/Schema_181.java b/java/com/google/gerrit/server/schema/Schema_181.java
new file mode 100644
index 0000000000..5a238efe53
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_181.java
@@ -0,0 +1,29 @@
+// 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.schema;
+
+import com.google.gerrit.gpg.PublicKeyStore;
+import org.eclipse.jgit.lib.Repository;
+
+public class Schema_181 implements NoteDbSchemaVersion {
+ @Override
+ public void upgrade(Arguments args, UpdateUI ui) throws Exception {
+ ui.message("Rebuild GPG note map to build subkey to master key map");
+ try (Repository repo = args.repoManager.openRepository(args.allUsers);
+ PublicKeyStore store = new PublicKeyStore(repo)) {
+ store.rebuildSubkeyMasterKeyMap();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/schema/Schema_83.java b/java/com/google/gerrit/server/schema/Schema_83.java
deleted file mode 100644
index decbfb1175..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_83.java
+++ /dev/null
@@ -1,33 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-public class Schema_83 extends SchemaVersion {
-
- @Inject
- Schema_83() {
- super(
- new Provider<SchemaVersion>() {
- @Override
- public SchemaVersion get() {
- throw new ProvisionException("Upgrade first to 2.8 or 2.9");
- }
- });
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_84.java b/java/com/google/gerrit/server/schema/Schema_84.java
deleted file mode 100644
index c96f650618..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_84.java
+++ /dev/null
@@ -1,26 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_84 extends SchemaVersion {
-
- @Inject
- Schema_84(Provider<Schema_83> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_85.java b/java/com/google/gerrit/server/schema/Schema_85.java
deleted file mode 100644
index e24e67cbc7..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_85.java
+++ /dev/null
@@ -1,25 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_85 extends SchemaVersion {
- @Inject
- Schema_85(Provider<Schema_84> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_86.java b/java/com/google/gerrit/server/schema/Schema_86.java
deleted file mode 100644
index d758189a66..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_86.java
+++ /dev/null
@@ -1,25 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_86 extends SchemaVersion {
- @Inject
- Schema_86(Provider<Schema_85> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_87.java b/java/com/google/gerrit/server/schema/Schema_87.java
deleted file mode 100644
index 5865af0d4b..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_87.java
+++ /dev/null
@@ -1,80 +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.schema;
-
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-
-public class Schema_87 extends SchemaVersion {
- @Inject
- Schema_87(Provider<Schema_86> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try (PreparedStatement uuidRetrieval =
- prepareStatement(db, "SELECT group_uuid FROM account_groups WHERE group_id = ?");
- PreparedStatement groupDeletion =
- prepareStatement(db, "DELETE FROM account_groups WHERE group_id = ?");
- PreparedStatement groupNameDeletion =
- prepareStatement(db, "DELETE FROM account_group_names WHERE group_id = ?")) {
- for (AccountGroup.Id id : scanSystemGroups(db)) {
- Optional<AccountGroup.UUID> groupUuid = getUuid(uuidRetrieval, id);
- if (groupUuid.filter(SystemGroupBackend::isSystemGroup).isPresent()) {
- groupDeletion.setInt(1, id.get());
- groupDeletion.executeUpdate();
-
- groupNameDeletion.setInt(1, id.get());
- groupNameDeletion.executeUpdate();
- }
- }
- }
- }
-
- private static Optional<AccountGroup.UUID> getUuid(
- PreparedStatement uuidRetrieval, AccountGroup.Id id) throws SQLException {
- uuidRetrieval.setInt(1, id.get());
- try (ResultSet uuidResults = uuidRetrieval.executeQuery()) {
- if (uuidResults.next()) {
- Optional.of(new AccountGroup.UUID(uuidResults.getString(1)));
- }
- }
- return Optional.empty();
- }
-
- private static Set<AccountGroup.Id> scanSystemGroups(ReviewDb db) throws SQLException {
- try (Statement stmt = newStatement(db);
- ResultSet rs =
- stmt.executeQuery("SELECT group_id FROM account_groups WHERE group_type = 'SYSTEM'")) {
- Set<AccountGroup.Id> ids = new HashSet<>();
- while (rs.next()) {
- ids.add(new AccountGroup.Id(rs.getInt(1)));
- }
- return ids;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_88.java b/java/com/google/gerrit/server/schema/Schema_88.java
deleted file mode 100644
index 0a7f14c994..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_88.java
+++ /dev/null
@@ -1,25 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_88 extends SchemaVersion {
- @Inject
- Schema_88(Provider<Schema_87> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_89.java b/java/com/google/gerrit/server/schema/Schema_89.java
deleted file mode 100644
index de84993214..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_89.java
+++ /dev/null
@@ -1,40 +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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-
-public class Schema_89 extends SchemaVersion {
- @Inject
- Schema_89(Provider<Schema_88> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- SqlDialect dialect = ((JdbcSchema) db).getDialect();
- try (StatementExecutor e = newExecutor(db)) {
- dialect.dropIndex(e, "patch_set_approvals", "patch_set_approvals_openByUser");
- dialect.dropIndex(e, "patch_set_approvals", "patch_set_approvals_closedByU");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_90.java b/java/com/google/gerrit/server/schema/Schema_90.java
deleted file mode 100644
index d8f02ae147..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_90.java
+++ /dev/null
@@ -1,35 +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.schema;
-
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_90 extends SchemaVersion {
- @Inject
- Schema_90(Provider<Schema_89> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- try (Statement stmt = newStatement(db)) {
- stmt.executeUpdate("UPDATE accounts set size_bar_in_change_table = 'Y'");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_91.java b/java/com/google/gerrit/server/schema/Schema_91.java
deleted file mode 100644
index 173793e711..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_91.java
+++ /dev/null
@@ -1,25 +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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_91 extends SchemaVersion {
- @Inject
- Schema_91(Provider<Schema_90> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_92.java b/java/com/google/gerrit/server/schema/Schema_92.java
deleted file mode 100644
index 5f5c141498..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_92.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_92 extends SchemaVersion {
- @Inject
- Schema_92(Provider<Schema_91> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_93.java b/java/com/google/gerrit/server/schema/Schema_93.java
deleted file mode 100644
index 3132aa4e4f..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_93.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_93 extends SchemaVersion {
- @Inject
- Schema_93(Provider<Schema_92> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_94.java b/java/com/google/gerrit/server/schema/Schema_94.java
deleted file mode 100644
index d4a189fae2..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_94.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2014 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_94 extends SchemaVersion {
- @Inject
- Schema_94(Provider<Schema_93> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- try (Statement stmt = newStatement(db)) {
- stmt.execute("CREATE INDEX patch_sets_byRevision ON patch_sets (revision)");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_95.java b/java/com/google/gerrit/server/schema/Schema_95.java
deleted file mode 100644
index 0ce0294dac..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_95.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2014 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.sql.SQLException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-public class Schema_95 extends SchemaVersion {
- private final AllUsersCreator allUsersCreator;
-
- @Inject
- Schema_95(Provider<Schema_94> prior, AllUsersCreator allUsersCreator) {
- super(prior);
- this.allUsersCreator = allUsersCreator;
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- try {
- allUsersCreator.create();
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException(e);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_96.java b/java/com/google/gerrit/server/schema/Schema_96.java
deleted file mode 100644
index bf1921386e..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_96.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_96 extends SchemaVersion {
- @Inject
- Schema_96(Provider<Schema_95> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_97.java b/java/com/google/gerrit/server/schema/Schema_97.java
deleted file mode 100644
index 06703778a5..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_97.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_97 extends SchemaVersion {
- @Inject
- Schema_97(Provider<Schema_96> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_98.java b/java/com/google/gerrit/server/schema/Schema_98.java
deleted file mode 100644
index eec3c9f52e..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_98.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2014 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 com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_98 extends SchemaVersion {
- @Inject
- Schema_98(Provider<Schema_97> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- ui.message("Migrate user preference showUserInReview to reviewCategoryStrategy");
- try (Statement stmt = newStatement(db)) {
- stmt.executeUpdate(
- "UPDATE accounts SET "
- + "REVIEW_CATEGORY_STRATEGY='NAME' "
- + "WHERE (SHOW_USER_IN_REVIEW='Y')");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/Schema_99.java b/java/com/google/gerrit/server/schema/Schema_99.java
deleted file mode 100644
index b7fab7f659..0000000000
--- a/java/com/google/gerrit/server/schema/Schema_99.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2014 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 com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_99 extends SchemaVersion {
- @Inject
- Schema_99(Provider<Schema_98> prior) {
- super(prior);
- }
-}
diff --git a/java/com/google/gerrit/server/schema/ScriptRunner.java b/java/com/google/gerrit/server/schema/ScriptRunner.java
deleted file mode 100644
index f4cba98afa..0000000000
--- a/java/com/google/gerrit/server/schema/ScriptRunner.java
+++ /dev/null
@@ -1,125 +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.server.schema;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.base.CharMatcher;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.gwtorm.server.OrmException;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Parses an SQL script from a resource file and later runs it. */
-class ScriptRunner {
- private final String name;
- private final List<String> commands;
-
- static final ScriptRunner NOOP =
- new ScriptRunner(null, null) {
- @Override
- void run(ReviewDb db) {}
- };
-
- ScriptRunner(String scriptName, InputStream script) {
- this.name = scriptName;
- try {
- this.commands = script != null ? parse(script) : null;
- } catch (IOException e) {
- throw new IllegalStateException("Cannot parse " + name, e);
- }
- }
-
- void run(ReviewDb db) throws OrmException {
- try {
- final JdbcSchema schema = (JdbcSchema) db;
- final Connection c = schema.getConnection();
- final SqlDialect dialect = schema.getDialect();
- try (Statement stmt = c.createStatement()) {
- for (String sql : commands) {
- try {
- if (!dialect.isStatementDelimiterSupported()) {
- sql = CharMatcher.is(';').trimTrailingFrom(sql);
- }
- stmt.execute(sql);
- } catch (SQLException e) {
- throw new OrmException("Error in " + name + ":\n" + sql, e);
- }
- }
- }
- } catch (SQLException e) {
- throw new OrmException("Cannot run statements for " + name, e);
- }
- }
-
- private List<String> parse(InputStream in) throws IOException {
- try (BufferedReader br = new BufferedReader(new InputStreamReader(in, UTF_8))) {
- String delimiter = ";";
- List<String> commands = new ArrayList<>();
- StringBuilder buffer = new StringBuilder();
- String line;
- while ((line = br.readLine()) != null) {
- if (line.isEmpty()) {
- continue;
- }
- if (line.startsWith("--")) {
- continue;
- }
-
- if (buffer.length() == 0 && line.toLowerCase().startsWith("delimiter ")) {
- delimiter = line.substring("delimiter ".length()).trim();
- continue;
- }
-
- if (buffer.length() > 0) {
- buffer.append('\n');
- }
- buffer.append(line);
-
- if (isDone(delimiter, line, buffer)) {
- String cmd = buffer.toString();
- commands.add(cmd);
- buffer = new StringBuilder();
- }
- }
- if (buffer.length() > 0) {
- commands.add(buffer.toString());
- }
- return commands;
- }
- }
-
- private boolean isDone(String delimiter, String line, StringBuilder buffer) {
- if (";".equals(delimiter)) {
- return buffer.charAt(buffer.length() - 1) == ';';
-
- } else if (line.equals(delimiter)) {
- buffer.setLength(buffer.length() - delimiter.length());
- return true;
-
- } else {
- return false;
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/UpdateUI.java b/java/com/google/gerrit/server/schema/UpdateUI.java
index 0c02607214..b5a6b6e4de 100644
--- a/java/com/google/gerrit/server/schema/UpdateUI.java
+++ b/java/com/google/gerrit/server/schema/UpdateUI.java
@@ -14,9 +14,6 @@
package com.google.gerrit.server.schema;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import java.util.List;
import java.util.Set;
public interface UpdateUI {
@@ -37,6 +34,4 @@ public interface UpdateUI {
String readString(String defaultValue, Set<String> allowedValues, String message);
boolean isBatch();
-
- void pruneSchema(StatementExecutor e, List<String> pruneList) throws OrmException;
}
diff --git a/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java b/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java
new file mode 100644
index 0000000000..63837b2627
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/testing/AllProjectsCreatorTestUtil.java
@@ -0,0 +1,177 @@
+// 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.schema.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.server.config.AllProjectsName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import java.io.IOException;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BlobBasedConfig;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+
+public class AllProjectsCreatorTestUtil {
+ private static final ImmutableList<String> DEFAULT_ALL_PROJECTS_PROJECT_SECTION =
+ ImmutableList.of("[project]", " description = Access inherited by all other projects.");
+ private static final ImmutableList<String> DEFAULT_ALL_PROJECTS_RECEIVE_SECTION =
+ ImmutableList.of(
+ "[receive]",
+ " requireContributorAgreement = false",
+ " requireSignedOffBy = false",
+ " requireChangeId = true",
+ " enableSignedPush = false");
+ private static final ImmutableList<String> DEFAULT_ALL_PROJECTS_SUBMIT_SECTION =
+ ImmutableList.of("[submit]", " mergeContent = true");
+ private static final ImmutableList<String> DEFAULT_ALL_PROJECTS_CAPABILITY_SECTION =
+ ImmutableList.of(
+ "[capability]",
+ " administrateServer = group Administrators",
+ " priority = batch group Non-Interactive Users",
+ " streamEvents = group Non-Interactive Users");
+ private static final ImmutableList<String> DEFAULT_ALL_PROJECTS_ACCESS_SECTION =
+ ImmutableList.of(
+ "[access \"refs/*\"]",
+ " read = group Administrators",
+ " read = group Anonymous Users",
+ "[access \"refs/for/*\"]",
+ " addPatchSet = group Registered Users",
+ "[access \"refs/for/refs/*\"]",
+ " push = group Registered Users",
+ " pushMerge = group Registered Users",
+ "[access \"refs/heads/*\"]",
+ " create = group Administrators",
+ " create = group Project Owners",
+ " editTopicName = +force group Administrators",
+ " editTopicName = +force group Project Owners",
+ " forgeAuthor = group Registered Users",
+ " forgeCommitter = group Administrators",
+ " forgeCommitter = group Project Owners",
+ " label-Code-Review = -2..+2 group Administrators",
+ " label-Code-Review = -2..+2 group Project Owners",
+ " label-Code-Review = -1..+1 group Registered Users",
+ " push = group Administrators",
+ " push = group Project Owners",
+ " submit = group Administrators",
+ " submit = group Project Owners",
+ "[access \"refs/meta/config\"]",
+ " exclusiveGroupPermissions = read",
+ " create = group Administrators",
+ " create = group Project Owners",
+ " label-Code-Review = -2..+2 group Administrators",
+ " label-Code-Review = -2..+2 group Project Owners",
+ " push = group Administrators",
+ " push = group Project Owners",
+ " read = group Administrators",
+ " read = group Project Owners",
+ " submit = group Administrators",
+ " submit = group Project Owners",
+ "[access \"refs/tags/*\"]",
+ " create = group Administrators",
+ " create = group Project Owners",
+ " createSignedTag = group Administrators",
+ " createSignedTag = group Project Owners",
+ " createTag = group Administrators",
+ " createTag = group Project Owners");
+ private static final ImmutableList<String> DEFAULT_ALL_PROJECTS_LABEL_SECTION =
+ ImmutableList.of(
+ "[label \"Code-Review\"]",
+ " function = MaxWithBlock",
+ " defaultValue = 0",
+ " copyMinScore = true",
+ " copyAllScoresOnTrivialRebase = true",
+ " value = -2 This shall not be merged",
+ " value = -1 I would prefer this is not merged as is",
+ " value = 0 No score",
+ " value = +1 Looks good to me, but someone else must approve",
+ " value = +2 Looks good to me, approved");
+
+ public static String getDefaultAllProjectsWithAllDefaultSections() {
+ return Streams.stream(
+ Iterables.concat(
+ DEFAULT_ALL_PROJECTS_PROJECT_SECTION,
+ DEFAULT_ALL_PROJECTS_RECEIVE_SECTION,
+ DEFAULT_ALL_PROJECTS_SUBMIT_SECTION,
+ DEFAULT_ALL_PROJECTS_CAPABILITY_SECTION,
+ DEFAULT_ALL_PROJECTS_ACCESS_SECTION,
+ DEFAULT_ALL_PROJECTS_LABEL_SECTION))
+ .collect(Collectors.joining("\n"));
+ }
+
+ public static String getAllProjectsWithoutDefaultAcls() {
+ return Streams.stream(
+ Iterables.concat(
+ DEFAULT_ALL_PROJECTS_PROJECT_SECTION,
+ DEFAULT_ALL_PROJECTS_RECEIVE_SECTION,
+ DEFAULT_ALL_PROJECTS_SUBMIT_SECTION,
+ DEFAULT_ALL_PROJECTS_LABEL_SECTION))
+ .collect(Collectors.joining("\n"));
+ }
+
+ // Loads the "project.config" from the All-Projects repo.
+ public static Config readAllProjectsConfig(
+ GitRepositoryManager repoManager, AllProjectsName allProjectsName)
+ throws IOException, ConfigInvalidException {
+ try (Repository repo = repoManager.openRepository(allProjectsName)) {
+ Ref configRef = repo.exactRef(RefNames.REFS_CONFIG);
+ return new BlobBasedConfig(null, repo, configRef.getObjectId(), "project.config");
+ }
+ }
+
+ public static void assertTwoConfigsEquivalent(Config config1, Config config2) {
+ Set<String> sections1 = config1.getSections();
+ Set<String> sections2 = config2.getSections();
+ assertThat(sections1).containsExactlyElementsIn(sections2);
+
+ sections1.forEach(s -> assertSectionEquivalent(config1, config2, s));
+ }
+
+ public static void assertSectionEquivalent(Config config1, Config config2, String section) {
+ assertSubsectionEquivalent(config1, config2, section, null);
+
+ Set<String> subsections1 = config1.getSubsections(section);
+ Set<String> subsections2 = config2.getSubsections(section);
+ assertThat(subsections1)
+ .named("section \"%s\"", section)
+ .containsExactlyElementsIn(subsections2);
+
+ subsections1.forEach(s -> assertSubsectionEquivalent(config1, config2, section, s));
+ }
+
+ private static void assertSubsectionEquivalent(
+ Config config1, Config config2, String section, String subsection) {
+ 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);
+
+ subsectionNames1.forEach(
+ n ->
+ assertThat(config1.getStringList(section, subsection, n))
+ .named(name)
+ .asList()
+ .containsExactlyElementsIn(config2.getStringList(section, subsection, n)));
+ }
+
+ private AllProjectsCreatorTestUtil() {}
+}
diff --git a/java/com/google/gerrit/server/schema/testing/BUILD b/java/com/google/gerrit/server/schema/testing/BUILD
new file mode 100644
index 0000000000..d641c47917
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/testing/BUILD
@@ -0,0 +1,16 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+ name = "testing",
+ testonly = True,
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/server",
+ "//lib:guava",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib/truth",
+ ],
+)
diff --git a/java/com/google/gerrit/server/securestore/SecureStoreClassName.java b/java/com/google/gerrit/server/securestore/SecureStoreClassName.java
index 0247fc112a..2989af07fc 100644
--- a/java/com/google/gerrit/server/securestore/SecureStoreClassName.java
+++ b/java/com/google/gerrit/server/securestore/SecureStoreClassName.java
@@ -1,3 +1,17 @@
+// Copyright (C) 2014 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.securestore;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
diff --git a/java/com/google/gerrit/server/securestore/testing/BUILD b/java/com/google/gerrit/server/securestore/testing/BUILD
new file mode 100644
index 0000000000..c2582b9a26
--- /dev/null
+++ b/java/com/google/gerrit/server/securestore/testing/BUILD
@@ -0,0 +1,13 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_testonly = True)
+
+java_library(
+ name = "testing",
+ srcs = glob(["*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/server",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/java/com/google/gerrit/server/securestore/testing/InMemorySecureStore.java b/java/com/google/gerrit/server/securestore/testing/InMemorySecureStore.java
new file mode 100644
index 0000000000..23894c12f6
--- /dev/null
+++ b/java/com/google/gerrit/server/securestore/testing/InMemorySecureStore.java
@@ -0,0 +1,59 @@
+// 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.securestore.testing;
+
+import com.google.gerrit.server.securestore.SecureStore;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
+
+public class InMemorySecureStore extends SecureStore {
+ private final Config cfg = new Config();
+
+ @Override
+ public String[] getList(String section, String subsection, String name) {
+ return cfg.getStringList(section, subsection, name);
+ }
+
+ @Override
+ public String[] getListForPlugin(
+ String pluginName, String section, String subsection, String name) {
+ throw new UnsupportedOperationException("not used by tests");
+ }
+
+ @Override
+ public void setList(String section, String subsection, String name, List<String> values) {
+ cfg.setStringList(section, subsection, name, values);
+ }
+
+ @Override
+ public void unset(String section, String subsection, String name) {
+ cfg.unset(section, subsection, name);
+ }
+
+ @Override
+ public Iterable<EntryKey> list() {
+ throw new UnsupportedOperationException("not used by tests");
+ }
+
+ @Override
+ public boolean isOutdated() {
+ throw new UnsupportedOperationException("not used by tests");
+ }
+
+ @Override
+ public void reload() {
+ throw new UnsupportedOperationException("not used by tests");
+ }
+}
diff --git a/java/com/google/gerrit/server/ssh/NoSshKeyCache.java b/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
index 387242c6ee..74bb50c912 100644
--- a/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
+++ b/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.ssh;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+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;
diff --git a/java/com/google/gerrit/server/ssh/SshKeyCreator.java b/java/com/google/gerrit/server/ssh/SshKeyCreator.java
index 55ba5ed7ac..beaf1bace7 100644
--- a/java/com/google/gerrit/server/ssh/SshKeyCreator.java
+++ b/java/com/google/gerrit/server/ssh/SshKeyCreator.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.ssh;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountSshKey;
diff --git a/java/com/google/gerrit/server/submit/ChangeSet.java b/java/com/google/gerrit/server/submit/ChangeSet.java
index 8c94a21534..e721be41ab 100644
--- a/java/com/google/gerrit/server/submit/ChangeSet.java
+++ b/java/com/google/gerrit/server/submit/ChangeSet.java
@@ -24,7 +24,6 @@ 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.gwtorm.server.OrmException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -61,14 +60,12 @@ public class ChangeSet {
}
public ChangeSet(Iterable<ChangeData> changes, Iterable<ChangeData> hiddenChanges) {
- changeData = index(changes, ImmutableList.<Change.Id>of());
+ changeData = index(changes, ImmutableList.of());
nonVisibleChanges = index(hiddenChanges, changeData.keySet());
}
public ChangeSet(ChangeData change, boolean visible) {
- this(
- visible ? ImmutableList.of(change) : ImmutableList.<ChangeData>of(),
- ImmutableList.of(change));
+ this(visible ? ImmutableList.of(change) : ImmutableList.of(), ImmutableList.of(change));
}
public ImmutableSet<Change.Id> ids() {
@@ -79,7 +76,7 @@ public class ChangeSet {
return changeData;
}
- public ListMultimap<Branch.NameKey, ChangeData> changesByBranch() throws OrmException {
+ public ListMultimap<Branch.NameKey, ChangeData> changesByBranch() {
ListMultimap<Branch.NameKey, ChangeData> ret =
MultimapBuilder.hashKeys().arrayListValues().build();
for (ChangeData cd : changeData.values()) {
diff --git a/java/com/google/gerrit/server/submit/CherryPick.java b/java/com/google/gerrit/server/submit/CherryPick.java
index 182c22aa68..8ff3cc5dbc 100644
--- a/java/com/google/gerrit/server/submit/CherryPick.java
+++ b/java/com/google/gerrit/server/submit/CherryPick.java
@@ -30,7 +30,6 @@ import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -91,14 +90,14 @@ public class CherryPick extends SubmitStrategy {
@Override
protected void updateRepoImpl(RepoContext ctx)
- throws IntegrationException, IOException, OrmException, MethodNotAllowedException {
+ throws IntegrationException, IOException, MethodNotAllowedException {
// If there is only one parent, a cherry-pick can be done by taking the
// delta relative to that one parent and redoing that on the current merge
// tip.
args.rw.parseBody(toMerge);
psId =
- ChangeUtil.nextPatchSetIdFromChangeRefsMap(
- ctx.getRepoView().getRefs(getId().toRefPrefix()),
+ ChangeUtil.nextPatchSetIdFromChangeRefs(
+ ctx.getRepoView().getRefs(getId().toRefPrefix()).keySet(),
toMerge.change().currentPatchSetId());
RevCommit mergeTip = args.mergeTip.getCurrentTip();
args.rw.parseBody(mergeTip);
@@ -146,8 +145,7 @@ public class CherryPick extends SubmitStrategy {
}
@Override
- public PatchSet updateChangeImpl(ChangeContext ctx)
- throws OrmException, NoSuchChangeException, IOException {
+ public PatchSet updateChangeImpl(ChangeContext ctx) throws NoSuchChangeException, IOException {
if (newCommit == null && toMerge.getStatusCode() == SKIPPED_IDENTICAL_TREE) {
return null;
}
@@ -157,15 +155,14 @@ public class CherryPick extends SubmitStrategy {
String.format(
"no new commit produced by CherryPick of %s, expected to fail fast",
toMerge.change().getId()));
- PatchSet prevPs = args.psUtil.current(ctx.getDb(), ctx.getNotes());
+ PatchSet prevPs = args.psUtil.current(ctx.getNotes());
PatchSet newPs =
args.psUtil.insert(
- ctx.getDb(),
ctx.getRevWalk(),
ctx.getUpdate(psId),
psId,
newCommit,
- prevPs != null ? prevPs.getGroups() : ImmutableList.<String>of(),
+ prevPs != null ? prevPs.getGroups() : 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 3163bd21e9..12172dd7b6 100644
--- a/java/com/google/gerrit/server/submit/CommitMergeStatus.java
+++ b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
@@ -16,10 +16,11 @@ 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.server.CurrentUser;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
import java.util.List;
import java.util.Optional;
@@ -88,15 +89,17 @@ public enum CommitMergeStatus {
}
public static String createMissingDependencyMessage(
- Provider<InternalChangeQuery> queryProvider, String commit, String otherCommit)
- throws OrmException {
+ @Nullable CurrentUser caller,
+ Provider<InternalChangeQuery> queryProvider,
+ String commit,
+ String otherCommit) {
List<ChangeData> changes = queryProvider.get().enforceVisibility(true).byCommit(otherCommit);
if (changes.isEmpty()) {
return String.format(
"Commit %s depends on commit %s which cannot be merged."
- + " Is the change of this commit not visible or was it deleted?",
- commit, otherCommit);
+ + " Is the change of this commit not visible to '%s' or was it deleted?",
+ 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)) {
diff --git a/java/com/google/gerrit/server/submit/EmailMerge.java b/java/com/google/gerrit/server/submit/EmailMerge.java
index a6b7344719..a1f56eb1ac 100644
--- a/java/com/google/gerrit/server/submit/EmailMerge.java
+++ b/java/com/google/gerrit/server/submit/EmailMerge.java
@@ -14,27 +14,20 @@
package com.google.gerrit.server.submit;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
-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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.config.SendEmailExecutor;
import com.google.gerrit.server.mail.send.MergedSender;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -47,46 +40,37 @@ class EmailMerge implements Runnable, RequestContext {
Project.NameKey project,
Change.Id changeId,
Account.Id submitter,
- NotifyHandling notifyHandling,
- ListMultimap<RecipientType, Account.Id> accountsToNotify);
+ NotifyResolver.Result notify);
}
private final ExecutorService sendEmailsExecutor;
private final MergedSender.Factory mergedSenderFactory;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ThreadLocalRequestContext requestContext;
private final IdentifiedUser.GenericFactory identifiedUserFactory;
private final Project.NameKey project;
private final Change.Id changeId;
private final Account.Id submitter;
- private final NotifyHandling notifyHandling;
- private final ListMultimap<RecipientType, Account.Id> accountsToNotify;
-
- private ReviewDb db;
+ private final NotifyResolver.Result notify;
@Inject
EmailMerge(
@SendEmailExecutor ExecutorService executor,
MergedSender.Factory mergedSenderFactory,
- SchemaFactory<ReviewDb> schemaFactory,
ThreadLocalRequestContext requestContext,
IdentifiedUser.GenericFactory identifiedUserFactory,
@Assisted Project.NameKey project,
@Assisted Change.Id changeId,
@Assisted @Nullable Account.Id submitter,
- @Assisted NotifyHandling notifyHandling,
- @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify) {
+ @Assisted NotifyResolver.Result notify) {
this.sendEmailsExecutor = executor;
this.mergedSenderFactory = mergedSenderFactory;
- this.schemaFactory = schemaFactory;
this.requestContext = requestContext;
this.identifiedUserFactory = identifiedUserFactory;
this.project = project;
this.changeId = changeId;
this.submitter = submitter;
- this.notifyHandling = notifyHandling;
- this.accountsToNotify = accountsToNotify;
+ this.notify = notify;
}
void sendAsync() {
@@ -102,17 +86,12 @@ class EmailMerge implements Runnable, RequestContext {
if (submitter != null) {
cm.setFrom(submitter);
}
- cm.setNotify(notifyHandling);
- cm.setAccountsToNotify(accountsToNotify);
+ cm.setNotify(notify);
cm.send();
} catch (Exception e) {
logger.atSevere().withCause(e).log("Cannot email merged notification for %s", changeId);
} finally {
requestContext.setContext(old);
- if (db != null) {
- db.close();
- db = null;
- }
}
}
@@ -128,21 +107,4 @@ class EmailMerge implements Runnable, RequestContext {
}
throw new OutOfScopeException("No user on email thread");
}
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- if (db == null) {
- try {
- db = schemaFactory.open();
- } catch (OrmException e) {
- throw new ProvisionException("Cannot open ReviewDb", e);
- }
- }
- return db;
- }
- };
- }
}
diff --git a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
index 9efb976a65..1219124fb3 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.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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -37,9 +37,9 @@ 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.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -87,26 +87,29 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
private final PermissionBackend permissionBackend;
private final Provider<InternalChangeQuery> queryProvider;
- private final Map<QueryKey, List<ChangeData>> queryCache;
+ private final Map<QueryKey, ImmutableList<ChangeData>> queryCache;
private final Map<Branch.NameKey, Optional<RevCommit>> heads;
private final ProjectCache projectCache;
+ private final ChangeIsVisibleToPredicate.Factory changeIsVisibleToPredicateFactory;
@Inject
LocalMergeSuperSetComputation(
PermissionBackend permissionBackend,
Provider<InternalChangeQuery> queryProvider,
- ProjectCache projectCache) {
+ ProjectCache projectCache,
+ ChangeIsVisibleToPredicate.Factory changeIsVisibleToPredicateFactory) {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.queryProvider = queryProvider;
this.queryCache = new HashMap<>();
this.heads = new HashMap<>();
+ this.changeIsVisibleToPredicateFactory = changeIsVisibleToPredicateFactory;
}
@Override
public ChangeSet completeWithoutTopic(
- ReviewDb db, MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
- throws OrmException, IOException, PermissionBackendException {
+ MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
+ throws IOException, PermissionBackendException {
Collection<ChangeData> visibleChanges = new ArrayList<>();
Collection<ChangeData> nonVisibleChanges = new ArrayList<>();
@@ -119,7 +122,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
List<RevCommit> visibleCommits = new ArrayList<>();
List<RevCommit> nonVisibleCommits = new ArrayList<>();
for (ChangeData cd : bc.get(b)) {
- boolean visible = isVisible(db, changeSet, cd, user);
+ boolean visible = isVisible(changeSet, cd, user);
if (submitType(cd) == SubmitType.CHERRY_PICK) {
if (visible) {
@@ -147,17 +150,19 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
Set<String> visibleHashes =
walkChangesByHashes(visibleCommits, Collections.emptySet(), or, b);
- Iterables.addAll(visibleChanges, byCommitsOnBranchNotMerged(or, db, b, visibleHashes));
-
Set<String> nonVisibleHashes = walkChangesByHashes(nonVisibleCommits, visibleHashes, or, b);
- Iterables.addAll(nonVisibleChanges, byCommitsOnBranchNotMerged(or, db, b, nonVisibleHashes));
+
+ ChangeSet partialSet =
+ byCommitsOnBranchNotMerged(or, b, visibleHashes, nonVisibleHashes, user);
+ Iterables.addAll(visibleChanges, partialSet.changes());
+ Iterables.addAll(nonVisibleChanges, partialSet.nonVisibleChanges());
}
return new ChangeSet(visibleChanges, nonVisibleChanges);
}
private static ImmutableListMultimap<Branch.NameKey, ChangeData> byBranch(
- Iterable<ChangeData> changes) throws OrmException {
+ Iterable<ChangeData> changes) {
ImmutableListMultimap.Builder<Branch.NameKey, ChangeData> builder =
ImmutableListMultimap.builder();
for (ChangeData cd : changes) {
@@ -176,7 +181,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
}
- private boolean isVisible(ReviewDb db, ChangeSet changeSet, ChangeData cd, CurrentUser user)
+ private boolean isVisible(ChangeSet changeSet, ChangeData cd, CurrentUser user)
throws PermissionBackendException, IOException {
ProjectState projectState = projectCache.checkedGet(cd.project());
boolean visible =
@@ -188,7 +193,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
try {
- permissionBackend.user(user).change(cd).database(db).check(ChangePermission.READ);
+ permissionBackend.user(user).change(cd).check(ChangePermission.READ);
return true;
} catch (AuthException e) {
// We thought the change was visible, but it isn't.
@@ -198,7 +203,7 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
}
- private SubmitType submitType(ChangeData cd) throws OrmException {
+ private SubmitType submitType(ChangeData cd) {
SubmitTypeRecord str = cd.submitTypeRecord();
if (!str.isOk()) {
logErrorAndThrow("Failed to get submit type for " + cd.getId() + ": " + str.errorMessage);
@@ -206,24 +211,42 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
return str.type;
}
- private List<ChangeData> byCommitsOnBranchNotMerged(
- OpenRepo or, ReviewDb db, Branch.NameKey branch, Set<String> hashes)
- throws OrmException, IOException {
+ private ChangeSet byCommitsOnBranchNotMerged(
+ OpenRepo or,
+ Branch.NameKey branch,
+ Set<String> visibleHashes,
+ Set<String> nonVisibleHashes,
+ CurrentUser user)
+ throws IOException {
+ List<ChangeData> potentiallyVisibleChanges =
+ byCommitsOnBranchNotMerged(or, branch, visibleHashes);
+ List<ChangeData> invisibleChanges =
+ new ArrayList<>(byCommitsOnBranchNotMerged(or, branch, nonVisibleHashes));
+ List<ChangeData> visibleChanges = new ArrayList<>(potentiallyVisibleChanges.size());
+ ChangeIsVisibleToPredicate changeIsVisibleToPredicate =
+ changeIsVisibleToPredicateFactory.forUser(user);
+ for (ChangeData cd : potentiallyVisibleChanges) {
+ if (changeIsVisibleToPredicate.match(cd)) {
+ visibleChanges.add(cd);
+ } else {
+ invisibleChanges.add(cd);
+ }
+ }
+ return new ChangeSet(visibleChanges, invisibleChanges);
+ }
+
+ private ImmutableList<ChangeData> byCommitsOnBranchNotMerged(
+ OpenRepo or, Branch.NameKey branch, Set<String> hashes) throws IOException {
if (hashes.isEmpty()) {
return ImmutableList.of();
}
QueryKey k = QueryKey.create(branch, hashes);
- List<ChangeData> cached = queryCache.get(k);
- if (cached != null) {
- return cached;
- }
-
- List<ChangeData> result = new ArrayList<>();
- Iterable<ChangeData> destChanges =
- queryProvider.get().byCommitsOnBranchNotMerged(or.repo, db, branch, hashes);
- for (ChangeData chd : destChanges) {
- result.add(chd);
+ if (queryCache.containsKey(k)) {
+ return queryCache.get(k);
}
+ ImmutableList<ChangeData> result =
+ ImmutableList.copyOf(
+ queryProvider.get().byCommitsOnBranchNotMerged(or.repo, branch, hashes));
queryCache.put(k, result);
return result;
}
@@ -265,8 +288,8 @@ public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
}
}
- private void logErrorAndThrow(String msg) throws OrmException {
+ private void logErrorAndThrow(String msg) {
logger.atSevere().log(msg);
- throw new OrmException(msg);
+ throw new StorageException(msg);
}
}
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 9c841ca990..63d60a68ef 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.submit;
+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 java.util.Comparator.comparing;
@@ -35,29 +36,29 @@ 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.extensions.api.changes.RecipientType;
+import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.client.SubmitType;
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.RestApiException;
+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.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.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
+import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
-import com.google.gerrit.server.change.NotifyUtil;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.git.CodeReviewCommit;
-import com.google.gerrit.server.git.LockFailureException;
import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.validators.MergeValidationException;
import com.google.gerrit.server.git.validators.MergeValidators;
@@ -78,7 +79,6 @@ import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryHelper.ActionType;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -124,7 +124,7 @@ public class MergeOp implements AutoCloseable {
private final ListMultimap<Change.Id, String> problems;
private final boolean allowClosed;
- private CommitStatus(ChangeSet cs, boolean allowClosed) throws OrmException {
+ private CommitStatus(ChangeSet cs, boolean allowClosed) {
checkArgument(
!cs.furtherHiddenChanges(), "CommitStatus must not be called with hidden changes");
changes = cs.changesById();
@@ -229,7 +229,7 @@ public class MergeOp implements AutoCloseable {
private final SubmitStrategyFactory submitStrategyFactory;
private final SubmoduleOp.Factory subOpFactory;
private final Provider<MergeOpRepoManager> ormProvider;
- private final NotifyUtil notifyUtil;
+ private final NotifyResolver notifyResolver;
private final RetryHelper retryHelper;
private final ChangeData.Factory changeDataFactory;
@@ -239,9 +239,8 @@ public class MergeOp implements AutoCloseable {
private MergeOpRepoManager orm;
private CommitStatus commitStatus;
- private ReviewDb db;
private SubmitInput submitInput;
- private ListMultimap<RecipientType, Account.Id> accountsToNotify;
+ private NotifyResolver.Result notify;
private Set<Project.NameKey> allProjects;
private boolean dryrun;
private TopicMetrics topicMetrics;
@@ -257,7 +256,7 @@ public class MergeOp implements AutoCloseable {
SubmitStrategyFactory submitStrategyFactory,
SubmoduleOp.Factory subOpFactory,
Provider<MergeOpRepoManager> ormProvider,
- NotifyUtil notifyUtil,
+ NotifyResolver notifyResolver,
TopicMetrics topicMetrics,
RetryHelper retryHelper,
ChangeData.Factory changeDataFactory) {
@@ -270,7 +269,7 @@ public class MergeOp implements AutoCloseable {
this.submitStrategyFactory = submitStrategyFactory;
this.subOpFactory = subOpFactory;
this.ormProvider = ormProvider;
- this.notifyUtil = notifyUtil;
+ this.notifyResolver = notifyResolver;
this.retryHelper = retryHelper;
this.topicMetrics = topicMetrics;
this.changeDataFactory = changeDataFactory;
@@ -284,7 +283,7 @@ public class MergeOp implements AutoCloseable {
}
public static void checkSubmitRule(ChangeData cd, boolean allowClosed)
- throws ResourceConflictException, OrmException {
+ throws ResourceConflictException {
PatchSet patchSet = cd.currentPatchSet();
if (patchSet == null) {
throw new ResourceConflictException("missing current patch set for change " + cd.getId());
@@ -333,7 +332,7 @@ public class MergeOp implements AutoCloseable {
return cd.submitRecords(submitRuleOptions(allowClosed));
}
- private static String describeNotReady(ChangeData cd, SubmitRecord record) throws OrmException {
+ private static String describeNotReady(ChangeData cd, SubmitRecord record) {
List<String> blockingConditions = new ArrayList<>();
if (record.labels != null) {
blockingConditions.add(describeLabels(cd, record.labels));
@@ -346,8 +345,7 @@ public class MergeOp implements AutoCloseable {
return Joiner.on("; ").join(blockingConditions);
}
- private static String describeLabels(ChangeData cd, List<SubmitRecord.Label> labels)
- throws OrmException {
+ private static String describeLabels(ChangeData cd, List<SubmitRecord.Label> labels) {
List<String> labelResults = new ArrayList<>();
for (SubmitRecord.Label lbl : labels) {
switch (lbl.status) {
@@ -383,12 +381,10 @@ public class MergeOp implements AutoCloseable {
!cs.furtherHiddenChanges(), "checkSubmitRulesAndState called for topic with hidden change");
for (ChangeData cd : cs.changes()) {
try {
- Change.Status status = cd.change().getStatus();
- if (status != Change.Status.NEW) {
- if (!(status == Change.Status.MERGED && allowMerged)) {
+ if (!cd.change().isNew()) {
+ if (!(cd.change().isMerged() && allowMerged)) {
commitStatus.problem(
- cd.getId(),
- "Change " + cd.getId() + " is " + cd.change().getStatus().toString().toLowerCase());
+ cd.getId(), "Change " + cd.getId() + " is " + ChangeUtil.status(cd.change()));
}
} else if (cd.change().isWorkInProgress()) {
commitStatus.problem(cd.getId(), "Change " + cd.getId() + " is work in progress");
@@ -397,7 +393,7 @@ public class MergeOp implements AutoCloseable {
}
} catch (ResourceConflictException e) {
commitStatus.problem(cd.getId(), e.getMessage());
- } catch (OrmException e) {
+ } catch (StorageException e) {
String msg = "Error checking submit rules for change";
logger.atWarning().withCause(e).log("%s %s", msg, cd.getId());
commitStatus.problem(cd.getId(), msg);
@@ -425,31 +421,29 @@ public class MergeOp implements AutoCloseable {
* topic or via superproject subscriptions. All affected changes are integrated using the projects
* integration strategy.
*
- * @param db the review database.
* @param change the change to be merged.
* @param caller the identity of the caller
* @param checkSubmitRules whether the prolog submit rules should be evaluated
* @param submitInput parameters regarding the merge
- * @throws OrmException an error occurred reading or writing the database.
* @throws RestApiException if an error occurred.
* @throws PermissionBackendException if permissions can't be checked
* @throws IOException an error occurred reading from NoteDb.
*/
public void merge(
- ReviewDb db,
Change change,
IdentifiedUser caller,
boolean checkSubmitRules,
SubmitInput submitInput,
boolean dryrun)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ throws RestApiException, UpdateException, IOException, ConfigInvalidException,
PermissionBackendException {
this.submitInput = submitInput;
- this.accountsToNotify = notifyUtil.resolveAccounts(submitInput.notifyDetails);
+ this.notify =
+ notifyResolver.resolve(
+ firstNonNull(submitInput.notify, NotifyHandling.ALL), submitInput.notifyDetails);
this.dryrun = dryrun;
this.caller = caller;
this.ts = TimeUtil.nowTs();
- this.db = db;
this.submissionId = new RequestId(change.getId().toString());
try (TraceContext traceContext =
@@ -459,7 +453,7 @@ public class MergeOp implements AutoCloseable {
logger.atFine().log("Beginning integration of %s", change);
try {
ChangeSet indexBackedChangeSet =
- mergeSuperSet.setMergeOpRepoManager(orm).completeChangeSet(db, change, caller);
+ mergeSuperSet.setMergeOpRepoManager(orm).completeChangeSet(change, caller);
checkState(
indexBackedChangeSet.ids().contains(change.getId()),
"change %s missing from %s",
@@ -524,7 +518,7 @@ public class MergeOp implements AutoCloseable {
}
} catch (IOException e) {
// Anything before the merge attempt is an error
- throw new OrmException(e);
+ throw new StorageException(e);
}
}
}
@@ -534,18 +528,16 @@ public class MergeOp implements AutoCloseable {
orm.close();
}
orm = ormProvider.get();
- orm.setContext(db, ts, caller);
+ orm.setContext(ts, caller, notify);
}
private ChangeSet reloadChanges(ChangeSet changeSet) {
List<ChangeData> visible = new ArrayList<>(changeSet.changes().size());
List<ChangeData> nonVisible = new ArrayList<>(changeSet.nonVisibleChanges().size());
- changeSet
- .changes()
- .forEach(c -> visible.add(changeDataFactory.create(db, c.project(), c.getId())));
+ changeSet.changes().forEach(c -> visible.add(changeDataFactory.create(c.project(), c.getId())));
changeSet
.nonVisibleChanges()
- .forEach(c -> nonVisible.add(changeDataFactory.create(db, c.project(), c.getId())));
+ .forEach(c -> nonVisible.add(changeDataFactory.create(c.project(), c.getId())));
return new ChangeSet(visible, nonVisible);
}
@@ -586,7 +578,7 @@ public class MergeOp implements AutoCloseable {
ListMultimap<Branch.NameKey, ChangeData> cbb;
try {
cbb = cs.changesByBranch();
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new IntegrationException("Error reading changes to submit", e);
}
Set<Branch.NameKey> branches = cbb.keySet();
@@ -605,7 +597,7 @@ public class MergeOp implements AutoCloseable {
SubmoduleOp submoduleOp = subOpFactory.create(branches, orm);
List<SubmitStrategy> strategies = getSubmitStrategies(toSubmit, submoduleOp, dryrun);
this.allProjects = submoduleOp.getProjectsInOrder();
- batchUpdateFactory.execute(
+ BatchUpdate.execute(
orm.batchUpdates(allProjects),
new SubmitStrategyListener(submitInput, strategies, commitStatus),
dryrun);
@@ -669,7 +661,6 @@ public class MergeOp implements AutoCloseable {
SubmitStrategy strategy =
submitStrategyFactory.create(
submitting.submitType(),
- db,
or.rw,
or.canMergeFlag,
getAlreadyAccepted(or, ob.oldTip),
@@ -680,7 +671,6 @@ public class MergeOp implements AutoCloseable {
commitStatus,
submissionId,
submitInput,
- accountsToNotify,
submoduleOp,
dryrun);
strategies.add(strategy);
@@ -750,7 +740,7 @@ public class MergeOp implements AutoCloseable {
notes = cd.notes();
chg = cd.change();
st = getSubmitType(cd);
- } catch (OrmException e) {
+ } catch (StorageException e) {
commitStatus.logProblem(changeId, e);
continue;
}
@@ -782,7 +772,7 @@ public class MergeOp implements AutoCloseable {
Branch.NameKey destBranch = chg.getDest();
try {
ps = cd.currentPatchSet();
- } catch (OrmException e) {
+ } catch (StorageException e) {
commitStatus.logProblem(changeId, e);
continue;
}
@@ -878,7 +868,7 @@ public class MergeOp implements AutoCloseable {
revisions.put(e.getValue().getObjectId(), PatchSet.Id.fromRef(e.getKey()));
}
return revisions;
- } catch (IOException | OrmException e) {
+ } catch (IOException | StorageException e) {
throw new IntegrationException("Failed to validate changes", e);
}
}
@@ -904,14 +894,14 @@ public class MergeOp implements AutoCloseable {
try {
for (ChangeData cd : queryProvider.get().byProjectOpen(destProject)) {
try (BatchUpdate bu =
- batchUpdateFactory.create(db, destProject, internalUserFactory.create(), ts)) {
+ batchUpdateFactory.create(destProject, internalUserFactory.create(), ts)) {
bu.addOp(
cd.getId(),
new BatchUpdateOp() {
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
Change change = ctx.getChange();
- if (!change.getStatus().isOpen()) {
+ if (!change.isNew()) {
return false;
}
@@ -924,8 +914,7 @@ public class MergeOp implements AutoCloseable {
change.getLastUpdatedOn(),
"Project was deleted.",
ChangeMessagesUtil.TAG_MERGED);
- cmUtil.addChangeMessage(
- ctx.getDb(), ctx.getUpdate(change.currentPatchSetId()), msg);
+ cmUtil.addChangeMessage(ctx.getUpdate(change.currentPatchSetId()), msg);
return true;
}
@@ -938,7 +927,7 @@ public class MergeOp implements AutoCloseable {
}
}
}
- } catch (OrmException e) {
+ } catch (StorageException e) {
logger.atWarning().withCause(e).log(
"Cannot abandon changes for deleted project %s", destProject);
}
diff --git a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
index 3f07ed7500..764aca8ce0 100644
--- a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
+++ b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
@@ -15,13 +15,14 @@
package com.google.gerrit.server.submit;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -105,12 +106,13 @@ public class MergeOpRepoManager implements AutoCloseable {
}
public BatchUpdate getUpdate() {
- checkState(db != null, "call setContext before getUpdate");
+ checkState(caller != null, "call setContext before getUpdate");
if (update == null) {
update =
batchUpdateFactory
- .create(db, getProjectName(), caller, ts)
+ .create(getProjectName(), caller, ts)
.setRepository(repo, rw, ins)
+ .setNotify(notify)
.setOnSubmitValidators(onSubmitValidatorsFactory.create());
}
return update;
@@ -157,9 +159,9 @@ public class MergeOpRepoManager implements AutoCloseable {
private final GitRepositoryManager repoManager;
private final ProjectCache projectCache;
- private ReviewDb db;
private Timestamp ts;
private IdentifiedUser caller;
+ private NotifyResolver.Result notify;
@Inject
MergeOpRepoManager(
@@ -175,10 +177,10 @@ public class MergeOpRepoManager implements AutoCloseable {
openRepos = new HashMap<>();
}
- public void setContext(ReviewDb db, Timestamp ts, IdentifiedUser caller) {
- this.db = db;
- this.ts = ts;
- this.caller = caller;
+ public void setContext(Timestamp ts, IdentifiedUser caller, NotifyResolver.Result notify) {
+ this.ts = requireNonNull(ts);
+ this.caller = requireNonNull(caller);
+ this.notify = requireNonNull(notify);
}
public OpenRepo getRepo(Project.NameKey project) throws NoSuchProjectException, IOException {
@@ -203,7 +205,7 @@ public class MergeOpRepoManager implements AutoCloseable {
throws NoSuchProjectException, IOException {
List<BatchUpdate> updates = new ArrayList<>(projects.size());
for (Project.NameKey project : projects) {
- updates.add(getRepo(project).getUpdate().setRefLogMessage("merged"));
+ updates.add(getRepo(project).getUpdate().setNotify(notify).setRefLogMessage("merged"));
}
return updates;
}
diff --git a/java/com/google/gerrit/server/submit/MergeSorter.java b/java/com/google/gerrit/server/submit/MergeSorter.java
index 6dbec325bd..f2f6537bc2 100644
--- a/java/com/google/gerrit/server/submit/MergeSorter.java
+++ b/java/com/google/gerrit/server/submit/MergeSorter.java
@@ -14,10 +14,11 @@
package com.google.gerrit.server.submit;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
import java.io.IOException;
import java.util.Collection;
@@ -29,6 +30,7 @@ import org.eclipse.jgit.revwalk.RevCommitList;
import org.eclipse.jgit.revwalk.RevFlag;
public class MergeSorter {
+ @Nullable private final CurrentUser caller;
private final CodeReviewRevWalk rw;
private final RevFlag canMergeFlag;
private final Set<RevCommit> accepted;
@@ -36,11 +38,13 @@ public class MergeSorter {
private final Set<CodeReviewCommit> incoming;
public MergeSorter(
+ @Nullable CurrentUser caller,
CodeReviewRevWalk rw,
Set<RevCommit> alreadyAccepted,
RevFlag canMergeFlag,
Provider<InternalChangeQuery> queryProvider,
Set<CodeReviewCommit> incoming) {
+ this.caller = caller;
this.rw = rw;
this.canMergeFlag = canMergeFlag;
this.accepted = alreadyAccepted;
@@ -49,7 +53,7 @@ public class MergeSorter {
}
public Collection<CodeReviewCommit> sort(Collection<CodeReviewCommit> toMerge)
- throws IOException, OrmException {
+ throws IOException {
final Set<CodeReviewCommit> heads = new HashSet<>();
final Set<CodeReviewCommit> sort = new HashSet<>(toMerge);
while (!sort.isEmpty()) {
@@ -70,7 +74,8 @@ public class MergeSorter {
//
n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY);
n.setStatusMessage(
- CommitMergeStatus.createMissingDependencyMessage(queryProvider, n.name(), c.name()));
+ CommitMergeStatus.createMissingDependencyMessage(
+ caller, queryProvider, n.name(), c.name()));
break;
}
contents.add(c);
diff --git a/java/com/google/gerrit/server/submit/MergeSuperSet.java b/java/com/google/gerrit/server/submit/MergeSuperSet.java
index f444fcb3e3..d729833a96 100644
--- a/java/com/google/gerrit/server/submit/MergeSuperSet.java
+++ b/java/com/google/gerrit/server/submit/MergeSuperSet.java
@@ -22,7 +22,6 @@ import com.google.common.collect.Iterables;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.logging.TraceContext;
@@ -34,7 +33,6 @@ 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.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -93,8 +91,8 @@ public class MergeSuperSet {
return this;
}
- public ChangeSet completeChangeSet(ReviewDb db, Change change, CurrentUser user)
- throws IOException, OrmException, PermissionBackendException {
+ public ChangeSet completeChangeSet(Change change, CurrentUser user)
+ throws IOException, PermissionBackendException {
try {
if (orm == null) {
orm = repoManagerProvider.get();
@@ -114,7 +112,7 @@ public class MergeSuperSet {
if (projectState.statePermitsRead()) {
try {
- permissionBackend.user(user).change(cd).database(db).check(ChangePermission.READ);
+ permissionBackend.user(user).change(cd).check(ChangePermission.READ);
visible = true;
} catch (AuthException e) {
// Do nothing.
@@ -124,10 +122,10 @@ public class MergeSuperSet {
ChangeSet changeSet = new ChangeSet(cd, visible);
if (wholeTopicEnabled(cfg)) {
- return completeChangeSetIncludingTopics(db, changeSet, user);
+ return completeChangeSetIncludingTopics(changeSet, user);
}
try (TraceContext traceContext = PluginContext.newTrace(mergeSuperSetComputation)) {
- return mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
+ return mergeSuperSetComputation.get().completeWithoutTopic(orm, changeSet, user);
}
} finally {
if (closeOrm && orm != null) {
@@ -141,9 +139,8 @@ public class MergeSuperSet {
* Completes {@code changeSet} with any additional changes from its topics
*
* <p>{@link #completeChangeSetIncludingTopics} calls this repeatedly, alternating with {@link
- * MergeSuperSetComputation#completeWithoutTopic(ReviewDb, MergeOpRepoManager, ChangeSet,
- * CurrentUser)}, to discover what additional changes should be submitted with a change until the
- * set stops growing.
+ * MergeSuperSetComputation#completeWithoutTopic(MergeOpRepoManager, ChangeSet, CurrentUser)}, to
+ * discover what additional changes should be submitted with a change until the set stops growing.
*
* <p>{@code topicsSeen} and {@code visibleTopicsSeen} keep track of topics already explored to
* avoid wasted work.
@@ -151,12 +148,8 @@ public class MergeSuperSet {
* @return the resulting larger {@link ChangeSet}
*/
private ChangeSet topicClosure(
- ReviewDb db,
- ChangeSet changeSet,
- CurrentUser user,
- Set<String> topicsSeen,
- Set<String> visibleTopicsSeen)
- throws OrmException, PermissionBackendException, IOException {
+ ChangeSet changeSet, CurrentUser user, Set<String> topicsSeen, Set<String> visibleTopicsSeen)
+ throws PermissionBackendException, IOException {
List<ChangeData> visibleChanges = new ArrayList<>();
List<ChangeData> nonVisibleChanges = new ArrayList<>();
@@ -167,7 +160,7 @@ public class MergeSuperSet {
continue;
}
for (ChangeData topicCd : byTopicOpen(topic)) {
- if (canRead(db, user, topicCd)) {
+ if (canRead(user, topicCd)) {
visibleChanges.add(topicCd);
} else {
nonVisibleChanges.add(topicCd);
@@ -190,33 +183,32 @@ public class MergeSuperSet {
return new ChangeSet(visibleChanges, nonVisibleChanges);
}
- private ChangeSet completeChangeSetIncludingTopics(
- ReviewDb db, ChangeSet changeSet, CurrentUser user)
- throws IOException, OrmException, PermissionBackendException {
+ private ChangeSet completeChangeSetIncludingTopics(ChangeSet changeSet, CurrentUser user)
+ throws IOException, PermissionBackendException {
Set<String> topicsSeen = new HashSet<>();
Set<String> visibleTopicsSeen = new HashSet<>();
int oldSeen;
int seen;
- changeSet = topicClosure(db, changeSet, user, topicsSeen, visibleTopicsSeen);
+ changeSet = topicClosure(changeSet, user, topicsSeen, visibleTopicsSeen);
seen = topicsSeen.size() + visibleTopicsSeen.size();
do {
oldSeen = seen;
try (TraceContext traceContext = PluginContext.newTrace(mergeSuperSetComputation)) {
- changeSet = mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
+ changeSet = mergeSuperSetComputation.get().completeWithoutTopic(orm, changeSet, user);
}
- changeSet = topicClosure(db, changeSet, user, topicsSeen, visibleTopicsSeen);
+ changeSet = topicClosure(changeSet, user, topicsSeen, visibleTopicsSeen);
seen = topicsSeen.size() + visibleTopicsSeen.size();
} while (seen != oldSeen);
return changeSet;
}
- private List<ChangeData> byTopicOpen(String topic) throws OrmException {
+ private List<ChangeData> byTopicOpen(String topic) {
return queryProvider.get().byTopicOpen(topic);
}
- private boolean canRead(ReviewDb db, CurrentUser user, ChangeData cd)
+ private boolean canRead(CurrentUser user, ChangeData cd)
throws PermissionBackendException, IOException {
ProjectState projectState = projectCache.checkedGet(cd.project());
if (projectState == null || !projectState.statePermitsRead()) {
@@ -224,7 +216,7 @@ public class MergeSuperSet {
}
try {
- permissionBackend.user(user).change(cd).database(db).check(ChangePermission.READ);
+ permissionBackend.user(user).change(cd).check(ChangePermission.READ);
return true;
} catch (AuthException e) {
return false;
diff --git a/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java
index dd9ad9bf20..99239e3aaa 100644
--- a/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java
+++ b/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java
@@ -15,10 +15,8 @@
package com.google.gerrit.server.submit;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
/**
@@ -41,13 +39,11 @@ public interface MergeSuperSetComputation {
* <p>This method is invoked iteratively while new changes to be submitted together are discovered
* by expanding the topics of the changes. This method must not do any topic expansion on its own.
*
- * @param db {@link ReviewDb} instance
* @param orm {@link MergeOpRepoManager} that should be used to access repositories
* @param changeSet A set of changes for which it is known that they should be submitted together
* @param user The user for which the visibility checks should be performed
* @return the completed set of changes that should be submitted together
*/
- ChangeSet completeWithoutTopic(
- ReviewDb db, MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
- throws OrmException, IOException, PermissionBackendException;
+ ChangeSet completeWithoutTopic(MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
+ throws IOException, PermissionBackendException;
}
diff --git a/java/com/google/gerrit/server/submit/RebaseSorter.java b/java/com/google/gerrit/server/submit/RebaseSorter.java
index 32f35580ba..829ee9c575 100644
--- a/java/com/google/gerrit/server/submit/RebaseSorter.java
+++ b/java/com/google/gerrit/server/submit/RebaseSorter.java
@@ -15,13 +15,13 @@
package com.google.gerrit.server.submit;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Change.Status;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
import java.io.IOException;
import java.util.ArrayList;
@@ -37,6 +37,7 @@ import org.eclipse.jgit.revwalk.RevFlag;
public class RebaseSorter {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final CurrentUser caller;
private final CodeReviewRevWalk rw;
private final RevFlag canMergeFlag;
private final RevCommit initialTip;
@@ -45,12 +46,14 @@ public class RebaseSorter {
private final Set<CodeReviewCommit> incoming;
public RebaseSorter(
+ CurrentUser caller,
CodeReviewRevWalk rw,
RevCommit initialTip,
Set<RevCommit> alreadyAccepted,
RevFlag canMergeFlag,
Provider<InternalChangeQuery> queryProvider,
Set<CodeReviewCommit> incoming) {
+ this.caller = caller;
this.rw = rw;
this.canMergeFlag = canMergeFlag;
this.initialTip = initialTip;
@@ -59,8 +62,7 @@ public class RebaseSorter {
this.incoming = incoming;
}
- public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort)
- throws IOException, OrmException {
+ public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort) throws IOException {
final List<CodeReviewCommit> sorted = new ArrayList<>();
final Set<CodeReviewCommit> sort = new HashSet<>(toSort);
while (!sort.isEmpty()) {
@@ -85,7 +87,7 @@ public class RebaseSorter {
n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY);
n.setStatusMessage(
CommitMergeStatus.createMissingDependencyMessage(
- queryProvider, n.name(), c.name()));
+ caller, queryProvider, n.name(), c.name()));
}
// Stop RevWalk because c is either a merged commit or a missing
// dependency. Not need to walk further.
@@ -122,15 +124,14 @@ public class RebaseSorter {
// check if the commit associated change is merged in the same branch
List<ChangeData> changes = queryProvider.get().byCommit(commit);
for (ChangeData change : changes) {
- if (change.change().getStatus() == Status.MERGED
- && change.change().getDest().equals(dest)) {
+ if (change.change().isMerged() && change.change().getDest().equals(dest)) {
logger.atFine().log(
"Dependency %s associated with merged change %s.", commit.getName(), change.getId());
return true;
}
}
return false;
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new IOException(e);
}
}
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index d59ce68c15..b2ad4e253a 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -19,6 +19,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.MergeConflictException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -34,7 +35,6 @@ import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -59,7 +59,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
List<CodeReviewCommit> sorted;
try {
sorted = args.rebaseSorter.sort(toMerge);
- } catch (IOException | OrmException e) {
+ } catch (IOException | StorageException e) {
throw new IntegrationException("Commit sorting failed", e);
}
List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
@@ -119,7 +119,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
@Override
public void updateRepoImpl(RepoContext ctx)
throws IntegrationException, InvalidChangeOperationException, RestApiException, IOException,
- OrmException, PermissionBackendException {
+ PermissionBackendException {
if (args.mergeUtil.canFastForward(
args.mergeSorter, args.mergeTip.getCurrentTip(), args.rw, toMerge)) {
if (!rebaseAlways) {
@@ -137,8 +137,8 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
// RebaseAlways means we modify commit message.
args.rw.parseBody(toMerge);
newPatchSetId =
- ChangeUtil.nextPatchSetIdFromChangeRefsMap(
- ctx.getRepoView().getRefs(getId().toRefPrefix()),
+ ChangeUtil.nextPatchSetIdFromChangeRefs(
+ ctx.getRepoView().getRefs(getId().toRefPrefix()).keySet(),
toMerge.change().currentPatchSetId());
RevCommit mergeTip = args.mergeTip.getCurrentTip();
args.rw.parseBody(mergeTip);
@@ -171,14 +171,13 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
ctx.addRefUpdate(ObjectId.zeroId(), newCommit, newPatchSetId.toRefName());
} else {
// Stale read of patch set is ok; see comments in RebaseChangeOp.
- PatchSet origPs = args.psUtil.get(ctx.getDb(), toMerge.getNotes(), toMerge.getPatchsetId());
+ PatchSet origPs = args.psUtil.get(toMerge.getNotes(), toMerge.getPatchsetId());
rebaseOp =
args.rebaseFactory
.create(toMerge.notes(), origPs, args.mergeTip.getCurrentTip())
.setFireRevisionCreated(false)
// Bypass approval copier since SubmitStrategyOp copy all approvals
// later anyway.
- .setCopyApprovals(false)
.setValidate(false)
.setCheckAddPatchSetPermission(false)
// RebaseAlways should set always modify commit message like
@@ -216,7 +215,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
@Override
public PatchSet updateChangeImpl(ChangeContext ctx)
- throws NoSuchChangeException, ResourceConflictException, OrmException, IOException {
+ throws NoSuchChangeException, ResourceConflictException, IOException {
if (newCommit == null) {
checkState(!rebaseAlways, "RebaseAlways must never fast forward");
// otherwise, took the fast-forward option, nothing to do.
@@ -229,15 +228,14 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
newPs = rebaseOp.getPatchSet();
} else {
// CherryPick
- PatchSet prevPs = args.psUtil.current(ctx.getDb(), ctx.getNotes());
+ PatchSet prevPs = args.psUtil.current(ctx.getNotes());
newPs =
args.psUtil.insert(
- ctx.getDb(),
ctx.getRevWalk(),
ctx.getUpdate(newPatchSetId),
newPatchSetId,
newCommit,
- prevPs != null ? prevPs.getGroups() : ImmutableList.<String>of(),
+ prevPs != null ? prevPs.getGroups() : ImmutableList.of(),
null,
null);
}
@@ -249,7 +247,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy {
}
@Override
- public void postUpdateImpl(Context ctx) throws OrmException {
+ public void postUpdateImpl(Context ctx) {
if (rebaseOp != null) {
rebaseOp.postUpdate(ctx);
}
diff --git a/java/com/google/gerrit/server/submit/SubmitDryRun.java b/java/com/google/gerrit/server/submit/SubmitDryRun.java
index e273652d9d..391d9562bb 100644
--- a/java/com/google/gerrit/server/submit/SubmitDryRun.java
+++ b/java/com/google/gerrit/server/submit/SubmitDryRun.java
@@ -19,8 +19,10 @@ import static java.util.stream.Collectors.toSet;
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.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;
import com.google.gerrit.server.git.MergeUtil;
@@ -106,6 +108,7 @@ public class SubmitDryRun {
}
public boolean run(
+ @Nullable CurrentUser caller,
SubmitType submitType,
Repository repo,
CodeReviewRevWalk rw,
@@ -124,7 +127,12 @@ public class SubmitDryRun {
rw,
mergeUtilFactory.create(getProject(destBranch)),
new MergeSorter(
- rw, alreadyAccepted, canMerge, queryProvider, ImmutableSet.of(toMergeCommit)));
+ caller,
+ rw,
+ alreadyAccepted,
+ canMerge,
+ queryProvider,
+ ImmutableSet.of(toMergeCommit)));
switch (submitType) {
case CHERRY_PICK:
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategy.java b/java/com/google/gerrit/server/submit/SubmitStrategy.java
index 4cefd7dc53..dc221f87a2 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategy.java
@@ -16,16 +16,12 @@ package com.google.gerrit.server.submit;
import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
-import com.google.gerrit.extensions.api.changes.RecipientType;
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.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.GerritPersonIdent;
@@ -34,6 +30,7 @@ import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.change.LabelNormalizer;
import com.google.gerrit.server.change.RebaseChangeOp;
+import com.google.gerrit.server.change.SetPrivateOp;
import com.google.gerrit.server.change.TestSubmitInput;
import com.google.gerrit.server.extensions.events.ChangeMerged;
import com.google.gerrit.server.git.CodeReviewCommit;
@@ -46,6 +43,7 @@ import com.google.gerrit.server.git.validators.OnSubmitValidators;
import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
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.query.change.InternalChangeQuery;
import com.google.gerrit.server.submit.MergeOp.CommitStatus;
@@ -90,12 +88,10 @@ public abstract class SubmitStrategy {
IdentifiedUser caller,
MergeTip mergeTip,
RevFlag canMergeFlag,
- ReviewDb db,
Set<RevCommit> alreadyAccepted,
Set<CodeReviewCommit> incoming,
RequestId submissionId,
SubmitInput submitInput,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
SubmoduleOp submoduleOp,
boolean dryrun);
}
@@ -115,6 +111,8 @@ public abstract class SubmitStrategy {
final OnSubmitValidators.Factory onSubmitValidatorsFactory;
final TagCache tagCache;
final Provider<InternalChangeQuery> queryProvider;
+ final ProjectConfig.Factory projectConfigFactory;
+ final SetPrivateOp.Factory setPrivateOpFactory;
final Branch.NameKey destBranch;
final CodeReviewRevWalk rw;
@@ -122,12 +120,10 @@ public abstract class SubmitStrategy {
final IdentifiedUser caller;
final MergeTip mergeTip;
final RevFlag canMergeFlag;
- final ReviewDb db;
final Set<RevCommit> alreadyAccepted;
final RequestId submissionId;
final SubmitType submitType;
final SubmitInput submitInput;
- final ListMultimap<RecipientType, Account.Id> accountsToNotify;
final SubmoduleOp submoduleOp;
final ProjectState project;
@@ -154,19 +150,19 @@ public abstract class SubmitStrategy {
OnSubmitValidators.Factory onSubmitValidatorsFactory,
TagCache tagCache,
Provider<InternalChangeQuery> queryProvider,
+ ProjectConfig.Factory projectConfigFactory,
+ SetPrivateOp.Factory setPrivateOpFactory,
@Assisted Branch.NameKey destBranch,
@Assisted CommitStatus commitStatus,
@Assisted CodeReviewRevWalk rw,
@Assisted IdentifiedUser caller,
@Assisted MergeTip mergeTip,
@Assisted RevFlag canMergeFlag,
- @Assisted ReviewDb db,
@Assisted Set<RevCommit> alreadyAccepted,
@Assisted Set<CodeReviewCommit> incoming,
@Assisted RequestId submissionId,
@Assisted SubmitType submitType,
@Assisted SubmitInput submitInput,
- @Assisted ListMultimap<RecipientType, Account.Id> accountsToNotify,
@Assisted SubmoduleOp submoduleOp,
@Assisted boolean dryrun) {
this.accountCache = accountCache;
@@ -176,12 +172,14 @@ public abstract class SubmitStrategy {
this.repoManager = repoManager;
this.cmUtil = cmUtil;
this.labelNormalizer = labelNormalizer;
+ this.projectConfigFactory = projectConfigFactory;
this.patchSetInfoFactory = patchSetInfoFactory;
this.psUtil = psUtil;
this.projectCache = projectCache;
this.rebaseFactory = rebaseFactory;
this.tagCache = tagCache;
this.queryProvider = queryProvider;
+ this.setPrivateOpFactory = setPrivateOpFactory;
this.serverIdent = serverIdent;
this.destBranch = destBranch;
@@ -190,12 +188,10 @@ public abstract class SubmitStrategy {
this.caller = caller;
this.mergeTip = mergeTip;
this.canMergeFlag = canMergeFlag;
- this.db = db;
this.alreadyAccepted = alreadyAccepted;
this.submissionId = submissionId;
this.submitType = submitType;
this.submitInput = submitInput;
- this.accountsToNotify = accountsToNotify;
this.submoduleOp = submoduleOp;
this.dryrun = dryrun;
@@ -204,10 +200,16 @@ public abstract class SubmitStrategy {
projectCache.get(destBranch.getParentKey()),
() -> String.format("project not found: %s", destBranch.getParentKey()));
this.mergeSorter =
- new MergeSorter(rw, alreadyAccepted, canMergeFlag, queryProvider, incoming);
+ new MergeSorter(caller, rw, alreadyAccepted, canMergeFlag, queryProvider, incoming);
this.rebaseSorter =
new RebaseSorter(
- rw, mergeTip.getInitialTip(), alreadyAccepted, canMergeFlag, queryProvider, incoming);
+ caller,
+ rw,
+ mergeTip.getInitialTip(),
+ alreadyAccepted,
+ canMergeFlag,
+ queryProvider,
+ incoming);
this.mergeUtil = mergeUtilFactory.create(project);
this.onSubmitValidatorsFactory = onSubmitValidatorsFactory;
}
@@ -246,12 +248,14 @@ public abstract class SubmitStrategy {
Collections.reverse(difference);
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));
maybeAddTestHelperOp(bu, id);
}
// Then ops for explicitly merged changes
for (SubmitStrategyOp op : ops) {
+ bu.addOp(op.getId(), args.setPrivateOpFactory.create(false, null));
bu.addOp(op.getId(), op);
maybeAddTestHelperOp(bu, op.getId());
}
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java b/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
index 7f70f37b86..30326f773d 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyFactory.java
@@ -14,14 +14,10 @@
package com.google.gerrit.server.submit;
-import com.google.common.collect.ListMultimap;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.extensions.api.changes.RecipientType;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
@@ -48,7 +44,6 @@ public class SubmitStrategyFactory {
public SubmitStrategy create(
SubmitType submitType,
- ReviewDb db,
CodeReviewRevWalk rw,
RevFlag canMergeFlag,
Set<RevCommit> alreadyAccepted,
@@ -59,7 +54,6 @@ public class SubmitStrategyFactory {
CommitStatus commitStatus,
RequestId submissionId,
SubmitInput submitInput,
- ListMultimap<RecipientType, Account.Id> accountsToNotify,
SubmoduleOp submoduleOp,
boolean dryrun)
throws IntegrationException {
@@ -72,12 +66,10 @@ public class SubmitStrategyFactory {
caller,
mergeTip,
canMergeFlag,
- db,
alreadyAccepted,
incoming,
submissionId,
submitInput,
- accountsToNotify,
submoduleOp,
dryrun);
switch (submitType) {
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyListener.java b/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
index d1f847bbd7..782cd7bde6 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyListener.java
@@ -99,7 +99,7 @@ public class SubmitStrategyListener implements BatchUpdateListener {
args.rw,
args.canMergeFlag,
args.mergeTip.getCurrentTip(),
- initialTip == null ? ImmutableSet.<RevCommit>of() : ImmutableSet.of(initialTip));
+ initialTip == null ? ImmutableSet.of() : ImmutableSet.of(initialTip));
}
}
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 303c95666b..14522a2a70 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -17,12 +17,13 @@ package com.google.gerrit.server.submit;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkState;
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.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.SubmitRecord;
+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;
@@ -32,8 +33,6 @@ 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.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.IdentifiedUser;
@@ -50,7 +49,6 @@ import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -145,7 +143,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
if (RefNames.REFS_CONFIG.equals(refName)) {
logger.atFine().log("Loading new configuration from %s", RefNames.REFS_CONFIG);
try {
- ProjectConfig cfg = new ProjectConfig(getProject());
+ ProjectConfig cfg = args.projectConfigFactory.create(getProject());
cfg.load(ctx.getRevWalk(), commit);
} catch (Exception e) {
throw new IntegrationException(
@@ -183,8 +181,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
continue; // Bogus ref, can't be merged into tip so we don't care.
}
}
- commits.sort(
- ReviewDbUtil.intKeyOrdering().reverse().onResultOf(CodeReviewCommit::getPatchsetId));
+ commits.sort(comparing((CodeReviewCommit c) -> c.getPatchsetId().get()).reversed());
CodeReviewCommit result = MergeUtil.findAnyMergedInto(rw, commits, tip);
if (result == null) {
return null;
@@ -218,7 +215,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
"%s#updateChange for change %s", getClass().getSimpleName(), toMerge.change().getId());
toMerge.setNotes(ctx.getNotes()); // Update change and notes from ctx.
- if (ctx.getChange().getStatus() == Change.Status.MERGED) {
+ if (ctx.getChange().isMerged()) {
// Either another thread won a race, or we are retrying a whole topic submission after one
// repo failed with lock failure.
if (alreadyMergedCommit == null) {
@@ -251,7 +248,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// during the submit strategy.
mergedPatchSet =
requireNonNull(
- args.psUtil.get(ctx.getDb(), ctx.getNotes(), oldPsId),
+ args.psUtil.get(ctx.getNotes(), oldPsId),
() -> String.format("missing old patch set %s", oldPsId));
} else {
PatchSet.Id n = newPatchSet.getId();
@@ -286,7 +283,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
: alreadyMergedCommit;
try {
setMerged(ctx, message(ctx, commit, s));
- } catch (OrmException err) {
+ } catch (StorageException err) {
String msg = "Error updating change status for " + id;
logger.atSevere().withCause(err).log(msg);
args.commitStatus.logProblem(id, msg);
@@ -297,16 +294,15 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
return true;
}
- private PatchSet getOrCreateAlreadyMergedPatchSet(ChangeContext ctx)
- throws IOException, OrmException {
+ private PatchSet getOrCreateAlreadyMergedPatchSet(ChangeContext ctx) throws IOException {
PatchSet.Id psId = alreadyMergedCommit.getPatchsetId();
logger.atFine().log("Fixing up already-merged patch set %s", psId);
- PatchSet prevPs = args.psUtil.current(ctx.getDb(), ctx.getNotes());
+ PatchSet prevPs = args.psUtil.current(ctx.getNotes());
ctx.getRevWalk().parseBody(alreadyMergedCommit);
ctx.getChange()
.setCurrentPatchSet(
psId, alreadyMergedCommit.getShortMessage(), ctx.getChange().getOriginalSubject());
- PatchSet existing = args.psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ PatchSet existing = args.psUtil.get(ctx.getNotes(), psId);
if (existing != null) {
logger.atFine().log("Patch set row exists, only updating change");
return existing;
@@ -317,18 +313,10 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
List<String> groups =
prevPs != null ? prevPs.getGroups() : GroupCollector.getDefaultGroups(alreadyMergedCommit);
return args.psUtil.insert(
- ctx.getDb(),
- ctx.getRevWalk(),
- ctx.getUpdate(psId),
- psId,
- alreadyMergedCommit,
- groups,
- null,
- null);
+ ctx.getRevWalk(), ctx.getUpdate(psId), psId, alreadyMergedCommit, groups, null, null);
}
- private void setApproval(ChangeContext ctx, IdentifiedUser user)
- throws OrmException, IOException {
+ private void setApproval(ChangeContext ctx, IdentifiedUser user) throws IOException {
Change.Id id = ctx.getChange().getId();
List<SubmitRecord> records = args.commitStatus.getSubmitRecords(id);
PatchSet.Id oldPsId = toMerge.getPatchsetId();
@@ -344,18 +332,18 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// If the submit strategy created a new revision (rebase, cherry-pick), copy
// approvals as well.
if (!newPsId.equals(oldPsId)) {
- saveApprovals(normalized, ctx, newPsUpdate, true);
+ saveApprovals(normalized, newPsUpdate, true);
submitter = convertPatchSet(newPsId).apply(submitter);
}
}
private LabelNormalizer.Result approve(ChangeContext ctx, ChangeUpdate update)
- throws OrmException, IOException {
+ throws IOException {
PatchSet.Id psId = update.getPatchSetId();
Map<PatchSetApproval.Key, PatchSetApproval> byKey = new HashMap<>();
for (PatchSetApproval psa :
args.approvalsUtil.byPatchSet(
- ctx.getDb(), ctx.getNotes(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
+ ctx.getNotes(), psId, ctx.getRevWalk(), ctx.getRepoView().getConfig())) {
byKey.put(psa.getKey(), psa);
}
@@ -371,19 +359,12 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
LabelNormalizer.Result normalized =
args.labelNormalizer.normalize(ctx.getNotes(), byKey.values());
update.putApproval(submitter.getLabel(), submitter.getValue());
- saveApprovals(normalized, ctx, update, false);
+ saveApprovals(normalized, update, false);
return normalized;
}
private void saveApprovals(
- LabelNormalizer.Result normalized,
- ChangeContext ctx,
- ChangeUpdate update,
- boolean includeUnchanged)
- throws OrmException {
- PatchSet.Id psId = update.getPatchSetId();
- ctx.getDb().patchSetApprovals().upsert(convertPatchSet(normalized.getNormalized(), psId));
- ctx.getDb().patchSetApprovals().upsert(zero(convertPatchSet(normalized.deleted(), psId)));
+ LabelNormalizer.Result normalized, ChangeUpdate update, boolean includeUnchanged) {
for (PatchSetApproval psa : normalized.updated()) {
update.putApprovalFor(psa.getAccountId(), psa.getLabel(), psa.getValue());
}
@@ -411,21 +392,6 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
};
}
- private static Iterable<PatchSetApproval> convertPatchSet(
- Iterable<PatchSetApproval> approvals, PatchSet.Id psId) {
- return Iterables.transform(approvals, convertPatchSet(psId));
- }
-
- private static Iterable<PatchSetApproval> zero(Iterable<PatchSetApproval> approvals) {
- return Iterables.transform(
- approvals,
- a -> {
- PatchSetApproval copy = new PatchSetApproval(a.getPatchSetId(), a);
- copy.setValue((short) 0);
- return copy;
- });
- }
-
private String getByAccountName() {
requireNonNull(submitter, "getByAccountName called before submitter populated");
Optional<Account> account =
@@ -436,8 +402,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
return "";
}
- private ChangeMessage message(ChangeContext ctx, CodeReviewCommit commit, CommitMergeStatus s)
- throws OrmException {
+ private ChangeMessage message(ChangeContext ctx, CodeReviewCommit commit, CommitMergeStatus s) {
requireNonNull(s, "CommitMergeStatus may not be null");
String txt = s.getDescription();
if (s == CommitMergeStatus.CLEAN_MERGE) {
@@ -483,9 +448,8 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
psId, ctx.getUser(), ctx.getWhen(), body, ChangeMessagesUtil.TAG_MERGED);
}
- private void setMerged(ChangeContext ctx, ChangeMessage msg) throws OrmException {
+ private void setMerged(ChangeContext ctx, ChangeMessage msg) {
Change c = ctx.getChange();
- ReviewDb db = ctx.getDb();
logger.atFine().log("Setting change %s merged", c.getId());
c.setStatus(Change.Status.MERGED);
c.setSubmissionId(args.submissionId.toStringForStorage());
@@ -494,7 +458,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// which is not the user from the update context. addMergedMessage was able
// to do this in the past.
if (msg != null) {
- args.cmUtil.addChangeMessage(db, ctx.getUpdate(msg.getPatchSetId()), msg);
+ args.cmUtil.addChangeMessage(ctx.getUpdate(msg.getPatchSetId()), msg);
}
}
@@ -534,12 +498,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
// have failed fast in one of the other steps.
try {
args.mergedSenderFactory
- .create(
- ctx.getProject(),
- getId(),
- submitter.getAccountId(),
- args.submitInput.notify,
- args.accountsToNotify)
+ .create(ctx.getProject(), getId(), submitter.getAccountId(), ctx.getNotify(getId()))
.sendAsync();
} catch (Exception e) {
logger.atSevere().withCause(e).log("Cannot email merged notification for %s", getId());
diff --git a/java/com/google/gerrit/server/submit/SubmoduleOp.java b/java/com/google/gerrit/server/submit/SubmoduleOp.java
index 070d80ff68..afcf9c5d76 100644
--- a/java/com/google/gerrit/server/submit/SubmoduleOp.java
+++ b/java/com/google/gerrit/server/submit/SubmoduleOp.java
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.MultimapBuilder;
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.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Branch;
@@ -28,7 +29,6 @@ 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.UsedAt;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.VerboseSuperprojectUpdate;
import com.google.gerrit.server.git.CodeReviewCommit;
@@ -101,39 +101,29 @@ public class SubmoduleOp {
private final Provider<PersonIdent> serverIdent;
private final Config cfg;
private final ProjectCache projectCache;
- private final BatchUpdate.Factory batchUpdateFactory;
@Inject
Factory(
GitModules.Factory gitmodulesFactory,
@GerritPersonIdent Provider<PersonIdent> serverIdent,
@GerritServerConfig Config cfg,
- ProjectCache projectCache,
- BatchUpdate.Factory batchUpdateFactory) {
+ ProjectCache projectCache) {
this.gitmodulesFactory = gitmodulesFactory;
this.serverIdent = serverIdent;
this.cfg = cfg;
this.projectCache = projectCache;
- this.batchUpdateFactory = batchUpdateFactory;
}
public SubmoduleOp create(Set<Branch.NameKey> updatedBranches, MergeOpRepoManager orm)
throws SubmoduleException {
return new SubmoduleOp(
- gitmodulesFactory,
- serverIdent.get(),
- cfg,
- projectCache,
- batchUpdateFactory,
- updatedBranches,
- orm);
+ gitmodulesFactory, serverIdent.get(), cfg, projectCache, updatedBranches, orm);
}
}
private final GitModules.Factory gitmodulesFactory;
private final PersonIdent myIdent;
private final ProjectCache projectCache;
- private final BatchUpdate.Factory batchUpdateFactory;
private final VerboseSuperprojectUpdate verboseSuperProject;
private final boolean enableSuperProjectSubscriptions;
private final long maxCombinedCommitMessageSize;
@@ -173,14 +163,12 @@ public class SubmoduleOp {
PersonIdent myIdent,
Config cfg,
ProjectCache projectCache,
- BatchUpdate.Factory batchUpdateFactory,
Set<Branch.NameKey> updatedBranches,
MergeOpRepoManager orm)
throws SubmoduleException {
this.gitmodulesFactory = gitmodulesFactory;
this.myIdent = myIdent;
this.projectCache = projectCache;
- this.batchUpdateFactory = batchUpdateFactory;
this.verboseSuperProject =
cfg.getEnum("submodule", null, "verboseSuperprojectUpdate", VerboseSuperprojectUpdate.TRUE);
this.enableSuperProjectSubscriptions =
@@ -420,7 +408,7 @@ public class SubmoduleOp {
}
}
}
- batchUpdateFactory.execute(orm.batchUpdates(superProjects), BatchUpdateListener.NONE, false);
+ BatchUpdate.execute(orm.batchUpdates(superProjects), BatchUpdateListener.NONE, false);
} catch (RestApiException | UpdateException | IOException | NoSuchProjectException e) {
throw new SubmoduleException("Cannot update gitlinks", e);
}
diff --git a/java/com/google/gerrit/server/update/BatchUpdate.java b/java/com/google/gerrit/server/update/BatchUpdate.java
index b3472d2dab..0ca9f5e60f 100644
--- a/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -17,7 +17,10 @@ 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.collect.ImmutableMultiset.toImmutableMultiset;
+import static com.google.common.flogger.LazyArgs.lazy;
+import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toSet;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
@@ -26,27 +29,36 @@ import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Multiset;
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.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.config.FactoryModule;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.change.NotifyResolver;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.validators.OnSubmitValidators;
+import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.logging.RequestId;
-import com.google.gerrit.server.notedb.NotesMigration;
+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.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.NoSuchRefException;
import com.google.inject.Inject;
import com.google.inject.Module;
-import com.google.inject.Singleton;
+import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -56,6 +68,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
+import java.util.TreeMap;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@@ -65,135 +78,106 @@ import org.eclipse.jgit.transport.PushCertificate;
import org.eclipse.jgit.transport.ReceiveCommand;
/**
- * Helper for a set of updates that should be applied for a site.
+ * Helper for a set of change updates that should be applied to the NoteDb database.
*
* <p>An update operation can be divided into three phases:
*
* <ol>
* <li>Git reference updates
- * <li>Database updates
+ * <li>Review metadata updates
* <li>Post-update steps
* <li>
* </ol>
*
* A single conceptual operation, such as a REST API call or a merge operation, may make multiple
* changes at each step, which all need to be serialized relative to each other. Moreover, for
- * consistency, <em>all</em> git ref updates must be performed before <em>any</em> database updates,
- * since database updates might refer to newly-created patch set refs. And all post-update steps,
- * such as hooks, should run only after all storage mutations have completed.
+ * consistency, the git ref updates must be visible to the review metadata updates, since for
+ * example the metadata might refer to newly-created patch set refs. In NoteDb, this is accomplished
+ * by combining these two phases into a single {@link BatchRefUpdate}.
*
- * <p>Depending on the backend used, each step might support batching, for example in a {@code
- * BatchRefUpdate} or one or more database transactions. All operations in one phase must complete
- * successfully before proceeding to the next phase.
+ * <p>Similarly, all post-update steps, such as sending email, must run only after all storage
+ * mutations have completed.
*/
-public abstract class BatchUpdate implements AutoCloseable {
+public class BatchUpdate implements AutoCloseable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static Module module() {
return new FactoryModule() {
@Override
public void configure() {
- factory(ReviewDbBatchUpdate.AssistedFactory.class);
- factory(NoteDbBatchUpdate.AssistedFactory.class);
+ factory(BatchUpdate.Factory.class);
}
};
}
- @Singleton
- public static class Factory {
- private final NotesMigration migration;
- private final ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory;
- private final NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory;
+ // 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);
+ }
- // TODO(dborowitz): Make this non-injectable to force all callers to use RetryHelper.
- @Inject
- Factory(
- NotesMigration migration,
- ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
- NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
- this.migration = migration;
- this.reviewDbBatchUpdateFactory = reviewDbBatchUpdateFactory;
- this.noteDbBatchUpdateFactory = noteDbBatchUpdateFactory;
+ public static void execute(
+ Collection<BatchUpdate> updates, BatchUpdateListener listener, boolean dryrun)
+ throws UpdateException, RestApiException {
+ requireNonNull(listener);
+ if (updates.isEmpty()) {
+ return;
}
- public BatchUpdate create(
- ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when) {
- if (migration.disableChangeReviewDb()) {
- return noteDbBatchUpdateFactory.create(db, project, user, when);
- }
- return reviewDbBatchUpdateFactory.create(db, project, user, when);
- }
-
- @SuppressWarnings({"rawtypes", "unchecked"})
- public void execute(
- Collection<BatchUpdate> updates, BatchUpdateListener listener, boolean dryRun)
- throws UpdateException, RestApiException {
- requireNonNull(listener);
- checkDifferentProject(updates);
- // It's safe to downcast all members of the input collection in this case, because the only
- // way a caller could have gotten any BatchUpdates in the first place is to call the create
- // method above, which always returns instances of the type we expect. Just to be safe,
- // copy them into an ImmutableList so there is no chance the callee can pollute the input
- // collection.
- if (migration.disableChangeReviewDb()) {
- ImmutableList<NoteDbBatchUpdate> noteDbUpdates =
- (ImmutableList) ImmutableList.copyOf(updates);
- NoteDbBatchUpdate.execute(noteDbUpdates, listener, dryRun);
- } else {
- ImmutableList<ReviewDbBatchUpdate> reviewDbUpdates =
- (ImmutableList) ImmutableList.copyOf(updates);
- ReviewDbBatchUpdate.execute(reviewDbUpdates, listener, dryRun);
+ checkDifferentProject(updates);
+
+ try {
+ List<ListenableFuture<?>> indexFutures = new ArrayList<>();
+ List<ChangesHandle> handles = new ArrayList<>(updates.size());
+ try {
+ for (BatchUpdate u : updates) {
+ u.executeUpdateRepo();
+ }
+ listener.afterUpdateRepos();
+ for (BatchUpdate u : updates) {
+ handles.add(u.executeChangeOps(dryrun));
+ }
+ for (ChangesHandle h : handles) {
+ h.execute();
+ indexFutures.addAll(h.startIndexFutures());
+ }
+ listener.afterUpdateRefs();
+ listener.afterUpdateChanges();
+ } finally {
+ for (ChangesHandle h : handles) {
+ h.close();
+ }
}
- }
- private static void checkDifferentProject(Collection<BatchUpdate> updates) {
- Multiset<Project.NameKey> projectCounts =
- updates.stream().map(u -> u.project).collect(toImmutableMultiset());
- checkArgument(
- projectCounts.entrySet().size() == updates.size(),
- "updates must all be for different projects, got: %s",
- projectCounts);
- }
- }
+ ((ListenableFuture<?>) Futures.allAsList(indexFutures)).get();
+
+ // Fire ref update events only after all mutations are finished, since callers may assume a
+ // patch set ref being created means the change was created, or a branch advancing meaning
+ // some changes were closed.
+ updates.stream()
+ .filter(u -> u.batchRefUpdate != null)
+ .forEach(
+ u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
- static Order getOrder(Collection<? extends BatchUpdate> updates, BatchUpdateListener listener) {
- Order o = null;
- for (BatchUpdate u : updates) {
- if (o == null) {
- o = u.order;
- } else if (u.order != o) {
- throw new IllegalArgumentException("cannot mix execution orders");
+ if (!dryrun) {
+ for (BatchUpdate u : updates) {
+ u.executePostOps();
+ }
}
+ } catch (Exception e) {
+ wrapAndThrowException(e);
}
- if (o != Order.REPO_BEFORE_DB) {
- checkArgument(
- listener == BatchUpdateListener.NONE,
- "BatchUpdateListener not supported for order %s",
- o);
- }
- return o;
}
- static boolean getUpdateChangesInParallel(Collection<? extends BatchUpdate> updates) {
- checkArgument(!updates.isEmpty());
- Boolean p = null;
- for (BatchUpdate u : updates) {
- if (p == null) {
- p = u.updateChangesInParallel;
- } else if (u.updateChangesInParallel != p) {
- throw new IllegalArgumentException("cannot mix parallel and non-parallel operations");
- }
- }
- // Properly implementing this would involve hoisting the parallel loop up
- // even further. As of this writing, the only user is ReceiveCommits,
- // which only executes a single BatchUpdate at a time. So bail for now.
+ private static void checkDifferentProject(Collection<BatchUpdate> updates) {
+ Multiset<Project.NameKey> projectCounts =
+ updates.stream().map(u -> u.project).collect(toImmutableMultiset());
checkArgument(
- !p || updates.size() <= 1,
- "cannot execute ChangeOps in parallel with more than 1 BatchUpdate");
- return p;
+ projectCounts.entrySet().size() == updates.size(),
+ "updates must all be for different projects, got: %s",
+ projectCounts);
}
- static void wrapAndThrowException(Exception e) throws UpdateException, RestApiException {
+ private static void wrapAndThrowException(Exception e) throws UpdateException, RestApiException {
Throwables.throwIfUnchecked(e);
// Propagate REST API exceptions thrown by operations; they commonly throw exceptions like
@@ -215,39 +199,146 @@ public abstract class BatchUpdate implements AutoCloseable {
throw new UpdateException(e);
}
- protected GitRepositoryManager repoManager;
+ class ContextImpl implements Context {
+ @Override
+ public RepoView getRepoView() throws IOException {
+ return BatchUpdate.this.getRepoView();
+ }
- protected final Project.NameKey project;
- protected final CurrentUser user;
- protected final Timestamp when;
- protected final TimeZone tz;
+ @Override
+ public RevWalk getRevWalk() throws IOException {
+ return getRepoView().getRevWalk();
+ }
- protected final ListMultimap<Change.Id, BatchUpdateOp> ops =
- MultimapBuilder.linkedHashKeys().arrayListValues().build();
- protected final Map<Change.Id, Change> newChanges = new HashMap<>();
- protected final List<RepoOnlyOp> repoOnlyOps = new ArrayList<>();
+ @Override
+ public Project.NameKey getProject() {
+ return project;
+ }
+
+ @Override
+ public Timestamp getWhen() {
+ return when;
+ }
+
+ @Override
+ public TimeZone getTimeZone() {
+ return tz;
+ }
+
+ @Override
+ public CurrentUser getUser() {
+ return user;
+ }
+
+ @Override
+ public NotifyResolver.Result getNotify(Change.Id changeId) {
+ NotifyHandling notifyHandling = perChangeNotifyHandling.get(changeId);
+ return notifyHandling != null ? notify.withHandling(notifyHandling) : notify;
+ }
+ }
+
+ private class RepoContextImpl extends ContextImpl implements RepoContext {
+ @Override
+ public ObjectInserter getInserter() throws IOException {
+ return getRepoView().getInserterWrapper();
+ }
+
+ @Override
+ public void addRefUpdate(ReceiveCommand cmd) throws IOException {
+ getRepoView().getCommands().add(cmd);
+ }
+ }
- protected RepoView repoView;
- protected BatchRefUpdate batchRefUpdate;
- protected Order order;
- protected OnSubmitValidators onSubmitValidators;
- protected PushCertificate pushCert;
- protected String refLogMessage;
+ private class ChangeContextImpl extends ContextImpl implements ChangeContext {
+ private final ChangeNotes notes;
+ private final Map<PatchSet.Id, ChangeUpdate> updates;
- private boolean updateChangesInParallel;
+ private boolean deleted;
- protected BatchUpdate(
+ ChangeContextImpl(ChangeNotes notes) {
+ this.notes = requireNonNull(notes);
+ updates = new TreeMap<>(comparing(PatchSet.Id::get));
+ }
+
+ @Override
+ public ChangeUpdate getUpdate(PatchSet.Id psId) {
+ ChangeUpdate u = updates.get(psId);
+ if (u == null) {
+ u = changeUpdateFactory.create(notes, user, when);
+ if (newChanges.containsKey(notes.getChangeId())) {
+ u.setAllowWriteToNewRef(true);
+ }
+ u.setPatchSetId(psId);
+ updates.put(psId, u);
+ }
+ return u;
+ }
+
+ @Override
+ public ChangeNotes getNotes() {
+ return notes;
+ }
+
+ @Override
+ public void deleteChange() {
+ deleted = true;
+ }
+ }
+
+ /** Per-change result status from {@link #executeChangeOps}. */
+ private enum ChangeResult {
+ SKIPPED,
+ UPSERTED,
+ DELETED;
+ }
+
+ private final GitRepositoryManager repoManager;
+ private final ChangeNotes.Factory changeNotesFactory;
+ private final ChangeUpdate.Factory changeUpdateFactory;
+ private final NoteDbUpdateManager.Factory updateManagerFactory;
+ private final ChangeIndexer indexer;
+ private final GitReferenceUpdated gitRefUpdated;
+
+ private final Project.NameKey project;
+ private final CurrentUser user;
+ private final Timestamp when;
+ private final TimeZone tz;
+
+ private final ListMultimap<Change.Id, BatchUpdateOp> ops =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ private final Map<Change.Id, Change> newChanges = new HashMap<>();
+ private final List<RepoOnlyOp> repoOnlyOps = new ArrayList<>();
+ private final Map<Change.Id, NotifyHandling> perChangeNotifyHandling = new HashMap<>();
+
+ private RepoView repoView;
+ private BatchRefUpdate batchRefUpdate;
+ private OnSubmitValidators onSubmitValidators;
+ private PushCertificate pushCert;
+ private String refLogMessage;
+ private NotifyResolver.Result notify = NotifyResolver.Result.all();
+
+ @Inject
+ BatchUpdate(
GitRepositoryManager repoManager,
- PersonIdent serverIdent,
- Project.NameKey project,
- CurrentUser user,
- Timestamp when) {
+ @GerritPersonIdent PersonIdent serverIdent,
+ ChangeNotes.Factory changeNotesFactory,
+ ChangeUpdate.Factory changeUpdateFactory,
+ NoteDbUpdateManager.Factory updateManagerFactory,
+ ChangeIndexer indexer,
+ GitReferenceUpdated gitRefUpdated,
+ @Assisted Project.NameKey project,
+ @Assisted CurrentUser user,
+ @Assisted Timestamp when) {
this.repoManager = repoManager;
+ this.changeNotesFactory = changeNotesFactory;
+ this.changeUpdateFactory = changeUpdateFactory;
+ this.updateManagerFactory = updateManagerFactory;
+ this.indexer = indexer;
+ this.gitRefUpdated = gitRefUpdated;
this.project = project;
this.user = user;
this.when = when;
tz = serverIdent.getTimeZone();
- order = Order.REPO_BEFORE_DB;
}
@Override
@@ -257,15 +348,14 @@ public abstract class BatchUpdate implements AutoCloseable {
}
}
- public abstract void execute(BatchUpdateListener listener)
- throws UpdateException, RestApiException;
+ public void execute(BatchUpdateListener listener) throws UpdateException, RestApiException {
+ execute(ImmutableList.of(this), listener, false);
+ }
public void execute() throws UpdateException, RestApiException {
execute(BatchUpdateListener.NONE);
}
- protected abstract Context newContext();
-
public BatchUpdate setRepository(Repository repo, RevWalk revWalk, ObjectInserter inserter) {
checkState(this.repoView == null, "repo already set");
repoView = new RepoView(repo, revWalk, inserter);
@@ -282,58 +372,58 @@ public abstract class BatchUpdate implements AutoCloseable {
return this;
}
- public BatchUpdate setOrder(Order order) {
- this.order = order;
+ /**
+ * Set the default notification settings for all changes in the batch.
+ *
+ * @param notify notification settings.
+ * @return this.
+ */
+ public BatchUpdate setNotify(NotifyResolver.Result notify) {
+ this.notify = requireNonNull(notify);
return this;
}
/**
- * Add a validation step for intended ref operations, which will be performed at the end of {@link
- * RepoOnlyOp#updateRepo(RepoContext)} step.
+ * Override the {@link NotifyHandling} on a per-change basis.
+ *
+ * <p>Only the handling enum can be overridden; all changes share the same value for {@link
+ * com.google.gerrit.server.change.NotifyResolver.Result#accounts()}.
+ *
+ * @param changeId change ID.
+ * @param notifyHandling notify handling.
+ * @return this.
*/
- public BatchUpdate setOnSubmitValidators(OnSubmitValidators onSubmitValidators) {
- this.onSubmitValidators = onSubmitValidators;
+ public BatchUpdate setNotifyHandling(Change.Id changeId, NotifyHandling notifyHandling) {
+ this.perChangeNotifyHandling.put(changeId, requireNonNull(notifyHandling));
return this;
}
/**
- * Execute {@link BatchUpdateOp#updateChange(ChangeContext)} in parallel for each change.
- *
- * <p>This improves performance of writing to multiple changes in separate ReviewDb transactions.
- * When only NoteDb is used, updates to all changes are written in a single batch ref update, so
- * parallelization is not used and this option is ignored.
+ * Add a validation step for intended ref operations, which will be performed at the end of {@link
+ * RepoOnlyOp#updateRepo(RepoContext)} step.
*/
- public BatchUpdate updateChangesInParallel() {
- this.updateChangesInParallel = true;
+ public BatchUpdate setOnSubmitValidators(OnSubmitValidators onSubmitValidators) {
+ this.onSubmitValidators = onSubmitValidators;
return this;
}
- protected void initRepository() throws IOException {
+ private void initRepository() throws IOException {
if (repoView == null) {
repoView = new RepoView(repoManager, project);
}
}
- protected RepoView getRepoView() throws IOException {
+ private RepoView getRepoView() throws IOException {
initRepository();
return repoView;
}
- protected CurrentUser getUser() {
- return user;
- }
-
- protected Optional<AccountState> getAccount() {
+ private Optional<AccountState> getAccount() {
return user.isIdentifiedUser()
? Optional.of(user.asIdentifiedUser().state())
: Optional.empty();
}
- protected RevWalk getRevWalk() throws IOException {
- initRepository();
- return repoView.getRevWalk();
- }
-
public Map<String, ReceiveCommand> getRefUpdates() {
return repoView != null ? repoView.getCommands().getCommands() : ImmutableMap.of();
}
@@ -352,7 +442,7 @@ public abstract class BatchUpdate implements AutoCloseable {
}
public BatchUpdate insertChange(InsertChangeOp op) throws IOException {
- Context ctx = newContext();
+ Context ctx = new ContextImpl();
Change c = op.createChange(ctx);
checkArgument(
!newChanges.containsKey(c.getId()), "only one op allowed to create change %s", c.getId());
@@ -361,43 +451,181 @@ public abstract class BatchUpdate implements AutoCloseable {
return this;
}
- protected static void logDebug(String msg, Throwable t) {
- // Only log if there is a requestId assigned, since those are the
- // expensive/complicated requests like MergeOp. Doing it every time would be
- // noisy.
- if (RequestId.isSet()) {
- logger.atFine().withCause(t).log("%s", msg);
+ private void executeUpdateRepo() throws UpdateException, RestApiException {
+ try {
+ logDebug("Executing updateRepo on %d ops", ops.size());
+ RepoContextImpl ctx = new RepoContextImpl();
+ for (BatchUpdateOp op : ops.values()) {
+ op.updateRepo(ctx);
+ }
+
+ logDebug("Executing updateRepo on %d RepoOnlyOps", repoOnlyOps.size());
+ for (RepoOnlyOp op : repoOnlyOps) {
+ op.updateRepo(ctx);
+ }
+
+ if (onSubmitValidators != null && !getRefUpdates().isEmpty()) {
+ // Validation of refs has to take place here and not at the beginning of executeRefUpdates.
+ // Otherwise, failing validation in a second BatchUpdate object will happen *after* the
+ // first update's executeRefUpdates has finished, hence after first repo's refs have been
+ // updated, which is too late.
+ onSubmitValidators.validate(
+ project, ctx.getRevWalk().getObjectReader(), repoView.getCommands());
+ }
+ } catch (Exception e) {
+ Throwables.throwIfInstanceOf(e, RestApiException.class);
+ throw new UpdateException(e);
}
}
- protected static void logDebug(String msg) {
- // Only log if there is a requestId assigned, since those are the
- // expensive/complicated requests like MergeOp. Doing it every time would be
- // noisy.
- if (RequestId.isSet()) {
- logger.atFine().log(msg);
+ private class ChangesHandle implements AutoCloseable {
+ private final NoteDbUpdateManager manager;
+ private final boolean dryrun;
+ private final Map<Change.Id, ChangeResult> results;
+
+ ChangesHandle(NoteDbUpdateManager manager, boolean dryrun) {
+ this.manager = manager;
+ this.dryrun = dryrun;
+ results = new HashMap<>();
+ }
+
+ @Override
+ public void close() {
+ manager.close();
+ }
+
+ void setResult(Change.Id id, ChangeResult result) {
+ ChangeResult old = results.putIfAbsent(id, result);
+ checkArgument(old == null, "result for change %s already set: %s", id, old);
+ }
+
+ void execute() throws IOException {
+ BatchUpdate.this.batchRefUpdate = manager.execute(dryrun);
+ }
+
+ List<ListenableFuture<?>> startIndexFutures() {
+ if (dryrun) {
+ return ImmutableList.of();
+ }
+ logDebug("Reindexing %d changes", results.size());
+ List<ListenableFuture<?>> indexFutures = new ArrayList<>(results.size());
+ for (Map.Entry<Change.Id, ChangeResult> e : results.entrySet()) {
+ Change.Id id = e.getKey();
+ switch (e.getValue()) {
+ case UPSERTED:
+ indexFutures.add(indexer.indexAsync(project, id));
+ break;
+ case DELETED:
+ indexFutures.add(indexer.deleteAsync(id));
+ break;
+ case SKIPPED:
+ break;
+ default:
+ throw new IllegalStateException("unexpected result: " + e.getValue());
+ }
+ }
+ return indexFutures;
}
}
- protected static void logDebug(String msg, @Nullable Object arg) {
+ private ChangesHandle executeChangeOps(boolean dryrun) throws Exception {
+ logDebug("Executing change ops");
+ initRepository();
+ Repository repo = repoView.getRepository();
+ checkState(
+ repo.getRefDatabase().performsAtomicTransactions(),
+ "cannot use NoteDb with a repository that does not support atomic batch ref updates: %s",
+ repo);
+
+ ChangesHandle handle =
+ new ChangesHandle(
+ updateManagerFactory
+ .create(project)
+ .setChangeRepo(
+ repo, repoView.getRevWalk(), repoView.getInserter(), repoView.getCommands()),
+ dryrun);
+ if (user.isIdentifiedUser()) {
+ handle.manager.setRefLogIdent(user.asIdentifiedUser().newRefLogIdent(when, tz));
+ }
+ handle.manager.setRefLogMessage(refLogMessage);
+ handle.manager.setPushCertificate(pushCert);
+ for (Map.Entry<Change.Id, Collection<BatchUpdateOp>> e : ops.asMap().entrySet()) {
+ Change.Id id = e.getKey();
+ ChangeContextImpl ctx = newChangeContext(id);
+ boolean dirty = false;
+ logDebug(
+ "Applying %d ops for change %s: %s",
+ e.getValue().size(),
+ id,
+ lazy(() -> e.getValue().stream().map(op -> op.getClass().getName()).collect(toSet())));
+ for (BatchUpdateOp op : e.getValue()) {
+ dirty |= op.updateChange(ctx);
+ }
+ if (!dirty) {
+ logDebug("No ops reported dirty, short-circuiting");
+ handle.setResult(id, ChangeResult.SKIPPED);
+ continue;
+ }
+ for (ChangeUpdate u : ctx.updates.values()) {
+ handle.manager.add(u);
+ }
+ if (ctx.deleted) {
+ logDebug("Change %s was deleted", id);
+ handle.manager.deleteChange(id);
+ handle.setResult(id, ChangeResult.DELETED);
+ } else {
+ handle.setResult(id, ChangeResult.UPSERTED);
+ }
+ }
+ return handle;
+ }
+
+ private ChangeContextImpl newChangeContext(Change.Id id) {
+ logDebug("Opening change %s for update", id);
+ Change c = newChanges.get(id);
+ boolean isNew = c != null;
+ if (!isNew) {
+ // Pass a synthetic change into ChangeNotes.Factory, which will take care of checking for
+ // existence and populating columns from the parsed notes state.
+ // TODO(dborowitz): This dance made more sense when using Reviewdb; consider a nicer way.
+ c = ChangeNotes.Factory.newChange(project, id);
+ } else {
+ logDebug("Change %s is new", id);
+ }
+ ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
+ return new ChangeContextImpl(notes);
+ }
+
+ private void executePostOps() throws Exception {
+ ContextImpl ctx = new ContextImpl();
+ for (BatchUpdateOp op : ops.values()) {
+ op.postUpdate(ctx);
+ }
+
+ for (RepoOnlyOp op : repoOnlyOps) {
+ op.postUpdate(ctx);
+ }
+ }
+
+ private static void logDebug(String msg) {
// Only log if there is a requestId assigned, since those are the
// expensive/complicated requests like MergeOp. Doing it every time would be
// noisy.
if (RequestId.isSet()) {
- logger.atFine().log(msg, arg);
+ logger.atFine().log(msg);
}
}
- protected static void logDebug(String msg, @Nullable Object arg1, @Nullable Object arg2) {
+ private static void logDebug(String msg, @Nullable Object arg) {
// Only log if there is a requestId assigned, since those are the
// expensive/complicated requests like MergeOp. Doing it every time would be
// noisy.
if (RequestId.isSet()) {
- logger.atFine().log(msg, arg1, arg2);
+ logger.atFine().log(msg, arg);
}
}
- protected static void logDebug(
+ private static void logDebug(
String msg, @Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
// Only log if there is a requestId assigned, since those are the
// expensive/complicated requests like MergeOp. Doing it every time would be
diff --git a/java/com/google/gerrit/server/update/BatchUpdateListener.java b/java/com/google/gerrit/server/update/BatchUpdateListener.java
index 847a7ca188..765bba1ca7 100644
--- a/java/com/google/gerrit/server/update/BatchUpdateListener.java
+++ b/java/com/google/gerrit/server/update/BatchUpdateListener.java
@@ -19,8 +19,6 @@ package com.google.gerrit.server.update;
*
* <p>When used during execution of multiple batch updates, the {@code after*} methods are called
* after that phase has been completed for <em>all</em> updates.
- *
- * <p>Listeners are only supported for the {@link Order#REPO_BEFORE_DB} order.
*/
public interface BatchUpdateListener {
public static final BatchUpdateListener NONE = new BatchUpdateListener() {};
diff --git a/java/com/google/gerrit/server/update/BatchUpdateReviewDb.java b/java/com/google/gerrit/server/update/BatchUpdateReviewDb.java
deleted file mode 100644
index 7d99b441d4..0000000000
--- a/java/com/google/gerrit/server/update/BatchUpdateReviewDb.java
+++ /dev/null
@@ -1,104 +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.server.update;
-
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.server.ChangeAccess;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gwtorm.server.AtomicUpdate;
-
-public class BatchUpdateReviewDb extends ReviewDbWrapper {
- private final ChangeAccess changesWrapper;
-
- BatchUpdateReviewDb(ReviewDb delegate) {
- super(delegate);
- changesWrapper = new BatchUpdateChanges(delegate.changes());
- }
-
- /** @return the underlying delegate. Supports BatchUpdateReviewDb too. */
- public static ReviewDb unwrap(ReviewDb db) {
- if (db instanceof BatchUpdateReviewDb) {
- db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
- }
- return ReviewDbUtil.unwrapDb(db);
- }
-
- @Override
- public ChangeAccess changes() {
- return changesWrapper;
- }
-
- @Override
- public void commit() {
- throw new UnsupportedOperationException(
- "do not call commit; BatchUpdate always manages transactions");
- }
-
- @Override
- public void rollback() {
- throw new UnsupportedOperationException(
- "do not call rollback; BatchUpdate always manages transactions");
- }
-
- private static class BatchUpdateChanges extends ChangeAccessWrapper {
- private BatchUpdateChanges(ChangeAccess delegate) {
- super(delegate);
- }
-
- @Override
- public void insert(Iterable<Change> instances) {
- throw new UnsupportedOperationException(
- "do not call insert; change is automatically inserted");
- }
-
- @Override
- public void upsert(Iterable<Change> instances) {
- throw new UnsupportedOperationException(
- "do not call upsert; existing changes are updated automatically,"
- + " or use InsertChangeOp for insertion");
- }
-
- @Override
- public void update(Iterable<Change> instances) {
- throw new UnsupportedOperationException(
- "do not call update; change is updated automatically");
- }
-
- @Override
- public void beginTransaction(Change.Id key) {
- throw new UnsupportedOperationException("updateChange is always called within a transaction");
- }
-
- @Override
- public void deleteKeys(Iterable<Change.Id> keys) {
- throw new UnsupportedOperationException(
- "do not call deleteKeys; use ChangeContext#deleteChange()");
- }
-
- @Override
- public void delete(Iterable<Change> instances) {
- throw new UnsupportedOperationException(
- "do not call delete; use ChangeContext#deleteChange()");
- }
-
- @Override
- public Change atomicUpdate(Change.Id key, AtomicUpdate<Change> update) {
- throw new UnsupportedOperationException(
- "do not call atomicUpdate; updateChange is always called within a transaction");
- }
- }
-}
diff --git a/java/com/google/gerrit/server/update/ChangeContext.java b/java/com/google/gerrit/server/update/ChangeContext.java
index 7b5ef0eb9d..28674fcbe6 100644
--- a/java/com/google/gerrit/server/update/ChangeContext.java
+++ b/java/com/google/gerrit/server/update/ChangeContext.java
@@ -53,16 +53,6 @@ public interface ChangeContext extends Context {
ChangeNotes getNotes();
/**
- * Don't bump the value of {@link Change#getLastUpdatedOn()}.
- *
- * <p>If called, don't bump the timestamp before storing to ReviewDb. Only has an effect in
- * ReviewDb, and the only usage should be to match the behavior of NoteDb. Specifically, in NoteDb
- * the timestamp is updated if and only if the change meta graph is updated, and is not updated
- * when only drafts are modified.
- */
- void dontBumpLastUpdatedOn();
-
- /**
* Instruct {@link BatchUpdate} to delete this change.
*
* <p>If called, all other updates are ignored.
diff --git a/java/com/google/gerrit/server/update/Context.java b/java/com/google/gerrit/server/update/Context.java
index ffb039299f..8704cf03fe 100644
--- a/java/com/google/gerrit/server/update/Context.java
+++ b/java/com/google/gerrit/server/update/Context.java
@@ -17,11 +17,12 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.change.NotifyResolver;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.TimeZone;
@@ -77,16 +78,6 @@ public interface Context {
TimeZone getTimeZone();
/**
- * Get the ReviewDb database.
- *
- * <p>Callers should not manage transactions or call mutating methods on the Changes table.
- * Mutations on other tables (including other entities in the change entity group) are fine.
- *
- * @return open database instance.
- */
- ReviewDb getDb();
-
- /**
* Get the user performing the update.
*
* <p>In the current implementation, this is always an {@link IdentifiedUser} or {@link
@@ -97,11 +88,16 @@ public interface Context {
CurrentUser getUser();
/**
- * Get the order in which operations are executed in this update.
+ * Get the notification settings configured by the caller.
+ *
+ * <p>If there are multiple changes in a batch, they may have different settings. For example, WIP
+ * changes may have reduced {@code NotifyHandling} levels, and may be in a batch with non-WIP
+ * changes.
*
- * @return order of operations.
+ * @param changeId change ID
+ * @return notification settings.
*/
- Order getOrder();
+ NotifyResolver.Result getNotify(Change.Id changeId);
/**
* Get the identified user performing the update.
diff --git a/java/com/google/gerrit/server/update/NoteDbBatchUpdate.java b/java/com/google/gerrit/server/update/NoteDbBatchUpdate.java
deleted file mode 100644
index a0c425f480..0000000000
--- a/java/com/google/gerrit/server/update/NoteDbBatchUpdate.java
+++ /dev/null
@@ -1,456 +0,0 @@
-// 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.update;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.flogger.LazyArgs.lazy;
-import static java.util.Comparator.comparing;
-import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.toSet;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * {@link BatchUpdate} implementation using only NoteDb that updates code refs and meta refs in a
- * single {@link org.eclipse.jgit.lib.BatchRefUpdate}.
- *
- * <p>Used when {@code noteDb.changes.disableReviewDb=true}, at which point ReviewDb is not
- * consulted during updates.
- */
-public class NoteDbBatchUpdate extends BatchUpdate {
- public interface AssistedFactory {
- NoteDbBatchUpdate create(
- ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when);
- }
-
- static void execute(
- ImmutableList<NoteDbBatchUpdate> updates, BatchUpdateListener listener, boolean dryrun)
- throws UpdateException, RestApiException {
- if (updates.isEmpty()) {
- return;
- }
-
- try {
- @SuppressWarnings("deprecation")
- List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
- new ArrayList<>();
- List<ChangesHandle> handles = new ArrayList<>(updates.size());
- Order order = getOrder(updates, listener);
- try {
- switch (order) {
- case REPO_BEFORE_DB:
- for (NoteDbBatchUpdate u : updates) {
- u.executeUpdateRepo();
- }
- listener.afterUpdateRepos();
- for (NoteDbBatchUpdate u : updates) {
- handles.add(u.executeChangeOps(dryrun));
- }
- for (ChangesHandle h : handles) {
- h.execute();
- indexFutures.addAll(h.startIndexFutures());
- }
- listener.afterUpdateRefs();
- listener.afterUpdateChanges();
- break;
-
- case DB_BEFORE_REPO:
- // Call updateChange for each op before updateRepo, but defer executing the
- // NoteDbUpdateManager until after calling updateRepo. They share an inserter and
- // BatchRefUpdate, so it will all execute as a single batch. But we have to let
- // NoteDbUpdateManager actually execute the update, since it has to interleave it
- // properly with All-Users updates.
- //
- // TODO(dborowitz): This may still result in multiple updates to All-Users, but that's
- // currently not a big deal because multi-change batches generally aren't affecting
- // drafts anyway.
- for (NoteDbBatchUpdate u : updates) {
- handles.add(u.executeChangeOps(dryrun));
- }
- for (NoteDbBatchUpdate u : updates) {
- u.executeUpdateRepo();
- }
- for (ChangesHandle h : handles) {
- // TODO(dborowitz): This isn't quite good enough: in theory updateRepo may want to
- // see the results of change meta commands, but they aren't actually added to the
- // BatchUpdate until the body of execute. To fix this, execute needs to be split up
- // into a method that returns a BatchRefUpdate before execution. Not a big deal at the
- // moment, because this order is only used for deleting changes, and those updateRepo
- // implementations definitely don't need to observe the updated change meta refs.
- h.execute();
- indexFutures.addAll(h.startIndexFutures());
- }
- break;
- default:
- throw new IllegalStateException("invalid execution order: " + order);
- }
- } finally {
- for (ChangesHandle h : handles) {
- h.close();
- }
- }
-
- ChangeIndexer.allAsList(indexFutures).get();
-
- // Fire ref update events only after all mutations are finished, since callers may assume a
- // patch set ref being created means the change was created, or a branch advancing meaning
- // some changes were closed.
- updates.stream()
- .filter(u -> u.batchRefUpdate != null)
- .forEach(
- u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
-
- if (!dryrun) {
- for (NoteDbBatchUpdate u : updates) {
- u.executePostOps();
- }
- }
- } catch (Exception e) {
- wrapAndThrowException(e);
- }
- }
-
- class ContextImpl implements Context {
- @Override
- public RepoView getRepoView() throws IOException {
- return NoteDbBatchUpdate.this.getRepoView();
- }
-
- @Override
- public RevWalk getRevWalk() throws IOException {
- return getRepoView().getRevWalk();
- }
-
- @Override
- public Project.NameKey getProject() {
- return project;
- }
-
- @Override
- public Timestamp getWhen() {
- return when;
- }
-
- @Override
- public TimeZone getTimeZone() {
- return tz;
- }
-
- @Override
- public ReviewDb getDb() {
- return db;
- }
-
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Order getOrder() {
- return order;
- }
- }
-
- private class RepoContextImpl extends ContextImpl implements RepoContext {
- @Override
- public ObjectInserter getInserter() throws IOException {
- return getRepoView().getInserterWrapper();
- }
-
- @Override
- public void addRefUpdate(ReceiveCommand cmd) throws IOException {
- getRepoView().getCommands().add(cmd);
- }
- }
-
- private class ChangeContextImpl extends ContextImpl implements ChangeContext {
- private final ChangeNotes notes;
- private final Map<PatchSet.Id, ChangeUpdate> updates;
-
- private boolean deleted;
-
- protected ChangeContextImpl(ChangeNotes notes) {
- this.notes = requireNonNull(notes);
- updates = new TreeMap<>(comparing(PatchSet.Id::get));
- }
-
- @Override
- public ChangeUpdate getUpdate(PatchSet.Id psId) {
- ChangeUpdate u = updates.get(psId);
- if (u == null) {
- u = changeUpdateFactory.create(notes, user, when);
- if (newChanges.containsKey(notes.getChangeId())) {
- u.setAllowWriteToNewRef(true);
- }
- u.setPatchSetId(psId);
- updates.put(psId, u);
- }
- return u;
- }
-
- @Override
- public ChangeNotes getNotes() {
- return notes;
- }
-
- @Override
- public void dontBumpLastUpdatedOn() {
- // Do nothing; NoteDb effectively updates timestamp if and only if a commit was written to the
- // change meta ref.
- }
-
- @Override
- public void deleteChange() {
- deleted = true;
- }
- }
-
- /** Per-change result status from {@link #executeChangeOps}. */
- private enum ChangeResult {
- SKIPPED,
- UPSERTED,
- DELETED;
- }
-
- private final ChangeNotes.Factory changeNotesFactory;
- private final ChangeUpdate.Factory changeUpdateFactory;
- private final NoteDbUpdateManager.Factory updateManagerFactory;
- private final ChangeIndexer indexer;
- private final GitReferenceUpdated gitRefUpdated;
- private final ReviewDb db;
-
- @Inject
- NoteDbBatchUpdate(
- GitRepositoryManager repoManager,
- @GerritPersonIdent PersonIdent serverIdent,
- ChangeNotes.Factory changeNotesFactory,
- ChangeUpdate.Factory changeUpdateFactory,
- NoteDbUpdateManager.Factory updateManagerFactory,
- ChangeIndexer indexer,
- GitReferenceUpdated gitRefUpdated,
- @Assisted ReviewDb db,
- @Assisted Project.NameKey project,
- @Assisted CurrentUser user,
- @Assisted Timestamp when) {
- super(repoManager, serverIdent, project, user, when);
- this.changeNotesFactory = changeNotesFactory;
- this.changeUpdateFactory = changeUpdateFactory;
- this.updateManagerFactory = updateManagerFactory;
- this.indexer = indexer;
- this.gitRefUpdated = gitRefUpdated;
- this.db = db;
- }
-
- @Override
- public void execute(BatchUpdateListener listener) throws UpdateException, RestApiException {
- execute(ImmutableList.of(this), listener, false);
- }
-
- @Override
- protected Context newContext() {
- return new ContextImpl();
- }
-
- private void executeUpdateRepo() throws UpdateException, RestApiException {
- try {
- logDebug("Executing updateRepo on %d ops", ops.size());
- RepoContextImpl ctx = new RepoContextImpl();
- for (BatchUpdateOp op : ops.values()) {
- op.updateRepo(ctx);
- }
-
- logDebug("Executing updateRepo on %d RepoOnlyOps", repoOnlyOps.size());
- for (RepoOnlyOp op : repoOnlyOps) {
- op.updateRepo(ctx);
- }
-
- if (onSubmitValidators != null && !getRefUpdates().isEmpty()) {
- // Validation of refs has to take place here and not at the beginning of executeRefUpdates.
- // Otherwise, failing validation in a second BatchUpdate object will happen *after* the
- // first update's executeRefUpdates has finished, hence after first repo's refs have been
- // updated, which is too late.
- onSubmitValidators.validate(
- project, ctx.getRevWalk().getObjectReader(), repoView.getCommands());
- }
- } catch (Exception e) {
- Throwables.throwIfInstanceOf(e, RestApiException.class);
- throw new UpdateException(e);
- }
- }
-
- private class ChangesHandle implements AutoCloseable {
- private final NoteDbUpdateManager manager;
- private final boolean dryrun;
- private final Map<Change.Id, ChangeResult> results;
-
- ChangesHandle(NoteDbUpdateManager manager, boolean dryrun) {
- this.manager = manager;
- this.dryrun = dryrun;
- results = new HashMap<>();
- }
-
- @Override
- public void close() {
- manager.close();
- }
-
- void setResult(Change.Id id, ChangeResult result) {
- ChangeResult old = results.putIfAbsent(id, result);
- checkArgument(old == null, "result for change %s already set: %s", id, old);
- }
-
- void execute() throws OrmException, IOException {
- NoteDbBatchUpdate.this.batchRefUpdate = manager.execute(dryrun);
- }
-
- @SuppressWarnings("deprecation")
- List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> startIndexFutures() {
- if (dryrun) {
- return ImmutableList.of();
- }
- logDebug("Reindexing %d changes", results.size());
- List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
- new ArrayList<>(results.size());
- for (Map.Entry<Change.Id, ChangeResult> e : results.entrySet()) {
- Change.Id id = e.getKey();
- switch (e.getValue()) {
- case UPSERTED:
- indexFutures.add(indexer.indexAsync(project, id));
- break;
- case DELETED:
- indexFutures.add(indexer.deleteAsync(id));
- break;
- case SKIPPED:
- break;
- default:
- throw new IllegalStateException("unexpected result: " + e.getValue());
- }
- }
- return indexFutures;
- }
- }
-
- private ChangesHandle executeChangeOps(boolean dryrun) throws Exception {
- logDebug("Executing change ops");
- initRepository();
- Repository repo = repoView.getRepository();
- checkState(
- repo.getRefDatabase().performsAtomicTransactions(),
- "cannot use NoteDb with a repository that does not support atomic batch ref updates: %s",
- repo);
-
- ChangesHandle handle =
- new ChangesHandle(
- updateManagerFactory
- .create(project)
- .setChangeRepo(
- repo, repoView.getRevWalk(), repoView.getInserter(), repoView.getCommands()),
- dryrun);
- if (user.isIdentifiedUser()) {
- handle.manager.setRefLogIdent(user.asIdentifiedUser().newRefLogIdent(when, tz));
- }
- handle.manager.setRefLogMessage(refLogMessage);
- handle.manager.setPushCertificate(pushCert);
- for (Map.Entry<Change.Id, Collection<BatchUpdateOp>> e : ops.asMap().entrySet()) {
- Change.Id id = e.getKey();
- ChangeContextImpl ctx = newChangeContext(id);
- boolean dirty = false;
- logDebug(
- "Applying %d ops for change %s: %s",
- e.getValue().size(),
- id,
- lazy(() -> e.getValue().stream().map(op -> op.getClass().getName()).collect(toSet())));
- for (BatchUpdateOp op : e.getValue()) {
- dirty |= op.updateChange(ctx);
- }
- if (!dirty) {
- logDebug("No ops reported dirty, short-circuiting");
- handle.setResult(id, ChangeResult.SKIPPED);
- continue;
- }
- for (ChangeUpdate u : ctx.updates.values()) {
- handle.manager.add(u);
- }
- if (ctx.deleted) {
- logDebug("Change %s was deleted", id);
- handle.manager.deleteChange(id);
- handle.setResult(id, ChangeResult.DELETED);
- } else {
- handle.setResult(id, ChangeResult.UPSERTED);
- }
- }
- return handle;
- }
-
- private ChangeContextImpl newChangeContext(Change.Id id) throws OrmException {
- logDebug("Opening change %s for update", id);
- Change c = newChanges.get(id);
- boolean isNew = c != null;
- if (!isNew) {
- // Pass a synthetic change into ChangeNotes.Factory, which will take care of checking for
- // existence and populating columns from the parsed notes state.
- // TODO(dborowitz): This dance made more sense when using Reviewdb; consider a nicer way.
- c = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
- } else {
- logDebug("Change %s is new", id);
- }
- ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
- return new ChangeContextImpl(notes);
- }
-
- private void executePostOps() throws Exception {
- ContextImpl ctx = new ContextImpl();
- for (BatchUpdateOp op : ops.values()) {
- op.postUpdate(ctx);
- }
-
- for (RepoOnlyOp op : repoOnlyOps) {
- op.postUpdate(ctx);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/update/Order.java b/java/com/google/gerrit/server/update/Order.java
deleted file mode 100644
index fb64b7ba0c..0000000000
--- a/java/com/google/gerrit/server/update/Order.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.update;
-
-/** Order of execution of the various phases of a {@link BatchUpdate}. */
-public enum Order {
- /**
- * Update the repository and execute all ref updates before touching the database.
- *
- * <p>The default and most common, as Gerrit does not behave well when a patch set has no
- * corresponding ref in the repo.
- */
- REPO_BEFORE_DB,
-
- /**
- * Update the database before touching the repository.
- *
- * <p>Generally only used when deleting patch sets, which should be deleted first from the
- * database (for the same reason as above.)
- */
- DB_BEFORE_REPO;
-}
diff --git a/java/com/google/gerrit/server/update/RefUpdateUtil.java b/java/com/google/gerrit/server/update/RefUpdateUtil.java
deleted file mode 100644
index 71eef9e38b..0000000000
--- a/java/com/google/gerrit/server/update/RefUpdateUtil.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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.update;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.server.git.LockFailureException;
-import java.io.IOException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/** Static utilities for working with JGit's ref update APIs. */
-public class RefUpdateUtil {
- /**
- * Execute a batch ref update, throwing a checked exception if not all updates succeeded.
- *
- * <p>Creates a new {@link RevWalk} used only for this operation.
- *
- * @param bru batch update; should already have been executed.
- * @param repo repository that created {@code bru}.
- * @throws LockFailureException if the transaction was aborted due to lock failure; see {@link
- * #checkResults(BatchRefUpdate)} for details.
- * @throws IOException if any result was not {@code OK}.
- */
- public static void executeChecked(BatchRefUpdate bru, Repository repo) throws IOException {
- try (RevWalk rw = new RevWalk(repo)) {
- executeChecked(bru, rw);
- }
- }
-
- /**
- * Execute a batch ref update, throwing a checked exception if not all updates succeeded.
- *
- * @param bru batch update; should already have been executed.
- * @param rw walk for executing the update.
- * @throws LockFailureException if the transaction was aborted due to lock failure; see {@link
- * #checkResults(BatchRefUpdate)} for details.
- * @throws IOException if any result was not {@code OK}.
- */
- public static void executeChecked(BatchRefUpdate bru, RevWalk rw) throws IOException {
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- checkResults(bru);
- }
-
- /**
- * Check results of all commands in the update batch, reducing to a single exception if there was
- * a failure.
- *
- * <p>Throws {@link LockFailureException} if at least one command failed with {@code
- * LOCK_FAILURE}, and the entire transaction was aborted, i.e. any non-{@code LOCK_FAILURE}
- * results, if there were any, failed with "transaction aborted".
- *
- * <p>In particular, if the underlying ref database does not {@link
- * org.eclipse.jgit.lib.RefDatabase#performsAtomicTransactions() perform atomic transactions},
- * then a combination of {@code LOCK_FAILURE} on one ref and {@code OK} or another result on other
- * refs will <em>not</em> throw {@code LockFailureException}.
- *
- * @param bru batch update; should already have been executed.
- * @throws LockFailureException if the transaction was aborted due to lock failure.
- * @throws IOException if any result was not {@code OK}.
- */
- @VisibleForTesting
- static void checkResults(BatchRefUpdate bru) throws IOException {
- if (bru.getCommands().isEmpty()) {
- return;
- }
-
- int lockFailure = 0;
- int aborted = 0;
- int failure = 0;
-
- for (ReceiveCommand cmd : bru.getCommands()) {
- if (cmd.getResult() != ReceiveCommand.Result.OK) {
- failure++;
- }
- if (cmd.getResult() == ReceiveCommand.Result.LOCK_FAILURE) {
- lockFailure++;
- } else if (cmd.getResult() == ReceiveCommand.Result.REJECTED_OTHER_REASON
- && JGitText.get().transactionAborted.equals(cmd.getMessage())) {
- aborted++;
- }
- }
-
- 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);
- }
- }
-
- /**
- * Delete a single ref, throwing a checked exception on failure.
- *
- * <p>Does not require that the ref have any particular old value. Succeeds as a no-op if the ref
- * did not exist.
- *
- * @param repo repository.
- * @param refName ref name to delete.
- * @throws LockFailureException if a low-level lock failure (e.g. compare-and-swap failure)
- * occurs.
- * @throws IOException if an error occurred.
- */
- public static void deleteChecked(Repository repo, String refName) throws IOException {
- RefUpdate ru = repo.updateRef(refName);
- ru.setForceUpdate(true);
- ru.setCheckConflicting(false);
- switch (ru.delete()) {
- case FORCED:
- // Ref was deleted.
- return;
-
- case NEW:
- // Ref didn't exist (yes, really).
- return;
-
- case LOCK_FAILURE:
- throw new LockFailureException("Failed to delete " + refName + ": " + ru.getResult(), ru);
-
- // Not really failures, but should not be the result of a deletion, so the best option is to
- // throw.
- case NO_CHANGE:
- case FAST_FORWARD:
- case RENAMED:
- case NOT_ATTEMPTED:
-
- case IO_FAILURE:
- case REJECTED:
- case REJECTED_CURRENT_BRANCH:
- case REJECTED_MISSING_OBJECT:
- case REJECTED_OTHER_REASON:
- default:
- throw new IOException("Failed to delete " + refName + ": " + ru.getResult());
- }
- }
-
- private RefUpdateUtil() {}
-}
diff --git a/java/com/google/gerrit/server/update/RepoView.java b/java/com/google/gerrit/server/update/RepoView.java
index 9ce842f792..73dd12f35e 100644
--- a/java/com/google/gerrit/server/update/RepoView.java
+++ b/java/com/google/gerrit/server/update/RepoView.java
@@ -40,8 +40,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
*
* <p>Second, the read methods take into account any pending operations on the repository that
* implementations have staged using the write methods on {@link RepoContext}. Callers do not have
- * to worry about whether operations have been performed yet, and the implementation details may
- * differ between ReviewDb and NoteDb, but callers just don't need to care.
+ * to worry about whether operations have been performed yet.
*/
public class RepoView {
private final Repository repo;
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index 10e3455b6e..ae8ba53725 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -34,14 +34,13 @@ import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
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.Description;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.LockFailureException;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.time.Duration;
@@ -69,7 +68,8 @@ public class RetryHelper {
ACCOUNT_UPDATE,
CHANGE_UPDATE,
GROUP_UPDATE,
- INDEX_QUERY
+ INDEX_QUERY,
+ PLUGIN_UPDATE
}
/**
@@ -141,7 +141,6 @@ public class RetryHelper {
return options().build();
}
- private final NotesMigration migration;
private final Metrics metrics;
private final BatchUpdate.Factory updateFactory;
private final Map<ActionType, Duration> defaultTimeouts;
@@ -149,27 +148,18 @@ public class RetryHelper {
@Nullable private final Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup;
@Inject
- RetryHelper(
- @GerritServerConfig Config cfg,
- Metrics metrics,
- NotesMigration migration,
- ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
- NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory) {
- this(cfg, metrics, migration, reviewDbBatchUpdateFactory, noteDbBatchUpdateFactory, null);
+ RetryHelper(@GerritServerConfig Config cfg, Metrics metrics, BatchUpdate.Factory updateFactory) {
+ this(cfg, metrics, updateFactory, null);
}
@VisibleForTesting
public RetryHelper(
@GerritServerConfig Config cfg,
Metrics metrics,
- NotesMigration migration,
- ReviewDbBatchUpdate.AssistedFactory reviewDbBatchUpdateFactory,
- NoteDbBatchUpdate.AssistedFactory noteDbBatchUpdateFactory,
+ BatchUpdate.Factory updateFactory,
@Nullable Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup) {
this.metrics = metrics;
- this.migration = migration;
- this.updateFactory =
- new BatchUpdate.Factory(migration, reviewDbBatchUpdateFactory, noteDbBatchUpdateFactory);
+ this.updateFactory = updateFactory;
Duration defaultTimeout =
Duration.ofMillis(
@@ -229,16 +219,6 @@ public class RetryHelper {
public <T> T execute(ChangeAction<T> changeAction, Options opts)
throws RestApiException, UpdateException {
try {
- if (!migration.disableChangeReviewDb()) {
- // Either we aren't full-NoteDb, or the underlying ref storage doesn't support atomic
- // transactions. Either way, retrying a partially-failed operation is not idempotent, so
- // don't do it automatically. Let the end user decide whether they want to retry.
- return executeWithTimeoutCount(
- ActionType.CHANGE_UPDATE,
- () -> changeAction.call(updateFactory),
- RetryerBuilder.<T>newBuilder().build());
- }
-
return execute(
ActionType.CHANGE_UPDATE,
() -> changeAction.call(updateFactory),
diff --git a/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java b/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
deleted file mode 100644
index f14e9997f0..0000000000
--- a/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
+++ /dev/null
@@ -1,844 +0,0 @@
-// Copyright (C) 2015 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 static com.google.common.base.Preconditions.checkState;
-import static java.util.Comparator.comparing;
-import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-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.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.RestApiException;
-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.Timer1;
-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.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.ChangeUpdateExecutor;
-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.git.InsertedObject;
-import com.google.gerrit.server.git.LockFailureException;
-import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.logging.TraceContext;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager.MismatchedStateException;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.ReceiveCommand;
-
-/**
- * {@link BatchUpdate} implementation that supports mixed ReviewDb/NoteDb operations, depending on
- * the migration state specified in {@link NotesMigration}.
- *
- * <p>When performing change updates in a mixed ReviewDb/NoteDb environment with ReviewDb primary,
- * the order of operations is very subtle:
- *
- * <ol>
- * <li>Stage NoteDb updates to get the new NoteDb state, but do not write to the repo.
- * <li>Write the new state in the Change entity, and commit this to ReviewDb.
- * <li>Update NoteDb, ignoring any write failures.
- * </ol>
- *
- * The implementation in this class is well-tested, and it is strongly recommended that you not
- * attempt to reimplement this logic. Use {@code BatchUpdate} if at all possible.
- */
-public class ReviewDbBatchUpdate extends BatchUpdate {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- public interface AssistedFactory {
- ReviewDbBatchUpdate create(
- ReviewDb db, Project.NameKey project, CurrentUser user, Timestamp when);
- }
-
- class ContextImpl implements Context {
- @Override
- public RepoView getRepoView() throws IOException {
- return ReviewDbBatchUpdate.this.getRepoView();
- }
-
- @Override
- public RevWalk getRevWalk() throws IOException {
- return getRepoView().getRevWalk();
- }
-
- @Override
- public Project.NameKey getProject() {
- return project;
- }
-
- @Override
- public Timestamp getWhen() {
- return when;
- }
-
- @Override
- public TimeZone getTimeZone() {
- return tz;
- }
-
- @Override
- public ReviewDb getDb() {
- return db;
- }
-
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Order getOrder() {
- return order;
- }
- }
-
- private class RepoContextImpl extends ContextImpl implements RepoContext {
- @Override
- public ObjectInserter getInserter() throws IOException {
- return getRepoView().getInserterWrapper();
- }
-
- @Override
- public void addRefUpdate(ReceiveCommand cmd) throws IOException {
- initRepository();
- repoView.getCommands().add(cmd);
- }
- }
-
- private class ChangeContextImpl extends ContextImpl implements ChangeContext {
- private final ChangeNotes notes;
- private final Map<PatchSet.Id, ChangeUpdate> updates;
- private final ReviewDbWrapper dbWrapper;
- private final Repository threadLocalRepo;
- private final RevWalk threadLocalRevWalk;
-
- private boolean deleted;
- private boolean bumpLastUpdatedOn = true;
-
- protected ChangeContextImpl(
- ChangeNotes notes, ReviewDbWrapper dbWrapper, Repository repo, RevWalk rw) {
- this.notes = requireNonNull(notes);
- this.dbWrapper = dbWrapper;
- this.threadLocalRepo = repo;
- this.threadLocalRevWalk = rw;
- updates = new TreeMap<>(comparing(PatchSet.Id::get));
- }
-
- @Override
- public ReviewDb getDb() {
- requireNonNull(dbWrapper);
- return dbWrapper;
- }
-
- @Override
- public RevWalk getRevWalk() {
- return threadLocalRevWalk;
- }
-
- @Override
- public ChangeUpdate getUpdate(PatchSet.Id psId) {
- ChangeUpdate u = updates.get(psId);
- if (u == null) {
- u = changeUpdateFactory.create(notes, user, when);
- if (newChanges.containsKey(notes.getChangeId())) {
- u.setAllowWriteToNewRef(true);
- }
- u.setPatchSetId(psId);
- updates.put(psId, u);
- }
- return u;
- }
-
- @Override
- public ChangeNotes getNotes() {
- return notes;
- }
-
- @Override
- public void dontBumpLastUpdatedOn() {
- bumpLastUpdatedOn = false;
- }
-
- @Override
- public void deleteChange() {
- deleted = true;
- }
- }
-
- @Singleton
- private static class Metrics {
- final Timer1<Boolean> executeChangeOpsLatency;
-
- @Inject
- Metrics(MetricMaker metricMaker) {
- executeChangeOpsLatency =
- metricMaker.newTimer(
- "batch_update/execute_change_ops",
- new Description("BatchUpdate change update latency, excluding reindexing")
- .setCumulative()
- .setUnit(Units.MILLISECONDS),
- Field.ofBoolean("success"));
- }
- }
-
- static void execute(
- ImmutableList<ReviewDbBatchUpdate> updates, BatchUpdateListener listener, boolean dryrun)
- throws UpdateException, RestApiException {
- if (updates.isEmpty()) {
- return;
- }
- try {
- Order order = getOrder(updates, listener);
- boolean updateChangesInParallel = getUpdateChangesInParallel(updates);
- switch (order) {
- case REPO_BEFORE_DB:
- for (ReviewDbBatchUpdate u : updates) {
- u.executeUpdateRepo();
- }
- listener.afterUpdateRepos();
- for (ReviewDbBatchUpdate u : updates) {
- u.executeRefUpdates(dryrun);
- }
- listener.afterUpdateRefs();
- for (ReviewDbBatchUpdate u : updates) {
- u.reindexChanges(u.executeChangeOps(updateChangesInParallel, dryrun));
- }
- listener.afterUpdateChanges();
- break;
- case DB_BEFORE_REPO:
- for (ReviewDbBatchUpdate u : updates) {
- u.reindexChanges(u.executeChangeOps(updateChangesInParallel, dryrun));
- }
- for (ReviewDbBatchUpdate u : updates) {
- u.executeUpdateRepo();
- }
- for (ReviewDbBatchUpdate u : updates) {
- u.executeRefUpdates(dryrun);
- }
- break;
- default:
- throw new IllegalStateException("invalid execution order: " + order);
- }
-
- ChangeIndexer.allAsList(
- updates.stream().flatMap(u -> u.indexFutures.stream()).collect(toList()))
- .get();
-
- // Fire ref update events only after all mutations are finished, since callers may assume a
- // patch set ref being created means the change was created, or a branch advancing meaning
- // some changes were closed.
- updates.stream()
- .filter(u -> u.batchRefUpdate != null)
- .forEach(
- u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
-
- if (!dryrun) {
- for (ReviewDbBatchUpdate u : updates) {
- u.executePostOps();
- }
- }
- } catch (Exception e) {
- wrapAndThrowException(e);
- }
- }
-
- private final AllUsersName allUsers;
- private final ChangeIndexer indexer;
- private final ChangeNotes.Factory changeNotesFactory;
- private final ChangeUpdate.Factory changeUpdateFactory;
- private final GitReferenceUpdated gitRefUpdated;
- private final ListeningExecutorService changeUpdateExecutor;
- private final Metrics metrics;
- private final NoteDbUpdateManager.Factory updateManagerFactory;
- private final NotesMigration notesMigration;
- private final ReviewDb db;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final long skewMs;
-
- @SuppressWarnings("deprecation")
- private final List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
- new ArrayList<>();
-
- @Inject
- ReviewDbBatchUpdate(
- @GerritServerConfig Config cfg,
- AllUsersName allUsers,
- ChangeIndexer indexer,
- ChangeNotes.Factory changeNotesFactory,
- @ChangeUpdateExecutor ListeningExecutorService changeUpdateExecutor,
- ChangeUpdate.Factory changeUpdateFactory,
- @GerritPersonIdent PersonIdent serverIdent,
- GitReferenceUpdated gitRefUpdated,
- GitRepositoryManager repoManager,
- Metrics metrics,
- NoteDbUpdateManager.Factory updateManagerFactory,
- NotesMigration notesMigration,
- SchemaFactory<ReviewDb> schemaFactory,
- @Assisted ReviewDb db,
- @Assisted Project.NameKey project,
- @Assisted CurrentUser user,
- @Assisted Timestamp when) {
- super(repoManager, serverIdent, project, user, when);
- this.allUsers = allUsers;
- this.changeNotesFactory = changeNotesFactory;
- this.changeUpdateExecutor = changeUpdateExecutor;
- this.changeUpdateFactory = changeUpdateFactory;
- this.gitRefUpdated = gitRefUpdated;
- this.indexer = indexer;
- this.metrics = metrics;
- this.notesMigration = notesMigration;
- this.schemaFactory = schemaFactory;
- this.updateManagerFactory = updateManagerFactory;
- this.db = db;
- skewMs = NoteDbChangeState.getReadOnlySkew(cfg);
- }
-
- @Override
- public void execute(BatchUpdateListener listener) throws UpdateException, RestApiException {
- execute(ImmutableList.of(this), listener, false);
- }
-
- @Override
- protected Context newContext() {
- return new ContextImpl();
- }
-
- private void executeUpdateRepo() throws UpdateException, RestApiException {
- try {
- logDebug("Executing updateRepo on %d ops", ops.size());
- RepoContextImpl ctx = new RepoContextImpl();
- for (BatchUpdateOp op : ops.values()) {
- op.updateRepo(ctx);
- }
-
- logDebug("Executing updateRepo on %d RepoOnlyOps", repoOnlyOps.size());
- for (RepoOnlyOp op : repoOnlyOps) {
- op.updateRepo(ctx);
- }
-
- if (onSubmitValidators != null && !getRefUpdates().isEmpty()) {
- // Validation of refs has to take place here and not at the beginning of executeRefUpdates.
- // Otherwise, failing validation in a second BatchUpdate object will happen *after* the
- // first update's executeRefUpdates has finished, hence after first repo's refs have been
- // updated, which is too late.
- onSubmitValidators.validate(
- project, ctx.getRevWalk().getObjectReader(), repoView.getCommands());
- }
-
- if (repoView != null) {
- logDebug("Flushing inserter");
- repoView.getInserter().flush();
- } else {
- logDebug("No objects to flush");
- }
- } catch (Exception e) {
- Throwables.throwIfInstanceOf(e, RestApiException.class);
- throw new UpdateException(e);
- }
- }
-
- private void executeRefUpdates(boolean dryrun) throws IOException, RestApiException {
- if (getRefUpdates().isEmpty()) {
- logDebug("No ref updates to execute");
- return;
- }
- // May not be opened if the caller added ref updates but no new objects.
- // TODO(dborowitz): Really?
- initRepository();
- batchRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
- batchRefUpdate.setPushCertificate(pushCert);
- batchRefUpdate.setRefLogMessage(refLogMessage, true);
- batchRefUpdate.setAllowNonFastForwards(true);
- repoView.getCommands().addTo(batchRefUpdate);
- if (user.isIdentifiedUser()) {
- batchRefUpdate.setRefLogIdent(user.asIdentifiedUser().newRefLogIdent(when, tz));
- }
- logDebug("Executing batch of %d ref updates", batchRefUpdate.getCommands().size());
- if (dryrun) {
- return;
- }
-
- // Force BatchRefUpdate to read newly referenced objects using a new RevWalk, rather than one
- // that might have access to unflushed objects.
- try (RevWalk updateRw = new RevWalk(repoView.getRepository())) {
- batchRefUpdate.execute(updateRw, NullProgressMonitor.INSTANCE);
- }
- boolean ok = true;
- for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
- if (cmd.getResult() != ReceiveCommand.Result.OK) {
- ok = false;
- break;
- }
- }
- if (!ok) {
- throw new RestApiException("BatchRefUpdate failed: " + batchRefUpdate);
- }
- }
-
- private List<ChangeTask> executeChangeOps(boolean parallel, boolean dryrun)
- throws UpdateException, RestApiException {
- List<ChangeTask> tasks;
- boolean success = false;
- Stopwatch sw = Stopwatch.createStarted();
- try {
- logDebug("Executing change ops (parallel? %s)", parallel);
- ListeningExecutorService executor =
- parallel ? changeUpdateExecutor : MoreExecutors.newDirectExecutorService();
-
- tasks = new ArrayList<>(ops.keySet().size());
- try {
- if (notesMigration.commitChangeWrites() && repoView != null) {
- // A NoteDb change may have been rebuilt since the repo was originally
- // opened, so make sure we see that.
- logDebug("Preemptively scanning for repo changes");
- repoView.getRepository().scanForRepoChanges();
- }
- if (!ops.isEmpty() && notesMigration.failChangeWrites()) {
- // Fail fast before attempting any writes if changes are read-only, as
- // this is a programmer error.
- logDebug("Failing early due to read-only Changes table");
- throw new OrmException(NoteDbUpdateManager.CHANGES_READ_ONLY);
- }
- List<ListenableFuture<?>> futures = new ArrayList<>(ops.keySet().size());
- for (Map.Entry<Change.Id, Collection<BatchUpdateOp>> e : ops.asMap().entrySet()) {
- ChangeTask task =
- new ChangeTask(e.getKey(), e.getValue(), Thread.currentThread(), dryrun);
- tasks.add(task);
- if (!parallel) {
- logDebug("Direct execution of task for ops: %s", ops);
- }
- futures.add(executor.submit(task));
- }
- if (parallel) {
- logDebug(
- "Waiting on futures for %d ops spanning %d changes", ops.size(), ops.keySet().size());
- }
- Futures.allAsList(futures).get();
-
- if (notesMigration.commitChangeWrites()) {
- if (!dryrun) {
- executeNoteDbUpdates(tasks);
- }
- }
- success = true;
- } catch (ExecutionException | InterruptedException e) {
- Throwables.throwIfInstanceOf(e.getCause(), UpdateException.class);
- Throwables.throwIfInstanceOf(e.getCause(), RestApiException.class);
- throw new UpdateException(e);
- } catch (OrmException | IOException e) {
- throw new UpdateException(e);
- }
- } finally {
- metrics.executeChangeOpsLatency.record(success, sw.elapsed(NANOSECONDS), NANOSECONDS);
- }
- return tasks;
- }
-
- private void reindexChanges(List<ChangeTask> tasks) {
- // Reindex changes.
- for (ChangeTask task : tasks) {
- if (task.deleted) {
- indexFutures.add(indexer.deleteAsync(task.id));
- } else if (task.dirty) {
- indexFutures.add(indexer.indexAsync(project, task.id));
- }
- }
- }
-
- private void executeNoteDbUpdates(List<ChangeTask> tasks)
- throws ResourceConflictException, IOException {
- // Aggregate together all NoteDb ref updates from the ops we executed,
- // possibly in parallel. Each task had its own NoteDbUpdateManager instance
- // with its own thread-local copy of the repo(s), but each of those was just
- // used for staging updates and was never executed.
- //
- // Use a new BatchRefUpdate as the original batchRefUpdate field is intended
- // for use only by the updateRepo phase.
- //
- // See the comments in NoteDbUpdateManager#execute() for why we execute the
- // updates on the change repo first.
- logDebug("Executing NoteDb updates for %d changes", tasks.size());
- try {
- initRepository();
- BatchRefUpdate changeRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
- boolean hasAllUsersCommands = false;
- try (ObjectInserter ins = repoView.getRepository().newObjectInserter()) {
- int objs = 0;
- for (ChangeTask task : tasks) {
- if (task.noteDbResult == null) {
- logDebug("No-op update to %s", task.id);
- continue;
- }
- for (ReceiveCommand cmd : task.noteDbResult.changeCommands()) {
- changeRefUpdate.addCommand(cmd);
- }
- for (InsertedObject obj : task.noteDbResult.changeObjects()) {
- objs++;
- ins.insert(obj.type(), obj.data().toByteArray());
- }
- hasAllUsersCommands |= !task.noteDbResult.allUsersCommands().isEmpty();
- }
- logDebug(
- "Collected %d objects and %d ref updates to change repo",
- objs, changeRefUpdate.getCommands().size());
- executeNoteDbUpdate(getRevWalk(), ins, changeRefUpdate);
- }
-
- if (hasAllUsersCommands) {
- try (Repository allUsersRepo = repoManager.openRepository(allUsers);
- RevWalk allUsersRw = new RevWalk(allUsersRepo);
- ObjectInserter allUsersIns = allUsersRepo.newObjectInserter()) {
- int objs = 0;
- BatchRefUpdate allUsersRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
- for (ChangeTask task : tasks) {
- if (task.noteDbResult == null) {
- continue;
- }
- for (ReceiveCommand cmd : task.noteDbResult.allUsersCommands()) {
- allUsersRefUpdate.addCommand(cmd);
- }
- for (InsertedObject obj : task.noteDbResult.allUsersObjects()) {
- allUsersIns.insert(obj.type(), obj.data().toByteArray());
- }
- }
- logDebug(
- "Collected %d objects and %d ref updates to All-Users",
- objs, allUsersRefUpdate.getCommands().size());
- executeNoteDbUpdate(allUsersRw, allUsersIns, allUsersRefUpdate);
- }
- } else {
- logDebug("No All-Users updates");
- }
- } catch (IOException e) {
- if (tasks.stream().allMatch(t -> t.storage == PrimaryStorage.REVIEW_DB)) {
- // Ignore all errors trying to update NoteDb at this point. We've already written the
- // NoteDbChangeStates to ReviewDb, which means if any state is out of date it will be
- // rebuilt the next time it is needed.
- //
- // Always log even without RequestId.
- logger.atFine().withCause(e).log("Ignoring NoteDb update error after ReviewDb write");
-
- // Otherwise, we can't prove it's safe to ignore the error, either because some change had
- // NOTE_DB primary, or a task failed before determining the primary storage.
- } else if (e instanceof LockFailureException) {
- // LOCK_FAILURE is a special case indicating there was a conflicting write to a meta ref,
- // although it happened too late for us to produce anything but a generic error message.
- throw new ResourceConflictException("Updating change failed due to conflicting write", e);
- }
- throw e;
- }
- }
-
- private void executeNoteDbUpdate(RevWalk rw, ObjectInserter ins, BatchRefUpdate bru)
- throws IOException {
- if (bru.getCommands().isEmpty()) {
- logDebug("No commands, skipping flush and ref update");
- return;
- }
- ins.flush();
- bru.setAllowNonFastForwards(true);
- bru.execute(rw, NullProgressMonitor.INSTANCE);
- for (ReceiveCommand cmd : bru.getCommands()) {
- // TODO(dborowitz): LOCK_FAILURE for NoteDb primary should be retried.
- if (cmd.getResult() != ReceiveCommand.Result.OK) {
- throw new IOException("Update failed: " + bru);
- }
- }
- }
-
- private class ChangeTask implements Callable<Void> {
- final Change.Id id;
- private final Collection<BatchUpdateOp> changeOps;
- private final Thread mainThread;
- private final boolean dryrun;
-
- PrimaryStorage storage;
- NoteDbUpdateManager.StagedResult noteDbResult;
- boolean dirty;
- boolean deleted;
-
- private ChangeTask(
- Change.Id id, Collection<BatchUpdateOp> changeOps, Thread mainThread, boolean dryrun) {
- this.id = id;
- this.changeOps = changeOps;
- this.mainThread = mainThread;
- this.dryrun = dryrun;
- }
-
- @Override
- public Void call() throws Exception {
- try (TraceContext traceContext =
- TraceContext.open()
- .addTag("TASK_ID", id.toString() + "-" + Thread.currentThread().getId())) {
- if (Thread.currentThread() == mainThread) {
- initRepository();
- Repository repo = repoView.getRepository();
- try (RevWalk rw = new RevWalk(repo)) {
- call(ReviewDbBatchUpdate.this.db, repo, rw);
- }
- } else {
- // Possible optimization: allow Ops to declare whether they need to
- // access the repo from updateChange, and don't open in this thread
- // unless we need it. However, as of this writing the only operations
- // that are executed in parallel are during ReceiveCommits, and they
- // all need the repo open anyway. (The non-parallel case above does not
- // reopen the repo.)
- try (ReviewDb threadLocalDb = schemaFactory.open();
- Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
- call(threadLocalDb, repo, rw);
- }
- }
- return null;
- }
- }
-
- private void call(ReviewDb db, Repository repo, RevWalk rw) throws Exception {
- @SuppressWarnings("resource") // Not always opened.
- NoteDbUpdateManager updateManager = null;
- try {
- db.changes().beginTransaction(id);
- try {
- ChangeContextImpl ctx = newChangeContext(db, repo, rw, id);
- NoteDbChangeState oldState = NoteDbChangeState.parse(ctx.getChange());
- NoteDbChangeState.checkNotReadOnly(oldState, skewMs);
-
- storage = PrimaryStorage.of(oldState);
- if (storage == PrimaryStorage.NOTE_DB && !notesMigration.readChanges()) {
- throw new OrmException("must have NoteDb enabled to update change " + id);
- }
-
- // Call updateChange on each op.
- logDebug("Calling updateChange on %s ops", changeOps.size());
- for (BatchUpdateOp op : changeOps) {
- dirty |= op.updateChange(ctx);
- }
- if (!dirty) {
- logDebug("No ops reported dirty, short-circuiting");
- return;
- }
- deleted = ctx.deleted;
- if (deleted) {
- logDebug("Change was deleted");
- }
-
- // Stage the NoteDb update and store its state in the Change.
- if (notesMigration.commitChangeWrites()) {
- updateManager = stageNoteDbUpdate(ctx, deleted);
- }
-
- if (storage == PrimaryStorage.REVIEW_DB) {
- // If primary storage of this change is in ReviewDb, bump
- // lastUpdatedOn or rowVersion and commit. Otherwise, don't waste
- // time updating ReviewDb at all.
- Iterable<Change> cs = changesToUpdate(ctx);
- if (isNewChange(id)) {
- // Insert rather than upsert in case of a race on change IDs.
- logDebug("Inserting change");
- db.changes().insert(cs);
- } else if (deleted) {
- logDebug("Deleting change");
- db.changes().delete(cs);
- } else {
- logDebug("Updating change");
- db.changes().update(cs);
- }
- if (!dryrun) {
- db.commit();
- }
- } else {
- logDebug("Skipping ReviewDb write since primary storage is %s", storage);
- }
- } finally {
- db.rollback();
- }
-
- // Do not execute the NoteDbUpdateManager, as we don't want too much
- // contention on the underlying repo, and we would rather use a single
- // ObjectInserter/BatchRefUpdate later.
- //
- // TODO(dborowitz): May or may not be worth trying to batch together
- // flushed inserters as well.
- if (storage == PrimaryStorage.NOTE_DB) {
- // Should have failed above if NoteDb is disabled.
- checkState(notesMigration.commitChangeWrites());
- noteDbResult = updateManager.stage().get(id);
- } else if (notesMigration.commitChangeWrites()) {
- try {
- noteDbResult = updateManager.stage().get(id);
- } catch (IOException ex) {
- // Ignore all errors trying to update NoteDb at this point. We've
- // already written the NoteDbChangeState to ReviewDb, which means
- // if the state is out of date it will be rebuilt the next time it
- // is needed.
- logger.atFine().withCause(ex).log("Ignoring NoteDb update error after ReviewDb write");
- }
- }
- } catch (Exception e) {
- logDebug("Error updating change (should be rethrown)", e);
- Throwables.propagateIfPossible(e, RestApiException.class);
- throw new UpdateException(e);
- } finally {
- if (updateManager != null) {
- updateManager.close();
- }
- }
- }
-
- private ChangeContextImpl newChangeContext(
- ReviewDb db, Repository repo, RevWalk rw, Change.Id id) throws OrmException {
- Change c = newChanges.get(id);
- boolean isNew = c != null;
- if (isNew) {
- // New change: populate noteDbState.
- checkState(c.getNoteDbState() == null, "noteDbState should not be filled in by callers");
- if (notesMigration.changePrimaryStorage() == PrimaryStorage.NOTE_DB) {
- c.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
- }
- } else {
- // Existing change.
- c = ChangeNotes.readOneReviewDbChange(db, id);
- if (c == null) {
- // Not in ReviewDb, but new changes are created with default primary
- // storage as NOTE_DB, so we can assume that a missing change is
- // NoteDb primary. Pass a synthetic change into ChangeNotes.Factory,
- // which lets ChangeNotes take care of the existence check.
- //
- // TODO(dborowitz): This assumption is potentially risky, because
- // it means once we turn this option on and start creating changes
- // without writing anything to ReviewDb, we can't turn this option
- // back off without making those changes inaccessible. The problem
- // is we have no way of distinguishing a change that only exists in
- // NoteDb because it only ever existed in NoteDb, from a change that
- // only exists in NoteDb because it used to exist in ReviewDb and
- // deleting from ReviewDb succeeded but deleting from NoteDb failed.
- //
- // TODO(dborowitz): We actually still have that problem anyway. Maybe
- // we need a cutoff timestamp? Or maybe we need to start leaving
- // tombstones in ReviewDb?
- c = ChangeNotes.Factory.newNoteDbOnlyChange(project, id);
- }
- NoteDbChangeState.checkNotReadOnly(c, skewMs);
- }
- ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
- return new ChangeContextImpl(notes, new BatchUpdateReviewDb(db), repo, rw);
- }
-
- private NoteDbUpdateManager stageNoteDbUpdate(ChangeContextImpl ctx, boolean deleted)
- throws OrmException, IOException {
- logDebug("Staging NoteDb update");
- NoteDbUpdateManager updateManager =
- updateManagerFactory
- .create(ctx.getProject())
- .setChangeRepo(
- ctx.threadLocalRepo,
- ctx.threadLocalRevWalk,
- null,
- new ChainedReceiveCommands(ctx.threadLocalRepo));
- if (ctx.getUser().isIdentifiedUser()) {
- updateManager.setRefLogIdent(
- ctx.getUser().asIdentifiedUser().newRefLogIdent(ctx.getWhen(), tz));
- }
- for (ChangeUpdate u : ctx.updates.values()) {
- updateManager.add(u);
- }
-
- Change c = ctx.getChange();
- if (deleted) {
- updateManager.deleteChange(c.getId());
- }
- try {
- updateManager.stageAndApplyDelta(c);
- } catch (MismatchedStateException ex) {
- // Refused to apply update because NoteDb was out of sync, which can
- // only happen if ReviewDb is the primary storage for this change.
- //
- // Go ahead with this ReviewDb update; it's still out of sync, but this
- // is no worse than before, and it will eventually get rebuilt.
- logDebug("Ignoring MismatchedStateException while staging");
- }
-
- return updateManager;
- }
-
- private boolean isNewChange(Change.Id id) {
- return newChanges.containsKey(id);
- }
- }
-
- private static Iterable<Change> changesToUpdate(ChangeContextImpl ctx) {
- Change c = ctx.getChange();
- if (ctx.bumpLastUpdatedOn && c.getLastUpdatedOn().before(ctx.getWhen())) {
- c.setLastUpdatedOn(ctx.getWhen());
- }
- return Collections.singleton(c);
- }
-
- private void executePostOps() throws Exception {
- ContextImpl ctx = new ContextImpl();
- for (BatchUpdateOp op : ops.values()) {
- op.postUpdate(ctx);
- }
-
- for (RepoOnlyOp op : repoOnlyOps) {
- op.postUpdate(ctx);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/util/CommitMessageUtil.java b/java/com/google/gerrit/server/util/CommitMessageUtil.java
index ef547150ff..4ad226b7fd 100644
--- a/java/com/google/gerrit/server/util/CommitMessageUtil.java
+++ b/java/com/google/gerrit/server/util/CommitMessageUtil.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.reviewdb.client.Change;
import java.security.NoSuchAlgorithmException;
@@ -40,19 +41,23 @@ public class CommitMessageUtil {
private CommitMessageUtil() {}
/**
- * Checks for null or empty commit messages and appends a newline character to the commit message.
+ * Checks for invalid (empty or containing \0) commit messages and appends a newline character to
+ * the commit message.
*
* @throws BadRequestException if the commit message is null or empty
* @returns the trimmed message with a trailing newline character
*/
- public static String checkAndSanitizeCommitMessage(String commitMessage)
+ public static String checkAndSanitizeCommitMessage(@Nullable String commitMessage)
throws BadRequestException {
- String wellFormedMessage = Strings.nullToEmpty(commitMessage).trim();
- if (wellFormedMessage.isEmpty()) {
+ String trimmed = Strings.nullToEmpty(commitMessage).trim();
+ if (trimmed.isEmpty()) {
throw new BadRequestException("Commit message cannot be null or empty");
}
- wellFormedMessage = wellFormedMessage + "\n";
- return wellFormedMessage;
+ if (trimmed.indexOf(0) >= 0) {
+ throw new BadRequestException("Commit message cannot have NUL character");
+ }
+ trimmed = trimmed + "\n";
+ return trimmed;
}
public static ObjectId generateChangeId() {
diff --git a/java/com/google/gerrit/server/util/FallbackRequestContext.java b/java/com/google/gerrit/server/util/FallbackRequestContext.java
index de1555f985..7a40dbf6d5 100644
--- a/java/com/google/gerrit/server/util/FallbackRequestContext.java
+++ b/java/com/google/gerrit/server/util/FallbackRequestContext.java
@@ -14,12 +14,9 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
/**
@@ -40,14 +37,4 @@ public class FallbackRequestContext implements RequestContext {
public CurrentUser getUser() {
return user;
}
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- throw new ProvisionException("Automatic ReviewDb only available in request scope");
- }
- };
- }
}
diff --git a/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java b/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
index 6dd5543e7c..99dd8bfc63 100644
--- a/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
+++ b/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
@@ -17,7 +17,6 @@ package com.google.gerrit.server.util;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Provider;
@@ -41,9 +40,8 @@ public class GuiceRequestScopePropagator extends RequestScopePropagator {
GuiceRequestScopePropagator(
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
@RemotePeer Provider<SocketAddress> remotePeerProvider,
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
- super(ServletScopes.REQUEST, local, dbProviderProvider);
+ ThreadLocalRequestContext local) {
+ super(ServletScopes.REQUEST, local);
this.url = urlProvider != null ? urlProvider.get() : null;
this.peer = remotePeerProvider.get();
}
diff --git a/java/com/google/gerrit/server/util/MagicBranch.java b/java/com/google/gerrit/server/util/MagicBranch.java
index a0491690a4..4e41be0e01 100644
--- a/java/com/google/gerrit/server/util/MagicBranch.java
+++ b/java/com/google/gerrit/server/util/MagicBranch.java
@@ -28,25 +28,19 @@ public final class MagicBranch {
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/";
- // TODO(xchangcheng): remove after migrating tools which are using this magic branch.
- public static final String NEW_PUBLISH_CHANGE = "refs/publish/";
/** 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;
- } else if (refName.startsWith(NEW_PUBLISH_CHANGE)) {
- magicBranch = NEW_PUBLISH_CHANGE;
}
return refName.substring(magicBranch.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_PUBLISH_CHANGE)
- || refName.startsWith(NEW_CHANGE);
+ return refName.startsWith(NEW_DRAFT_CHANGE) || refName.startsWith(NEW_CHANGE);
}
/** Returns the ref name prefix for a magic branch, {@code null} if the branch is not magic */
@@ -54,9 +48,6 @@ public final class MagicBranch {
if (refName.startsWith(NEW_DRAFT_CHANGE)) {
return NEW_DRAFT_CHANGE;
}
- if (refName.startsWith(NEW_PUBLISH_CHANGE)) {
- return NEW_PUBLISH_CHANGE;
- }
if (refName.startsWith(NEW_CHANGE)) {
return NEW_CHANGE;
}
@@ -80,11 +71,6 @@ public final class MagicBranch {
if (result != Capable.OK) {
return result;
}
- result = checkMagicBranchRef(NEW_PUBLISH_CHANGE, repo, project);
- if (result != Capable.OK) {
- return result;
- }
-
return Capable.OK;
}
diff --git a/java/com/google/gerrit/server/util/ManualRequestContext.java b/java/com/google/gerrit/server/util/ManualRequestContext.java
index 620a2bc950..7790b5ff74 100644
--- a/java/com/google/gerrit/server/util/ManualRequestContext.java
+++ b/java/com/google/gerrit/server/util/ManualRequestContext.java
@@ -14,35 +14,23 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
/** Closeable version of a {@link RequestContext} with manually-specified providers. */
public class ManualRequestContext implements RequestContext, AutoCloseable {
private final Provider<CurrentUser> userProvider;
- private final Provider<ReviewDb> db;
private final ThreadLocalRequestContext requestContext;
private final RequestContext old;
- public ManualRequestContext(
- CurrentUser user,
- SchemaFactory<ReviewDb> schemaFactory,
- ThreadLocalRequestContext requestContext)
- throws OrmException {
- this(Providers.of(user), schemaFactory, requestContext);
+ public ManualRequestContext(CurrentUser user, ThreadLocalRequestContext requestContext) {
+ this(Providers.of(user), requestContext);
}
public ManualRequestContext(
- Provider<CurrentUser> userProvider,
- SchemaFactory<ReviewDb> schemaFactory,
- ThreadLocalRequestContext requestContext)
- throws OrmException {
+ Provider<CurrentUser> userProvider, ThreadLocalRequestContext requestContext) {
this.userProvider = userProvider;
- this.db = Providers.of(schemaFactory.open());
this.requestContext = requestContext;
old = requestContext.setContext(this);
}
@@ -53,13 +41,7 @@ public class ManualRequestContext implements RequestContext, AutoCloseable {
}
@Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return db;
- }
-
- @Override
public void close() {
requestContext.setContext(old);
- db.get().close();
}
}
diff --git a/java/com/google/gerrit/server/util/MostSpecificComparator.java b/java/com/google/gerrit/server/util/MostSpecificComparator.java
index f243726672..b22617cc9b 100644
--- a/java/com/google/gerrit/server/util/MostSpecificComparator.java
+++ b/java/com/google/gerrit/server/util/MostSpecificComparator.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.common.data.RefConfigSection;
+import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.server.project.RefPattern;
import java.util.Comparator;
import org.apache.commons.lang.StringUtils;
@@ -40,7 +40,7 @@ import org.apache.commons.lang.StringUtils;
* are infinite, but refs/heads/[a-zA-Z]* has more transitions, which after all turns it more
* specific.
*/
-public final class MostSpecificComparator implements Comparator<RefConfigSection> {
+public final class MostSpecificComparator implements Comparator<AccessSection> {
private final String refName;
public MostSpecificComparator(String refName) {
@@ -48,7 +48,7 @@ public final class MostSpecificComparator implements Comparator<RefConfigSection
}
@Override
- public int compare(RefConfigSection a, RefConfigSection b) {
+ public int compare(AccessSection a, AccessSection b) {
return compare(a.getName(), b.getName());
}
diff --git a/java/com/google/gerrit/server/util/OneOffRequestContext.java b/java/com/google/gerrit/server/util/OneOffRequestContext.java
index 28be66968d..1788343bae 100644
--- a/java/com/google/gerrit/server/util/OneOffRequestContext.java
+++ b/java/com/google/gerrit/server/util/OneOffRequestContext.java
@@ -15,48 +15,38 @@
package com.google.gerrit.server.util;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Helper to create one-off request contexts.
*
- * <p>Each call to {@link #open()} opens a new {@link ReviewDb}, so this class should only be used
- * in a bounded try/finally block.
- *
* <p>The user in the request context is {@link InternalUser} or the {@link IdentifiedUser}
* associated to the userId passed as parameter.
*/
@Singleton
public class OneOffRequestContext {
private final InternalUser.Factory userFactory;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ThreadLocalRequestContext requestContext;
private final IdentifiedUser.GenericFactory identifiedUserFactory;
@Inject
OneOffRequestContext(
InternalUser.Factory userFactory,
- SchemaFactory<ReviewDb> schemaFactory,
ThreadLocalRequestContext requestContext,
IdentifiedUser.GenericFactory identifiedUserFactory) {
this.userFactory = userFactory;
- this.schemaFactory = schemaFactory;
this.requestContext = requestContext;
this.identifiedUserFactory = identifiedUserFactory;
}
- public ManualRequestContext open() throws OrmException {
- return new ManualRequestContext(userFactory.create(), schemaFactory, requestContext);
+ public ManualRequestContext open() {
+ return new ManualRequestContext(userFactory.create(), requestContext);
}
- public ManualRequestContext openAs(Account.Id userId) throws OrmException {
- return new ManualRequestContext(
- identifiedUserFactory.create(userId), schemaFactory, requestContext);
+ public ManualRequestContext openAs(Account.Id userId) {
+ return new ManualRequestContext(identifiedUserFactory.create(userId), requestContext);
}
}
diff --git a/java/com/google/gerrit/server/util/PluginRequestContext.java b/java/com/google/gerrit/server/util/PluginRequestContext.java
index 3f3f647449..ec1578689f 100644
--- a/java/com/google/gerrit/server/util/PluginRequestContext.java
+++ b/java/com/google/gerrit/server/util/PluginRequestContext.java
@@ -14,11 +14,8 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PluginUser;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
/** RequestContext active while plugins load or unload. */
public class PluginRequestContext implements RequestContext {
@@ -32,14 +29,4 @@ public class PluginRequestContext implements RequestContext {
public CurrentUser getUser() {
return user;
}
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- throw new ProvisionException("Automatic ReviewDb only available in request scope");
- }
- };
- }
}
diff --git a/java/com/google/gerrit/server/util/RequestContext.java b/java/com/google/gerrit/server/util/RequestContext.java
index 37fd7bc122..04ee5c6ae9 100644
--- a/java/com/google/gerrit/server/util/RequestContext.java
+++ b/java/com/google/gerrit/server/util/RequestContext.java
@@ -14,9 +14,7 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
-import com.google.inject.Provider;
/**
* The RequestContext is an interface exposing the fields that are needed by the GerritGlobalModule
@@ -24,6 +22,4 @@ import com.google.inject.Provider;
*/
public interface RequestContext {
CurrentUser getUser();
-
- Provider<ReviewDb> getReviewDbProvider();
}
diff --git a/java/com/google/gerrit/server/util/RequestScopePropagator.java b/java/com/google/gerrit/server/util/RequestScopePropagator.java
index e5b0eaf12c..789b9b16a7 100644
--- a/java/com/google/gerrit/server/util/RequestScopePropagator.java
+++ b/java/com/google/gerrit/server/util/RequestScopePropagator.java
@@ -18,13 +18,9 @@ import static java.util.Objects.requireNonNull;
import com.google.common.base.Throwables;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.RequestCleanup;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.git.ProjectRunnable;
import com.google.inject.Key;
-import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.servlet.ServletScopes;
import java.util.concurrent.Callable;
@@ -47,15 +43,10 @@ public abstract class RequestScopePropagator {
private final Scope scope;
private final ThreadLocalRequestContext local;
- private final Provider<RequestScopedReviewDbProvider> dbProviderProvider;
- protected RequestScopePropagator(
- Scope scope,
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
+ protected RequestScopePropagator(Scope scope, ThreadLocalRequestContext local) {
this.scope = scope;
this.local = local;
- this.dbProviderProvider = dbProviderProvider;
}
/**
@@ -174,19 +165,7 @@ public abstract class RequestScopePropagator {
protected <T> Callable<T> context(RequestContext context, Callable<T> callable) {
return () -> {
- RequestContext old =
- local.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return context.getUser();
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return dbProviderProvider.get();
- }
- });
+ RequestContext old = local.setContext(context::getUser);
try {
return callable.call();
} finally {
@@ -198,16 +177,7 @@ public abstract class RequestScopePropagator {
protected <T> Callable<T> cleanup(Callable<T> callable) {
return () -> {
RequestCleanup cleanup =
- scope
- .scope(
- Key.get(RequestCleanup.class),
- new Provider<RequestCleanup>() {
- @Override
- public RequestCleanup get() {
- return new RequestCleanup();
- }
- })
- .get();
+ scope.scope(Key.get(RequestCleanup.class), RequestCleanup::new).get();
try {
return callable.call();
} finally {
diff --git a/java/com/google/gerrit/server/util/ServerRequestContext.java b/java/com/google/gerrit/server/util/ServerRequestContext.java
index af903c4c5b..037645dbe0 100644
--- a/java/com/google/gerrit/server/util/ServerRequestContext.java
+++ b/java/com/google/gerrit/server/util/ServerRequestContext.java
@@ -14,12 +14,9 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.InternalUser;
import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
/** RequestContext with an InternalUser making the internals visible. */
public class ServerRequestContext implements RequestContext {
@@ -34,14 +31,4 @@ public class ServerRequestContext implements RequestContext {
public CurrentUser getUser() {
return user;
}
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return new Provider<ReviewDb>() {
- @Override
- public ReviewDb get() {
- throw new ProvisionException("Automatic ReviewDb only available in request scope");
- }
- };
- }
}
diff --git a/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java b/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
index e065c6bd8e..afd699c5cc 100644
--- a/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
+++ b/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
@@ -16,8 +16,7 @@ package com.google.gerrit.server.util;
import com.google.common.base.MoreObjects;
import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.NotSignedInException;
-import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.exceptions.NotSignedInException;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.AbstractModule;
@@ -63,11 +62,6 @@ public class ThreadLocalRequestContext {
}
throw new ProvisionException(NotSignedInException.MESSAGE, new NotSignedInException());
}
-
- @Provides
- ReviewDb provideReviewDb(RequestContext ctx) {
- return ctx.getReviewDbProvider().get();
- }
};
}
diff --git a/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java b/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java
index 90fb9948b0..92f5488c76 100644
--- a/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java
+++ b/java/com/google/gerrit/server/util/ThreadLocalRequestScopePropagator.java
@@ -14,9 +14,7 @@
package com.google.gerrit.server.util;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
import com.google.inject.Scope;
import java.util.concurrent.Callable;
@@ -31,11 +29,8 @@ public abstract class ThreadLocalRequestScopePropagator<C> extends RequestScopeP
private final ThreadLocal<C> threadLocal;
protected ThreadLocalRequestScopePropagator(
- Scope scope,
- ThreadLocal<C> threadLocal,
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
- super(scope, local, dbProviderProvider);
+ Scope scope, ThreadLocal<C> threadLocal, ThreadLocalRequestContext local) {
+ super(scope, local);
this.threadLocal = threadLocal;
}
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index f617ebb79b..90f0c4365d 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -48,6 +48,7 @@ public abstract class AbstractGitCommand extends BaseCommand {
@Override
public void start(Environment env) {
+ enableGracefulStop();
Context ctx = context.subContext(newSession(), context.getCommandLine());
final Context old = sshScope.set(ctx);
try {
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index 7bf7746f10..f6f9b52935 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -7,7 +7,9 @@ java_library(
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/json",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/reviewdb:server",
@@ -24,9 +26,8 @@ java_library(
"//lib:args4j",
"//lib:gson",
"//lib:guava",
- "//lib:gwtorm",
"//lib:jsch",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
"//lib/bouncycastle:bcprov-neverlink",
diff --git a/java/com/google/gerrit/sshd/BaseCommand.java b/java/com/google/gerrit/sshd/BaseCommand.java
index 7c77a2c091..1d9635f108 100644
--- a/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/java/com/google/gerrit/sshd/BaseCommand.java
@@ -401,6 +401,10 @@ public abstract class BaseCommand implements Command {
}
}
+ protected void enableGracefulStop() {
+ context.getSession().setGracefulStop(true);
+ }
+
protected String getTaskDescription() {
String[] ta = getTrimmedArguments();
if (ta != null) {
diff --git a/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index ce3b8a9e3f..ed6f87feab 100644
--- a/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -19,7 +19,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -30,7 +29,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -41,7 +39,6 @@ import java.util.Map;
public class ChangeArgumentParser {
private final ChangesCollection changesCollection;
private final ChangeFinder changeFinder;
- private final ReviewDb db;
private final ChangeNotes.Factory changeNotesFactory;
private final PermissionBackend permissionBackend;
@@ -49,24 +46,22 @@ public class ChangeArgumentParser {
ChangeArgumentParser(
ChangesCollection changesCollection,
ChangeFinder changeFinder,
- ReviewDb db,
ChangeNotes.Factory changeNotesFactory,
PermissionBackend permissionBackend) {
this.changesCollection = changesCollection;
this.changeFinder = changeFinder;
- this.db = db;
this.changeNotesFactory = changeNotesFactory;
this.permissionBackend = permissionBackend;
}
public void addChange(String id, Map<Change.Id, ChangeResource> changes)
- throws UnloggedFailure, OrmException, PermissionBackendException, IOException {
+ throws UnloggedFailure, PermissionBackendException, IOException {
addChange(id, changes, null);
}
public void addChange(
String id, Map<Change.Id, ChangeResource> changes, @Nullable ProjectState projectState)
- throws UnloggedFailure, OrmException, PermissionBackendException, IOException {
+ throws UnloggedFailure, PermissionBackendException, IOException {
addChange(id, changes, projectState, true);
}
@@ -75,7 +70,7 @@ public class ChangeArgumentParser {
Map<Change.Id, ChangeResource> changes,
@Nullable ProjectState projectState,
boolean useIndex)
- throws UnloggedFailure, OrmException, PermissionBackendException, IOException {
+ throws UnloggedFailure, PermissionBackendException, IOException {
List<ChangeNotes> matched = useIndex ? changeFinder.find(id) : changeFromNotesFactory(id);
List<ChangeNotes> toAdd = new ArrayList<>(changes.size());
boolean canMaintainServer;
@@ -98,7 +93,7 @@ public class ChangeArgumentParser {
}
try {
- permissionBackend.currentUser().change(notes).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(notes).check(ChangePermission.READ);
toAdd.add(notes);
} catch (AuthException e) {
// Do nothing.
@@ -121,8 +116,8 @@ public class ChangeArgumentParser {
changes.put(cId, changeResource);
}
- private List<ChangeNotes> changeFromNotesFactory(String id) throws OrmException, UnloggedFailure {
- return changeNotesFactory.create(db, parseId(id));
+ private List<ChangeNotes> changeFromNotesFactory(String id) throws UnloggedFailure {
+ return changeNotesFactory.create(parseId(id));
}
private List<Change.Id> parseId(String id) throws UnloggedFailure {
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 89a1654101..c003b467ae 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -19,12 +19,10 @@ import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.gerrit.sshd.SshScope.Context;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -57,7 +55,6 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
private final SshScope sshScope;
private final ScheduledExecutorService startExecutor;
private final ExecutorService destroyExecutor;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final DynamicItem<SshCreateCommandInterceptor> createCommandInterceptor;
@Inject
@@ -67,12 +64,10 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
WorkQueue workQueue,
SshLog l,
SshScope s,
- SchemaFactory<ReviewDb> sf,
DynamicItem<SshCreateCommandInterceptor> i) {
dispatcher = d;
log = l;
sshScope = s;
- schemaFactory = sf;
createCommandInterceptor = i;
int threads = cfg.getInt("sshd", "commandStartThreads", 2);
@@ -96,16 +91,13 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
@Override
public CommandFactory get() {
- return new CommandFactory() {
- @Override
- public Command createCommand(String requestCommand) {
- String c = requestCommand;
- SshCreateCommandInterceptor interceptor = createCommandInterceptor.get();
- if (interceptor != null) {
- c = interceptor.intercept(c);
- }
- return new Trampoline(c);
+ return requestCommand -> {
+ String c = requestCommand;
+ SshCreateCommandInterceptor interceptor = createCommandInterceptor.get();
+ if (interceptor != null) {
+ c = interceptor.intercept(c);
}
+ return new Trampoline(c);
};
}
@@ -152,7 +144,7 @@ class CommandFactoryProvider implements Provider<CommandFactory>, LifecycleListe
@Override
public void setSession(ServerSession session) {
final SshSession s = session.getAttribute(SshSession.KEY);
- this.ctx = sshScope.newContext(schemaFactory, s, commandLine);
+ this.ctx = sshScope.newContext(s, commandLine);
}
@Override
diff --git a/java/com/google/gerrit/sshd/DispatchCommandProvider.java b/java/com/google/gerrit/sshd/DispatchCommandProvider.java
index 7ff7224b9a..2a65ed06ec 100644
--- a/java/com/google/gerrit/sshd/DispatchCommandProvider.java
+++ b/java/com/google/gerrit/sshd/DispatchCommandProvider.java
@@ -50,24 +50,14 @@ public class DispatchCommandProvider implements Provider<DispatchCommand> {
if (m.putIfAbsent(name.value(), commandProvider) != null) {
throw new IllegalArgumentException(name.value() + " exists");
}
- return new RegistrationHandle() {
- @Override
- public void remove() {
- m.remove(name.value(), commandProvider);
- }
- };
+ return () -> m.remove(name.value(), commandProvider);
}
public RegistrationHandle replace(CommandName name, Provider<Command> cmd) {
final ConcurrentMap<String, CommandProvider> m = getMap();
final CommandProvider commandProvider = new CommandProvider(cmd, null);
m.put(name.value(), commandProvider);
- return new RegistrationHandle() {
- @Override
- public void remove() {
- m.remove(name.value(), commandProvider);
- }
- };
+ return () -> m.remove(name.value(), commandProvider);
}
ConcurrentMap<String, CommandProvider> getMap() {
diff --git a/java/com/google/gerrit/sshd/NoShell.java b/java/com/google/gerrit/sshd/NoShell.java
index 02355547f0..5aaa647912 100644
--- a/java/com/google/gerrit/sshd/NoShell.java
+++ b/java/com/google/gerrit/sshd/NoShell.java
@@ -15,13 +15,11 @@
package com.google.gerrit.sshd;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.sshd.SshScope.Context;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -59,7 +57,6 @@ class NoShell implements Factory<Command> {
static class SendMessage implements Command, SessionAware {
private final Provider<MessageFactory> messageFactory;
- private final SchemaFactory<ReviewDb> schemaFactory;
private final SshScope sshScope;
private InputStream in;
@@ -69,10 +66,8 @@ class NoShell implements Factory<Command> {
private Context context;
@Inject
- SendMessage(
- Provider<MessageFactory> messageFactory, SchemaFactory<ReviewDb> sf, SshScope sshScope) {
+ SendMessage(Provider<MessageFactory> messageFactory, SshScope sshScope) {
this.messageFactory = messageFactory;
- this.schemaFactory = sf;
this.sshScope = sshScope;
}
@@ -99,7 +94,7 @@ class NoShell implements Factory<Command> {
@Override
public void setSession(ServerSession session) {
SshSession s = session.getAttribute(SshSession.KEY);
- this.context = sshScope.newContext(schemaFactory, s, "");
+ this.context = sshScope.newContext(s, "");
}
@Override
diff --git a/java/com/google/gerrit/sshd/SshCommand.java b/java/com/google/gerrit/sshd/SshCommand.java
index 47c60981ff..98289570b4 100644
--- a/java/com/google/gerrit/sshd/SshCommand.java
+++ b/java/com/google/gerrit/sshd/SshCommand.java
@@ -34,18 +34,15 @@ public abstract class SshCommand extends BaseCommand {
@Override
public void start(Environment env) throws IOException {
startThread(
- new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- stdout = toPrintWriter(out);
- stderr = toPrintWriter(err);
- try (TraceContext traceContext = enableTracing()) {
- SshCommand.this.run();
- } finally {
- stdout.flush();
- stderr.flush();
- }
+ () -> {
+ parseCommandLine();
+ stdout = toPrintWriter(out);
+ stderr = toPrintWriter(err);
+ try (TraceContext traceContext = enableTracing()) {
+ SshCommand.this.run();
+ } finally {
+ stdout.flush();
+ stderr.flush();
}
},
AccessPath.SSH_COMMAND);
diff --git a/java/com/google/gerrit/sshd/SshDaemon.java b/java/com/google/gerrit/sshd/SshDaemon.java
index 433e0e92dd..8265413d7b 100644
--- a/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/java/com/google/gerrit/sshd/SshDaemon.java
@@ -61,16 +61,16 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.sshd.common.BaseBuilder;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.channel.RequestHandler;
import org.apache.sshd.common.cipher.Cipher;
import org.apache.sshd.common.compression.BuiltinCompressions;
import org.apache.sshd.common.compression.Compression;
-import org.apache.sshd.common.file.FileSystemFactory;
import org.apache.sshd.common.forward.DefaultForwarderFactory;
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.SshFutureListener;
@@ -87,8 +87,8 @@ import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.mac.Mac;
import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.random.SingletonRandomFactory;
-import org.apache.sshd.common.session.ConnectionService;
import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.helpers.AbstractSession;
import org.apache.sshd.common.session.helpers.DefaultUnknownChannelReferenceHandler;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -101,7 +101,6 @@ import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.auth.gss.UserAuthGSSFactory;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
import org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory;
-import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.command.CommandFactory;
import org.apache.sshd.server.forward.ForwardingFilter;
import org.apache.sshd.server.global.CancelTcpipForwardHandler;
@@ -146,6 +145,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
private final List<HostKey> hostKeys;
private volatile IoAcceptor daemonAcceptor;
private final Config cfg;
+ private final long gracefulStopTimeout;
@Inject
SshDaemon(
@@ -215,6 +215,8 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
SshSessionBackend backend = cfg.getEnum("sshd", null, "backend", SshSessionBackend.NIO2);
+ gracefulStopTimeout = cfg.getTimeUnit("sshd", null, "gracefulStopTimeout", 0, TimeUnit.SECONDS);
+
System.setProperty(
IoServiceFactoryFactory.class.getName(),
backend == SshSessionBackend.MINA
@@ -276,14 +278,11 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
// Log a session close without authentication as a failure.
//
s.addCloseFutureListener(
- new SshFutureListener<CloseFuture>() {
- @Override
- public void operationComplete(CloseFuture future) {
- connected.decrementAndGet();
- if (sd.isAuthenticationError()) {
- authFailures.increment();
- sshLog.onAuthFail(sd);
- }
+ future -> {
+ connected.decrementAndGet();
+ if (sd.isAuthenticationError()) {
+ authFailures.increment();
+ sshLog.onAuthFail(sd);
}
});
return s;
@@ -295,7 +294,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
}
});
setGlobalRequestHandlers(
- Arrays.<RequestHandler<ConnectionService>>asList(
+ Arrays.asList(
new KeepAliveHandler(),
new NoMoreSessionsHandler(),
new TcpipForwardHandler(),
@@ -347,6 +346,12 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
public synchronized void stop() {
if (daemonAcceptor != null) {
try {
+ if (gracefulStopTimeout > 0) {
+ logger.atInfo().log(
+ "Stopping SSHD sessions gracefully with %d seconds timeout.", gracefulStopTimeout);
+ daemonAcceptor.unbind(daemonAcceptor.getBoundAddresses());
+ waitForSessionClose();
+ }
daemonAcceptor.close(true).await();
shutdownExecutors();
logger.atInfo().log("Stopped Gerrit SSHD");
@@ -358,6 +363,40 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
}
}
+ private void waitForSessionClose() {
+ Collection<IoSession> ioSessions = daemonAcceptor.getManagedSessions().values();
+ CountDownLatch allSessionsClosed = new CountDownLatch(ioSessions.size());
+ for (IoSession io : ioSessions) {
+ AbstractSession serverSession = AbstractSession.getSession(io, true);
+ SshSession sshSession =
+ serverSession != null ? serverSession.getAttribute(SshSession.KEY) : null;
+ if (sshSession != null && sshSession.requiresGracefulStop()) {
+ logger.atFine().log("Waiting for session %s to stop.", io.getId());
+ io.addCloseFutureListener(
+ new SshFutureListener<CloseFuture>() {
+ @Override
+ public void operationComplete(CloseFuture future) {
+ logger.atFine().log("Session %s was stopped.", io.getId());
+ allSessionsClosed.countDown();
+ }
+ });
+ } else {
+ logger.atFine().log("Stopping session %s immediately.", io.getId());
+ io.close(true);
+ allSessionsClosed.countDown();
+ }
+ }
+ try {
+ if (!allSessionsClosed.await(gracefulStopTimeout, TimeUnit.SECONDS)) {
+ logger.atWarning().log(
+ "Timeout waiting for SSH session to close. SSHD will be shut down immediately.");
+ }
+ } catch (InterruptedException e) {
+ logger.atWarning().withCause(e).log(
+ "Interrupted waiting for SSH-sessions to close. SSHD will be shut down immediately.");
+ }
+ }
+
private void shutdownExecutors() {
if (executor != null) {
executor.shutdownNow();
@@ -660,7 +699,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
}
private void initSubsystems() {
- setSubsystemFactories(Collections.<NamedFactory<Command>>emptyList());
+ setSubsystemFactories(Collections.emptyList());
}
private void initUserAuth(
@@ -727,10 +766,8 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
private void initFileSystemFactory() {
setFileSystemFactory(
- new FileSystemFactory() {
- @Override
- public FileSystem createFileSystem(Session session) throws IOException {
- return new FileSystem() {
+ session ->
+ new FileSystem() {
@Override
public void close() throws IOException {}
@@ -788,8 +825,6 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
public Set<String> supportedFileAttributeViews() {
return null;
}
- };
- }
- });
+ });
}
}
diff --git a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
index e2fbdcb2af..b2853b9dfd 100644
--- a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
@@ -15,7 +15,7 @@
package com.google.gerrit.sshd;
import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+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;
diff --git a/java/com/google/gerrit/sshd/SshModule.java b/java/com/google/gerrit/sshd/SshModule.java
index df1f633f94..e4aa14cf30 100644
--- a/java/com/google/gerrit/sshd/SshModule.java
+++ b/java/com/google/gerrit/sshd/SshModule.java
@@ -20,10 +20,8 @@ import static com.google.inject.Scopes.SINGLETON;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
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.lifecycle.LifecycleModule;
-import com.google.gerrit.server.DynamicOptions;
import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.GerritConfigListener;
@@ -36,7 +34,6 @@ import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gerrit.sshd.commands.QueryShell;
import com.google.inject.Inject;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.servlet.RequestScoped;
@@ -77,7 +74,6 @@ public class SshModule extends LifecycleModule {
bind(SshInfo.class).to(SshDaemon.class).in(SINGLETON);
factory(DispatchCommand.Factory.class);
- factory(QueryShell.Factory.class);
factory(PeerDaemonUser.Factory.class);
bind(DispatchCommandProvider.class)
@@ -104,7 +100,6 @@ public class SshModule extends LifecycleModule {
.annotatedWith(UniqueAnnotations.create())
.to(SshPluginStarterCallback.class);
- DynamicMap.mapOf(binder(), DynamicOptions.DynamicBean.class);
DynamicItem.itemOf(binder(), SshCreateCommandInterceptor.class);
DynamicSet.setOf(binder(), SshExecuteCommandInterceptor.class);
diff --git a/java/com/google/gerrit/sshd/SshScope.java b/java/com/google/gerrit/sshd/SshScope.java
index 99787d8f8e..340b910c59 100644
--- a/java/com/google/gerrit/sshd/SshScope.java
+++ b/java/com/google/gerrit/sshd/SshScope.java
@@ -14,22 +14,18 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RequestCleanup;
-import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestScopePropagator;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
import com.google.inject.Scope;
-import com.google.inject.util.Providers;
import java.util.HashMap;
import java.util.Map;
@@ -37,13 +33,9 @@ import java.util.Map;
public class SshScope {
private static final Key<RequestCleanup> RC_KEY = Key.get(RequestCleanup.class);
- private static final Key<RequestScopedReviewDbProvider> DB_KEY =
- Key.get(RequestScopedReviewDbProvider.class);
-
class Context implements RequestContext {
private final RequestCleanup cleanup = new RequestCleanup();
private final Map<Key<?>, Object> map = new HashMap<>();
- private final SchemaFactory<ReviewDb> schemaFactory;
private final SshSession session;
private final String commandLine;
@@ -51,17 +43,15 @@ public class SshScope {
volatile long started;
volatile long finished;
- private Context(SchemaFactory<ReviewDb> sf, SshSession s, String c, long at) {
- schemaFactory = sf;
+ private Context(SshSession s, String c, long at) {
session = s;
commandLine = c;
created = started = finished = at;
map.put(RC_KEY, cleanup);
- map.put(DB_KEY, new RequestScopedReviewDbProvider(schemaFactory, Providers.of(cleanup)));
}
private Context(Context p, SshSession s, String c) {
- this(p.schemaFactory, s, c, p.created);
+ this(s, c, p.created);
started = p.started;
finished = p.finished;
}
@@ -85,11 +75,6 @@ public class SshScope {
return user;
}
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return (RequestScopedReviewDbProvider) map.get(DB_KEY);
- }
-
synchronized <T> T get(Key<T> key, Provider<T> creator) {
@SuppressWarnings("unchecked")
T t = (T) map.get(key);
@@ -125,11 +110,8 @@ public class SshScope {
private final SshScope sshScope;
@Inject
- Propagator(
- SshScope sshScope,
- ThreadLocalRequestContext local,
- Provider<RequestScopedReviewDbProvider> dbProviderProvider) {
- super(REQUEST, current, local, dbProviderProvider);
+ Propagator(SshScope sshScope, ThreadLocalRequestContext local) {
+ super(REQUEST, current, local);
this.sshScope = sshScope;
}
@@ -160,8 +142,8 @@ public class SshScope {
this.userFactory = userFactory;
}
- Context newContext(SchemaFactory<ReviewDb> sf, SshSession s, String cmd) {
- return new Context(sf, s, cmd, TimeUtil.nowMs());
+ Context newContext(SshSession s, String cmd) {
+ return new Context(s, cmd, TimeUtil.nowMs());
}
private Context newContinuingContext(Context ctx) {
diff --git a/java/com/google/gerrit/sshd/SshSession.java b/java/com/google/gerrit/sshd/SshSession.java
index 1a60a20f90..a2c3d93648 100644
--- a/java/com/google/gerrit/sshd/SshSession.java
+++ b/java/com/google/gerrit/sshd/SshSession.java
@@ -35,6 +35,8 @@ public class SshSession {
private volatile String authError;
private volatile String peerAgent;
+ private volatile boolean gracefulStop = false;
+
SshSession(int sessionId, SocketAddress peer) {
this.sessionId = sessionId;
this.remoteAddress = peer;
@@ -58,6 +60,14 @@ public class SshSession {
return sessionId;
}
+ public boolean requiresGracefulStop() {
+ return gracefulStop;
+ }
+
+ public void setGracefulStop(boolean gracefulStop) {
+ this.gracefulStop = gracefulStop;
+ }
+
/** Identity of the authenticated user account on the socket. */
public CurrentUser getUser() {
return identity;
diff --git a/java/com/google/gerrit/sshd/SshUtil.java b/java/com/google/gerrit/sshd/SshUtil.java
index ce354224b5..670d64ccc4 100644
--- a/java/com/google/gerrit/sshd/SshUtil.java
+++ b/java/com/google/gerrit/sshd/SshUtil.java
@@ -30,8 +30,6 @@ 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.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.server.session.ServerSession;
@@ -127,7 +125,7 @@ public class SshUtil {
// session, record a login event in the log and add
// a close listener to record a logout event.
//
- Context ctx = sshScope.newContext(null, sd, null);
+ Context ctx = sshScope.newContext(sd, null);
Context old = sshScope.set(ctx);
try {
sshLog.onLogin();
@@ -136,16 +134,13 @@ public class SshUtil {
}
session.addCloseFutureListener(
- new SshFutureListener<CloseFuture>() {
- @Override
- public void operationComplete(CloseFuture future) {
- final Context ctx = sshScope.newContext(null, sd, null);
- final Context old = sshScope.set(ctx);
- try {
- sshLog.onLogout();
- } finally {
- sshScope.set(old);
- }
+ future -> {
+ final Context ctx1 = sshScope.newContext(sd, null);
+ final Context old1 = sshScope.set(ctx1);
+ try {
+ sshLog.onLogout();
+ } finally {
+ sshScope.set(old1);
}
});
}
diff --git a/java/com/google/gerrit/sshd/commands/AdminQueryShell.java b/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
deleted file mode 100644
index c520e79414..0000000000
--- a/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
+++ /dev/null
@@ -1,61 +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.sshd.commands;
-
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
-import com.google.gerrit.extensions.restapi.AuthException;
-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.sshd.AdminHighPriorityCommand;
-import com.google.gerrit.sshd.CommandMetaData;
-import com.google.gerrit.sshd.SshCommand;
-import com.google.inject.Inject;
-import org.kohsuke.args4j.Option;
-
-/** Opens a query processor. */
-@AdminHighPriorityCommand
-@RequiresCapability(GlobalCapability.ACCESS_DATABASE)
-@CommandMetaData(name = "gsql", description = "Administrative interface to active database")
-final class AdminQueryShell extends SshCommand {
- @Inject private PermissionBackend permissionBackend;
- @Inject private QueryShell.Factory factory;
-
- @Option(name = "--format", usage = "Set output format")
- private QueryShell.OutputFormat format = QueryShell.OutputFormat.PRETTY;
-
- @Option(name = "-c", metaVar = "SQL QUERY", usage = "Query to execute")
- private String query;
-
- @Override
- protected void run() throws Failure {
- try {
- permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
- } catch (AuthException err) {
- throw die(err.getMessage());
- } catch (PermissionBackendException e) {
- throw new Failure(1, "unavailable", e);
- }
-
- QueryShell shell = factory.create(in, out);
- shell.setOutputFormat(format);
- if (query != null) {
- shell.execute(query);
- } else {
- shell.run();
- }
- }
-}
diff --git a/java/com/google/gerrit/sshd/commands/ApproveOption.java b/java/com/google/gerrit/sshd/commands/ApproveOption.java
deleted file mode 100644
index cda340de51..0000000000
--- a/java/com/google/gerrit/sshd/commands/ApproveOption.java
+++ /dev/null
@@ -1,170 +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.sshd.commands;
-
-import static com.google.gerrit.util.cli.Localizable.localizable;
-
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelValue;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedElement;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.FieldSetter;
-import org.kohsuke.args4j.spi.OneArgumentOptionHandler;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Setter;
-
-final class ApproveOption implements Option, Setter<Short> {
- private final String name;
- private final String usage;
- private final LabelType type;
-
- private Short value;
-
- ApproveOption(String name, String usage, LabelType type) {
- this.name = name;
- this.usage = usage;
- this.type = type;
- }
-
- @Override
- public String[] aliases() {
- return new String[0];
- }
-
- @Override
- public String[] depends() {
- return new String[] {};
- }
-
- @Override
- public boolean hidden() {
- return false;
- }
-
- @Override
- public Class<? extends OptionHandler<Short>> handler() {
- return Handler.class;
- }
-
- @Override
- public String metaVar() {
- return "N";
- }
-
- @Override
- public String name() {
- return name;
- }
-
- @Override
- public boolean required() {
- return false;
- }
-
- @Override
- public String usage() {
- return usage;
- }
-
- public Short value() {
- return value;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return null;
- }
-
- @Override
- public FieldSetter asFieldSetter() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public AnnotatedElement asAnnotatedElement() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void addValue(Short val) {
- this.value = val;
- }
-
- @Override
- public Class<Short> getType() {
- return Short.class;
- }
-
- @Override
- public boolean isMultiValued() {
- return false;
- }
-
- @Override
- public String[] forbids() {
- return null;
- }
-
- @Override
- public boolean help() {
- return false;
- }
-
- String getLabelName() {
- return type.getName();
- }
-
- public static class Handler extends OneArgumentOptionHandler<Short> {
- private final ApproveOption cmdOption;
-
- // CS IGNORE RedundantModifier FOR NEXT 1 LINES. REASON: needed by org.kohsuke.args4j.Option
- public Handler(CmdLineParser parser, OptionDef option, Setter<Short> setter) {
- super(parser, option, setter);
- this.cmdOption = (ApproveOption) setter;
- }
-
- @Override
- protected Short parse(String token) throws NumberFormatException, CmdLineException {
- String argument = token;
- if (argument.startsWith("+")) {
- argument = argument.substring(1);
- }
-
- final short value = Short.parseShort(argument);
- final LabelValue min = cmdOption.type.getMin();
- final LabelValue max = cmdOption.type.getMax();
-
- if (value < min.getValue() || value > max.getValue()) {
- final String name = cmdOption.name();
- final String e =
- "\""
- + token
- + "\" must be in range "
- + min.formatValue()
- + ".."
- + max.formatValue()
- + " for \""
- + name
- + "\"";
- throw new CmdLineException(owner, localizable(e));
- }
- return value;
- }
- }
-}
diff --git a/java/com/google/gerrit/sshd/commands/AproposCommand.java b/java/com/google/gerrit/sshd/commands/AproposCommand.java
index d3db70d1db..e7a88a1836 100644
--- a/java/com/google/gerrit/sshd/commands/AproposCommand.java
+++ b/java/com/google/gerrit/sshd/commands/AproposCommand.java
@@ -39,6 +39,7 @@ final class AproposCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
try {
List<QueryDocumentationExecutor.DocResult> res = searcher.doQuery(q);
for (DocResult docResult : res) {
diff --git a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index 415ac4c774..42e5624718 100644
--- a/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -63,6 +63,7 @@ public class BanCommitCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
BanCommitInput input =
BanCommitInput.fromCommits(Lists.transform(commitsToBan, ObjectId::getName));
diff --git a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
index affb919b51..ad8e20d7db 100644
--- a/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
+++ b/java/com/google/gerrit/sshd/commands/BaseTestPrologCommand.java
@@ -19,7 +19,7 @@ import com.google.gerrit.extensions.common.TestSubmitRuleInput.Filters;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.server.restapi.change.Revisions;
@@ -59,6 +59,7 @@ abstract class BaseTestPrologCommand extends SshCommand {
@Override
protected final void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
RevisionResource revision =
revisions.parse(
diff --git a/java/com/google/gerrit/sshd/commands/CloseConnection.java b/java/com/google/gerrit/sshd/commands/CloseConnection.java
index 60a878a62c..6cd009b54b 100644
--- a/java/com/google/gerrit/sshd/commands/CloseConnection.java
+++ b/java/com/google/gerrit/sshd/commands/CloseConnection.java
@@ -60,6 +60,7 @@ 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");
diff --git a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index 8a37cce803..00fc20fc02 100644
--- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -28,7 +28,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.account.CreateAccount;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.BufferedReader;
import java.io.IOException;
@@ -72,8 +71,8 @@ final class CreateAccountCommand extends SshCommand {
@Override
protected void run()
- throws OrmException, IOException, ConfigInvalidException, UnloggedFailure,
- PermissionBackendException {
+ throws IOException, ConfigInvalidException, UnloggedFailure, PermissionBackendException {
+ enableGracefulStop();
AccountInput input = new AccountInput();
input.username = username;
input.email = email;
diff --git a/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java b/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
index aad96a171d..a837ecda79 100644
--- a/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
@@ -44,6 +44,7 @@ public final class CreateBranchCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
BranchInput in = new BranchInput();
in.revision = revision;
diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 917c138c80..8169901677 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -33,7 +33,6 @@ import com.google.gerrit.server.restapi.group.CreateGroup;
import com.google.gerrit.server.restapi.group.GroupsCollection;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.HashSet;
@@ -102,8 +101,8 @@ final class CreateGroupCommand extends SshCommand {
@Override
protected void run()
- throws Failure, OrmException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws Failure, IOException, ConfigInvalidException, PermissionBackendException {
+ enableGracefulStop();
try {
GroupResource rsrc = createGroup();
@@ -120,8 +119,7 @@ final class CreateGroupCommand extends SshCommand {
}
private GroupResource createGroup()
- throws RestApiException, OrmException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
GroupInput input = new GroupInput();
input.description = groupDescription;
input.visibleToAll = visibleToAll;
@@ -136,8 +134,7 @@ final class CreateGroupCommand extends SshCommand {
}
private void addMembers(GroupResource rsrc)
- throws RestApiException, OrmException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
AddMembers.Input input =
AddMembers.Input.fromMembers(
initialMembers.stream().map(Object::toString).collect(toList()));
@@ -145,8 +142,7 @@ final class CreateGroupCommand extends SshCommand {
}
private void addSubgroups(GroupResource rsrc)
- throws RestApiException, OrmException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
AddSubgroups.Input input =
AddSubgroups.Input.fromGroups(
initialGroups.stream().map(AccountGroup.UUID::get).collect(toList()));
diff --git a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index df86d63807..ad7021b12c 100644
--- a/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -166,6 +166,7 @@ final class CreateProjectCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
if (!suggestParent) {
if (projectName == null) {
diff --git a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
index 1c857e4c28..87b6f02144 100644
--- a/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
+++ b/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -90,7 +90,6 @@ public class DefaultCommandModule extends CommandModule {
command(gerrit, CreateGroupCommand.class);
command(gerrit, CreateProjectCommand.class);
command(gerrit, SetHeadCommand.class);
- command(gerrit, AdminQueryShell.class);
if (slaveMode) {
command("git-receive-pack").to(ReceiveSlaveMode.class);
diff --git a/java/com/google/gerrit/sshd/commands/FlushCaches.java b/java/com/google/gerrit/sshd/commands/FlushCaches.java
index 3a06660513..e048eae557 100644
--- a/java/com/google/gerrit/sshd/commands/FlushCaches.java
+++ b/java/com/google/gerrit/sshd/commands/FlushCaches.java
@@ -56,6 +56,7 @@ final class FlushCaches extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
if (list) {
if (all || !caches.isEmpty()) {
diff --git a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
index ecbb3733c1..97737b0e01 100644
--- a/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -62,6 +62,7 @@ public class GarbageCollectionCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
verifyCommandLine();
runGC();
}
diff --git a/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java b/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
index 0804d08c3e..30dc5c485b 100644
--- a/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexActivateCommand.java
@@ -34,6 +34,7 @@ public class IndexActivateCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
if (versionManager.isKnownIndex(name)) {
if (versionManager.activateLatestIndex(name)) {
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index fad74f54a9..376b8370fa 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -14,6 +14,7 @@
package com.google.gerrit.sshd.commands;
+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;
@@ -22,7 +23,6 @@ import com.google.gerrit.server.restapi.change.Index;
import com.google.gerrit.sshd.ChangeArgumentParser;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.LinkedHashMap;
@@ -44,7 +44,7 @@ final class IndexChangesCommand extends SshCommand {
void addChange(String token) {
try {
changeArgumentParser.addChange(token, changes, null, false);
- } catch (UnloggedFailure | OrmException | PermissionBackendException | IOException e) {
+ } catch (UnloggedFailure | StorageException | PermissionBackendException | IOException e) {
writeError("warning", e.getMessage());
}
}
@@ -53,6 +53,7 @@ final class IndexChangesCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
boolean ok = true;
for (ChangeResource rsrc : changes.values()) {
try {
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
index 56b00a573d..168dc19755 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesInProjectCommand.java
@@ -43,6 +43,7 @@ final class IndexChangesInProjectCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure, Failure, Exception {
+ enableGracefulStop();
if (projects.isEmpty()) {
throw die("needs at least one project as command arguments");
}
diff --git a/java/com/google/gerrit/sshd/commands/IndexStartCommand.java b/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
index f3d349c809..5433b174d3 100644
--- a/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexStartCommand.java
@@ -38,6 +38,7 @@ public class IndexStartCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
try {
if (versionManager.isKnownIndex(name)) {
if (versionManager.startReindexer(name, force)) {
diff --git a/java/com/google/gerrit/sshd/commands/KillCommand.java b/java/com/google/gerrit/sshd/commands/KillCommand.java
index df74f86366..a633a8a932 100644
--- a/java/com/google/gerrit/sshd/commands/KillCommand.java
+++ b/java/com/google/gerrit/sshd/commands/KillCommand.java
@@ -47,6 +47,7 @@ final class KillCommand extends SshCommand {
@Override
protected void run() {
+ enableGracefulStop();
ConfigResource cfgRsrc = new ConfigResource();
for (String id : taskIds) {
try {
diff --git a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
index f3ba308277..a6fe833479 100644
--- a/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -52,6 +52,7 @@ public class ListGroupsCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
if (listGroups.getUser() != null && !listGroups.getProjects().isEmpty()) {
throw die("--user and --project options are not compatible.");
}
diff --git a/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java b/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
index c8b8fa111d..1a7be32338 100644
--- a/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListLoggingLevelCommand.java
@@ -40,6 +40,7 @@ public class ListLoggingLevelCommand extends SshCommand {
@SuppressWarnings("unchecked")
@Override
protected void run() {
+ enableGracefulStop();
Map<String, String> logs = new TreeMap<>();
for (Enumeration<Logger> logger = LogManager.getCurrentLoggers(); logger.hasMoreElements(); ) {
Logger log = logger.nextElement();
diff --git a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
index 1565ecb8bc..53d4ac6407 100644
--- a/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListMembersCommand.java
@@ -45,6 +45,7 @@ public class ListMembersCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
impl.display(stdout);
}
diff --git a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
index 9f2ffa9772..e711d57a97 100644
--- a/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
@@ -32,6 +32,7 @@ public class ListProjectsCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
if (!impl.getFormat().isJson()) {
List<String> showBranch = impl.getShowBranch();
if (impl.isShowTree() && (showBranch != null) && !showBranch.isEmpty()) {
diff --git a/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index f68e34c9de..a1af9a084b 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -15,10 +15,11 @@
package com.google.gerrit.sshd.commands;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
import com.google.gerrit.common.data.GlobalCapability;
+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;
@@ -32,7 +33,6 @@ import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.Map;
@@ -74,27 +74,27 @@ public class LsUserRefs extends SshCommand {
@Override
protected void run() throws Failure {
- Account userAccount;
+ enableGracefulStop();
+ Account.Id userAccountId;
try {
- userAccount = accountResolver.find(userName);
- } catch (OrmException | IOException | ConfigInvalidException e) {
- throw die(e);
- }
- if (userAccount == null) {
- stdout.print("No single user could be found when searching for: " + userName + '\n');
+ userAccountId = accountResolver.resolve(userName).asUnique().getAccount().getId();
+ } catch (UnprocessableEntityException e) {
+ stdout.println(e.getMessage());
stdout.flush();
return;
+ } catch (StorageException | IOException | ConfigInvalidException e) {
+ throw die(e);
}
Project.NameKey projectName = projectState.getNameKey();
try (Repository repo = repoManager.openRepository(projectName);
- ManualRequestContext ctx = requestContext.openAs(userAccount.getId())) {
+ ManualRequestContext ctx = requestContext.openAs(userAccountId)) {
try {
Map<String, Ref> refsMap =
permissionBackend
.user(ctx.getUser())
.project(projectName)
- .filter(repo.getRefDatabase().getRefs(ALL), repo, RefFilterOptions.defaults());
+ .filter(repo.getRefDatabase().getRefs(), repo, RefFilterOptions.defaults());
for (String ref : refsMap.keySet()) {
if (!onlyRefsHeads || ref.startsWith(RefNames.REFS_HEADS)) {
@@ -106,7 +106,7 @@ public class LsUserRefs extends SshCommand {
}
} catch (RepositoryNotFoundException e) {
throw die("'" + projectName + "': not a git archive", e);
- } catch (IOException | OrmException e) {
+ } catch (IOException e) {
throw die("Error opening: '" + projectName, e);
}
}
diff --git a/java/com/google/gerrit/sshd/commands/PatchSetParser.java b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
index 2dd035c937..45210f6c1b 100644
--- a/java/com/google/gerrit/sshd/commands/PatchSetParser.java
+++ b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
@@ -19,7 +19,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeFinder;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -28,7 +27,6 @@ import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -37,7 +35,6 @@ import java.util.List;
@Singleton
public class PatchSetParser {
- private final Provider<ReviewDb> db;
private final Provider<InternalChangeQuery> queryProvider;
private final ChangeNotes.Factory notesFactory;
private final PatchSetUtil psUtil;
@@ -45,12 +42,10 @@ public class PatchSetParser {
@Inject
PatchSetParser(
- Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
ChangeNotes.Factory notesFactory,
PatchSetUtil psUtil,
ChangeFinder changeFinder) {
- this.db = db;
this.queryProvider = queryProvider;
this.notesFactory = notesFactory;
this.psUtil = psUtil;
@@ -58,7 +53,7 @@ public class PatchSetParser {
}
public PatchSet parsePatchSet(String token, ProjectState projectState, String branch)
- throws UnloggedFailure, OrmException {
+ throws UnloggedFailure {
// By commit?
//
if (token.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
@@ -107,7 +102,7 @@ public class PatchSetParser {
throw error("\"" + token + "\" is not a valid patch set", e);
}
ChangeNotes notes = getNotes(projectState, patchSetId.getParentKey());
- PatchSet patchSet = psUtil.get(db.get(), notes, patchSetId);
+ PatchSet patchSet = psUtil.get(notes, patchSetId);
if (patchSet == null) {
throw error("\"" + token + "\" no such patch set");
}
@@ -127,13 +122,13 @@ public class PatchSetParser {
}
private ChangeNotes getNotes(@Nullable ProjectState projectState, Change.Id changeId)
- throws OrmException, UnloggedFailure {
+ throws UnloggedFailure {
if (projectState != null) {
- return notesFactory.create(db.get(), projectState.getNameKey(), changeId);
+ return notesFactory.create(projectState.getNameKey(), changeId);
}
try {
ChangeNotes notes = changeFinder.findOne(changeId);
- return notesFactory.create(db.get(), notes.getProjectName(), changeId);
+ return notesFactory.create(notes.getProjectName(), changeId);
} catch (NoSuchChangeException e) {
throw error("\"" + changeId + "\" no such change", e);
}
diff --git a/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java b/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
index 7e32615180..086081c6d3 100644
--- a/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginAdminSshCommand.java
@@ -28,6 +28,7 @@ public abstract class PluginAdminSshCommand extends SshCommand {
@Override
protected final void run() throws UnloggedFailure {
+ enableGracefulStop();
if (!loader.isRemoteAdminEnabled()) {
throw die("remote plugin administration is disabled");
}
diff --git a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index ceb5f07017..7216ed9bbd 100644
--- a/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -21,7 +21,7 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.server.plugins.ListPlugins;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -41,6 +41,7 @@ public class PluginLsCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE);
if (format.isJson()) {
@@ -49,15 +50,17 @@ public class PluginLsCommand extends SshCommand {
.toJson(output, new TypeToken<Map<String, PluginInfo>>() {}.getType(), stdout);
stdout.print('\n');
} else {
- stdout.format("%-30s %-10s %-8s %s\n", "Name", "Version", "Status", "File");
+ String template = "%-30s %-10s %-16s %-8s %s\n";
+ stdout.format(template, "Name", "Version", "Api-Version", "Status", "File");
stdout.print(
"-------------------------------------------------------------------------------\n");
for (Map.Entry<String, PluginInfo> p : output.entrySet()) {
PluginInfo info = p.getValue();
stdout.format(
- "%-30s %-10s %-8s %s\n",
+ template,
p.getKey(),
Strings.nullToEmpty(info.version),
+ Strings.nullToEmpty(info.apiVersion),
status(info.disabled),
Strings.nullToEmpty(info.filename));
}
diff --git a/java/com/google/gerrit/sshd/commands/Query.java b/java/com/google/gerrit/sshd/commands/Query.java
index 4d8351ec6f..772eabe085 100644
--- a/java/com/google/gerrit/sshd/commands/Query.java
+++ b/java/com/google/gerrit/sshd/commands/Query.java
@@ -91,6 +91,11 @@ public class Query extends SshCommand implements DynamicOptions.BeanReceiver {
processor.setStart(start);
}
+ @Option(name = "--no-limit", usage = "Return all results, overriding the default limit")
+ void setNoLimit(boolean on) {
+ processor.setNoLimit(on);
+ }
+
@Argument(
index = 0,
required = true,
@@ -101,6 +106,7 @@ public class Query extends SshCommand implements DynamicOptions.BeanReceiver {
@Override
protected void run() throws Exception {
+ enableGracefulStop();
processor.query(join(query, " "));
}
diff --git a/java/com/google/gerrit/sshd/commands/QueryShell.java b/java/com/google/gerrit/sshd/commands/QueryShell.java
deleted file mode 100644
index fe102ab750..0000000000
--- a/java/com/google/gerrit/sshd/commands/QueryShell.java
+++ /dev/null
@@ -1,781 +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.sshd.commands;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.common.Version;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-/** Simple interactive SQL query tool. */
-public class QueryShell {
- public interface Factory {
- QueryShell create(@Assisted InputStream in, @Assisted OutputStream out);
- }
-
- public enum OutputFormat {
- PRETTY,
- JSON,
- JSON_SINGLE
- }
-
- private final BufferedReader in;
- private final PrintWriter out;
- private final SchemaFactory<ReviewDb> dbFactory;
- private OutputFormat outputFormat = OutputFormat.PRETTY;
-
- private ReviewDb db;
- private Connection connection;
- private Statement statement;
-
- @Inject
- QueryShell(
- @ReviewDbFactory final SchemaFactory<ReviewDb> dbFactory,
- @Assisted final InputStream in,
- @Assisted final OutputStream out) {
- this.dbFactory = dbFactory;
- this.in = new BufferedReader(new InputStreamReader(in, UTF_8));
- this.out = new PrintWriter(new OutputStreamWriter(out, UTF_8));
- }
-
- public void setOutputFormat(OutputFormat fmt) {
- outputFormat = fmt;
- }
-
- public void run() {
- try {
- db = ReviewDbUtil.unwrapDb(dbFactory.open());
- try {
- connection = ((JdbcSchema) db).getConnection();
- connection.setAutoCommit(true);
-
- statement = connection.createStatement();
- try {
- showBanner();
- readEvalPrintLoop();
- } finally {
- statement.close();
- statement = null;
- }
- } finally {
- db.close();
- db = null;
- }
- } catch (OrmException | SQLException err) {
- out.println("fatal: Cannot open connection: " + err.getMessage());
- } finally {
- out.flush();
- }
- }
-
- public void execute(String query) {
- try {
- db = dbFactory.open();
- try {
- connection = ((JdbcSchema) db).getConnection();
- connection.setAutoCommit(true);
-
- statement = connection.createStatement();
- try {
- executeStatement(query);
- } finally {
- statement.close();
- statement = null;
- }
- } finally {
- db.close();
- db = null;
- }
- } catch (OrmException | SQLException err) {
- out.println("fatal: Cannot open connection: " + err.getMessage());
- } finally {
- out.flush();
- }
- }
-
- private void readEvalPrintLoop() {
- final StringBuilder buffer = new StringBuilder();
- boolean executed = false;
- for (; ; ) {
- if (outputFormat == OutputFormat.PRETTY) {
- print(buffer.length() == 0 || executed ? "gerrit> " : " -> ");
- }
- String line = readLine();
- if (line == null) {
- return;
- }
-
- if (line.startsWith("\\")) {
- // Shell command, check the various cases we recognize
- //
- line = line.substring(1);
- if (line.equals("h") || line.equals("?")) {
- showHelp();
-
- } else if (line.equals("q")) {
- if (outputFormat == OutputFormat.PRETTY) {
- println("Bye");
- }
- return;
-
- } else if (line.equals("r")) {
- buffer.setLength(0);
- executed = false;
-
- } else if (line.equals("p")) {
- println(buffer.toString());
-
- } else if (line.equals("g")) {
- if (buffer.length() > 0) {
- executeStatement(buffer.toString());
- executed = true;
- }
-
- } else if (line.equals("d")) {
- listTables();
-
- } else if (line.startsWith("d ")) {
- showTable(line.substring(2).trim());
-
- } else {
- final String msg = "'\\" + line + "' not supported";
- switch (outputFormat) {
- case JSON_SINGLE:
- case JSON:
- {
- final JsonObject err = new JsonObject();
- err.addProperty("type", "error");
- err.addProperty("message", msg);
- println(err.toString());
- break;
- }
- case PRETTY:
- default:
- println("ERROR: " + msg);
- println("");
- showHelp();
- break;
- }
- }
- continue;
- }
-
- if (executed) {
- buffer.setLength(0);
- executed = false;
- }
- if (buffer.length() > 0) {
- buffer.append('\n');
- }
- buffer.append(line);
-
- if (buffer.length() > 0 && buffer.charAt(buffer.length() - 1) == ';') {
- executeStatement(buffer.toString());
- executed = true;
- }
- }
- }
-
- private void listTables() {
- final DatabaseMetaData meta;
- try {
- meta = connection.getMetaData();
- } catch (SQLException e) {
- error(e);
- return;
- }
-
- final String[] types = {"TABLE", "VIEW"};
- try (ResultSet rs = meta.getTables(null, null, null, types)) {
- if (outputFormat == OutputFormat.PRETTY) {
- println(" List of relations");
- }
- showResultSet(
- rs,
- false,
- 0,
- Identity.create(rs, "TABLE_SCHEM"),
- Identity.create(rs, "TABLE_NAME"),
- Identity.create(rs, "TABLE_TYPE"));
- } catch (SQLException e) {
- error(e);
- }
-
- println("");
- }
-
- private void showTable(String tableName) {
- final DatabaseMetaData meta;
- try {
- meta = connection.getMetaData();
-
- if (meta.storesUpperCaseIdentifiers()) {
- tableName = tableName.toUpperCase();
- } else if (meta.storesLowerCaseIdentifiers()) {
- tableName = tableName.toLowerCase();
- }
- } catch (SQLException e) {
- error(e);
- return;
- }
-
- try (ResultSet rs = meta.getColumns(null, null, tableName, null)) {
- if (!rs.next()) {
- throw new SQLException("Table " + tableName + " not found");
- }
-
- if (outputFormat == OutputFormat.PRETTY) {
- println(" Table " + tableName);
- }
- showResultSet(
- rs,
- true,
- 0,
- Identity.create(rs, "COLUMN_NAME"),
- new Function("TYPE") {
- @Override
- String apply(ResultSet rs) throws SQLException {
- String type = rs.getString("TYPE_NAME");
- switch (rs.getInt("DATA_TYPE")) {
- case java.sql.Types.CHAR:
- case java.sql.Types.VARCHAR:
- type += "(" + rs.getInt("COLUMN_SIZE") + ")";
- break;
- }
-
- String def = rs.getString("COLUMN_DEF");
- if (def != null && !def.isEmpty()) {
- type += " DEFAULT " + def;
- }
-
- int nullable = rs.getInt("NULLABLE");
- if (nullable == DatabaseMetaData.columnNoNulls) {
- type += " NOT NULL";
- }
- return type;
- }
- });
- } catch (SQLException e) {
- error(e);
- return;
- }
-
- try (ResultSet rs = meta.getIndexInfo(null, null, tableName, false, true)) {
- Map<String, IndexInfo> indexes = new TreeMap<>();
- while (rs.next()) {
- final String indexName = rs.getString("INDEX_NAME");
- IndexInfo def = indexes.get(indexName);
- if (def == null) {
- def = new IndexInfo();
- def.name = indexName;
- indexes.put(indexName, def);
- }
-
- if (!rs.getBoolean("NON_UNIQUE")) {
- def.unique = true;
- }
-
- final int pos = rs.getInt("ORDINAL_POSITION");
- final String col = rs.getString("COLUMN_NAME");
- String desc = rs.getString("ASC_OR_DESC");
- if ("D".equals(desc)) {
- desc = " DESC";
- } else {
- desc = "";
- }
- def.addColumn(pos, col + desc);
-
- String filter = rs.getString("FILTER_CONDITION");
- if (filter != null && !filter.isEmpty()) {
- def.filter.append(filter);
- }
- }
-
- if (outputFormat == OutputFormat.PRETTY) {
- println("");
- println("Indexes on " + tableName + ":");
- for (IndexInfo def : indexes.values()) {
- println(" " + def);
- }
- }
- } catch (SQLException e) {
- error(e);
- return;
- }
-
- println("");
- }
-
- private void executeStatement(String sql) {
- final long start = TimeUtil.nowMs();
- final boolean hasResultSet;
- try {
- hasResultSet = statement.execute(sql);
- } catch (SQLException e) {
- error(e);
- return;
- }
-
- try {
- if (hasResultSet) {
- try (ResultSet rs = statement.getResultSet()) {
- showResultSet(rs, false, start);
- }
-
- } else {
- final int updateCount = statement.getUpdateCount();
- final long ms = TimeUtil.nowMs() - start;
- switch (outputFormat) {
- case JSON_SINGLE:
- case JSON:
- {
- final JsonObject tail = new JsonObject();
- tail.addProperty("type", "update-stats");
- tail.addProperty("rowCount", updateCount);
- tail.addProperty("runTimeMilliseconds", ms);
- println(tail.toString());
- break;
- }
-
- case PRETTY:
- default:
- println("UPDATE " + updateCount + "; " + ms + " ms");
- break;
- }
- }
- } catch (SQLException e) {
- error(e);
- }
- }
-
- /**
- * Outputs a result set to stdout.
- *
- * @param rs ResultSet to show.
- * @param alreadyOnRow true if rs is already on the first row. false otherwise.
- * @param start Timestamp in milliseconds when executing the statement started. This timestamp is
- * used to compute statistics about the statement. If no statistics should be shown, set it to
- * 0.
- * @param show Functions to map columns
- * @throws SQLException
- */
- private void showResultSet(ResultSet rs, boolean alreadyOnRow, long start, Function... show)
- throws SQLException {
- switch (outputFormat) {
- case JSON_SINGLE:
- case JSON:
- showResultSetJson(rs, alreadyOnRow, start, show);
- break;
- case PRETTY:
- default:
- showResultSetPretty(rs, alreadyOnRow, start, show);
- break;
- }
- }
-
- /**
- * Outputs a result set to stdout in Json format.
- *
- * @param rs ResultSet to show.
- * @param alreadyOnRow true if rs is already on the first row. false otherwise.
- * @param start Timestamp in milliseconds when executing the statement started. This timestamp is
- * used to compute statistics about the statement. If no statistics should be shown, set it to
- * 0.
- * @param show Functions to map columns
- * @throws SQLException
- */
- private void showResultSetJson(
- final ResultSet rs, boolean alreadyOnRow, long start, Function... show) throws SQLException {
- JsonArray collector = new JsonArray();
- final ResultSetMetaData meta = rs.getMetaData();
- final Function[] columnMap;
- if (show != null && 0 < show.length) {
- columnMap = show;
-
- } else {
- final int colCnt = meta.getColumnCount();
- columnMap = new Function[colCnt];
- for (int colId = 0; colId < colCnt; colId++) {
- final int p = colId + 1;
- final String name = meta.getColumnLabel(p);
- columnMap[colId] = new Identity(p, name);
- }
- }
-
- int rowCnt = 0;
- while (alreadyOnRow || rs.next()) {
- final JsonObject row = new JsonObject();
- final JsonObject cols = new JsonObject();
- for (Function function : columnMap) {
- String v = function.apply(rs);
- if (v == null) {
- continue;
- }
- cols.addProperty(function.name.toLowerCase(), v);
- }
- row.addProperty("type", "row");
- row.add("columns", cols);
- switch (outputFormat) {
- case JSON:
- println(row.toString());
- break;
- case JSON_SINGLE:
- collector.add(row);
- break;
- case PRETTY:
- default:
- final JsonObject obj = new JsonObject();
- obj.addProperty("type", "error");
- obj.addProperty("message", "Unsupported Json variant");
- println(obj.toString());
- return;
- }
- alreadyOnRow = false;
- rowCnt++;
- }
-
- JsonObject tail = null;
- if (start != 0) {
- tail = new JsonObject();
- tail.addProperty("type", "query-stats");
- tail.addProperty("rowCount", rowCnt);
- final long ms = TimeUtil.nowMs() - start;
- tail.addProperty("runTimeMilliseconds", ms);
- }
-
- switch (outputFormat) {
- case JSON:
- if (tail != null) {
- println(tail.toString());
- }
- break;
- case JSON_SINGLE:
- if (tail != null) {
- collector.add(tail);
- }
- println(collector.toString());
- break;
- case PRETTY:
- default:
- final JsonObject obj = new JsonObject();
- obj.addProperty("type", "error");
- obj.addProperty("message", "Unsupported Json variant");
- println(obj.toString());
- }
- }
-
- /**
- * Outputs a result set to stdout in plain text format.
- *
- * @param rs ResultSet to show.
- * @param alreadyOnRow true if rs is already on the first row. false otherwise.
- * @param start Timestamp in milliseconds when executing the statement started. This timestamp is
- * used to compute statistics about the statement. If no statistics should be shown, set it to
- * 0.
- * @param show Functions to map columns
- * @throws SQLException
- */
- private void showResultSetPretty(
- final ResultSet rs, boolean alreadyOnRow, long start, Function... show) throws SQLException {
- final ResultSetMetaData meta = rs.getMetaData();
-
- final Function[] columnMap;
- if (show != null && 0 < show.length) {
- columnMap = show;
-
- } else {
- final int colCnt = meta.getColumnCount();
- columnMap = new Function[colCnt];
- for (int colId = 0; colId < colCnt; colId++) {
- final int p = colId + 1;
- final String name = meta.getColumnLabel(p);
- columnMap[colId] = new Identity(p, name);
- }
- }
-
- final int colCnt = columnMap.length;
- final int[] widths = new int[colCnt];
- for (int c = 0; c < colCnt; c++) {
- widths[c] = columnMap[c].name.length();
- }
-
- final List<String[]> rows = new ArrayList<>();
- while (alreadyOnRow || rs.next()) {
- final String[] row = new String[columnMap.length];
- for (int c = 0; c < colCnt; c++) {
- row[c] = columnMap[c].apply(rs);
- if (row[c] == null) {
- row[c] = "NULL";
- }
- widths[c] = Math.max(widths[c], row[c].length());
- }
- rows.add(row);
- alreadyOnRow = false;
- }
-
- final StringBuilder b = new StringBuilder();
- for (int c = 0; c < colCnt; c++) {
- if (0 < c) {
- b.append(" | ");
- }
-
- String n = columnMap[c].name;
- if (widths[c] < n.length()) {
- n = n.substring(0, widths[c]);
- }
- b.append(n);
-
- if (c < colCnt - 1) {
- for (int pad = n.length(); pad < widths[c]; pad++) {
- b.append(' ');
- }
- }
- }
- println(" " + b.toString());
-
- b.setLength(0);
- for (int c = 0; c < colCnt; c++) {
- if (0 < c) {
- b.append("-+-");
- }
- for (int pad = 0; pad < widths[c]; pad++) {
- b.append('-');
- }
- }
- println(" " + b.toString());
-
- boolean dataTruncated = false;
- for (String[] row : rows) {
- b.setLength(0);
- b.append(' ');
-
- for (int c = 0; c < colCnt; c++) {
- final int max = widths[c];
- if (0 < c) {
- b.append(" | ");
- }
-
- String s = row[c];
- if (1 < colCnt && max < s.length()) {
- s = s.substring(0, max);
- dataTruncated = true;
- }
- b.append(s);
-
- if (c < colCnt - 1) {
- for (int pad = s.length(); pad < max; pad++) {
- b.append(' ');
- }
- }
- }
- println(b.toString());
- }
-
- if (dataTruncated) {
- warning("some column data was truncated");
- }
-
- if (start != 0) {
- final int rowCount = rows.size();
- final long ms = TimeUtil.nowMs() - start;
- println("(" + rowCount + (rowCount == 1 ? " row" : " rows") + "; " + ms + " ms)");
- }
- }
-
- private void warning(String msg) {
- switch (outputFormat) {
- case JSON_SINGLE:
- case JSON:
- {
- final JsonObject obj = new JsonObject();
- obj.addProperty("type", "warning");
- obj.addProperty("message", msg);
- println(obj.toString());
- break;
- }
-
- case PRETTY:
- default:
- println("WARNING: " + msg);
- break;
- }
- }
-
- private void error(SQLException err) {
- switch (outputFormat) {
- case JSON_SINGLE:
- case JSON:
- {
- final JsonObject obj = new JsonObject();
- obj.addProperty("type", "error");
- obj.addProperty("message", err.getMessage());
- println(obj.toString());
- break;
- }
-
- case PRETTY:
- default:
- println("ERROR: " + err.getMessage());
- break;
- }
- }
-
- private void print(String s) {
- out.print(s);
- out.flush();
- }
-
- private void println(String s) {
- out.print(s);
- out.print('\n');
- out.flush();
- }
-
- private String readLine() {
- try {
- return in.readLine();
- } catch (IOException e) {
- return null;
- }
- }
-
- private void showBanner() {
- if (outputFormat == OutputFormat.PRETTY) {
- println("Welcome to Gerrit Code Review " + Version.getVersion());
- try {
- print("(");
- print(connection.getMetaData().getDatabaseProductName());
- print(" ");
- print(connection.getMetaData().getDatabaseProductVersion());
- println(")");
- } catch (SQLException err) {
- error(err);
- }
- println("");
- println("Type '\\h' for help. Type '\\r' to clear the buffer.");
- println("");
- }
- }
-
- private void showHelp() {
- final StringBuilder help = new StringBuilder();
- help.append("General\n");
- help.append(" \\q quit\n");
-
- help.append("\n");
- help.append("Query Buffer\n");
- help.append(" \\g execute the query buffer\n");
- help.append(" \\p display the current buffer\n");
- help.append(" \\r clear the query buffer\n");
-
- help.append("\n");
- help.append("Informational\n");
- help.append(" \\d list all tables\n");
- help.append(" \\d NAME describe table\n");
-
- help.append("\n");
- print(help.toString());
- }
-
- private abstract static class Function {
- final String name;
-
- Function(String name) {
- this.name = name;
- }
-
- abstract String apply(ResultSet rs) throws SQLException;
- }
-
- private static class Identity extends Function {
- static Identity create(ResultSet rs, String name) throws SQLException {
- return new Identity(rs.findColumn(name), name);
- }
-
- final int colId;
-
- Identity(int colId, String name) {
- super(name);
- this.colId = colId;
- }
-
- @Override
- String apply(ResultSet rs) throws SQLException {
- return rs.getString(colId);
- }
- }
-
- private static class IndexInfo {
- String name;
- boolean unique;
- final Map<Integer, String> columns = new TreeMap<>();
- final StringBuilder filter = new StringBuilder();
-
- void addColumn(int pos, String column) {
- columns.put(Integer.valueOf(pos), column);
- }
-
- @Override
- public String toString() {
- final StringBuilder r = new StringBuilder();
- r.append(name);
- if (unique) {
- r.append(" UNIQUE");
- }
- r.append(" (");
- boolean first = true;
- for (String c : columns.values()) {
- if (!first) {
- r.append(", ");
- }
- r.append(c);
- first = false;
- }
- r.append(")");
- if (filter.length() > 0) {
- r.append(" WHERE ");
- r.append(filter);
- }
- return r.toString();
- }
- }
-}
diff --git a/java/com/google/gerrit/sshd/commands/ReloadConfig.java b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
index cbe3c57031..eeb48bb864 100644
--- a/java/com/google/gerrit/sshd/commands/ReloadConfig.java
+++ b/java/com/google/gerrit/sshd/commands/ReloadConfig.java
@@ -38,6 +38,7 @@ public class ReloadConfig extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
Multimap<UpdateResult, ConfigUpdateEntry> updates = gerritServerConfigReloader.reloadConfig();
if (updates.isEmpty()) {
stdout.println("No config entries updated!");
diff --git a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
index 166ad68c67..976e7bd789 100644
--- a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -46,6 +46,7 @@ public class RenameGroupCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
try {
GroupResource rsrc = groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(groupName));
NameInput input = new NameInput();
diff --git a/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 786048fc03..4202aac9d4 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -14,13 +14,16 @@
package com.google.gerrit.sshd.commands;
+import static com.google.gerrit.util.cli.Localizable.localizable;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
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.exceptions.StorageException;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.AbandonInput;
import com.google.gerrit.extensions.api.changes.ChangeApi;
@@ -30,8 +33,8 @@ import com.google.gerrit.extensions.api.changes.RestoreInput;
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.OutputFormat;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
@@ -40,20 +43,26 @@ import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.gerrit.util.cli.CmdLineParser;
+import com.google.gerrit.util.cli.OptionUtil;
import com.google.gson.JsonSyntaxException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.util.ArrayList;
+import java.lang.reflect.AnnotatedElement;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
+import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.FieldSetter;
+import org.kohsuke.args4j.spi.OneArgumentOptionHandler;
+import org.kohsuke.args4j.spi.Setter;
@CommandMetaData(name = "review", description = "Apply reviews to one or more patch sets")
public class ReviewCommand extends SshCommand {
@@ -61,10 +70,8 @@ public class ReviewCommand extends SshCommand {
@Override
protected final CmdLineParser newCmdLineParser(Object options) {
- final CmdLineParser parser = super.newCmdLineParser(options);
- for (ApproveOption c : optionList) {
- parser.addOption(c, c);
- }
+ CmdLineParser parser = super.newCmdLineParser(options);
+ optionMap.forEach((o, s) -> parser.addOption(s, o));
return parser;
}
@@ -82,7 +89,7 @@ public class ReviewCommand extends SshCommand {
patchSets.add(ps);
} catch (UnloggedFailure e) {
throw new IllegalArgumentException(e.getMessage(), e);
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new IllegalArgumentException("database error", e);
}
}
@@ -154,11 +161,12 @@ public class ReviewCommand extends SshCommand {
@Inject private PatchSetParser psParser;
- private List<ApproveOption> optionList;
+ private Map<Option, LabelSetter> optionMap;
private Map<String, Short> customLabels;
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
if (abandonChange) {
if (restoreChange) {
throw die("abandon and restore actions are mutually exclusive");
@@ -257,11 +265,8 @@ public class ReviewCommand extends SshCommand {
review.notify = notify;
review.labels = new TreeMap<>();
review.drafts = ReviewInput.DraftHandling.PUBLISH;
- for (ApproveOption ao : optionList) {
- Short v = ao.value();
- if (v != null) {
- review.labels.put(ao.getLabelName(), v);
- }
+ for (LabelSetter setter : optionMap.values()) {
+ setter.getValue().ifPresent(v -> review.labels.put(setter.getLabelName(), v));
}
review.labels.putAll(customLabels);
@@ -315,7 +320,7 @@ public class ReviewCommand extends SshCommand {
@Override
protected void parseCommandLine() throws UnloggedFailure {
- optionList = new ArrayList<>();
+ optionMap = new LinkedHashMap<>();
customLabels = new HashMap<>();
ProjectState allProjectsState;
@@ -332,10 +337,111 @@ public class ReviewCommand extends SshCommand {
usage.append(v.format()).append("\n");
}
- final String name = "--" + type.getName().toLowerCase();
- optionList.add(new ApproveOption(name, usage.toString(), type));
+ optionMap.put(newApproveOption(type, usage.toString()), new LabelSetter(type));
}
super.parseCommandLine();
}
+
+ private static String asOptionName(LabelType type) {
+ return "--" + type.getName().toLowerCase();
+ }
+
+ private static Option newApproveOption(LabelType type, String usage) {
+ return OptionUtil.newOption(
+ asOptionName(type),
+ new String[0],
+ usage,
+ "N",
+ false,
+ false,
+ false,
+ LabelHandler.class,
+ new String[0],
+ new String[0]);
+ }
+
+ private static class LabelSetter implements Setter<Short> {
+ private final LabelType type;
+ private Optional<Short> value;
+
+ LabelSetter(LabelType type) {
+ this.type = requireNonNull(type);
+ this.value = Optional.empty();
+ }
+
+ Optional<Short> getValue() {
+ return value;
+ }
+
+ LabelType getLabelType() {
+ return type;
+ }
+
+ String getLabelName() {
+ return type.getName();
+ }
+
+ @Override
+ public void addValue(Short value) {
+ this.value = Optional.of(value);
+ }
+
+ @Override
+ public Class<Short> getType() {
+ return Short.class;
+ }
+
+ @Override
+ public boolean isMultiValued() {
+ return false;
+ }
+
+ @Override
+ public FieldSetter asFieldSetter() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public AnnotatedElement asAnnotatedElement() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public static class LabelHandler extends OneArgumentOptionHandler<Short> {
+ private final LabelType type;
+
+ public LabelHandler(
+ org.kohsuke.args4j.CmdLineParser parser, OptionDef option, Setter<Short> setter) {
+ super(parser, option, setter);
+ this.type = ((LabelSetter) setter).getLabelType();
+ }
+
+ @Override
+ protected Short parse(String token) throws NumberFormatException, CmdLineException {
+ String argument = token;
+ if (argument.startsWith("+")) {
+ argument = argument.substring(1);
+ }
+
+ short value = Short.parseShort(argument);
+ LabelValue min = type.getMin();
+ LabelValue max = type.getMax();
+
+ if (value < min.getValue() || value > max.getValue()) {
+ String e =
+ "\""
+ + token
+ + "\" must be in range "
+ + min.formatValue()
+ + ".."
+ + max.formatValue()
+ + " for \""
+ + asOptionName(type)
+ + "\"";
+ throw new CmdLineException(owner, localizable(e));
+ }
+ return value;
+ }
+ }
}
diff --git a/java/com/google/gerrit/sshd/commands/ScpCommand.java b/java/com/google/gerrit/sshd/commands/ScpCommand.java
index b3a9a16cc1..5122b3503b 100644
--- a/java/com/google/gerrit/sshd/commands/ScpCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ScpCommand.java
@@ -27,7 +27,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.tools.ToolsCatalog;
-import com.google.gerrit.server.tools.ToolsCatalog.Entry;
import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
import java.io.ByteArrayOutputStream;
@@ -104,14 +103,14 @@ final class ScpCommand extends BaseCommand {
root = "";
}
- final Entry ent = toc.get(root);
+ final ToolsCatalog.Entry ent = toc.get(root);
if (ent == null) {
throw new IOException(root + " not found");
- } else if (Entry.Type.FILE == ent.getType()) {
+ } else if (ToolsCatalog.Entry.Type.FILE == ent.getType()) {
readFile(ent);
- } else if (Entry.Type.DIR == ent.getType()) {
+ } else if (ToolsCatalog.Entry.Type.DIR == ent.getType()) {
if (!opt_r) {
throw new IOException(root + " not a regular file");
}
@@ -156,7 +155,7 @@ final class ScpCommand extends BaseCommand {
}
}
- private void readFile(Entry ent) throws IOException {
+ private void readFile(ToolsCatalog.Entry ent) throws IOException {
byte[] data = ent.getBytes();
if (data == null) {
throw new FileNotFoundException(ent.getPath());
@@ -170,12 +169,12 @@ final class ScpCommand extends BaseCommand {
readAck();
}
- private void readDir(Entry dir) throws IOException {
+ private void readDir(ToolsCatalog.Entry dir) throws IOException {
header(dir, 0);
readAck();
- for (Entry e : dir.getChildren()) {
- if (Entry.Type.DIR == e.getType()) {
+ for (ToolsCatalog.Entry e : dir.getChildren()) {
+ if (ToolsCatalog.Entry.Type.DIR == e.getType()) {
readDir(e);
} else {
readFile(e);
@@ -187,7 +186,8 @@ final class ScpCommand extends BaseCommand {
readAck();
}
- private void header(Entry dir, int len) throws IOException, UnsupportedEncodingException {
+ private void header(ToolsCatalog.Entry dir, int len)
+ throws IOException, UnsupportedEncodingException {
final StringBuilder buf = new StringBuilder();
switch (dir.getType()) {
case DIR:
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 0ff19f74f1..1ecb591d8d 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -18,7 +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.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.api.accounts.SshKeyInput;
import com.google.gerrit.extensions.common.EmailInfo;
@@ -52,7 +52,6 @@ import com.google.gerrit.server.restapi.account.PutName;
import com.google.gerrit.server.restapi.account.PutPreferred;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.BufferedReader;
@@ -155,6 +154,7 @@ final class SetAccountCommand extends SshCommand {
@Override
public void run() throws Exception {
+ enableGracefulStop();
user = genericUserFactory.create(id);
validate();
@@ -213,8 +213,7 @@ final class SetAccountCommand extends SshCommand {
}
private void setAccount()
- throws OrmException, IOException, UnloggedFailure, ConfigInvalidException,
- PermissionBackendException {
+ throws IOException, UnloggedFailure, ConfigInvalidException, PermissionBackendException {
user = genericUserFactory.create(id);
rsrc = new AccountResource(user.asIdentifiedUser());
try {
@@ -273,8 +272,7 @@ final class SetAccountCommand extends SshCommand {
}
private void addSshKeys(List<String> sshKeys)
- throws RestApiException, OrmException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
for (String sshKey : sshKeys) {
SshKeyInput in = new SshKeyInput();
in.raw = RawInputUtil.create(sshKey.getBytes(UTF_8), "text/plain");
@@ -283,8 +281,8 @@ final class SetAccountCommand extends SshCommand {
}
private void deleteSshKeys(List<String> sshKeys)
- throws RestApiException, OrmException, RepositoryNotFoundException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+ PermissionBackendException {
List<SshKeyInfo> infos = getSshKeys.apply(rsrc);
if (sshKeys.contains("ALL")) {
for (SshKeyInfo i : infos) {
@@ -302,14 +300,14 @@ final class SetAccountCommand extends SshCommand {
}
private void deleteSshKey(SshKeyInfo i)
- throws AuthException, OrmException, RepositoryNotFoundException, IOException,
- ConfigInvalidException, PermissionBackendException {
+ throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+ PermissionBackendException {
AccountSshKey sshKey = AccountSshKey.create(user.getAccountId(), i.seq, i.sshPublicKey);
deleteSshKey.apply(new AccountResource.SshKey(user.asIdentifiedUser(), sshKey), null);
}
private void addEmail(String email)
- throws UnloggedFailure, RestApiException, OrmException, IOException, ConfigInvalidException,
+ throws UnloggedFailure, RestApiException, IOException, ConfigInvalidException,
PermissionBackendException {
EmailInput in = new EmailInput();
in.email = email;
@@ -322,8 +320,7 @@ final class SetAccountCommand extends SshCommand {
}
private void deleteEmail(String email)
- throws RestApiException, OrmException, IOException, ConfigInvalidException,
- PermissionBackendException {
+ throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (email.equals("ALL")) {
List<EmailInfo> emails = getEmails.apply(rsrc);
for (EmailInfo e : emails) {
@@ -335,8 +332,7 @@ final class SetAccountCommand extends SshCommand {
}
private void putPreferred(String email)
- throws RestApiException, OrmException, IOException, PermissionBackendException,
- ConfigInvalidException {
+ throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
for (EmailInfo e : getEmails.apply(rsrc)) {
if (e.email.equals(email)) {
putPreferred.apply(new AccountResource.Email(user.asIdentifiedUser(), email), null);
diff --git a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
index fd7ef75e55..b6d283eb02 100644
--- a/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -43,6 +43,7 @@ public class SetHeadCommand extends SshCommand {
@Override
protected void run() throws Exception {
+ enableGracefulStop();
HeadInput input = new HeadInput();
input.ref = newHead;
try {
diff --git a/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java b/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
index cfdd73562c..3faf5989a4 100644
--- a/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetLoggingLevelCommand.java
@@ -61,6 +61,7 @@ public class SetLoggingLevelCommand extends SshCommand {
@SuppressWarnings("unchecked")
@Override
protected void run() throws MalformedURLException {
+ enableGracefulStop();
if (level == LevelOption.RESET) {
reset();
} else {
diff --git a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
index 324257b93a..5557b16ace 100644
--- a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
@@ -102,6 +102,7 @@ public class SetMembersCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure, Failure, Exception {
+ enableGracefulStop();
try {
for (AccountGroup.UUID groupUuid : groups) {
GroupResource resource =
diff --git a/java/com/google/gerrit/sshd/commands/SetParentCommand.java b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
index f2d8c4c8ae..87722d926b 100644
--- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
@@ -16,6 +16,7 @@ package com.google.gerrit.sshd.commands;
import static java.util.stream.Collectors.toList;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.projects.ParentInput;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -33,7 +34,6 @@ import com.google.gerrit.server.restapi.project.ListChildProjects;
import com.google.gerrit.server.restapi.project.SetParent;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -90,6 +90,7 @@ final class SetParentCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
if (oldParent == null && children.isEmpty()) {
throw die(
"child projects have to be specified as "
@@ -112,7 +113,7 @@ final class SetParentCommand extends SshCommand {
childProjects.addAll(getChildrenForReparenting(oldParent));
} catch (PermissionBackendException e) {
throw new Failure(1, "permissions unavailable", e);
- } catch (OrmException | RestApiException e) {
+ } catch (StorageException | RestApiException e) {
throw new Failure(1, "failure in request", e);
}
}
@@ -149,7 +150,7 @@ final class SetParentCommand extends SshCommand {
* reparenting.
*/
private List<Project.NameKey> getChildrenForReparenting(ProjectState parent)
- throws PermissionBackendException, OrmException, RestApiException {
+ throws PermissionBackendException, RestApiException {
final List<Project.NameKey> childProjects = new ArrayList<>();
final List<Project.NameKey> excluded = new ArrayList<>(excludedChildren.size());
for (ProjectState excludedChild : excludedChildren) {
diff --git a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
index 8c9fc9fa5b..9866c4e4e8 100644
--- a/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
@@ -132,6 +132,7 @@ final class SetProjectCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
ConfigInput configInput = new ConfigInput();
configInput.requireChangeId = requireChangeID;
configInput.submitType = submitType;
diff --git a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index a4a8ea865f..5a04026638 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -15,6 +15,7 @@
package com.google.gerrit.sshd.commands;
import com.google.common.flogger.FluentLogger;
+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;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.restapi.change.PostReviewers;
import com.google.gerrit.sshd.ChangeArgumentParser;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -75,7 +75,7 @@ public class SetReviewersCommand extends SshCommand {
changeArgumentParser.addChange(token, changes, projectState);
} catch (IOException | UnloggedFailure e) {
throw new IllegalArgumentException(e.getMessage(), e);
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new IllegalArgumentException("database is down", e);
} catch (PermissionBackendException e) {
throw new IllegalArgumentException("can't check permissions", e);
@@ -96,6 +96,7 @@ public class SetReviewersCommand extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
boolean ok = true;
for (ChangeResource rsrc : changes.values()) {
try {
diff --git a/java/com/google/gerrit/sshd/commands/ShowCaches.java b/java/com/google/gerrit/sshd/commands/ShowCaches.java
index 831b1d93bb..039b74e3d6 100644
--- a/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -47,7 +47,6 @@ import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
-import java.util.Map.Entry;
import org.apache.sshd.common.io.IoAcceptor;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.io.mina.MinaSession;
@@ -112,6 +111,7 @@ final class ShowCaches extends SshCommand {
@Override
protected void run() throws UnloggedFailure {
+ enableGracefulStop();
nw = columns - 50;
Date now = new Date();
stdout.format(
@@ -267,7 +267,7 @@ final class ShowCaches extends SshCommand {
stdout.print(String.format(" %14s", s.name()));
}
stdout.print('\n');
- for (Entry<String, Map<Thread.State, Integer>> e : threadSummary.counts.entrySet()) {
+ for (Map.Entry<String, Map<Thread.State, Integer>> e : threadSummary.counts.entrySet()) {
stdout.print(String.format(" %-22s", e.getKey()));
for (Thread.State s : Thread.State.values()) {
stdout.print(String.format(" %14d", nullToZero(e.getValue().get(s))));
diff --git a/java/com/google/gerrit/sshd/commands/ShowConnections.java b/java/com/google/gerrit/sshd/commands/ShowConnections.java
index d579ef68a7..52e824b483 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -85,6 +85,7 @@ final class ShowConnections extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
final IoAcceptor acceptor = daemon.getIoAcceptor();
if (acceptor == null) {
throw new Failure(1, "fatal: sshd no longer running");
diff --git a/java/com/google/gerrit/sshd/commands/ShowQueue.java b/java/com/google/gerrit/sshd/commands/ShowQueue.java
index 2a7bd6e0a5..04d06eebb9 100644
--- a/java/com/google/gerrit/sshd/commands/ShowQueue.java
+++ b/java/com/google/gerrit/sshd/commands/ShowQueue.java
@@ -84,6 +84,7 @@ final class ShowQueue extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
maxCommandWidth = wide ? Integer.MAX_VALUE : columns - 8 - 12 - 12 - 4 - 4;
stdout.print(
String.format(
diff --git a/java/com/google/gerrit/sshd/commands/UploadArchive.java b/java/com/google/gerrit/sshd/commands/UploadArchive.java
index ac914a5268..24f82a78ab 100644
--- a/java/com/google/gerrit/sshd/commands/UploadArchive.java
+++ b/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -238,7 +238,7 @@ public class UploadArchive extends AbstractGitCommand {
options.level9)
.indexOf(true);
if (value >= 0) {
- return ImmutableMap.<String, Object>of("level", Integer.valueOf(value));
+ return ImmutableMap.of("level", Integer.valueOf(value));
}
}
return Collections.emptyMap();
diff --git a/java/com/google/gerrit/sshd/commands/VersionCommand.java b/java/com/google/gerrit/sshd/commands/VersionCommand.java
index 8fac979be2..f8771fb21f 100644
--- a/java/com/google/gerrit/sshd/commands/VersionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/VersionCommand.java
@@ -25,6 +25,7 @@ final class VersionCommand extends SshCommand {
@Override
protected void run() throws Failure {
+ enableGracefulStop();
String v = Version.getVersion();
if (v == null) {
throw new Failure(1, "fatal: version unavailable");
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index 7bdfe7e42a..dc1c150853 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -16,6 +16,7 @@ java_library(
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/gpg",
"//java/com/google/gerrit/index",
@@ -36,7 +37,6 @@ java_library(
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/server/util/time",
"//lib:guava",
- "//lib:gwtorm",
"//lib:h2",
"//lib:junit",
"//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/testing/DisabledReviewDb.java b/java/com/google/gerrit/testing/DisabledReviewDb.java
deleted file mode 100644
index 2bf95b043c..0000000000
--- a/java/com/google/gerrit/testing/DisabledReviewDb.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2015 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 com.google.gerrit.reviewdb.server.ChangeAccess;
-import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
-import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
-import com.google.gerrit.reviewdb.server.PatchSetAccess;
-import com.google.gerrit.reviewdb.server.PatchSetApprovalAccess;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.SchemaVersionAccess;
-import com.google.gwtorm.server.Access;
-import com.google.gwtorm.server.StatementExecutor;
-
-/** ReviewDb that is disabled for testing. */
-public class DisabledReviewDb implements ReviewDb {
- public static class Disabled extends RuntimeException {
- private static final long serialVersionUID = 1L;
-
- private Disabled() {
- super("ReviewDb is disabled for this test");
- }
- }
-
- @Override
- public void close() {
- // Do nothing.
- }
-
- @Override
- public void commit() {
- throw new Disabled();
- }
-
- @Override
- public void rollback() {
- throw new Disabled();
- }
-
- @Override
- public void updateSchema(StatementExecutor e) {
- throw new Disabled();
- }
-
- @Override
- public void pruneSchema(StatementExecutor e) {
- throw new Disabled();
- }
-
- @Override
- public Access<?, ?>[] allRelations() {
- throw new Disabled();
- }
-
- @Override
- public SchemaVersionAccess schemaVersion() {
- throw new Disabled();
- }
-
- @Override
- public ChangeAccess changes() {
- throw new Disabled();
- }
-
- @Override
- public PatchSetApprovalAccess patchSetApprovals() {
- throw new Disabled();
- }
-
- @Override
- public ChangeMessageAccess changeMessages() {
- throw new Disabled();
- }
-
- @Override
- public PatchSetAccess patchSets() {
- throw new Disabled();
- }
-
- @Override
- public PatchLineCommentAccess patchComments() {
- throw new Disabled();
- }
-
- @Override
- public int nextAccountId() {
- throw new Disabled();
- }
-
- @Override
- public int nextAccountGroupId() {
- throw new Disabled();
- }
-
- @Override
- public int nextChangeId() {
- throw new Disabled();
- }
-}
diff --git a/java/com/google/gerrit/testing/FakeEmailSender.java b/java/com/google/gerrit/testing/FakeEmailSender.java
index dbb83c086c..a60995b5d7 100644
--- a/java/com/google/gerrit/testing/FakeEmailSender.java
+++ b/java/com/google/gerrit/testing/FakeEmailSender.java
@@ -21,7 +21,7 @@ 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.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
import com.google.gerrit.mail.Address;
import com.google.gerrit.mail.EmailHeader;
import com.google.gerrit.mail.MailHeader;
@@ -87,7 +87,7 @@ public class FakeEmailSender implements EmailSender {
@Inject
FakeEmailSender(WorkQueue workQueue) {
this.workQueue = workQueue;
- messages = Collections.synchronizedList(new ArrayList<Message>());
+ messages = Collections.synchronizedList(new ArrayList<>());
messagesRead = 0;
}
diff --git a/java/com/google/gerrit/testing/FakeGroupAuditService.java b/java/com/google/gerrit/testing/FakeGroupAuditService.java
deleted file mode 100644
index d7f7549359..0000000000
--- a/java/com/google/gerrit/testing/FakeGroupAuditService.java
+++ /dev/null
@@ -1,112 +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.testing;
-
-import com.google.common.collect.ImmutableSet;
-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.AuditEvent;
-import com.google.gerrit.server.audit.AuditListener;
-import com.google.gerrit.server.audit.group.GroupAuditListener;
-import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
-import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
-import com.google.gerrit.server.group.GroupAuditService;
-import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-
-@Singleton
-public class FakeGroupAuditService implements GroupAuditService {
-
- protected final PluginSetContext<GroupAuditListener> groupAuditListeners;
- protected final PluginSetContext<AuditListener> auditListeners;
-
- public final List<AuditEvent> auditEvents = new ArrayList<>();
-
- public static class Module extends AbstractModule {
- @Override
- public void configure() {
- DynamicSet.setOf(binder(), GroupAuditListener.class);
- DynamicSet.setOf(binder(), AuditListener.class);
- bind(GroupAuditService.class).to(FakeGroupAuditService.class);
- }
- }
-
- @Inject
- public FakeGroupAuditService(
- PluginSetContext<GroupAuditListener> groupAuditListeners,
- PluginSetContext<AuditListener> auditListeners) {
- this.groupAuditListeners = groupAuditListeners;
- this.auditListeners = auditListeners;
- }
-
- public void clearEvents() {
- auditEvents.clear();
- }
-
- @Override
- public void dispatch(AuditEvent action) {
- auditEvents.add(action);
- }
-
- @Override
- public void dispatchAddMembers(
- Account.Id actor,
- AccountGroup.UUID updatedGroup,
- ImmutableSet<Account.Id> addedMembers,
- Timestamp addedOn) {
- GroupMemberAuditEvent event =
- GroupMemberAuditEvent.create(actor, updatedGroup, addedMembers, addedOn);
- groupAuditListeners.runEach(l -> l.onAddMembers(event));
- }
-
- @Override
- public void dispatchDeleteMembers(
- Account.Id actor,
- AccountGroup.UUID updatedGroup,
- ImmutableSet<Account.Id> deletedMembers,
- Timestamp deletedOn) {
- GroupMemberAuditEvent event =
- GroupMemberAuditEvent.create(actor, updatedGroup, deletedMembers, deletedOn);
- groupAuditListeners.runEach(l -> l.onDeleteMembers(event));
- }
-
- @Override
- public void dispatchAddSubgroups(
- Account.Id actor,
- AccountGroup.UUID updatedGroup,
- ImmutableSet<AccountGroup.UUID> addedSubgroups,
- Timestamp addedOn) {
- GroupSubgroupAuditEvent event =
- GroupSubgroupAuditEvent.create(actor, updatedGroup, addedSubgroups, addedOn);
- groupAuditListeners.runEach(l -> l.onAddSubgroups(event));
- }
-
- @Override
- public void dispatchDeleteSubgroups(
- Account.Id actor,
- AccountGroup.UUID updatedGroup,
- ImmutableSet<AccountGroup.UUID> deletedSubgroups,
- Timestamp deletedOn) {
- GroupSubgroupAuditEvent event =
- GroupSubgroupAuditEvent.create(actor, updatedGroup, deletedSubgroups, deletedOn);
- groupAuditListeners.runEach(l -> l.onDeleteSubgroups(event));
- }
-}
diff --git a/java/com/google/gerrit/testing/GerritBaseTests.java b/java/com/google/gerrit/testing/GerritBaseTests.java
index d7f3e0d2d5..500d791685 100644
--- a/java/com/google/gerrit/testing/GerritBaseTests.java
+++ b/java/com/google/gerrit/testing/GerritBaseTests.java
@@ -15,8 +15,6 @@
package com.google.gerrit.testing;
import com.google.common.base.CharMatcher;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
@@ -25,10 +23,6 @@ import org.junit.rules.TestName;
@Ignore
public abstract class GerritBaseTests {
- static {
- KeyUtil.setEncoderImpl(new StandardKeyEncoder());
- }
-
@Rule public ExpectedException exception = ExpectedException.none();
@Rule public final TestName testName = new TestName();
diff --git a/java/com/google/gerrit/testing/GerritServerTests.java b/java/com/google/gerrit/testing/GerritServerTests.java
index 69806e1657..9a922d6449 100644
--- a/java/com/google/gerrit/testing/GerritServerTests.java
+++ b/java/com/google/gerrit/testing/GerritServerTests.java
@@ -14,11 +14,9 @@
package com.google.gerrit.testing;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
import org.eclipse.jgit.lib.Config;
import org.junit.Rule;
import org.junit.rules.TestRule;
-import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
@@ -28,32 +26,13 @@ public class GerritServerTests extends GerritBaseTests {
@ConfigSuite.Name private String configName;
- protected MutableNotesMigration notesMigration;
-
@Rule
public TestRule testRunner =
- new TestRule() {
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
+ (base, description) ->
+ new Statement() {
@Override
public void evaluate() throws Throwable {
- beforeTest();
- try {
- base.evaluate();
- } finally {
- afterTest();
- }
+ base.evaluate();
}
};
- }
- };
-
- public void beforeTest() throws Exception {
- notesMigration = NoteDbMode.newNotesMigrationFromEnv();
- }
-
- public void afterTest() {
- NoteDbMode.resetFromEnv(notesMigration);
- }
}
diff --git a/java/com/google/gerrit/testing/InMemoryDatabase.java b/java/com/google/gerrit/testing/InMemoryDatabase.java
deleted file mode 100644
index 66a5290cd3..0000000000
--- a/java/com/google/gerrit/testing/InMemoryDatabase.java
+++ /dev/null
@@ -1,194 +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.testing;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.pgm.init.index.elasticsearch.ElasticIndexModuleOnInit;
-import com.google.gerrit.pgm.init.index.lucene.LuceneIndexModuleOnInit;
-import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.index.IndexModule;
-import com.google.gerrit.server.schema.SchemaCreator;
-import com.google.gerrit.server.schema.SchemaVersion;
-import com.google.gwtorm.jdbc.Database;
-import com.google.gwtorm.jdbc.SimpleDataSource;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Properties;
-import javax.sql.DataSource;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-/**
- * An in-memory test instance of {@link ReviewDb} database.
- *
- * <p>Test classes should create one instance of this class for each unique test database they want
- * to use. When the tests needing this instance are complete, ensure that {@link
- * #drop(InMemoryDatabase)} is called to free the resources so the JVM running the unit tests
- * doesn't run out of heap space.
- */
-public class InMemoryDatabase implements SchemaFactory<ReviewDb> {
- public static InMemoryDatabase newDatabase(LifecycleManager lifecycle) {
- Injector injector = Guice.createInjector(new InMemoryModule());
- lifecycle.add(injector);
- return injector.getInstance(InMemoryDatabase.class);
- }
-
- /** Drop the database from memory; does nothing if the instance was null. */
- public static void drop(InMemoryDatabase db) {
- if (db != null) {
- db.dbInstance.drop();
- }
- }
-
- private final SchemaCreator schemaCreator;
- private final Instance dbInstance;
-
- private boolean created;
-
- @Inject
- InMemoryDatabase(Injector injector) throws OrmException {
- Injector childInjector =
- injector.createChildInjector(
- new AbstractModule() {
- @Override
- protected void configure() {
- switch (IndexModule.getIndexType(injector)) {
- case LUCENE:
- install(new LuceneIndexModuleOnInit());
- break;
- case ELASTICSEARCH:
- install(new ElasticIndexModuleOnInit());
- break;
- default:
- throw new IllegalStateException("unsupported index.type");
- }
- }
- });
- this.schemaCreator = childInjector.getInstance(SchemaCreator.class);
- Instance dbInstanceFromInjector = childInjector.getInstance(Instance.class);
- if (dbInstanceFromInjector != null) {
- this.dbInstance = dbInstanceFromInjector;
- this.created = true;
- } else {
- this.dbInstance = new Instance();
- }
- }
-
- InMemoryDatabase(SchemaCreator schemaCreator) throws OrmException {
- this.schemaCreator = schemaCreator;
- this.dbInstance = new Instance();
- }
-
- public Instance getDbInstance() {
- return dbInstance;
- }
-
- public Database<ReviewDb> getDatabase() {
- return dbInstance.database;
- }
-
- @Override
- public ReviewDb open() throws OrmException {
- return getDatabase().open();
- }
-
- /** Ensure the database schema has been created and initialized. */
- public InMemoryDatabase create() throws OrmException {
- if (!created) {
- created = true;
- try (ReviewDb c = open()) {
- schemaCreator.create(c);
- } catch (IOException | ConfigInvalidException e) {
- throw new OrmException("Cannot create in-memory database", e);
- }
- }
- return this;
- }
-
- public CurrentSchemaVersion getSchemaVersion() throws OrmException {
- try (ReviewDb c = open()) {
- return c.schemaVersion().get(new CurrentSchemaVersion.Key());
- }
- }
-
- public void assertSchemaVersion() throws OrmException {
- assertThat(getSchemaVersion().versionNbr).isEqualTo(SchemaVersion.getBinaryVersion());
- }
-
- public static class Instance {
- private static int dbCnt;
-
- private Connection openHandle;
- private Database<ReviewDb> database;
- private boolean keepOpen;
-
- private static synchronized DataSource newDataSource() throws SQLException {
- final Properties p = new Properties();
- p.setProperty("driver", org.h2.Driver.class.getName());
- p.setProperty("url", "jdbc:h2:mem:Test_" + (++dbCnt));
- return new SimpleDataSource(p);
- }
-
- private Instance() throws OrmException {
- try {
- DataSource dataSource = newDataSource();
-
- // Open one connection. This will peg the database into memory
- // until someone calls drop on us, allowing subsequent connections
- // opened against the same URL to go to the same set of tables.
- //
- openHandle = dataSource.getConnection();
-
- // Build the access layer around the connection factory.
- //
- database = new Database<>(dataSource, ReviewDb.class);
-
- } catch (SQLException e) {
- throw new OrmException(e);
- }
- }
-
- public void setKeepOpen(boolean keepOpen) {
- this.keepOpen = keepOpen;
- }
-
- /** Drop this database from memory so it no longer exists. */
- public void drop() {
- if (keepOpen) {
- return;
- }
-
- if (openHandle != null) {
- try {
- openHandle.close();
- } catch (SQLException e) {
- System.err.println("WARNING: Cannot close database connection");
- e.printStackTrace(System.err);
- }
- openHandle = null;
- database = null;
- }
- }
- }
-}
diff --git a/java/com/google/gerrit/testing/InMemoryH2Type.java b/java/com/google/gerrit/testing/InMemoryH2Type.java
deleted file mode 100644
index ae3bf36857..0000000000
--- a/java/com/google/gerrit/testing/InMemoryH2Type.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2012 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 com.google.gerrit.server.schema.BaseDataSourceType;
-
-public class InMemoryH2Type extends BaseDataSourceType {
-
- protected InMemoryH2Type() {
- super(null);
- }
-
- @Override
- public String getUrl() {
- // not used
- throw new UnsupportedOperationException();
- }
-}
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index 18dfea0e86..83b9e2b6e0 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.Scopes.SINGLETON;
import com.google.common.base.Strings;
-import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.extensions.config.FactoryModule;
@@ -28,7 +27,6 @@ import com.google.gerrit.index.SchemaDefinitions;
import com.google.gerrit.index.project.ProjectSchemaDefinitions;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
@@ -46,7 +44,6 @@ import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.ChangeUpdateExecutor;
import com.google.gerrit.server.config.DefaultUrlFormatter;
import com.google.gerrit.server.config.GerritGlobalModule;
import com.google.gerrit.server.config.GerritInstanceNameModule;
@@ -70,38 +67,29 @@ import com.google.gerrit.server.index.account.AllAccountsIndexer;
import com.google.gerrit.server.index.change.AllChangesIndexer;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.index.group.AllGroupsIndexer;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.DiffExecutor;
import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
import com.google.gerrit.server.plugins.ServerInformationImpl;
import com.google.gerrit.server.project.DefaultProjectNameLockManager;
import com.google.gerrit.server.restapi.RestApiModule;
-import com.google.gerrit.server.schema.DataSourceType;
-import com.google.gerrit.server.schema.InMemoryAccountPatchReviewStore;
-import com.google.gerrit.server.schema.NotesMigrationSchemaFactory;
-import com.google.gerrit.server.schema.ReviewDbFactory;
+import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
import com.google.gerrit.server.schema.SchemaCreator;
+import com.google.gerrit.server.schema.SchemaCreatorImpl;
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.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
-import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
import com.google.inject.servlet.RequestScoped;
import com.google.inject.util.Providers;
import java.lang.reflect.InvocationTargetException;
@@ -122,6 +110,8 @@ public class InMemoryModule extends FactoryModule {
}
public static void setDefaults(Config cfg) {
+ cfg.setString(
+ "accountPatchReviewDb", null, "url", JdbcAccountPatchReviewStore.TEST_IN_MEMORY_URL);
cfg.setEnum("auth", null, "type", AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT);
cfg.setString("gerrit", null, "allProjects", "Test-Projects");
cfg.setString("gerrit", null, "basePath", "git");
@@ -137,15 +127,13 @@ public class InMemoryModule extends FactoryModule {
}
private final Config cfg;
- private final MutableNotesMigration notesMigration;
public InMemoryModule() {
- this(newDefaultConfig(), NoteDbMode.newNotesMigrationFromEnv());
+ this(newDefaultConfig());
}
- public InMemoryModule(Config cfg, MutableNotesMigration notesMigration) {
+ public InMemoryModule(Config cfg) {
this.cfg = cfg;
- this.notesMigration = notesMigration;
}
public void inject(Object instance) {
@@ -188,33 +176,14 @@ public class InMemoryModule extends FactoryModule {
// 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(cfg, false, false, false));
- bind(PersonIdent.class)
- .annotatedWith(GerritPersonIdent.class)
- .toProvider(GerritPersonIdentProvider.class);
- bind(String.class)
- .annotatedWith(AnonymousCowardName.class)
- .toProvider(AnonymousCowardNameProvider.class);
-
- bind(AllProjectsName.class).toProvider(AllProjectsNameProvider.class);
- bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
+ bind(GerritOptions.class).toInstance(new GerritOptions(false, false, false));
+
bind(GitRepositoryManager.class).to(InMemoryRepositoryManager.class);
bind(InMemoryRepositoryManager.class).in(SINGLETON);
bind(TrackingFooters.class).toProvider(TrackingFootersProvider.class).in(SINGLETON);
- bind(MutableNotesMigration.class).toInstance(notesMigration);
- bind(NotesMigration.class).to(MutableNotesMigration.class);
- bind(ListeningExecutorService.class)
- .annotatedWith(ChangeUpdateExecutor.class)
- .toInstance(MoreExecutors.newDirectExecutorService());
- bind(DataSourceType.class).to(InMemoryH2Type.class);
- bind(ChangeBundleReader.class).to(GwtormChangeBundleReader.class);
bind(SecureStore.class).to(DefaultSecureStore.class);
- TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
- new TypeLiteral<SchemaFactory<ReviewDb>>() {};
- bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
- bind(Key.get(schemaFactory, ReviewDbFactory.class)).to(InMemoryDatabase.class);
-
+ install(new InMemorySchemaModule());
install(NoSshKeyCache.module());
install(new GerritInstanceNameModule());
install(
@@ -243,7 +212,6 @@ public class InMemoryModule extends FactoryModule {
install(new FakeEmailSender.Module());
install(new SignedTokenEmailTokenVerifier.Module());
install(new GpgModule(cfg));
- install(new InMemoryAccountPatchReviewStore.Module());
install(new LocalMergeSuperSetComputation.Module());
bind(AllAccountsIndexer.class).toProvider(Providers.of(null));
@@ -274,6 +242,41 @@ public class InMemoryModule extends FactoryModule {
install(new DefaultProjectNameLockManager.Module());
}
+ /** Copy of SchemaModule with a slightly different server ID provider. */
+ // TODO(dborowitz): Better code sharing.
+ private class InMemorySchemaModule extends FactoryModule {
+ @Override
+ public void configure() {
+ bind(PersonIdent.class)
+ .annotatedWith(GerritPersonIdent.class)
+ .toProvider(GerritPersonIdentProvider.class);
+
+ bind(AllProjectsName.class).toProvider(AllProjectsNameProvider.class).in(SINGLETON);
+
+ bind(AllUsersName.class).toProvider(AllUsersNameProvider.class).in(SINGLETON);
+
+ bind(String.class)
+ .annotatedWith(AnonymousCowardName.class)
+ .toProvider(AnonymousCowardNameProvider.class);
+
+ bind(GroupIndexCollection.class);
+ bind(SchemaCreator.class).to(SchemaCreatorImpl.class);
+ }
+
+ @Provides
+ @Singleton
+ @GerritServerId
+ public String createServerId() {
+ String serverId =
+ cfg.getString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY);
+ if (!Strings.isNullOrEmpty(serverId)) {
+ return serverId;
+ }
+
+ return "gerrit";
+ }
+ }
+
@Provides
@Singleton
@SendEmailExecutor
@@ -288,25 +291,6 @@ public class InMemoryModule extends FactoryModule {
return queues.createQueue(2, "FanOut");
}
- @Provides
- @Singleton
- @GerritServerId
- public String createServerId() {
- String serverId =
- cfg.getString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY);
- if (!Strings.isNullOrEmpty(serverId)) {
- return serverId;
- }
-
- return "gerrit";
- }
-
- @Provides
- @Singleton
- InMemoryDatabase getInMemoryDatabase(SchemaCreator schemaCreator) throws OrmException {
- return new InMemoryDatabase(schemaCreator);
- }
-
private Module luceneIndexModule() {
return indexModule("com.google.gerrit.lucene.LuceneIndexModule");
}
diff --git a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
index cebd139612..05672aa044 100644
--- a/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
+++ b/java/com/google/gerrit/testing/InMemoryTestEnvironment.java
@@ -16,20 +16,15 @@ package com.google.gerrit.testing;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.schema.SchemaCreator;
-import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import org.eclipse.jgit.lib.Config;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
@@ -39,7 +34,7 @@ import org.junit.runners.model.Statement;
* An in-memory test environment for integration tests.
*
* <p>This test environment emulates the internals of a Gerrit server without starting a Gerrit
- * site. ReviewDb as well as NoteDb are represented by in-memory representations.
+ * site. Git repositories, including NoteDb, are stored in memory.
*
* <p>Each test is executed with a fresh and clean test environment. Hence, modifications applied in
* one test don't carry over to subsequent ones.
@@ -49,13 +44,9 @@ public final class InMemoryTestEnvironment implements MethodRule {
@Inject private AccountManager accountManager;
@Inject private IdentifiedUser.GenericFactory userFactory;
- @Inject private SchemaFactory<ReviewDb> schemaFactory;
@Inject private SchemaCreator schemaCreator;
@Inject private ThreadLocalRequestContext requestContext;
- // Only for use in setting up/tearing down injector.
- @Inject private InMemoryDatabase inMemoryDatabase;
- private ReviewDb db;
private LifecycleManager lifecycle;
/** Create a test environment using an empty base config. */
@@ -92,40 +83,26 @@ public final class InMemoryTestEnvironment implements MethodRule {
public void setApiUser(Account.Id id) {
IdentifiedUser user = userFactory.create(id);
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(() -> user);
}
private void setUp(Object target) throws Exception {
Config cfg = configProvider.get();
InMemoryModule.setDefaults(cfg);
- Injector injector =
- Guice.createInjector(new InMemoryModule(cfg, NoteDbMode.newNotesMigrationFromEnv()));
+ Injector injector = Guice.createInjector(new InMemoryModule(cfg));
injector.injectMembers(this);
lifecycle = new LifecycleManager();
lifecycle.add(injector);
lifecycle.start();
- try (ReviewDb underlyingDb = inMemoryDatabase.getDatabase().open()) {
- schemaCreator.create(underlyingDb);
- }
- db = schemaFactory.open();
+ schemaCreator.create();
// The first user is added to the "Administrators" group. See AccountManager#create().
setApiUser(accountManager.authenticate(AuthRequest.forUser("admin")).getAccountId());
- // Inject target members after setting API user, so it can @Inject a ReviewDb if it wants.
+ // Inject target members after setting API user, so it can @Inject request-scoped objects if it
+ // wants.
injector.injectMembers(target);
}
@@ -136,9 +113,5 @@ public final class InMemoryTestEnvironment implements MethodRule {
if (requestContext != null) {
requestContext.setContext(null);
}
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(inMemoryDatabase);
}
}
diff --git a/java/com/google/gerrit/testing/NoteDbChecker.java b/java/com/google/gerrit/testing/NoteDbChecker.java
deleted file mode 100644
index 1dc8ee2131..0000000000
--- a/java/com/google/gerrit/testing/NoteDbChecker.java
+++ /dev/null
@@ -1,225 +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.testing;
-
-import static com.google.common.truth.Truth.assertThat;
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.flogger.FluentLogger;
-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.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.runner.Description;
-
-@Singleton
-public class NoteDbChecker {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- private final Provider<ReviewDb> dbProvider;
- private final GitRepositoryManager repoManager;
- private final MutableNotesMigration notesMigration;
- private final ChangeBundleReader bundleReader;
- private final ChangeNotes.Factory notesFactory;
- private final ChangeRebuilder changeRebuilder;
- private final CommentsUtil commentsUtil;
-
- @Inject
- NoteDbChecker(
- Provider<ReviewDb> dbProvider,
- GitRepositoryManager repoManager,
- MutableNotesMigration notesMigration,
- ChangeBundleReader bundleReader,
- ChangeNotes.Factory notesFactory,
- ChangeRebuilder changeRebuilder,
- CommentsUtil commentsUtil) {
- this.dbProvider = dbProvider;
- this.repoManager = repoManager;
- this.bundleReader = bundleReader;
- this.notesMigration = notesMigration;
- this.notesFactory = notesFactory;
- this.changeRebuilder = changeRebuilder;
- this.commentsUtil = commentsUtil;
- }
-
- public void rebuildAndCheckAllChanges() throws Exception {
- rebuildAndCheckChanges(
- getUnwrappedDb().changes().all().toList().stream().map(Change::getId),
- ImmutableListMultimap.of());
- }
-
- public void rebuildAndCheckChanges(Change.Id... changeIds) throws Exception {
- rebuildAndCheckChanges(Arrays.stream(changeIds), ImmutableListMultimap.of());
- }
-
- private void rebuildAndCheckChanges(
- Stream<Change.Id> changeIds, ListMultimap<Change.Id, String> expectedDiffs) throws Exception {
- ReviewDb db = getUnwrappedDb();
-
- List<ChangeBundle> allExpected = readExpected(changeIds);
-
- boolean oldWrite = notesMigration.rawWriteChangesSetting();
- boolean oldRead = notesMigration.readChanges();
- try {
- notesMigration.setWriteChanges(true);
- notesMigration.setReadChanges(true);
- List<String> msgs = new ArrayList<>();
- for (ChangeBundle expected : allExpected) {
- Change c = expected.getChange();
- try {
- changeRebuilder.rebuild(db, c.getId());
- } catch (RepositoryNotFoundException e) {
- msgs.add("Repository not found for change, cannot convert: " + c);
- }
- }
-
- checkActual(allExpected, expectedDiffs, msgs);
- } finally {
- notesMigration.setReadChanges(oldRead);
- notesMigration.setWriteChanges(oldWrite);
- }
- }
-
- public void checkChanges(Change.Id... changeIds) throws Exception {
- checkActual(
- readExpected(Arrays.stream(changeIds)), ImmutableListMultimap.of(), new ArrayList<>());
- }
-
- public void rebuildAndCheckChange(Change.Id changeId, String... expectedDiff) throws Exception {
- ImmutableListMultimap.Builder<Change.Id, String> b = ImmutableListMultimap.builder();
- b.putAll(changeId, Arrays.asList(expectedDiff));
- rebuildAndCheckChanges(Stream.of(changeId), b.build());
- }
-
- public void assertNoChangeRef(Project.NameKey project, Change.Id changeId) throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef(RefNames.changeMetaRef(changeId))).isNull();
- }
- }
-
- public void assertNoReviewDbChanges(Description desc) throws Exception {
- ReviewDb db = getUnwrappedDb();
- assertThat(db.changes().all().toList()).named("Changes in " + desc.getTestClass()).isEmpty();
- assertThat(db.changeMessages().all().toList())
- .named("ChangeMessages in " + desc.getTestClass())
- .isEmpty();
- assertThat(db.patchSets().all().toList())
- .named("PatchSets in " + desc.getTestClass())
- .isEmpty();
- assertThat(db.patchSetApprovals().all().toList())
- .named("PatchSetApprovals in " + desc.getTestClass())
- .isEmpty();
- assertThat(db.patchComments().all().toList())
- .named("PatchLineComments in " + desc.getTestClass())
- .isEmpty();
- }
-
- private List<ChangeBundle> readExpected(Stream<Change.Id> changeIds) throws Exception {
- boolean old = notesMigration.readChanges();
- try {
- notesMigration.setReadChanges(false);
- return changeIds
- .sorted(comparing(IntKey::get))
- .map(this::readBundleUnchecked)
- .collect(toList());
- } finally {
- notesMigration.setReadChanges(old);
- }
- }
-
- private ChangeBundle readBundleUnchecked(Change.Id id) {
- try {
- return bundleReader.fromReviewDb(getUnwrappedDb(), id);
- } catch (OrmException e) {
- throw new OrmRuntimeException(e);
- }
- }
-
- private void checkActual(
- List<ChangeBundle> allExpected,
- ListMultimap<Change.Id, String> expectedDiffs,
- List<String> msgs)
- throws Exception {
- ReviewDb db = getUnwrappedDb();
- boolean oldRead = notesMigration.readChanges();
- boolean oldWrite = notesMigration.rawWriteChangesSetting();
- try {
- notesMigration.setWriteChanges(true);
- notesMigration.setReadChanges(true);
- for (ChangeBundle expected : allExpected) {
- Change c = expected.getChange();
- ChangeBundle actual;
- try {
- actual =
- ChangeBundle.fromNotes(
- commentsUtil, notesFactory.create(db, c.getProject(), c.getId()));
- } catch (Throwable t) {
- String msg = "Error converting change: " + c;
- msgs.add(msg);
- logger.atSevere().withCause(t).log(msg);
- continue;
- }
- List<String> diff = expected.differencesFrom(actual);
- List<String> expectedDiff = expectedDiffs.get(c.getId());
- if (!diff.equals(expectedDiff)) {
- msgs.add("Differences between ReviewDb and NoteDb for " + c + ":");
- msgs.addAll(diff);
- if (!expectedDiff.isEmpty()) {
- msgs.add("Expected differences:");
- msgs.addAll(expectedDiff);
- }
- msgs.add("");
- } else {
- System.err.println("NoteDb conversion of change " + c.getId() + " successful");
- }
- }
- } finally {
- notesMigration.setReadChanges(oldRead);
- notesMigration.setWriteChanges(oldWrite);
- }
- if (!msgs.isEmpty()) {
- throw new AssertionError(Joiner.on('\n').join(msgs));
- }
- }
-
- private ReviewDb getUnwrappedDb() {
- ReviewDb db = dbProvider.get();
- return ReviewDbUtil.unwrapDb(db);
- }
-}
diff --git a/java/com/google/gerrit/testing/NoteDbMode.java b/java/com/google/gerrit/testing/NoteDbMode.java
deleted file mode 100644
index d4a7c7eec6..0000000000
--- a/java/com/google/gerrit/testing/NoteDbMode.java
+++ /dev/null
@@ -1,87 +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.testing;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.base.Enums;
-import com.google.common.base.Strings;
-import com.google.gerrit.server.notedb.MutableNotesMigration;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-
-public enum NoteDbMode {
- /** NoteDb is disabled. */
- OFF(NotesMigrationState.REVIEW_DB),
-
- /** Writing data to NoteDb is enabled. */
- WRITE(NotesMigrationState.WRITE),
-
- /** Reading and writing all data to NoteDb is enabled. */
- READ_WRITE(NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY),
-
- /** Changes are created with their primary storage as NoteDb. */
- PRIMARY(NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY),
-
- /** All change tables are entirely disabled, and code/meta ref updates are fused. */
- ON(NotesMigrationState.NOTE_DB),
-
- /**
- * Run tests with NoteDb disabled, then convert ReviewDb to NoteDb and check that the results
- * match.
- */
- CHECK(NotesMigrationState.REVIEW_DB);
-
- private static final String ENV_VAR = "GERRIT_NOTEDB";
- private static final String SYS_PROP = "gerrit.notedb";
-
- public static NoteDbMode get() {
- String value = System.getenv(ENV_VAR);
- if (Strings.isNullOrEmpty(value)) {
- value = System.getProperty(SYS_PROP);
- }
- if (Strings.isNullOrEmpty(value)) {
- return OFF;
- }
- value = value.toUpperCase().replace("-", "_");
- NoteDbMode mode = Enums.getIfPresent(NoteDbMode.class, value).orNull();
- if (!Strings.isNullOrEmpty(System.getenv(ENV_VAR))) {
- checkArgument(
- mode != null, "Invalid value for env variable %s: %s", ENV_VAR, System.getenv(ENV_VAR));
- } else {
- checkArgument(
- mode != null,
- "Invalid value for system property %s: %s",
- SYS_PROP,
- System.getProperty(SYS_PROP));
- }
- return mode;
- }
-
- public static MutableNotesMigration newNotesMigrationFromEnv() {
- MutableNotesMigration m = MutableNotesMigration.newDisabled();
- resetFromEnv(m);
- return m;
- }
-
- public static void resetFromEnv(MutableNotesMigration migration) {
- migration.setFrom(get().state);
- }
-
- private final NotesMigrationState state;
-
- private NoteDbMode(NotesMigrationState state) {
- this.state = state;
- }
-}
diff --git a/java/com/google/gerrit/testing/TempFileUtil.java b/java/com/google/gerrit/testing/TempFileUtil.java
deleted file mode 100644
index c42bd74dad..0000000000
--- a/java/com/google/gerrit/testing/TempFileUtil.java
+++ /dev/null
@@ -1,66 +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.testing;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class TempFileUtil {
- private static List<File> allDirsCreated = new ArrayList<>();
-
- public static synchronized File createTempDirectory() throws IOException {
- File tmp = File.createTempFile("gerrit_test_", "").getCanonicalFile();
- if (!tmp.delete() || !tmp.mkdir()) {
- throw new IOException("Cannot create " + tmp.getPath());
- }
- allDirsCreated.add(tmp);
- return tmp;
- }
-
- public static synchronized void cleanup() throws IOException {
- for (File dir : allDirsCreated) {
- recursivelyDelete(dir);
- }
- allDirsCreated.clear();
- }
-
- public static void recursivelyDelete(File dir) throws IOException {
- if (!dir.getPath().equals(dir.getCanonicalPath())) {
- // Directory symlink reaching outside of temporary space.
- return;
- }
- File[] contents = dir.listFiles();
- if (contents != null) {
- for (File d : contents) {
- if (d.isDirectory()) {
- recursivelyDelete(d);
- } else {
- deleteNowOrOnExit(d);
- }
- }
- }
- deleteNowOrOnExit(dir);
- }
-
- private static void deleteNowOrOnExit(File dir) {
- if (!dir.delete()) {
- dir.deleteOnExit();
- }
- }
-
- private TempFileUtil() {}
-}
diff --git a/java/com/google/gerrit/testing/TestChanges.java b/java/com/google/gerrit/testing/TestChanges.java
index 8e752faa63..d0a939da61 100644
--- a/java/com/google/gerrit/testing/TestChanges.java
+++ b/java/com/google/gerrit/testing/TestChanges.java
@@ -30,7 +30,6 @@ import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.AbstractChangeNotes;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Injector;
import java.util.TimeZone;
@@ -77,8 +76,8 @@ public class TestChanges {
return ps;
}
- public static ChangeUpdate newUpdate(Injector injector, Change c, CurrentUser user)
- throws Exception {
+ public static ChangeUpdate newUpdate(
+ Injector injector, Change c, CurrentUser user, boolean shouldExist) throws Exception {
injector =
injector.createChildInjector(
new FactoryModule() {
@@ -91,23 +90,24 @@ public class TestChanges {
injector
.getInstance(ChangeUpdate.Factory.class)
.create(
- new ChangeNotes(injector.getInstance(AbstractChangeNotes.Args.class), c).load(),
+ new ChangeNotes(
+ injector.getInstance(AbstractChangeNotes.Args.class), c, shouldExist, null)
+ .load(),
user,
TimeUtil.nowTs(),
- Ordering.<String>natural());
+ Ordering.natural());
ChangeNotes notes = update.getNotes();
boolean hasPatchSets = notes.getPatchSets() != null && !notes.getPatchSets().isEmpty();
- NotesMigration migration = injector.getInstance(NotesMigration.class);
- if (hasPatchSets || !migration.readChanges()) {
+ if (hasPatchSets) {
return update;
}
// Change doesn't exist yet. NoteDb requires that there be a commit for the
// first patch set, so create one.
GitRepositoryManager repoManager = injector.getInstance(GitRepositoryManager.class);
- try (Repository repo = repoManager.openRepository(c.getProject())) {
- TestRepository<Repository> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(c.getProject());
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
PersonIdent ident =
user.asIdentifiedUser().newCommitterIdent(update.getWhen(), TimeZone.getDefault());
TestRepository<Repository>.CommitBuilder cb =
diff --git a/java/com/google/gerrit/testing/TestTimeUtil.java b/java/com/google/gerrit/testing/TestTimeUtil.java
index 9228123b82..2020e5d0b3 100644
--- a/java/com/google/gerrit/testing/TestTimeUtil.java
+++ b/java/com/google/gerrit/testing/TestTimeUtil.java
@@ -118,6 +118,15 @@ public class TestTimeUtil {
clockMs.addAndGet(clockStepUnit.toMillis(clockStep));
}
+ /**
+ * Returns the current timestamp.
+ *
+ * @return current timestamp
+ */
+ public static synchronized Timestamp getCurrentTimestamp() {
+ return new Timestamp(clockMs.get());
+ }
+
/** Reset the clock to use the actual system clock. */
public static synchronized void useSystemTime() {
clockMs = null;
diff --git a/java/com/google/gerrit/testing/TestUpdateUI.java b/java/com/google/gerrit/testing/TestUpdateUI.java
index f36fc7eb5d..76671fb484 100644
--- a/java/com/google/gerrit/testing/TestUpdateUI.java
+++ b/java/com/google/gerrit/testing/TestUpdateUI.java
@@ -15,9 +15,6 @@
package com.google.gerrit.testing;
import com.google.gerrit.server.schema.UpdateUI;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.StatementExecutor;
-import java.util.List;
import java.util.Set;
public class TestUpdateUI implements UpdateUI {
@@ -41,11 +38,4 @@ public class TestUpdateUI implements UpdateUI {
public boolean isBatch() {
return true;
}
-
- @Override
- public void pruneSchema(StatementExecutor e, List<String> pruneList) throws OrmException {
- for (String sql : pruneList) {
- e.execute(sql);
- }
- }
}
diff --git a/java/com/google/gerrit/truth/BUILD b/java/com/google/gerrit/truth/BUILD
index b832543538..f21e3c9548 100644
--- a/java/com/google/gerrit/truth/BUILD
+++ b/java/com/google/gerrit/truth/BUILD
@@ -6,6 +6,7 @@ java_library(
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/common:annotations",
"//lib:guava",
"//lib/truth",
],
diff --git a/java/com/google/gerrit/truth/CacheStatsSubject.java b/java/com/google/gerrit/truth/CacheStatsSubject.java
new file mode 100644
index 0000000000..f1a9393a50
--- /dev/null
+++ b/java/com/google/gerrit/truth/CacheStatsSubject.java
@@ -0,0 +1,62 @@
+// 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.cache.CacheStats;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+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 static CacheStatsSubject assertThat(CacheStats stats) {
+ return assertAbout(CacheStatsSubject::new).that(stats);
+ }
+
+ public static CacheStats cloneStats(CacheStats other) {
+ return new CacheStats(
+ other.hitCount(),
+ other.missCount(),
+ other.loadSuccessCount(),
+ other.loadExceptionCount(),
+ other.totalLoadTime(),
+ other.evictionCount());
+ }
+
+ private CacheStats start = new CacheStats(0, 0, 0, 0, 0, 0);
+
+ private CacheStatsSubject(FailureMetadata failureMetadata, CacheStats stats) {
+ super(failureMetadata, stats);
+ }
+
+ public CacheStatsSubject since(CacheStats start) {
+ this.start = requireNonNull(start);
+ return this;
+ }
+
+ public void hasHitCount(int expectedHitCount) {
+ isNotNull();
+ check("hitCount()").that(actual().minus(start).hitCount()).isEqualTo(expectedHitCount);
+ }
+
+ public void hasMissCount(int expectedMissCount) {
+ isNotNull();
+ check("missCount()").that(actual().minus(start).missCount()).isEqualTo(expectedMissCount);
+ }
+}
diff --git a/java/com/google/gerrit/truth/ListSubject.java b/java/com/google/gerrit/truth/ListSubject.java
index bd9df303b6..9a839dda89 100644
--- a/java/com/google/gerrit/truth/ListSubject.java
+++ b/java/com/google/gerrit/truth/ListSubject.java
@@ -18,28 +18,34 @@ 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 com.google.common.collect.Iterables;
+import com.google.common.truth.CustomSubjectBuilder;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IterableSubject;
+import com.google.common.truth.StandardSubjectBuilder;
import com.google.common.truth.Subject;
import java.util.List;
-import java.util.function.Function;
+import java.util.function.BiFunction;
public class ListSubject<S extends Subject<S, E>, E> extends IterableSubject {
- private final Function<E, S> elementAssertThatFunction;
+ private final BiFunction<StandardSubjectBuilder, E, S> elementSubjectCreator;
- @SuppressWarnings("unchecked")
public static <S extends Subject<S, E>, E> ListSubject<S, E> assertThat(
- List<E> list, Function<E, S> elementAssertThatFunction) {
- // The ListSubjectFactory always returns ListSubjects. -> Casting is appropriate.
- return (ListSubject<S, E>)
- assertAbout(new ListSubjectFactory<>(elementAssertThatFunction)).that(list);
+ List<E> list, Subject.Factory<S, E> subjectFactory) {
+ return assertAbout(elements()).thatCustom(list, subjectFactory);
+ }
+
+ public static CustomSubjectBuilder.Factory<ListSubjectBuilder> elements() {
+ return ListSubjectBuilder::new;
}
private ListSubject(
- FailureMetadata failureMetadata, List<E> list, Function<E, S> elementAssertThatFunction) {
+ FailureMetadata failureMetadata,
+ List<E> list,
+ BiFunction<StandardSubjectBuilder, E, S> elementSubjectCreator) {
super(failureMetadata, list);
- this.elementAssertThatFunction = elementAssertThatFunction;
+ this.elementSubjectCreator = elementSubjectCreator;
}
public S element(int index) {
@@ -49,20 +55,21 @@ public class ListSubject<S extends Subject<S, E>, E> extends IterableSubject {
if (index >= list.size()) {
failWithoutActual(fact("expected to have element at index", index));
}
- return elementAssertThatFunction.apply(list.get(index));
+ return elementSubjectCreator.apply(check("element(%s)", index), list.get(index));
}
public S onlyElement() {
isNotNull();
hasSize(1);
- return element(0);
+ List<E> list = getActualList();
+ return elementSubjectCreator.apply(check("onlyElement()"), Iterables.getOnlyElement(list));
}
public S lastElement() {
isNotNull();
isNotEmpty();
List<E> list = getActualList();
- return element(list.size() - 1);
+ return elementSubjectCreator.apply(check("lastElement()"), Iterables.getLast(list));
}
@SuppressWarnings("unchecked")
@@ -78,20 +85,20 @@ public class ListSubject<S extends Subject<S, E>, E> extends IterableSubject {
return (ListSubject<S, E>) super.named(s, objects);
}
- private static class ListSubjectFactory<S extends Subject<S, T>, T>
- implements Subject.Factory<IterableSubject, Iterable<?>> {
+ public static class ListSubjectBuilder extends CustomSubjectBuilder {
- private Function<T, S> elementAssertThatFunction;
+ ListSubjectBuilder(FailureMetadata failureMetadata) {
+ super(failureMetadata);
+ }
- ListSubjectFactory(Function<T, S> elementAssertThatFunction) {
- this.elementAssertThatFunction = elementAssertThatFunction;
+ public <S extends Subject<S, E>, E> ListSubject<S, E> thatCustom(
+ List<E> list, Subject.Factory<S, E> subjectFactory) {
+ return that(list, (builder, element) -> builder.about(subjectFactory).that(element));
}
- @SuppressWarnings("unchecked")
- @Override
- public ListSubject<S, T> createSubject(FailureMetadata failureMetadata, Iterable<?> objects) {
- // The constructor of ListSubject only accepts lists. -> Casting is appropriate.
- return new ListSubject<>(failureMetadata, (List<T>) objects, elementAssertThatFunction);
+ public <S extends Subject<S, E>, E> ListSubject<S, E> that(
+ List<E> list, BiFunction<StandardSubjectBuilder, E, 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 d91f07b7d2..b5fc5d0472 100644
--- a/java/com/google/gerrit/truth/OptionalSubject.java
+++ b/java/com/google/gerrit/truth/OptionalSubject.java
@@ -17,41 +17,52 @@ package com.google.gerrit.truth;
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 com.google.common.truth.Truth;
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>> {
- private final Function<? super T, ? extends S> valueAssertThatFunction;
+ private final BiFunction<StandardSubjectBuilder, ? super T, ? extends S> valueSubjectCreator;
- public static <S extends Subject<S, ? super T>, T> OptionalSubject<S, T> assertThat(
+ // 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) {
- OptionalSubjectFactory<S, T> optionalSubjectFactory =
- new OptionalSubjectFactory<>(elementAssertThatFunction);
- return assertAbout(optionalSubjectFactory).that(optional);
+ 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) {
+ return assertAbout(optionals()).thatCustom(optional, valueSubjectFactory);
}
public static OptionalSubject<DefaultSubject, ?> assertThat(Optional<?> optional) {
- // Unfortunately, we need to cast to DefaultSubject as Truth.assertThat()
+ // 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.
- Function<Object, DefaultSubject> valueAssertThatFunction =
- value -> (DefaultSubject) Truth.assertThat(value);
- return assertThat(optional, valueAssertThatFunction);
+ return assertAbout(optionals())
+ .that(optional, (builder, value) -> (DefaultSubject) builder.that(value));
+ }
+
+ public static CustomSubjectBuilder.Factory<OptionalSubjectBuilder> optionals() {
+ return OptionalSubjectBuilder::new;
}
private OptionalSubject(
FailureMetadata failureMetadata,
Optional<T> optional,
- Function<? super T, ? extends S> valueAssertThatFunction) {
+ BiFunction<StandardSubjectBuilder, ? super T, ? extends S> valueSubjectCreator) {
super(failureMetadata, optional);
- this.valueAssertThatFunction = valueAssertThatFunction;
+ this.valueSubjectCreator = valueSubjectCreator;
}
public void isPresent() {
@@ -78,22 +89,28 @@ public class OptionalSubject<S extends Subject<S, ? super T>, T>
isNotNull();
isPresent();
Optional<T> optional = actual();
- return valueAssertThatFunction.apply(optional.get());
+ return valueSubjectCreator.apply(check("value()"), optional.get());
}
- private static class OptionalSubjectFactory<S extends Subject<S, ? super T>, T>
- implements Subject.Factory<OptionalSubject<S, T>, Optional<T>> {
+ public static class OptionalSubjectBuilder extends CustomSubjectBuilder {
- private Function<? super T, ? extends S> valueAssertThatFunction;
+ OptionalSubjectBuilder(FailureMetadata failureMetadata) {
+ super(failureMetadata);
+ }
+
+ public <S extends Subject<S, T>, T> OptionalSubject<S, T> thatCustom(
+ Optional<T> optional, Subject.Factory<S, T> valueSubjectFactory) {
+ return that(optional, (builder, value) -> builder.about(valueSubjectFactory).that(value));
+ }
- OptionalSubjectFactory(Function<? super T, ? extends S> valueAssertThatFunction) {
- this.valueAssertThatFunction = valueAssertThatFunction;
+ public OptionalSubject<DefaultSubject, ?> that(Optional<?> optional) {
+ return that(optional, (builder, value) -> (DefaultSubject) builder.that(value));
}
- @Override
- public OptionalSubject<S, T> createSubject(
- FailureMetadata failureMetadata, Optional<T> optional) {
- return new OptionalSubject<>(failureMetadata, optional, valueAssertThatFunction);
+ public <S extends Subject<S, ? super T>, 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/BUILD b/java/com/google/gerrit/util/cli/BUILD
index e8e2911add..ebcc67ed70 100644
--- a/java/com/google/gerrit/util/cli/BUILD
+++ b/java/com/google/gerrit/util/cli/BUILD
@@ -8,6 +8,7 @@ java_library(
"//java/com/google/gerrit/common:annotations",
"//lib:args4j",
"//lib:guava",
+ "//lib/auto:auto-value-annotations",
"//lib/flogger:api",
"//lib/guice",
"//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/util/cli/CmdLineParser.java b/java/com/google/gerrit/util/cli/CmdLineParser.java
index 5b7ea3f8ea..1c16133a6d 100644
--- a/java/com/google/gerrit/util/cli/CmdLineParser.java
+++ b/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -34,7 +34,9 @@
package com.google.gerrit.util.cli;
+import static com.google.common.base.Preconditions.checkArgument;
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.ListMultimap;
@@ -45,13 +47,14 @@ import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.StringWriter;
import java.io.Writer;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
@@ -65,7 +68,6 @@ import org.kohsuke.args4j.OptionDef;
import org.kohsuke.args4j.ParserProperties;
import org.kohsuke.args4j.spi.BooleanOptionHandler;
import org.kohsuke.args4j.spi.EnumOptionHandler;
-import org.kohsuke.args4j.spi.FieldSetter;
import org.kohsuke.args4j.spi.MethodSetter;
import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Setter;
@@ -85,6 +87,89 @@ public class CmdLineParser {
CmdLineParser create(Object bean);
}
+ /**
+ * This may be used by an option handler during parsing to "call" additional parameters simulating
+ * as if they had been passed from the command line originally.
+ *
+ * <p>To call additional parameters from within an option handler, instantiate this class with the
+ * parameters and then call callParameters() with the additional parameters to be parsed.
+ * OptionHandlers may optionally pass this class to other methods which may then both
+ * parse/consume more parameters and call additional parameters.
+ */
+ public static class Parameters implements org.kohsuke.args4j.spi.Parameters {
+ protected final String[] args;
+ protected MyParser parser;
+ protected int consumed = 0;
+
+ public Parameters(org.kohsuke.args4j.spi.Parameters args, MyParser parser)
+ throws CmdLineException {
+ this.args = new String[args.size()];
+ for (int i = 0; i < args.size(); i++) {
+ this.args[i] = args.getParameter(i);
+ }
+ this.parser = parser;
+ }
+
+ public Parameters(String[] args, MyParser parser) {
+ this.args = args;
+ this.parser = parser;
+ }
+
+ @Override
+ public String getParameter(int idx) throws CmdLineException {
+ return args[idx];
+ }
+
+ /**
+ * get and consume (consider parsed) a parameter
+ *
+ * @return the consumed parameter
+ */
+ public String consumeParameter() throws CmdLineException {
+ return getParameter(consumed++);
+ }
+
+ @Override
+ public int size() {
+ return args.length;
+ }
+
+ /**
+ * Add 'count' to the value of parsed parameters. May be called more than once.
+ *
+ * @param count How many parameters were just parsed.
+ */
+ public void consume(int count) {
+ consumed += count;
+ }
+
+ /**
+ * Reports handlers how many parameters were parsed
+ *
+ * @return the count of parsed parameters
+ */
+ public int getConsumed() {
+ return consumed;
+ }
+
+ /**
+ * Use during parsing to call additional parameters simulating as if they had been passed from
+ * the command line originally.
+ *
+ * @param args A variable amount of parameters to call immediately
+ * <p>The parameters will be parsed immediately, before the remaining parameter will be
+ * parsed.
+ * <p>Note: Since this is done outside of the arg4j parsing loop, it will not match exactly
+ * what would happen if they were actually passed from the command line, but it will be
+ * pretty close. If this were moved to args4j, the interface could be the same and it could
+ * match exactly the behavior as if passed from the command line originally.
+ */
+ public void callParameters(String... args) throws CmdLineException {
+ Parameters impl = new Parameters(Arrays.copyOfRange(args, 1, args.length), parser);
+ parser.findOptionByName(args[0]).parseArguments(impl);
+ }
+ }
+
private final OptionHandlers handlers;
private final MyParser parser;
@@ -200,7 +285,7 @@ public class CmdLineParser {
}
public boolean wasHelpRequestedByOption() {
- return parser.help.value;
+ return parser.help;
}
public void parseArgument(String... args) throws CmdLineException {
@@ -270,6 +355,10 @@ public class CmdLineParser {
parser.parseWithPrefix(prefix, bean);
}
+ public void drainOptionQueue() {
+ parser.addOptionsWithMetRequirements();
+ }
+
private String makeOption(String name) {
if (!name.startsWith("-")) {
if (name.length() == 1) {
@@ -330,92 +419,82 @@ public class CmdLineParser {
throw new CmdLineException(parser, localizable("invalid boolean \"%s=%s\""), name, value);
}
- private static class PrefixedOption implements Option {
- private final String prefix;
- private final Option o;
-
- PrefixedOption(String prefix, Option o) {
- this.prefix = prefix;
- this.o = o;
- }
-
- @Override
- public String name() {
- return getPrefixedName(prefix, o.name());
- }
-
- @Override
- public String[] aliases() {
- String[] prefixedAliases = new String[o.aliases().length];
- for (int i = 0; i < prefixedAliases.length; i++) {
- prefixedAliases[i] = getPrefixedName(prefix, o.aliases()[i]);
- }
- return prefixedAliases;
- }
-
- @Override
- public String usage() {
- return o.usage();
- }
-
- @Override
- public String metaVar() {
- return o.metaVar();
- }
-
- @Override
- public boolean required() {
- return o.required();
- }
+ 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);
+ return OptionUtil.newOption(
+ prefix + o.name(),
+ aliases,
+ o.usage(),
+ o.metaVar(),
+ o.required(),
+ false,
+ o.hidden(),
+ o.handler(),
+ o.depends(),
+ new String[0]);
+ }
- @Override
- public boolean hidden() {
- return o.hidden();
- }
+ public class MyParser extends org.kohsuke.args4j.CmdLineParser {
+ boolean help;
@SuppressWarnings("rawtypes")
- @Override
- public Class<? extends OptionHandler> handler() {
- return o.handler();
- }
+ private List<OptionHandler> optionsList;
- @Override
- public String[] depends() {
- return o.depends();
- }
+ private Map<String, QueuedOption> queuedOptionsByName = new LinkedHashMap<>();
- @Override
- public String[] forbids() {
- return null;
- }
+ private class QueuedOption {
+ public final Option option;
- @Override
- public boolean help() {
- return false;
- }
+ @SuppressWarnings("rawtypes")
+ public final Setter setter;
- @Override
- public Class<? extends Annotation> annotationType() {
- return o.annotationType();
- }
+ public final String[] requiredOptions;
- private static String getPrefixedName(String prefix, String name) {
- return prefix + name;
+ private QueuedOption(
+ Option option,
+ @SuppressWarnings("rawtypes") Setter setter,
+ RequiresOptions requiresOptions) {
+ this.option = option;
+ this.setter = setter;
+ this.requiredOptions = requiresOptions != null ? requiresOptions.value() : new String[0];
+ }
}
- }
-
- private class MyParser extends org.kohsuke.args4j.CmdLineParser {
- @SuppressWarnings("rawtypes")
- private List<OptionHandler> optionsList;
-
- private HelpOption help;
MyParser(Object bean) {
super(bean, ParserProperties.defaults().withAtSyntax(false));
parseAdditionalOptions(bean, new HashSet<>());
+ addOptionsWithMetRequirements();
ensureOptionsInitialized();
}
+ public int addOptionsWithMetRequirements() {
+ int count = 0;
+ for (Iterator<Map.Entry<String, QueuedOption>> it = queuedOptionsByName.entrySet().iterator();
+ it.hasNext(); ) {
+ QueuedOption queuedOption = it.next().getValue();
+ if (hasAllRequiredOptions(queuedOption)) {
+ addOption(queuedOption.setter, queuedOption.option);
+ it.remove();
+ count++;
+ }
+ }
+ if (count > 0) {
+ count += addOptionsWithMetRequirements();
+ }
+ return count;
+ }
+
+ private boolean hasAllRequiredOptions(QueuedOption queuedOption) {
+ for (String name : queuedOption.requiredOptions) {
+ if (findOptionByName(name) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
// NOTE: Argument annotations on bean are ignored.
public void parseWithPrefix(String prefix, Object bean) {
parseWithPrefix(prefix, bean, new HashSet<>());
@@ -430,13 +509,19 @@ public class CmdLineParser {
for (Method m : c.getDeclaredMethods()) {
Option o = m.getAnnotation(Option.class);
if (o != null) {
- addOption(new MethodSetter(this, bean, m), new PrefixedOption(prefix, o));
+ queueOption(
+ newPrefixedOption(prefix, o),
+ new MethodSetter(this, bean, m),
+ m.getAnnotation(RequiresOptions.class));
}
}
for (Field f : c.getDeclaredFields()) {
Option o = f.getAnnotation(Option.class);
if (o != null) {
- addOption(Setters.create(f, bean), new PrefixedOption(prefix, o));
+ queueOption(
+ newPrefixedOption(prefix, o),
+ Setters.create(f, bean),
+ f.getAnnotation(RequiresOptions.class));
}
if (f.isAnnotationPresent(Options.class)) {
try {
@@ -480,6 +565,41 @@ public class CmdLineParser {
return add(super.createOptionHandler(option, setter));
}
+ /**
+ * Finds a registered {@code OptionHandler} by its name or its alias.
+ *
+ * @param name name
+ * @return the {@code OptionHandler} or {@code null}
+ * <p>Note: this is cut & pasted from the parent class in arg4j, it was private and it
+ * needed to be exposed.
+ */
+ @SuppressWarnings("rawtypes")
+ public OptionHandler findOptionByName(String name) {
+ for (OptionHandler h : optionsList) {
+ NamedOptionDef option = (NamedOptionDef) h.option;
+ if (name.equals(option.name())) {
+ return h;
+ }
+ for (String alias : option.aliases()) {
+ if (name.equals(alias)) {
+ return h;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void queueOption(
+ Option option,
+ @SuppressWarnings("rawtypes") Setter setter,
+ RequiresOptions requiresOptions) {
+ if (queuedOptionsByName.put(option.name(), new QueuedOption(option, setter, requiresOptions))
+ != null) {
+ throw new IllegalAnnotationError(
+ "Option name " + option.name() + " is used more than once");
+ }
+ }
+
@SuppressWarnings("rawtypes")
private OptionHandler add(OptionHandler handler) {
ensureOptionsInitialized();
@@ -489,12 +609,33 @@ public class CmdLineParser {
private void ensureOptionsInitialized() {
if (optionsList == null) {
- help = new HelpOption();
optionsList = new ArrayList<>();
- addOption(help, help);
+ addOption(newHelpSetter(), newHelpOption());
}
}
+ private Setter<?> newHelpSetter() {
+ try {
+ return Setters.create(getClass().getDeclaredField("help"), this);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private Option newHelpOption() {
+ return OptionUtil.newOption(
+ "--help",
+ new String[] {"-h"},
+ "display this help text",
+ "",
+ false,
+ false,
+ false,
+ BooleanOptionHandler.class,
+ new String[0],
+ new String[0]);
+ }
+
private boolean isHandlerSpecified(OptionDef option) {
return option.handler() != OptionHandler.class;
}
@@ -508,90 +649,6 @@ public class CmdLineParser {
}
}
- private static class HelpOption implements Option, Setter<Boolean> {
- private boolean value;
-
- @Override
- public String name() {
- return "--help";
- }
-
- @Override
- public String[] aliases() {
- return new String[] {"-h"};
- }
-
- @Override
- public String[] depends() {
- return new String[] {};
- }
-
- @Override
- public boolean hidden() {
- return false;
- }
-
- @Override
- public String usage() {
- return "display this help text";
- }
-
- @Override
- public void addValue(Boolean val) {
- value = val;
- }
-
- @Override
- public Class<? extends OptionHandler<Boolean>> handler() {
- return BooleanOptionHandler.class;
- }
-
- @Override
- public String metaVar() {
- return "";
- }
-
- @Override
- public boolean required() {
- return false;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return Option.class;
- }
-
- @Override
- public FieldSetter asFieldSetter() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public AnnotatedElement asAnnotatedElement() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Class<Boolean> getType() {
- return Boolean.class;
- }
-
- @Override
- public boolean isMultiValued() {
- return false;
- }
-
- @Override
- public String[] forbids() {
- return null;
- }
-
- @Override
- public boolean help() {
- return false;
- }
- }
-
public CmdLineException reject(String message) {
return new CmdLineException(parser, localizable(message));
}
diff --git a/java/com/google/gerrit/util/cli/OptionHandlers.java b/java/com/google/gerrit/util/cli/OptionHandlers.java
index 84a0809b28..95474101d5 100644
--- a/java/com/google/gerrit/util/cli/OptionHandlers.java
+++ b/java/com/google/gerrit/util/cli/OptionHandlers.java
@@ -24,7 +24,7 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import java.lang.reflect.ParameterizedType;
-import java.util.Map.Entry;
+import java.util.Map;
@Singleton
public class OptionHandlers {
@@ -53,7 +53,7 @@ public class OptionHandlers {
private static ImmutableMap<Class<?>, Provider<OptionHandlerFactory<?>>> build(Injector i) {
ImmutableMap.Builder<Class<?>, Provider<OptionHandlerFactory<?>>> map = ImmutableMap.builder();
for (; i != null; i = i.getParent()) {
- for (Entry<Key<?>, Binding<?>> e : i.getBindings().entrySet()) {
+ for (Map.Entry<Key<?>, Binding<?>> e : i.getBindings().entrySet()) {
TypeLiteral<?> type = e.getKey().getTypeLiteral();
if (type.getRawType() == OptionHandlerFactory.class
&& e.getKey().getAnnotation() == null
diff --git a/java/com/google/gerrit/util/cli/OptionUtil.java b/java/com/google/gerrit/util/cli/OptionUtil.java
new file mode 100644
index 0000000000..1125a0d367
--- /dev/null
+++ b/java/com/google/gerrit/util/cli/OptionUtil.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.util.cli;
+
+import com.google.auto.value.AutoAnnotation;
+import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.spi.OptionHandler;
+
+/** Utilities to support creating new {@link Option} instances. */
+public class OptionUtil {
+ @AutoAnnotation
+ @SuppressWarnings("rawtypes")
+ public static Option newOption(
+ String name,
+ String[] aliases,
+ String usage,
+ String metaVar,
+ boolean required,
+ boolean help,
+ boolean hidden,
+ Class<? extends OptionHandler> handler,
+ String[] depends,
+ String[] forbids) {
+ return new AutoAnnotation_OptionUtil_newOption(
+ name, aliases, usage, metaVar, required, help, hidden, handler, depends, forbids);
+ }
+}
diff --git a/java/com/google/gerrit/util/cli/RequiresOptions.java b/java/com/google/gerrit/util/cli/RequiresOptions.java
new file mode 100644
index 0000000000..de6ba44e1c
--- /dev/null
+++ b/java/com/google/gerrit/util/cli/RequiresOptions.java
@@ -0,0 +1,45 @@
+// 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.util.cli;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a field/setter annotated with {@literal @}Option as having a dependency on multiple other
+ * command line option.
+ *
+ * <p>If any of the required command line options are not present, the {@literal @}Option will be
+ * ignored.
+ *
+ * <p>For example:
+ *
+ * <pre>
+ * {@literal @}RequiresOptions({"--help", "--usage"})
+ * {@literal @}Option(name = "--help-as-json",
+ * usage = "display help text in json format")
+ * public boolean displayHelpAsJson;
+ * </pre>
+ */
+@Retention(RUNTIME)
+@Target({FIELD, METHOD, PARAMETER})
+public @interface RequiresOptions {
+ String[] value();
+}
diff --git a/java/com/google/gerrit/util/http/BUILD b/java/com/google/gerrit/util/http/BUILD
index 5ecb7a1edc..fbd1379d4d 100644
--- a/java/com/google/gerrit/util/http/BUILD
+++ b/java/com/google/gerrit/util/http/BUILD
@@ -4,5 +4,5 @@ java_library(
name = "http",
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
- deps = ["//lib:servlet-api-3_1"],
+ deps = ["//lib:servlet-api"],
)
diff --git a/java/com/google/gerrit/util/http/RequestUtil.java b/java/com/google/gerrit/util/http/RequestUtil.java
index 2a359cacd0..92d9967267 100644
--- a/java/com/google/gerrit/util/http/RequestUtil.java
+++ b/java/com/google/gerrit/util/http/RequestUtil.java
@@ -56,5 +56,43 @@ public class RequestUtil {
return pathInfo;
}
+ /**
+ * Trims leading '/' and 'a/'. Removes the context path, but keeps the servlet path. Removes all
+ * IDs from the rest of the URI.
+ *
+ * <p>The returned string is a good fit for cases where one wants the full context of the request
+ * without any identifiable data. For example: Logging or quota checks.
+ *
+ * <p>Examples:
+ *
+ * <ul>
+ * <li>/a/accounts/self/detail => /accounts/detail
+ * <li>/changes/123/revisions/current/detail => /changes/revisions/detail
+ * <li>/changes/ => /changes
+ * </ul>
+ */
+ public static String getRestPathWithoutIds(HttpServletRequest req) {
+ String encodedPathInfo = req.getRequestURI().substring(req.getContextPath().length());
+ if (encodedPathInfo.startsWith("/")) {
+ encodedPathInfo = encodedPathInfo.substring(1);
+ }
+ if (encodedPathInfo.startsWith("a/")) {
+ encodedPathInfo = encodedPathInfo.substring(2);
+ }
+
+ String[] parts = encodedPathInfo.split("/");
+ StringBuilder result = new StringBuilder(parts.length);
+ for (int i = 0; i < parts.length; i = i + 2) {
+ result.append("/");
+ result.append(parts[i]);
+ }
+ return result.toString();
+ }
+
+ public static boolean acceptsGzipEncoding(HttpServletRequest request) {
+ String accepts = request.getHeader("Accept-Encoding");
+ return accepts != null && accepts.indexOf("gzip") != -1;
+ }
+
private RequestUtil() {}
}
diff --git a/java/com/google/gwtexpui/clippy/BUILD b/java/com/google/gwtexpui/clippy/BUILD
deleted file mode 100644
index 80b6767e17..0000000000
--- a/java/com/google/gwtexpui/clippy/BUILD
+++ /dev/null
@@ -1,23 +0,0 @@
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "clippy",
- srcs = glob(["client/*.java"]),
- data = [
- "//lib:LICENSE-clippy",
- "//lib:LICENSE-silk_icons",
- ],
- gwt_xml = "Clippy.gwt.xml",
- resources = [
- "client/CopyableLabelText.properties",
- "client/clippy.css",
- "client/clippy.swf",
- "client/page_white_copy.png",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//java/com/google/gwtexpui/safehtml",
- "//java/com/google/gwtexpui/user:agent",
- "//lib/gwt:user-neverlink",
- ],
-)
diff --git a/java/com/google/gwtexpui/clippy/Clippy.gwt.xml b/java/com/google/gwtexpui/clippy/Clippy.gwt.xml
deleted file mode 100644
index 0e9b0727f5..0000000000
--- a/java/com/google/gwtexpui/clippy/Clippy.gwt.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<module>
- <inherits name='com.google.gwt.resources.Resources'/>
- <inherits name="com.google.gwtexpui.safehtml.SafeHtml"/>
- <inherits name="com.google.gwtexpui.user.User"/>
-</module>
diff --git a/java/com/google/gwtexpui/clippy/client/ClippyCss.java b/java/com/google/gwtexpui/clippy/client/ClippyCss.java
deleted file mode 100644
index 0d340ff2ad..0000000000
--- a/java/com/google/gwtexpui/clippy/client/ClippyCss.java
+++ /dev/null
@@ -1,25 +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.gwtexpui.clippy.client;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface ClippyCss extends CssResource {
- String label();
-
- String copier();
-
- String swf();
-}
diff --git a/java/com/google/gwtexpui/clippy/client/ClippyResources.java b/java/com/google/gwtexpui/clippy/client/ClippyResources.java
deleted file mode 100644
index a97b39241c..0000000000
--- a/java/com/google/gwtexpui/clippy/client/ClippyResources.java
+++ /dev/null
@@ -1,35 +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.gwtexpui.clippy.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.DataResource;
-import com.google.gwt.resources.client.DataResource.DoNotEmbed;
-import com.google.gwt.resources.client.ImageResource;
-
-public interface ClippyResources extends ClientBundle {
- ClippyResources I = GWT.create(ClippyResources.class);
-
- @Source("clippy.css")
- ClippyCss css();
-
- @Source("clippy.swf")
- @DoNotEmbed
- DataResource swf();
-
- @Source("page_white_copy.png")
- ImageResource clipboard();
-}
diff --git a/java/com/google/gwtexpui/clippy/client/CopyableLabel.java b/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
deleted file mode 100644
index 7b70c6a47c..0000000000
--- a/java/com/google/gwtexpui/clippy/client/CopyableLabel.java
+++ /dev/null
@@ -1,315 +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.gwtexpui.clippy.client;
-
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style.Display;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.KeyUpEvent;
-import com.google.gwt.event.dom.client.KeyUpHandler;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.http.client.URL;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HasText;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtexpui.user.client.Tooltip;
-import com.google.gwtexpui.user.client.UserAgent;
-
-/**
- * Label which permits the user to easily copy the complete content.
- *
- * <p>If the Flash plugin is available a "movie" is embedded that provides one-click copying of the
- * content onto the system clipboard. The label (if visible) can also be clicked, switching from a
- * label to an input box, allowing the user to copy the text with a keyboard shortcut.
- */
-public class CopyableLabel extends Composite implements HasText {
- private static final int SWF_WIDTH = 110;
- private static final int SWF_HEIGHT = 14;
- private static boolean flashEnabled = true;
-
- static {
- ClippyResources.I.css().ensureInjected();
- }
-
- public static boolean isFlashEnabled() {
- return flashEnabled;
- }
-
- public static void setFlashEnabled(boolean on) {
- flashEnabled = on;
- }
-
- private static String swfUrl() {
- return ClippyResources.I.swf().getSafeUri().asString();
- }
-
- private final FlowPanel content;
- private String text;
- private int visibleLen;
- private Label textLabel;
- private TextBox textBox;
- private Button copier;
- private Element swf;
-
- public CopyableLabel() {
- this("");
- }
-
- /**
- * Create a new label
- *
- * @param str initial content
- */
- public CopyableLabel(String str) {
- this(str, true);
- }
-
- /**
- * Create a new label
- *
- * @param str initial content
- * @param showLabel if true, the content is shown, if false it is hidden from view and only the
- * copy icon is displayed.
- */
- public CopyableLabel(String str, boolean showLabel) {
- content = new FlowPanel();
- initWidget(content);
-
- text = str;
- visibleLen = text.length();
-
- if (showLabel) {
- textLabel = new InlineLabel(getText());
- textLabel.setStyleName(ClippyResources.I.css().label());
- textLabel.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- showTextBox();
- }
- });
- content.add(textLabel);
- }
-
- if (UserAgent.hasJavaScriptClipboard()) {
- copier =
- new Button(
- new SafeHtmlBuilder()
- .openElement("img")
- .setAttribute("src", ClippyResources.I.clipboard().getSafeUri().asString())
- .setWidth(14)
- .setHeight(14)
- .closeSelf());
- copier.setStyleName(ClippyResources.I.css().copier());
- Tooltip.addStyle(copier);
- Tooltip.setLabel(copier, CopyableLabelText.I.tooltip());
- copier.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- copy();
- }
- });
- copier.addMouseOutHandler(
- new MouseOutHandler() {
- @Override
- public void onMouseOut(MouseOutEvent event) {
- Tooltip.setLabel(copier, CopyableLabelText.I.tooltip());
- }
- });
-
- FlowPanel p = new FlowPanel();
- p.getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
- p.add(copier);
- content.add(p);
- } else {
- embedMovie();
- }
- }
-
- /**
- * Change the text which is displayed in the clickable label.
- *
- * @param text the new preview text, should be shorter than the original text which would be
- * copied to the clipboard.
- */
- public void setPreviewText(String text) {
- if (textLabel != null) {
- textLabel.setText(text);
- }
- }
-
- private void embedMovie() {
- if (copier == null && flashEnabled && !text.isEmpty() && UserAgent.Flash.isInstalled()) {
- final String flashVars = "text=" + URL.encodeQueryString(getText());
- final SafeHtmlBuilder h = new SafeHtmlBuilder();
-
- h.openElement("div");
- h.setStyleName(ClippyResources.I.css().swf());
-
- h.openElement("object");
- h.setWidth(SWF_WIDTH);
- h.setHeight(SWF_HEIGHT);
- h.setAttribute("classid", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000");
- h.paramElement("movie", swfUrl());
- h.paramElement("FlashVars", flashVars);
-
- h.openElement("embed");
- h.setWidth(SWF_WIDTH);
- h.setHeight(SWF_HEIGHT);
- h.setAttribute("wmode", "transparent");
- h.setAttribute("type", "application/x-shockwave-flash");
- h.setAttribute("src", swfUrl());
- h.setAttribute("FlashVars", flashVars);
- h.closeSelf();
-
- h.closeElement("object");
- h.closeElement("div");
-
- if (swf != null) {
- getElement().removeChild(swf);
- }
- DOM.appendChild(getElement(), swf = SafeHtml.parse(h));
- }
- }
-
- @Override
- public String getText() {
- return text;
- }
-
- @Override
- public void setText(String newText) {
- text = newText;
- visibleLen = newText.length();
-
- if (textLabel != null) {
- textLabel.setText(getText());
- }
- if (textBox != null) {
- textBox.setText(getText());
- textBox.selectAll();
- }
- embedMovie();
- }
-
- private void showTextBox() {
- if (textBox == null) {
- textBox = new TextBox();
- textBox.setText(getText());
- textBox.setVisibleLength(visibleLen);
- textBox.setReadOnly(true);
- textBox.addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.isControlKeyDown() || event.isMetaKeyDown()) {
- switch (event.getCharCode()) {
- case 'c':
- case 'x':
- textBox.addKeyUpHandler(
- new KeyUpHandler() {
- @Override
- public void onKeyUp(KeyUpEvent event) {
- Scheduler.get()
- .scheduleDeferred(
- new Command() {
- @Override
- public void execute() {
- hideTextBox();
- }
- });
- }
- });
- break;
- }
- }
- }
- });
- textBox.addBlurHandler(
- new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- hideTextBox();
- }
- });
- content.insert(textBox, 1);
- }
-
- textLabel.setVisible(false);
- textBox.setVisible(true);
- Scheduler.get()
- .scheduleDeferred(
- new Command() {
- @Override
- public void execute() {
- textBox.selectAll();
- textBox.setFocus(true);
- }
- });
- }
-
- private void hideTextBox() {
- if (textBox != null) {
- textBox.removeFromParent();
- textBox = null;
- }
- textLabel.setVisible(true);
- }
-
- private void copy() {
- TextBox t = new TextBox();
- try {
- t.setText(getText());
- content.add(t);
- t.setFocus(true);
- t.selectAll();
-
- boolean ok = execCommand("copy");
- Tooltip.setLabel(copier, ok ? CopyableLabelText.I.copied() : CopyableLabelText.I.failed());
- if (!ok) {
- // Disable JavaScript clipboard and try flash movie in another instance.
- UserAgent.disableJavaScriptClipboard();
- }
- } finally {
- t.removeFromParent();
- }
- }
-
- private static boolean execCommand(String command) {
- try {
- return nativeExec(command);
- } catch (Exception e) {
- return false;
- }
- }
-
- private static native boolean nativeExec(String c) /*-{ return !! $doc.execCommand(c) }-*/;
-}
diff --git a/java/com/google/gwtexpui/clippy/client/CopyableLabelText.java b/java/com/google/gwtexpui/clippy/client/CopyableLabelText.java
deleted file mode 100644
index ff36541913..0000000000
--- a/java/com/google/gwtexpui/clippy/client/CopyableLabelText.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2015 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.gwtexpui.clippy.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Constants;
-
-interface CopyableLabelText extends Constants {
- CopyableLabelText I = GWT.create(CopyableLabelText.class);
-
- String tooltip();
-
- String copied();
-
- String failed();
-}
diff --git a/java/com/google/gwtexpui/clippy/client/CopyableLabelText.properties b/java/com/google/gwtexpui/clippy/client/CopyableLabelText.properties
deleted file mode 100644
index cf93bfa4b8..0000000000
--- a/java/com/google/gwtexpui/clippy/client/CopyableLabelText.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-tooltip = Copy to clipboard
-copied = Copied
-failed = Failed !
diff --git a/java/com/google/gwtexpui/clippy/client/clippy.css b/java/com/google/gwtexpui/clippy/client/clippy.css
deleted file mode 100644
index b25e00694b..0000000000
--- a/java/com/google/gwtexpui/clippy/client/clippy.css
+++ /dev/null
@@ -1,39 +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.
- */
-
-.label {
- vertical-align: top;
-}
-.swf, .copier {
- margin-left: 5px;
- height: 14px;
- width: 14px;
-}
-.swf {
- display: inline-block !important;
- overflow: hidden;
-}
-.copier {
- display: inline-block;
- font-size: 12px;
- vertical-align: top;
- padding: 0;
- border: 0;
- background-color: inherit;
- cursor: pointer;
-}
-.copier:focus {
- outline: none;
-}
diff --git a/java/com/google/gwtexpui/clippy/client/clippy.swf b/java/com/google/gwtexpui/clippy/client/clippy.swf
deleted file mode 100644
index e46886cd11..0000000000
--- a/java/com/google/gwtexpui/clippy/client/clippy.swf
+++ /dev/null
Binary files differ
diff --git a/java/com/google/gwtexpui/clippy/client/page_white_copy.png b/java/com/google/gwtexpui/clippy/client/page_white_copy.png
deleted file mode 100644
index a9f31a278e..0000000000
--- a/java/com/google/gwtexpui/clippy/client/page_white_copy.png
+++ /dev/null
Binary files differ
diff --git a/java/com/google/gwtexpui/css/BUILD b/java/com/google/gwtexpui/css/BUILD
deleted file mode 100644
index 34029adff4..0000000000
--- a/java/com/google/gwtexpui/css/BUILD
+++ /dev/null
@@ -1,9 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-java_library(
- name = "css",
- srcs = glob(["rebind/*.java"]),
- resources = ["CSS.gwt.xml"],
- visibility = ["//visibility:public"],
- deps = ["//lib/gwt:dev"],
-)
diff --git a/java/com/google/gwtexpui/css/CSS.gwt.xml b/java/com/google/gwtexpui/css/CSS.gwt.xml
deleted file mode 100644
index b3859873a1..0000000000
--- a/java/com/google/gwtexpui/css/CSS.gwt.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<module>
- <define-linker name='cachecss' class='com.google.gwtexpui.css.rebind.CssLinker'/>
- <add-linker name='cachecss'/>
-</module>
diff --git a/java/com/google/gwtexpui/css/rebind/CssLinker.java b/java/com/google/gwtexpui/css/rebind/CssLinker.java
deleted file mode 100644
index 6ef5d7b56c..0000000000
--- a/java/com/google/gwtexpui/css/rebind/CssLinker.java
+++ /dev/null
@@ -1,121 +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.gwtexpui.css.rebind;
-
-import com.google.gwt.core.ext.LinkerContext;
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.linker.AbstractLinker;
-import com.google.gwt.core.ext.linker.Artifact;
-import com.google.gwt.core.ext.linker.ArtifactSet;
-import com.google.gwt.core.ext.linker.LinkerOrder;
-import com.google.gwt.core.ext.linker.PublicResource;
-import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
-import com.google.gwt.core.ext.linker.impl.StandardStylesheetReference;
-import com.google.gwt.dev.util.Util;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-
-@LinkerOrder(LinkerOrder.Order.PRE)
-public class CssLinker extends AbstractLinker {
- @Override
- public String getDescription() {
- return "CssLinker";
- }
-
- @Override
- public ArtifactSet link(final TreeLogger logger, LinkerContext context, ArtifactSet artifacts)
- throws UnableToCompleteException {
- final ArtifactSet returnTo = new ArtifactSet();
- int index = 0;
-
- final HashMap<String, PublicResource> css = new HashMap<>();
-
- for (StandardStylesheetReference ssr :
- artifacts.<StandardStylesheetReference>find(StandardStylesheetReference.class)) {
- css.put(ssr.getSrc(), null);
- }
- for (PublicResource pr : artifacts.<PublicResource>find(PublicResource.class)) {
- if (css.containsKey(pr.getPartialPath())) {
- css.put(pr.getPartialPath(), new CssPubRsrc(name(logger, pr), pr));
- }
- }
-
- for (Artifact<?> a : artifacts) {
- if (a instanceof PublicResource) {
- final PublicResource r = (PublicResource) a;
- if (css.containsKey(r.getPartialPath())) {
- a = css.get(r.getPartialPath());
- }
- } else if (a instanceof StandardStylesheetReference) {
- final StandardStylesheetReference r = (StandardStylesheetReference) a;
- final PublicResource p = css.get(r.getSrc());
- a = new StandardStylesheetReference(p.getPartialPath(), index);
- }
-
- returnTo.add(a);
- index++;
- }
- return returnTo;
- }
-
- private String name(TreeLogger logger, PublicResource r) throws UnableToCompleteException {
- byte[] out;
- try (ByteArrayOutputStream tmp = new ByteArrayOutputStream();
- InputStream in = r.getContents(logger)) {
- final byte[] buf = new byte[2048];
- int n;
- while ((n = in.read(buf)) >= 0) {
- tmp.write(buf, 0, n);
- }
- out = tmp.toByteArray();
- } catch (IOException e) {
- final UnableToCompleteException ute = new UnableToCompleteException();
- ute.initCause(e);
- throw ute;
- }
-
- String base = r.getPartialPath();
- final int s = base.lastIndexOf('/');
- if (0 < s) {
- base = base.substring(0, s + 1);
- } else {
- base = "";
- }
- return base + Util.computeStrongName(out) + ".cache.css";
- }
-
- private static class CssPubRsrc extends PublicResource {
- private static final long serialVersionUID = 1L;
- private final PublicResource src;
-
- CssPubRsrc(String partialPath, PublicResource r) {
- super(StandardLinkerContext.class, partialPath);
- src = r;
- }
-
- @Override
- public InputStream getContents(TreeLogger logger) throws UnableToCompleteException {
- return src.getContents(logger);
- }
-
- @Override
- public long getLastModified() {
- return src.getLastModified();
- }
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/BUILD b/java/com/google/gwtexpui/globalkey/BUILD
deleted file mode 100644
index 2651c4a402..0000000000
--- a/java/com/google/gwtexpui/globalkey/BUILD
+++ /dev/null
@@ -1,16 +0,0 @@
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "globalkey",
- srcs = glob(["client/*.java"]),
- gwt_xml = "GlobalKey.gwt.xml",
- resources = [
- "client/KeyConstants.properties",
- "client/key.css",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//java/com/google/gwtexpui/safehtml",
- "//lib/gwt:user",
- ],
-)
diff --git a/java/com/google/gwtexpui/globalkey/GlobalKey.gwt.xml b/java/com/google/gwtexpui/globalkey/GlobalKey.gwt.xml
deleted file mode 100644
index 771050f203..0000000000
--- a/java/com/google/gwtexpui/globalkey/GlobalKey.gwt.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<module>
- <inherits name='com.google.gwt.resources.Resources'/>
- <inherits name='com.google.gwtexpui.user.User'/>
- <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
-</module>
diff --git a/java/com/google/gwtexpui/globalkey/client/CompoundKeyCommand.java b/java/com/google/gwtexpui/globalkey/client/CompoundKeyCommand.java
deleted file mode 100644
index 5a4f6aad35..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/CompoundKeyCommand.java
+++ /dev/null
@@ -1,40 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.event.dom.client.KeyPressEvent;
-
-public final class CompoundKeyCommand extends KeyCommand {
- final KeyCommandSet set;
-
- public CompoundKeyCommand(int mask, char key, String help, KeyCommandSet s) {
- super(mask, key, help);
- set = s;
- }
-
- public CompoundKeyCommand(int mask, int key, String help, KeyCommandSet s) {
- super(mask, key, help);
- set = s;
- }
-
- public KeyCommandSet getSet() {
- return set;
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- GlobalKey.temporaryWithTimeout(set);
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/DocWidget.java b/java/com/google/gwtexpui/globalkey/client/DocWidget.java
deleted file mode 100644
index 320010eb59..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/DocWidget.java
+++ /dev/null
@@ -1,59 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.dom.client.Document;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.event.dom.client.HasKeyPressHandlers;
-import com.google.gwt.event.dom.client.HasMouseMoveHandlers;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.dom.client.MouseMoveEvent;
-import com.google.gwt.event.dom.client.MouseMoveHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-public class DocWidget extends Widget implements HasKeyPressHandlers, HasMouseMoveHandlers {
- private static DocWidget me;
-
- public static DocWidget get() {
- if (me == null) {
- me = new DocWidget();
- }
- return me;
- }
-
- private DocWidget() {
- setElement((Element) docnode());
- onAttach();
- RootPanel.detachOnWindowClose(this);
- }
-
- @Override
- public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
- return addDomHandler(handler, KeyPressEvent.getType());
- }
-
- @Override
- public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
- return addDomHandler(handler, MouseMoveEvent.getType());
- }
-
- private static Node docnode() {
- return Document.get();
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/GlobalKey.java b/java/com/google/gwtexpui/globalkey/client/GlobalKey.java
deleted file mode 100644
index cbaca61658..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/GlobalKey.java
+++ /dev/null
@@ -1,191 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.event.dom.client.DomEvent;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.Widget;
-
-public class GlobalKey {
- public static final KeyPressHandler STOP_PROPAGATION = DomEvent::stopPropagation;
-
- private static State global;
- static State active;
- private static CloseHandler<PopupPanel> restoreGlobal;
- private static Timer restoreTimer;
-
- static {
- KeyResources.I.css().ensureInjected();
- }
-
- private static void initEvents() {
- if (active == null) {
- DocWidget.get()
- .addKeyPressHandler(
- new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- final KeyCommandSet s = active.live;
- if (s != active.all) {
- active.live = active.all;
- restoreTimer.cancel();
- }
- s.onKeyPress(event);
- }
- });
-
- restoreTimer =
- new Timer() {
- @Override
- public void run() {
- active.live = active.all;
- }
- };
-
- global = new State(null);
- active = global;
- }
- }
-
- private static void initDialog() {
- if (restoreGlobal == null) {
- restoreGlobal =
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- active = global;
- }
- };
- }
- }
-
- static void temporaryWithTimeout(KeyCommandSet s) {
- active.live = s;
- restoreTimer.schedule(250);
- }
-
- public static void dialog(PopupPanel panel) {
- initEvents();
- initDialog();
- assert panel.isShowing();
- assert active == global;
- active = new State(panel);
- active.add(new HidePopupPanelCommand(0, KeyCodes.KEY_ESCAPE, panel));
- panel.addCloseHandler(restoreGlobal);
- panel.addDomHandler(
- new KeyDownHandler() {
- @Override
- public void onKeyDown(KeyDownEvent event) {
- if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
- panel.hide();
- }
- }
- },
- KeyDownEvent.getType());
- }
-
- public static HandlerRegistration addApplication(Widget widget, KeyCommand appKey) {
- initEvents();
- final State state = stateFor(widget);
- state.add(appKey);
- return new HandlerRegistration() {
- @Override
- public void removeHandler() {
- state.remove(appKey);
- }
- };
- }
-
- public static HandlerRegistration add(Widget widget, KeyCommandSet cmdSet) {
- initEvents();
- final State state = stateFor(widget);
- state.add(cmdSet);
- return new HandlerRegistration() {
- @Override
- public void removeHandler() {
- state.remove(cmdSet);
- }
- };
- }
-
- private static State stateFor(Widget w) {
- while (w != null) {
- if (w == active.root) {
- return active;
- }
- w = w.getParent();
- }
- return global;
- }
-
- public static void filter(KeyCommandFilter filter) {
- active.filter(filter);
- if (active != global) {
- global.filter(filter);
- }
- }
-
- private GlobalKey() {}
-
- static class State {
- final Widget root;
- final KeyCommandSet app;
- final KeyCommandSet all;
- KeyCommandSet live;
-
- State(Widget r) {
- root = r;
-
- app = new KeyCommandSet(KeyConstants.I.applicationSection());
- app.add(ShowHelpCommand.INSTANCE);
-
- all = new KeyCommandSet();
- all.add(app);
-
- live = all;
- }
-
- void add(KeyCommand k) {
- app.add(k);
- all.add(k);
- }
-
- void remove(KeyCommand k) {
- app.remove(k);
- all.remove(k);
- }
-
- void add(KeyCommandSet s) {
- all.add(s);
- }
-
- void remove(KeyCommandSet s) {
- all.remove(s);
- }
-
- void filter(KeyCommandFilter f) {
- all.filter(f);
- }
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/HidePopupPanelCommand.java b/java/com/google/gwtexpui/globalkey/client/HidePopupPanelCommand.java
deleted file mode 100644
index 8222f8b250..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/HidePopupPanelCommand.java
+++ /dev/null
@@ -1,33 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.user.client.ui.PopupPanel;
-
-/** Hides the given popup panel when invoked. */
-public class HidePopupPanelCommand extends KeyCommand {
- private final PopupPanel panel;
-
- public HidePopupPanelCommand(int mask, int key, PopupPanel panel) {
- super(mask, key, KeyConstants.I.closeCurrentDialog());
- this.panel = panel;
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- panel.hide();
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyCommand.java b/java/com/google/gwtexpui/globalkey/client/KeyCommand.java
deleted file mode 100644
index f1c92e02fc..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyCommand.java
+++ /dev/null
@@ -1,104 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-public abstract class KeyCommand implements KeyPressHandler {
- public static final int M_CTRL = 1 << 16;
- public static final int M_ALT = 2 << 16;
- public static final int M_META = 4 << 16;
- public static final int M_SHIFT = 8 << 16;
-
- public static boolean same(KeyCommand a, KeyCommand b) {
- return a.getClass() == b.getClass() && a.helpText.equals(b.helpText) && a.sibling == b.sibling;
- }
-
- final int keyMask;
- private final String helpText;
- KeyCommand sibling;
-
- public KeyCommand(int mask, int key, String help) {
- this(mask, (char) key, help);
- }
-
- public KeyCommand(int mask, char key, String help) {
- assert help != null;
- keyMask = mask | key;
- helpText = help;
- }
-
- public String getHelpText() {
- return helpText;
- }
-
- SafeHtml describeKeyStroke() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
-
- if ((keyMask & M_CTRL) == M_CTRL) {
- modifier(b, KeyConstants.I.keyCtrl());
- }
- if ((keyMask & M_ALT) == M_ALT) {
- modifier(b, KeyConstants.I.keyAlt());
- }
- if ((keyMask & M_META) == M_META) {
- modifier(b, KeyConstants.I.keyMeta());
- }
- if ((keyMask & M_SHIFT) == M_SHIFT) {
- modifier(b, KeyConstants.I.keyShift());
- }
-
- final char c = (char) (keyMask & 0xffff);
- switch (c) {
- case KeyCodes.KEY_ENTER:
- namedKey(b, KeyConstants.I.keyEnter());
- break;
- case KeyCodes.KEY_ESCAPE:
- namedKey(b, KeyConstants.I.keyEsc());
- break;
- case KeyCodes.KEY_LEFT:
- namedKey(b, KeyConstants.I.keyLeft());
- break;
- case KeyCodes.KEY_RIGHT:
- namedKey(b, KeyConstants.I.keyRight());
- break;
- default:
- b.openSpan();
- b.setStyleName(KeyResources.I.css().helpKey());
- b.append(String.valueOf(c));
- b.closeSpan();
- break;
- }
-
- return b;
- }
-
- private void modifier(SafeHtmlBuilder b, String name) {
- namedKey(b, name);
- b.append(" + ");
- }
-
- private void namedKey(SafeHtmlBuilder b, String name) {
- b.append('<');
- b.openSpan();
- b.setStyleName(KeyResources.I.css().helpKey());
- b.append(name);
- b.closeSpan();
- b.append(">");
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyCommandFilter.java b/java/com/google/gwtexpui/globalkey/client/KeyCommandFilter.java
deleted file mode 100644
index 4b67260054..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyCommandFilter.java
+++ /dev/null
@@ -1,19 +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.gwtexpui.globalkey.client;
-
-public interface KeyCommandFilter {
- boolean include(KeyCommand key);
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyCommandSet.java b/java/com/google/gwtexpui/globalkey/client/KeyCommandSet.java
deleted file mode 100644
index 90aa419b4c..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyCommandSet.java
+++ /dev/null
@@ -1,145 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-public class KeyCommandSet implements KeyPressHandler {
- private final Map<Integer, KeyCommand> map;
- private List<KeyCommandSet> sets;
- private String name;
-
- public KeyCommandSet() {
- this("");
- }
-
- public KeyCommandSet(String setName) {
- map = new HashMap<>();
- name = setName;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String setName) {
- assert setName != null;
- name = setName;
- }
-
- public boolean isEmpty() {
- return map.isEmpty();
- }
-
- public void add(KeyCommand a, KeyCommand b) {
- add(a);
- add(b);
- pair(a, b);
- }
-
- public void pair(KeyCommand a, KeyCommand b) {
- a.sibling = b;
- b.sibling = a;
- }
-
- public void add(KeyCommand k) {
- assert !map.containsKey(k.keyMask)
- : "Key " + k.describeKeyStroke().asString() + " already registered";
- if (!map.containsKey(k.keyMask)) {
- map.put(k.keyMask, k);
- }
- }
-
- public void remove(KeyCommand k) {
- assert map.get(k.keyMask) == k;
- map.remove(k.keyMask);
- }
-
- public void add(KeyCommandSet set) {
- if (sets == null) {
- sets = new ArrayList<>();
- }
- assert !sets.contains(set);
- sets.add(set);
- for (KeyCommand k : set.map.values()) {
- add(k);
- }
- }
-
- public void remove(KeyCommandSet set) {
- assert sets != null;
- assert sets.contains(set);
- sets.remove(set);
- for (KeyCommand k : set.map.values()) {
- remove(k);
- }
- }
-
- public void filter(KeyCommandFilter filter) {
- if (sets != null) {
- for (KeyCommandSet s : sets) {
- s.filter(filter);
- }
- }
- for (Iterator<KeyCommand> i = map.values().iterator(); i.hasNext(); ) {
- final KeyCommand kc = i.next();
- if (!filter.include(kc)) {
- i.remove();
- } else if (kc instanceof CompoundKeyCommand) {
- ((CompoundKeyCommand) kc).set.filter(filter);
- }
- }
- }
-
- public Collection<KeyCommand> getKeys() {
- return map.values();
- }
-
- public Collection<KeyCommandSet> getSets() {
- return sets != null ? sets : Collections.<KeyCommandSet>emptyList();
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- final KeyCommand k = map.get(toMask(event));
- if (k != null) {
- event.preventDefault();
- event.stopPropagation();
- k.onKeyPress(event);
- }
- }
-
- static int toMask(KeyPressEvent event) {
- int mask = event.getUnicodeCharCode();
- if (mask == 0) {
- mask = event.getNativeEvent().getKeyCode();
- }
- if (event.isControlKeyDown()) {
- mask |= KeyCommand.M_CTRL;
- }
- if (event.isMetaKeyDown()) {
- mask |= KeyCommand.M_META;
- }
- return mask;
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyConstants.java b/java/com/google/gwtexpui/globalkey/client/KeyConstants.java
deleted file mode 100644
index 209b170941..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyConstants.java
+++ /dev/null
@@ -1,52 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.i18n.client.Constants;
-
-public interface KeyConstants extends Constants {
- KeyConstants I = GWT.create(KeyConstants.class);
-
- String applicationSection();
-
- String showHelp();
-
- String closeCurrentDialog();
-
- String keyboardShortcuts();
-
- String closeButton();
-
- String orOtherKey();
-
- String thenOtherKey();
-
- String keyCtrl();
-
- String keyAlt();
-
- String keyMeta();
-
- String keyShift();
-
- String keyEnter();
-
- String keyEsc();
-
- String keyLeft();
-
- String keyRight();
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyConstants.properties b/java/com/google/gwtexpui/globalkey/client/KeyConstants.properties
deleted file mode 100644
index 76a0318cd0..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyConstants.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-applicationSection = Application
-showHelp = Open shortcut help
-closeCurrentDialog = Close current dialog
-
-keyboardShortcuts = Keyboard Shortcuts
-closeButton = Close
-orOtherKey = or
-thenOtherKey = then
-
-keyCtrl = Ctrl
-keyAlt = Alt
-keyMeta = Meta
-keyShift = Shift
-keyEnter = Enter
-keyEsc = Esc
-keyLeft = Left
-keyRight = Right
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyCss.java b/java/com/google/gwtexpui/globalkey/client/KeyCss.java
deleted file mode 100644
index 658af57545..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyCss.java
+++ /dev/null
@@ -1,37 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface KeyCss extends CssResource {
- String helpPopup();
-
- String helpHeader();
-
- String helpHeaderGlue();
-
- String helpTable();
-
- String helpTableGlue();
-
- String helpGroup();
-
- String helpKeyStroke();
-
- String helpSeparator();
-
- String helpKey();
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
deleted file mode 100644
index 20d093e31e..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
+++ /dev/null
@@ -1,233 +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.gwtexpui.globalkey.client;
-
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyDownEvent;
-import com.google.gwt.event.dom.client.KeyDownHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FocusPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class KeyHelpPopup extends PopupPanel implements KeyPressHandler, KeyDownHandler {
- private final FocusPanel focus;
-
- public KeyHelpPopup() {
- super(true /* autohide */, true /* modal */);
- setStyleName(KeyResources.I.css().helpPopup());
-
- final Anchor closer = new Anchor(KeyConstants.I.closeButton());
- closer.addClickHandler(
- new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- }
- });
-
- final Grid header = new Grid(1, 3);
- header.setStyleName(KeyResources.I.css().helpHeader());
- header.setText(0, 0, KeyConstants.I.keyboardShortcuts());
- header.setWidget(0, 2, closer);
-
- final CellFormatter fmt = header.getCellFormatter();
- fmt.addStyleName(0, 1, KeyResources.I.css().helpHeaderGlue());
- fmt.setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_RIGHT);
-
- final Grid lists = new Grid(0, 7);
- lists.setStyleName(KeyResources.I.css().helpTable());
- populate(lists);
- lists.getCellFormatter().addStyleName(0, 3, KeyResources.I.css().helpTableGlue());
-
- final FlowPanel body = new FlowPanel();
- body.add(header);
- body.getElement().appendChild(DOM.createElement("hr"));
- body.add(lists);
-
- focus = new FocusPanel(body);
- focus.getElement().getStyle().setProperty("outline", "0px");
- focus.getElement().setAttribute("hideFocus", "true");
- focus.addKeyPressHandler(this);
- focus.addKeyDownHandler(this);
- add(focus);
- }
-
- @Override
- public void setVisible(boolean show) {
- super.setVisible(show);
- if (show) {
- focus.setFocus(true);
- }
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (KeyCommandSet.toMask(event) == ShowHelpCommand.INSTANCE.keyMask) {
- // Block the '?' key from triggering us to show right after
- // we just hide ourselves.
- //
- event.stopPropagation();
- event.preventDefault();
- }
- hide();
- }
-
- @Override
- public void onKeyDown(KeyDownEvent event) {
- if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
- hide();
- }
- }
-
- private void populate(Grid lists) {
- int[] end = new int[5];
- int column = 0;
- for (KeyCommandSet set : combinedSetsByName()) {
- int row = end[column];
- row = formatGroup(lists, row, column, set);
- end[column] = row;
- if (column == 0) {
- column = 4;
- } else {
- column = 0;
- }
- }
- }
-
- /**
- * @return an ordered collection of KeyCommandSet, combining sets which share the same name, so
- * that each set name appears at most once.
- */
- private static Collection<KeyCommandSet> combinedSetsByName() {
- LinkedHashMap<String, KeyCommandSet> byName = new LinkedHashMap<>();
- for (KeyCommandSet set : GlobalKey.active.all.getSets()) {
- KeyCommandSet v = byName.get(set.getName());
- if (v == null) {
- v = new KeyCommandSet(set.getName());
- byName.put(v.getName(), v);
- }
- v.add(set);
- }
- return byName.values();
- }
-
- private int formatGroup(Grid lists, int row, int col, KeyCommandSet set) {
- if (set.isEmpty()) {
- return row;
- }
-
- if (lists.getRowCount() < row + 1) {
- lists.resizeRows(row + 1);
- }
- lists.setText(row, col + 2, set.getName());
- lists.getCellFormatter().addStyleName(row, col + 2, KeyResources.I.css().helpGroup());
- row++;
-
- return formatKeys(lists, row, col, set, null);
- }
-
- private int formatKeys(final Grid lists, int row, int col, KeyCommandSet set, SafeHtml prefix) {
- final CellFormatter fmt = lists.getCellFormatter();
- final List<KeyCommand> keys = sort(set);
- if (lists.getRowCount() < row + keys.size()) {
- lists.resizeRows(row + keys.size());
- }
-
- Map<KeyCommand, Integer> rows = new HashMap<>();
- FORMAT_KEYS:
- for (int i = 0; i < keys.size(); i++) {
- final KeyCommand k = keys.get(i);
- if (rows.containsKey(k)) {
- continue;
- }
-
- if (k instanceof CompoundKeyCommand) {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.append(k.describeKeyStroke());
- row = formatKeys(lists, row, col, ((CompoundKeyCommand) k).getSet(), b);
- continue;
- }
-
- for (int prior = 0; prior < i; prior++) {
- if (KeyCommand.same(keys.get(prior), k)) {
- final int r = rows.get(keys.get(prior));
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.append(SafeHtml.get(lists, r, col + 0));
- b.append(" ");
- b.append(KeyConstants.I.orOtherKey());
- b.append(" ");
- if (prefix != null) {
- b.append(prefix);
- b.append(" ");
- b.append(KeyConstants.I.thenOtherKey());
- b.append(" ");
- }
- b.append(k.describeKeyStroke());
- SafeHtml.set(lists, r, col + 0, b);
- rows.put(k, r);
- continue FORMAT_KEYS;
- }
- }
-
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- String t = k.getHelpText();
- if (prefix != null) {
- b.append(prefix);
- b.append(" ");
- b.append(KeyConstants.I.thenOtherKey());
- b.append(" ");
- }
- b.append(k.describeKeyStroke());
- if (k.sibling != null) {
- b.append(" / ").append(k.sibling.describeKeyStroke());
- t += " / " + k.sibling.getHelpText();
- rows.put(k.sibling, row);
- }
- SafeHtml.set(lists, row, col + 0, b);
- lists.setText(row, col + 1, ":");
- lists.setText(row, col + 2, t);
- rows.put(k, row);
-
- fmt.addStyleName(row, col + 0, KeyResources.I.css().helpKeyStroke());
- fmt.addStyleName(row, col + 1, KeyResources.I.css().helpSeparator());
- row++;
- }
-
- return row;
- }
-
- private List<KeyCommand> sort(KeyCommandSet set) {
- return set.getKeys().stream().sorted(comparing(KeyCommand::getHelpText)).collect(toList());
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyResources.java b/java/com/google/gwtexpui/globalkey/client/KeyResources.java
deleted file mode 100644
index 562e12d04d..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/KeyResources.java
+++ /dev/null
@@ -1,25 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-
-public interface KeyResources extends ClientBundle {
- KeyResources I = GWT.create(KeyResources.class);
-
- @Source("key.css")
- KeyCss css();
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/NpTextArea.java b/java/com/google/gwtexpui/globalkey/client/NpTextArea.java
deleted file mode 100644
index fd0da747e3..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/NpTextArea.java
+++ /dev/null
@@ -1,27 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.user.client.ui.TextArea;
-
-public class NpTextArea extends TextArea {
- public NpTextArea() {
- addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
- }
-
- public void setSpellCheck(boolean spell) {
- getElement().setPropertyBoolean("spellcheck", spell);
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/NpTextBox.java b/java/com/google/gwtexpui/globalkey/client/NpTextBox.java
deleted file mode 100644
index 1392675b43..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/NpTextBox.java
+++ /dev/null
@@ -1,29 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.ui.TextBox;
-
-public class NpTextBox extends TextBox {
- public NpTextBox() {
- addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
- }
-
- public NpTextBox(Element element) {
- super(element);
- addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/ShowHelpCommand.java b/java/com/google/gwtexpui/globalkey/client/ShowHelpCommand.java
deleted file mode 100644
index 08217f4880..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/ShowHelpCommand.java
+++ /dev/null
@@ -1,72 +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.gwtexpui.globalkey.client;
-
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.event.shared.SimpleEventBus;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
-
-public class ShowHelpCommand extends KeyCommand {
- public static final ShowHelpCommand INSTANCE = new ShowHelpCommand();
- private static final EventBus BUS = new SimpleEventBus();
- private static KeyHelpPopup current;
-
- public static HandlerRegistration addFocusHandler(FocusHandler fh) {
- return BUS.addHandler(FocusEvent.getType(), fh);
- }
-
- public ShowHelpCommand() {
- super(0, '?', KeyConstants.I.showHelp());
- }
-
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (current != null) {
- // Already open? Close the dialog.
- //
- current.hide();
- return;
- }
-
- final KeyHelpPopup help = new KeyHelpPopup();
- help.addCloseHandler(
- new CloseHandler<PopupPanel>() {
- @Override
- public void onClose(CloseEvent<PopupPanel> event) {
- current = null;
- BUS.fireEvent(new FocusEvent() {});
- }
- });
- current = help;
- help.setPopupPositionAndShow(
- new PositionCallback() {
- @Override
- public void setPosition(int pWidth, int pHeight) {
- final int left = (Window.getClientWidth() - pWidth) >> 1;
- final int wLeft = Window.getScrollLeft();
- final int wTop = Window.getScrollTop();
- help.setPopupPosition(wLeft + left, wTop + 50);
- }
- });
- }
-}
diff --git a/java/com/google/gwtexpui/globalkey/client/key.css b/java/com/google/gwtexpui/globalkey/client/key.css
deleted file mode 100644
index 755e686b3e..0000000000
--- a/java/com/google/gwtexpui/globalkey/client/key.css
+++ /dev/null
@@ -1,99 +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.
- */
-
-@external .popupContent;
-
-.helpPopup {
- background: #000000 none repeat scroll 0 50%;
- color: #ffffff;
- font-family: arial,sans-serif;
- font-weight: bold;
- overflow: hidden;
- text-align: left;
- text-shadow: 1px 1px 7px #000000;
- width: 92%;
- z-index: 1002;
- opacity: 0.85;
- border-radius: 10px;
- }
-
-@if user.agent safari {
- .helpPopup {
- \-webkit-border-radius: 10px;
- }
-}
-@if user.agent gecko1_8 {
- .helpPopup {
- \-moz-border-radius: 10px;
- }
-}
-
-.helpPopup .popupContent {
- margin: 10px;
-}
-
-.helpPopup hr {
- width: 100%;
-}
-
-.helpHeader {
- width: 100%;
-}
-
-.helpHeader td {
- white-space: nowrap;
- color: #ffffff;
-}
-
-.helpHeader a,
-.helpHeader a:visited,
-.helpHeader a:hover {
- color: #dddd00;
-}
-
-.helpHeaderGlue {
- width: 100%;
-}
-
-.helpTable {
- width: 90%;
-}
-.helpTable td {
- vertical-align: top;
- white-space: nowrap;
-}
-
-.helpTableGlue {
- width: 25px;
-}
-
-.helpGroup {
- color: #dddd00;
- text-align: left;
-}
-
-.helpKeyStroke {
- text-align: right;
-}
-
-.helpSeparator {
- width: 0.5em;
- text-align: center;
- font-weight: bold;
-}
-
-.helpKey {
- color: #dddd00;
-}
diff --git a/java/com/google/gwtexpui/progress/BUILD b/java/com/google/gwtexpui/progress/BUILD
deleted file mode 100644
index 74caa577ad..0000000000
--- a/java/com/google/gwtexpui/progress/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "progress",
- srcs = glob(["client/*.java"]),
- gwt_xml = "Progress.gwt.xml",
- resources = ["client/progress.css"],
- visibility = ["//visibility:public"],
- deps = ["//lib/gwt:user"],
-)
diff --git a/java/com/google/gwtexpui/progress/Progress.gwt.xml b/java/com/google/gwtexpui/progress/Progress.gwt.xml
deleted file mode 100644
index 0df89283a3..0000000000
--- a/java/com/google/gwtexpui/progress/Progress.gwt.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<module>
- <inherits name='com.google.gwt.resources.Resources'/>
- <inherits name="com.google.gwt.user.User"/>
-</module>
diff --git a/java/com/google/gwtexpui/progress/client/ProgressBar.java b/java/com/google/gwtexpui/progress/client/ProgressBar.java
deleted file mode 100644
index f133e4d7d1..0000000000
--- a/java/com/google/gwtexpui/progress/client/ProgressBar.java
+++ /dev/null
@@ -1,77 +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.gwtexpui.progress.client;
-
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Label;
-
-/**
- * A simple progress bar with a text label.
- *
- * <p>The bar is 200 pixels wide and 20 pixels high. To keep the implementation simple and
- * lightweight this dimensions are fixed and shouldn't be modified by style overrides in client code
- * or CSS.
- */
-public class ProgressBar extends Composite {
- static {
- ProgressResources.I.css().ensureInjected();
- }
-
- private final String callerText;
- private final Label bar;
- private final Label msg;
- private int value;
-
- /** Create a bar with no message text. */
- public ProgressBar() {
- this("");
- }
-
- /** Create a bar displaying the specified message. */
- public ProgressBar(String text) {
- if (text == null || text.length() == 0) {
- callerText = "";
- } else {
- callerText = text + " ";
- }
-
- final FlowPanel body = new FlowPanel();
- body.setStyleName(ProgressResources.I.css().container());
-
- msg = new Label(callerText);
- msg.setStyleName(ProgressResources.I.css().text());
- body.add(msg);
-
- bar = new Label("");
- bar.setStyleName(ProgressResources.I.css().bar());
- body.add(bar);
-
- initWidget(body);
- }
-
- /** @return the current value of the progress meter. */
- public int getValue() {
- return value;
- }
-
- /** Update the bar's percent completion. */
- public void setValue(int pComplete) {
- assert 0 <= pComplete && pComplete <= 100;
- value = pComplete;
- bar.setWidth(2 * pComplete + "px");
- msg.setText(callerText + pComplete + "%");
- }
-}
diff --git a/java/com/google/gwtexpui/progress/client/ProgressCss.java b/java/com/google/gwtexpui/progress/client/ProgressCss.java
deleted file mode 100644
index ec27490063..0000000000
--- a/java/com/google/gwtexpui/progress/client/ProgressCss.java
+++ /dev/null
@@ -1,25 +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.gwtexpui.progress.client;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface ProgressCss extends CssResource {
- String container();
-
- String text();
-
- String bar();
-}
diff --git a/java/com/google/gwtexpui/progress/client/ProgressResources.java b/java/com/google/gwtexpui/progress/client/ProgressResources.java
deleted file mode 100644
index 6bcf2c4b69..0000000000
--- a/java/com/google/gwtexpui/progress/client/ProgressResources.java
+++ /dev/null
@@ -1,25 +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.gwtexpui.progress.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-
-public interface ProgressResources extends ClientBundle {
- ProgressResources I = GWT.create(ProgressResources.class);
-
- @Source("progress.css")
- ProgressCss css();
-}
diff --git a/java/com/google/gwtexpui/progress/client/progress.css b/java/com/google/gwtexpui/progress/client/progress.css
deleted file mode 100644
index 683396e6a1..0000000000
--- a/java/com/google/gwtexpui/progress/client/progress.css
+++ /dev/null
@@ -1,43 +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.
- */
-
-.container {
- position: relative;
- border: 1px solid #6B90DA;
- height: 20px;
- width: 200px;
-}
-
-.text {
- position: absolute;
- bottom: 0;
- left: 0;
- z-index: 2;
- width: 200px;
- padding-bottom: 3px;
- text-align: center;
- font-weight: bold;
- font-style: italic;
- font-size: smaller;
-}
-
-.bar {
- background: #F0F7F9;
- border-right: 1px solid #D0D7D9;
- position: absolute;
- top: 0;
- left: 0;
- height: 20px;
-}
diff --git a/java/com/google/gwtexpui/safehtml/BUILD b/java/com/google/gwtexpui/safehtml/BUILD
deleted file mode 100644
index af85c33c2d..0000000000
--- a/java/com/google/gwtexpui/safehtml/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "safehtml",
- srcs = glob(["client/*.java"]),
- gwt_xml = "SafeHtml.gwt.xml",
- resources = ["client/safehtml.css"],
- visibility = ["//visibility:public"],
- deps = ["//lib/gwt:user"],
-)
diff --git a/java/com/google/gwtexpui/safehtml/SafeHtml.gwt.xml b/java/com/google/gwtexpui/safehtml/SafeHtml.gwt.xml
deleted file mode 100644
index 0df89283a3..0000000000
--- a/java/com/google/gwtexpui/safehtml/SafeHtml.gwt.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<module>
- <inherits name='com.google.gwt.resources.Resources'/>
- <inherits name="com.google.gwt.user.User"/>
-</module>
diff --git a/java/com/google/gwtexpui/safehtml/client/AttMap.java b/java/com/google/gwtexpui/safehtml/client/AttMap.java
deleted file mode 100644
index c93a78b75b..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/AttMap.java
+++ /dev/null
@@ -1,141 +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.gwtexpui.safehtml.client;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/** Lightweight map of names/values for element attribute construction. */
-class AttMap {
- private static final Tag ANY = new AnyTag();
- private static final HashMap<String, Tag> TAGS;
-
- static {
- final Tag src = new SrcTag();
- TAGS = new HashMap<>();
- TAGS.put("a", new AnchorTag());
- TAGS.put("form", new FormTag());
- TAGS.put("img", src);
- TAGS.put("script", src);
- TAGS.put("frame", src);
- }
-
- private final ArrayList<String> names = new ArrayList<>();
- private final ArrayList<String> values = new ArrayList<>();
-
- private Tag tag = ANY;
- private int live;
-
- void reset(String tagName) {
- tag = TAGS.get(tagName.toLowerCase());
- if (tag == null) {
- tag = ANY;
- }
- live = 0;
- }
-
- void onto(Buffer raw, SafeHtmlBuilder esc) {
- for (int i = 0; i < live; i++) {
- final String v = values.get(i);
- if (v.length() > 0) {
- raw.append(" ");
- raw.append(names.get(i));
- raw.append("=\"");
- esc.append(v);
- raw.append("\"");
- }
- }
- }
-
- String get(String name) {
- name = name.toLowerCase();
-
- for (int i = 0; i < live; i++) {
- if (name.equals(names.get(i))) {
- return values.get(i);
- }
- }
- return "";
- }
-
- void set(String name, String value) {
- name = name.toLowerCase();
- tag.assertSafe(name, value);
-
- for (int i = 0; i < live; i++) {
- if (name.equals(names.get(i))) {
- values.set(i, value);
- return;
- }
- }
-
- final int i = live++;
- if (names.size() < live) {
- names.add(name);
- values.add(value);
- } else {
- names.set(i, name);
- values.set(i, value);
- }
- }
-
- private static void assertNotJavascriptUrl(String value) {
- if (value.startsWith("#")) {
- // common in GWT, and safe, so bypass further checks
-
- } else if (value.trim().toLowerCase().startsWith("javascript:")) {
- // possibly unsafe, we could have random user code here
- // we can't tell if its safe or not so we refuse to accept
- //
- throw new RuntimeException("javascript unsafe in href: " + value);
- }
- }
-
- private interface Tag {
- void assertSafe(String name, String value);
- }
-
- private static class AnyTag implements Tag {
- @Override
- public void assertSafe(String name, String value) {}
- }
-
- private static class AnchorTag implements Tag {
- @Override
- public void assertSafe(String name, String value) {
- if ("href".equals(name)) {
- assertNotJavascriptUrl(value);
- }
- }
- }
-
- private static class FormTag implements Tag {
- @Override
- public void assertSafe(String name, String value) {
- if ("action".equals(name)) {
- assertNotJavascriptUrl(value);
- }
- }
- }
-
- private static class SrcTag implements Tag {
- @Override
- public void assertSafe(String name, String value) {
- if ("src".equals(name)) {
- assertNotJavascriptUrl(value);
- }
- }
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/Buffer.java b/java/com/google/gwtexpui/safehtml/client/Buffer.java
deleted file mode 100644
index 12389b4531..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/Buffer.java
+++ /dev/null
@@ -1,34 +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.gwtexpui.safehtml.client;
-
-interface Buffer {
- void append(boolean v);
-
- void append(char v);
-
- void append(int v);
-
- void append(long v);
-
- void append(float v);
-
- void append(double v);
-
- void append(String v);
-
- @Override
- String toString();
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/BufferDirect.java b/java/com/google/gwtexpui/safehtml/client/BufferDirect.java
deleted file mode 100644
index c6e1d300d8..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/BufferDirect.java
+++ /dev/null
@@ -1,63 +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.gwtexpui.safehtml.client;
-
-final class BufferDirect implements Buffer {
- private final StringBuilder strbuf = new StringBuilder();
-
- boolean isEmpty() {
- return strbuf.length() == 0;
- }
-
- @Override
- public void append(boolean v) {
- strbuf.append(v);
- }
-
- @Override
- public void append(char v) {
- strbuf.append(v);
- }
-
- @Override
- public void append(int v) {
- strbuf.append(v);
- }
-
- @Override
- public void append(long v) {
- strbuf.append(v);
- }
-
- @Override
- public void append(float v) {
- strbuf.append(v);
- }
-
- @Override
- public void append(double v) {
- strbuf.append(v);
- }
-
- @Override
- public void append(String v) {
- strbuf.append(v);
- }
-
- @Override
- public String toString() {
- return strbuf.toString();
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/BufferSealElement.java b/java/com/google/gwtexpui/safehtml/client/BufferSealElement.java
deleted file mode 100644
index bdd9801952..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/BufferSealElement.java
+++ /dev/null
@@ -1,63 +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.gwtexpui.safehtml.client;
-
-final class BufferSealElement implements Buffer {
- private final SafeHtmlBuilder shb;
-
- BufferSealElement(SafeHtmlBuilder safeHtmlBuilder) {
- shb = safeHtmlBuilder;
- }
-
- @Override
- public void append(boolean v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public void append(char v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public void append(double v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public void append(float v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public void append(int v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public void append(long v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public void append(String v) {
- shb.sealElement().append(v);
- }
-
- @Override
- public String toString() {
- return shb.sealElement().toString();
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/FindReplace.java b/java/com/google/gwtexpui/safehtml/client/FindReplace.java
deleted file mode 100644
index 4fb32465fd..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/FindReplace.java
+++ /dev/null
@@ -1,36 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.regexp.shared.RegExp;
-
-/** A Find/Replace pair used against the {@link SafeHtml} block of text. */
-public interface FindReplace {
- /** @return regular expression to match substrings with; should be treated as immutable. */
- RegExp pattern();
-
- /**
- * Find and replace a single instance of this pattern in an input.
- *
- * <p><b>WARNING:</b> No XSS sanitization is done on the return value of this method, e.g. this
- * value may be passed directly to {@link SafeHtml#replaceAll(String, String)}. Implementations
- * must sanitize output appropriately.
- *
- * @param input input string.
- * @return result of regular expression replacement.
- * @throws IllegalArgumentException if the input could not be safely sanitized.
- */
- String replace(String input);
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
deleted file mode 100644
index 758521fc0b..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
+++ /dev/null
@@ -1,151 +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.gwtexpui.safehtml.client;
-
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
-
-import com.google.gwt.user.client.ui.SuggestOracle;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * A suggestion oracle that tries to highlight the matched text.
- *
- * <p>Suggestions supplied by the implementation of {@link #onRequestSuggestions(Request, Callback)}
- * are modified to wrap all occurrences of the {@link
- * com.google.gwt.user.client.ui.SuggestOracle.Request#getQuery()} substring in HTML {@code
- * &lt;strong&gt;} tags, so they can be emphasized to the user.
- */
-public abstract class HighlightSuggestOracle extends SuggestOracle {
- private static String escape(String ds) {
- return new SafeHtmlBuilder().append(ds).asString();
- }
-
- @Override
- public final boolean isDisplayStringHTML() {
- return true;
- }
-
- @Override
- public final void requestSuggestions(Request request, Callback cb) {
- onRequestSuggestions(
- request,
- new Callback() {
- @Override
- public void onSuggestionsReady(Request request, Response response) {
- final String qpat = getQueryPattern(request.getQuery());
- final boolean html = isHTML();
- final ArrayList<Suggestion> r = new ArrayList<>();
- for (Suggestion s : response.getSuggestions()) {
- r.add(new BoldSuggestion(qpat, s, html));
- }
- cb.onSuggestionsReady(request, new Response(r));
- }
- });
- }
-
- protected String getQueryPattern(String query) {
- return query;
- }
-
- /**
- * @return true if {@link
- * com.google.gwt.user.client.ui.SuggestOracle.Suggestion#getDisplayString()} returns HTML;
- * false if the text must be escaped before evaluating in an HTML like context.
- */
- protected boolean isHTML() {
- return false;
- }
-
- /** Compute the suggestions and return them for display. */
- protected abstract void onRequestSuggestions(Request request, Callback done);
-
- private static class BoldSuggestion implements Suggestion {
- private final Suggestion suggestion;
- private final String displayString;
-
- BoldSuggestion(String qstr, Suggestion s, boolean html) {
- suggestion = s;
-
- String ds = s.getDisplayString();
- if (!html) {
- ds = escape(ds);
- }
-
- if (qstr != null && !qstr.isEmpty()) {
- StringBuilder pattern = new StringBuilder();
- for (String qterm : splitQuery(qstr)) {
- qterm = escape(qterm);
- // We now surround qstr by <strong>. But the chosen approach is not too
- // smooth, if qstr is small (e.g.: "t") and this small qstr may occur in
- // escapes (e.g.: "Tim &lt;email@example.org&gt;"). Those escapes will
- // get <strong>-ed as well (e.g.: "&lt;" -> "&<strong>l</strong>t;"). But
- // as repairing those mangled escapes is easier than not mangling them in
- // the first place, we repair them afterwards.
- if (pattern.length() > 0) {
- pattern.append("|");
- }
- pattern.append(qterm);
- }
-
- ds = sgi(ds, "(" + pattern.toString() + ")", "<strong>$1</strong>");
-
- // Repairing <strong>-ed escapes.
- ds = sgi(ds, "(&[a-z]*)<strong>([a-z]*)</strong>([a-z]*;)", "$1$2$3");
- }
-
- displayString = ds;
- }
-
- /**
- * Split the query by whitespace and filter out query terms which are substrings of other query
- * terms.
- */
- private static List<String> splitQuery(String query) {
- List<String> queryTerms =
- Arrays.stream(query.split("\\s+")).sorted(comparing(String::length)).collect(toList());
-
- List<String> result = new ArrayList<>();
- for (String s : queryTerms) {
- boolean add = true;
- for (String queryTerm : result) {
- if (queryTerm.toLowerCase().contains(s.toLowerCase())) {
- add = false;
- break;
- }
- }
- if (add) {
- result.add(s);
- }
- }
- return result;
- }
-
- private static native String sgi(String inString, String pat, String newHtml)
- /*-{ return inString.replace(RegExp(pat, 'gi'), newHtml); }-*/ ;
-
- @Override
- public String getDisplayString() {
- return displayString;
- }
-
- @Override
- public String getReplacementString() {
- return suggestion.getReplacementString();
- }
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java b/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java
deleted file mode 100644
index cf0e51db7b..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java
+++ /dev/null
@@ -1,82 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.regexp.shared.RegExp;
-
-/**
- * A Find/Replace pair whose replacement string is a link.
- *
- * <p>It is safe to pass arbitrary user-provided links to this class. Links are sanitized as
- * follows:
- *
- * <ul>
- * <li>Only http(s) and mailto links are supported; any other scheme results in an {@link
- * IllegalArgumentException} from {@link #replace(String)}.
- * <li>Special characters in the link after regex replacement are escaped with {@link
- * SafeHtmlBuilder}.
- * </ul>
- */
-public class LinkFindReplace implements FindReplace {
- public static boolean hasValidScheme(String link) {
- int colon = link.indexOf(':');
- if (colon < 0) {
- return true;
- }
- String scheme = link.substring(0, colon);
- return "http".equalsIgnoreCase(scheme)
- || "https".equalsIgnoreCase(scheme)
- || "mailto".equalsIgnoreCase(scheme);
- }
-
- private RegExp pat;
- private String link;
-
- protected LinkFindReplace() {}
-
- /**
- * @param find regular expression pattern to match substrings with.
- * @param link replacement link href. Capture groups within {@code find} can be referenced with
- * {@code $<i>n</i>}.
- */
- public LinkFindReplace(String find, String link) {
- this.pat = RegExp.compile(find);
- this.link = link;
- }
-
- @Override
- public RegExp pattern() {
- return pat;
- }
-
- @Override
- public String replace(String input) {
- String href = pat.replace(input, link);
- if (!hasValidScheme(href)) {
- throw new IllegalArgumentException("Invalid scheme (" + toString() + "): " + href);
- }
- return new SafeHtmlBuilder()
- .openAnchor()
- .setAttribute("href", href)
- .append(SafeHtml.asis(input))
- .closeAnchor()
- .asString();
- }
-
- @Override
- public String toString() {
- return "find = " + pat.getSource() + ", link = " + link;
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/RawFindReplace.java b/java/com/google/gwtexpui/safehtml/client/RawFindReplace.java
deleted file mode 100644
index dc39af6e3b..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/RawFindReplace.java
+++ /dev/null
@@ -1,54 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.regexp.shared.RegExp;
-
-/**
- * A Find/Replace pair whose replacement string is arbitrary HTML.
- *
- * <p><b>WARNING:</b> This class is not safe used with user-provided patterns.
- */
-public class RawFindReplace implements FindReplace {
- private RegExp pat;
- private String replace;
-
- protected RawFindReplace() {}
-
- /**
- * @param find regular expression pattern to match substrings with.
- * @param replace replacement expression. Capture groups within {@code find} can be referenced
- * with {@code $<i>n</i>}.
- */
- public RawFindReplace(String find, String replace) {
- this.pat = RegExp.compile(find);
- this.replace = replace;
- }
-
- @Override
- public RegExp pattern() {
- return pat;
- }
-
- @Override
- public String replace(String input) {
- return pat.replace(input, replace);
- }
-
- @Override
- public String toString() {
- return "find = " + pat.getSource() + ", replace = " + replace;
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/SafeHtml.java b/java/com/google/gwtexpui/safehtml/client/SafeHtml.java
deleted file mode 100644
index 5b3b9b607a..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/SafeHtml.java
+++ /dev/null
@@ -1,335 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HTMLTable;
-import com.google.gwt.user.client.ui.HasHTML;
-import com.google.gwt.user.client.ui.InlineHTML;
-import com.google.gwt.user.client.ui.Widget;
-import java.util.Iterator;
-import java.util.List;
-
-/** Immutable string safely placed as HTML without further escaping. */
-public abstract class SafeHtml implements com.google.gwt.safehtml.shared.SafeHtml {
- private static final long serialVersionUID = 1L;
-
- public static final SafeHtmlResources RESOURCES;
-
- static {
- if (GWT.isClient()) {
- RESOURCES = GWT.create(SafeHtmlResources.class);
- RESOURCES.css().ensureInjected();
-
- } else {
- RESOURCES =
- new SafeHtmlResources() {
- @Override
- public SafeHtmlCss css() {
- return new SafeHtmlCss() {
- @Override
- public String wikiList() {
- return "wikiList";
- }
-
- @Override
- public String wikiPreFormat() {
- return "wikiPreFormat";
- }
-
- @Override
- public String wikiQuote() {
- return "wikiQuote";
- }
-
- @Override
- public boolean ensureInjected() {
- return false;
- }
-
- @Override
- public String getName() {
- return null;
- }
-
- @Override
- public String getText() {
- return null;
- }
- };
- }
- };
- }
- }
-
- /** @return the existing HTML property of a widget. */
- public static SafeHtml get(HasHTML t) {
- return new SafeHtmlString(t.getHTML());
- }
-
- /** @return the existing HTML text, wrapped in a safe buffer. */
- public static SafeHtml asis(String htmlText) {
- return new SafeHtmlString(htmlText);
- }
-
- /** Set the HTML property of a widget. */
- public static <T extends HasHTML> T set(T e, SafeHtml str) {
- e.setHTML(str.asString());
- return e;
- }
-
- /** @return the existing inner HTML of any element. */
- public static SafeHtml get(Element e) {
- return new SafeHtmlString(e.getInnerHTML());
- }
-
- /** Set the inner HTML of any element. */
- public static Element setInnerHTML(Element e, SafeHtml str) {
- e.setInnerHTML(str.asString());
- return e;
- }
-
- /** @return the existing inner HTML of a table cell. */
- public static SafeHtml get(HTMLTable t, int row, int col) {
- return new SafeHtmlString(t.getHTML(row, col));
- }
-
- /** Set the inner HTML of a table cell. */
- public static <T extends HTMLTable> T set(final T t, int row, int col, SafeHtml str) {
- t.setHTML(row, col, str.asString());
- return t;
- }
-
- /** Parse an HTML block and return the first (typically root) element. */
- public static Element parse(SafeHtml html) {
- Element e = DOM.createDiv();
- setInnerHTML(e, html);
- return DOM.getFirstChild(e);
- }
-
- /** Convert bare http:// and https:// URLs into &lt;a href&gt; tags. */
- public SafeHtml linkify() {
- final String part = "(?:[a-zA-Z0-9$_+!*'%;:@=?#/~-]|&(?!lt;|gt;)|[.,](?!(?:\\s|$)))";
- return replaceAll(
- "(https?://" + part + "{2,}(?:[(]" + part + "*[)])*" + part + "*)",
- "<a href=\"$1\" target=\"_blank\" rel=\"nofollow\">$1</a>");
- }
-
- /**
- * Apply {@link #linkify()}, and "\n\n" to &lt;p&gt;.
- *
- * <p>Lines that start with whitespace are assumed to be preformatted, and are formatted by the
- * {@link SafeHtmlCss#wikiPreFormat()} CSS class.
- */
- public SafeHtml wikify() {
- final SafeHtmlBuilder r = new SafeHtmlBuilder();
- for (String p : linkify().asString().split("\n\n")) {
- if (isQuote(p)) {
- wikifyQuote(r, p);
-
- } else if (isPreFormat(p)) {
- r.openElement("p");
- for (String line : p.split("\n")) {
- r.openSpan();
- r.setStyleName(RESOURCES.css().wikiPreFormat());
- r.append(asis(line));
- r.closeSpan();
- r.br();
- }
- r.closeElement("p");
-
- } else if (isList(p)) {
- wikifyList(r, p);
-
- } else {
- r.openElement("p");
- r.append(asis(p));
- r.closeElement("p");
- }
- }
- return r.toSafeHtml();
- }
-
- private void wikifyList(SafeHtmlBuilder r, String p) {
- boolean in_ul = false;
- boolean in_p = false;
- for (String line : p.split("\n")) {
- if (line.startsWith("-") || line.startsWith("*")) {
- if (!in_ul) {
- if (in_p) {
- in_p = false;
- r.closeElement("p");
- }
-
- in_ul = true;
- r.openElement("ul");
- r.setStyleName(RESOURCES.css().wikiList());
- }
- line = line.substring(1).trim();
-
- } else if (!in_ul) {
- if (!in_p) {
- in_p = true;
- r.openElement("p");
- } else {
- r.append(' ');
- }
- r.append(asis(line));
- continue;
- }
-
- r.openElement("li");
- r.append(asis(line));
- r.closeElement("li");
- }
-
- if (in_ul) {
- r.closeElement("ul");
- } else if (in_p) {
- r.closeElement("p");
- }
- }
-
- private void wikifyQuote(SafeHtmlBuilder r, String p) {
- r.openElement("blockquote");
- r.setStyleName(RESOURCES.css().wikiQuote());
- if (p.startsWith("&gt; ")) {
- p = p.substring(5);
- } else if (p.startsWith(" &gt; ")) {
- p = p.substring(6);
- }
- p = p.replaceAll("\\n ?&gt; ", "\n");
- for (String e : p.split("\n\n")) {
- if (isQuote(e)) {
- SafeHtmlBuilder b = new SafeHtmlBuilder();
- wikifyQuote(b, e);
- r.append(b);
- } else {
- r.append(asis(e));
- }
- }
- r.closeElement("blockquote");
- }
-
- private static boolean isQuote(String p) {
- return p.startsWith("&gt; ") || p.startsWith(" &gt; ");
- }
-
- private static boolean isPreFormat(String p) {
- return p.contains("\n ") || p.contains("\n\t") || p.startsWith(" ") || p.startsWith("\t");
- }
-
- private static boolean isList(String p) {
- return p.contains("\n- ") || p.contains("\n* ") || p.startsWith("- ") || p.startsWith("* ");
- }
-
- /**
- * Replace first occurrence of {@code regex} with {@code repl} .
- *
- * <p><b>WARNING:</b> This replacement is being performed against an otherwise safe HTML string.
- * The caller must ensure that the replacement does not introduce cross-site scripting attack
- * entry points.
- *
- * @param regex regular expression pattern to match the substring with.
- * @param repl replacement expression. Capture groups within {@code regex} can be referenced with
- * {@code $<i>n</i>}.
- * @return a new string, after the replacement has been made.
- */
- public SafeHtml replaceFirst(String regex, String repl) {
- return new SafeHtmlString(asString().replaceFirst(regex, repl));
- }
-
- /**
- * Replace each occurrence of {@code regex} with {@code repl} .
- *
- * <p><b>WARNING:</b> This replacement is being performed against an otherwise safe HTML string.
- * The caller must ensure that the replacement does not introduce cross-site scripting attack
- * entry points.
- *
- * @param regex regular expression pattern to match substrings with.
- * @param repl replacement expression. Capture groups within {@code regex} can be referenced with
- * {@code $<i>n</i>}.
- * @return a new string, after the replacements have been made.
- */
- public SafeHtml replaceAll(String regex, String repl) {
- return new SafeHtmlString(asString().replaceAll(regex, repl));
- }
-
- /**
- * Replace all find/replace pairs in the list in a single pass.
- *
- * @param findReplaceList find/replace pairs to use.
- * @return a new string, after the replacements have been made.
- */
- public <T> SafeHtml replaceAll(List<? extends FindReplace> findReplaceList) {
- if (findReplaceList == null || findReplaceList.isEmpty()) {
- return this;
- }
-
- StringBuilder pat = new StringBuilder();
- Iterator<? extends FindReplace> it = findReplaceList.iterator();
- while (it.hasNext()) {
- FindReplace fr = it.next();
- pat.append(fr.pattern().getSource());
- if (it.hasNext()) {
- pat.append('|');
- }
- }
-
- StringBuilder result = new StringBuilder();
- RegExp re = RegExp.compile(pat.toString(), "g");
- String orig = asString();
- int index = 0;
- MatchResult mat;
- while ((mat = re.exec(orig)) != null) {
- String g = mat.getGroup(0);
- // Re-run each candidate to find which one matched.
- for (FindReplace fr : findReplaceList) {
- if (fr.pattern().test(g)) {
- try {
- String repl = fr.replace(g);
- result.append(orig.substring(index, mat.getIndex()));
- result.append(repl);
- } catch (IllegalArgumentException e) {
- continue;
- }
- index = mat.getIndex() + g.length();
- break;
- }
- }
- }
- result.append(orig.substring(index, orig.length()));
- return asis(result.toString());
- }
-
- /** @return a GWT block display widget displaying this HTML. */
- public Widget toBlockWidget() {
- return new HTML(asString());
- }
-
- /** @return a GWT inline display widget displaying this HTML. */
- public Widget toInlineWidget() {
- return new InlineHTML(asString());
- }
-
- /** @return a clean HTML string safe for inclusion in any context. */
- @Override
- public abstract String asString();
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java b/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java
deleted file mode 100644
index cde0f2af75..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/SafeHtmlBuilder.java
+++ /dev/null
@@ -1,425 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.core.client.GWT;
-
-/** Safely constructs a {@link SafeHtml}, escaping user provided content. */
-public class SafeHtmlBuilder extends SafeHtml {
- private static final long serialVersionUID = 1L;
-
- private static final Impl impl;
-
- static {
- if (GWT.isClient()) {
- impl = new ClientImpl();
- } else {
- impl = new ServerImpl();
- }
- }
-
- private final BufferDirect dBuf;
- private Buffer cb;
-
- private BufferSealElement sBuf;
- private AttMap att;
-
- public SafeHtmlBuilder() {
- cb = dBuf = new BufferDirect();
- }
-
- /** @return true if this builder has not had an append occur yet. */
- public boolean isEmpty() {
- return dBuf.isEmpty();
- }
-
- /** @return true if this builder has content appended into it. */
- public boolean hasContent() {
- return !isEmpty();
- }
-
- public SafeHtmlBuilder append(boolean in) {
- cb.append(in);
- return this;
- }
-
- public SafeHtmlBuilder append(char in) {
- switch (in) {
- case '&':
- cb.append("&amp;");
- break;
-
- case '>':
- cb.append("&gt;");
- break;
-
- case '<':
- cb.append("&lt;");
- break;
-
- case '"':
- cb.append("&quot;");
- break;
-
- case '\'':
- cb.append("&#39;");
- break;
-
- default:
- cb.append(in);
- break;
- }
- return this;
- }
-
- public SafeHtmlBuilder append(int in) {
- cb.append(in);
- return this;
- }
-
- public SafeHtmlBuilder append(long in) {
- cb.append(in);
- return this;
- }
-
- public SafeHtmlBuilder append(float in) {
- cb.append(in);
- return this;
- }
-
- public SafeHtmlBuilder append(double in) {
- cb.append(in);
- return this;
- }
-
- /** Append already safe HTML as-is, avoiding double escaping. */
- public SafeHtmlBuilder append(com.google.gwt.safehtml.shared.SafeHtml in) {
- if (in != null) {
- cb.append(in.asString());
- }
- return this;
- }
-
- /** Append already safe HTML as-is, avoiding double escaping. */
- public SafeHtmlBuilder append(SafeHtml in) {
- if (in != null) {
- cb.append(in.asString());
- }
- return this;
- }
-
- /** Append the string, escaping unsafe characters. */
- public SafeHtmlBuilder append(String in) {
- if (in != null) {
- impl.escapeStr(this, in);
- }
- return this;
- }
-
- /** Append the string, escaping unsafe characters. */
- public SafeHtmlBuilder append(StringBuilder in) {
- if (in != null) {
- append(in.toString());
- }
- return this;
- }
-
- /** Append the string, escaping unsafe characters. */
- public SafeHtmlBuilder append(StringBuffer in) {
- if (in != null) {
- append(in.toString());
- }
- return this;
- }
-
- /** Append the result of toString(), escaping unsafe characters. */
- public SafeHtmlBuilder append(Object in) {
- if (in != null) {
- append(in.toString());
- }
- return this;
- }
-
- /** Append the string, escaping unsafe characters. */
- public SafeHtmlBuilder append(CharSequence in) {
- if (in != null) {
- escapeCS(this, in);
- }
- return this;
- }
-
- /**
- * Open an element, appending "{@code <tagName>}" to the buffer.
- *
- * <p>After the element is open the attributes may be manipulated until the next {@code append},
- * {@code openElement}, {@code closeSelf} or {@code closeElement} call.
- *
- * @param tagName name of the HTML element to open.
- */
- public SafeHtmlBuilder openElement(String tagName) {
- assert isElementName(tagName);
- cb.append("<");
- cb.append(tagName);
- if (sBuf == null) {
- att = new AttMap();
- sBuf = new BufferSealElement(this);
- }
- att.reset(tagName);
- cb = sBuf;
- return this;
- }
-
- /**
- * Get an attribute of the last opened element.
- *
- * @param name name of the attribute to read.
- * @return the attribute value, as a string. The empty string if the attribute has not been
- * assigned a value. The returned string is the raw (unescaped) value.
- */
- public String getAttribute(String name) {
- assert isAttributeName(name);
- assert cb == sBuf;
- return att.get(name);
- }
-
- /**
- * Set an attribute of the last opened element.
- *
- * @param name name of the attribute to set.
- * @param value value to assign; any existing value is replaced. The value is escaped (if
- * necessary) during the assignment.
- */
- public SafeHtmlBuilder setAttribute(String name, String value) {
- assert isAttributeName(name);
- assert cb == sBuf;
- att.set(name, value != null ? value : "");
- return this;
- }
-
- /**
- * Set an attribute of the last opened element.
- *
- * @param name name of the attribute to set.
- * @param value value to assign, any existing value is replaced.
- */
- public SafeHtmlBuilder setAttribute(String name, int value) {
- return setAttribute(name, String.valueOf(value));
- }
-
- /**
- * Append a new value into a whitespace delimited attribute.
- *
- * <p>If the attribute is not yet assigned, this method sets the attribute. If the attribute is
- * already assigned, the new value is appended onto the end, after appending a single space to
- * delimit the values.
- *
- * @param name name of the attribute to append onto.
- * @param value additional value to append.
- */
- public SafeHtmlBuilder appendAttribute(String name, String value) {
- if (value != null && value.length() > 0) {
- final String e = getAttribute(name);
- return setAttribute(name, e.length() > 0 ? e + " " + value : value);
- }
- return this;
- }
-
- /** Set the height attribute of the current element. */
- public SafeHtmlBuilder setHeight(int height) {
- return setAttribute("height", height);
- }
-
- /** Set the width attribute of the current element. */
- public SafeHtmlBuilder setWidth(int width) {
- return setAttribute("width", width);
- }
-
- /** Set the CSS class name for this element. */
- public SafeHtmlBuilder setStyleName(String style) {
- assert isCssName(style);
- return setAttribute("class", style);
- }
-
- /**
- * Add an additional CSS class name to this element.
- *
- * <p>If no CSS class name has been specified yet, this method initializes it to the single name.
- */
- public SafeHtmlBuilder addStyleName(String style) {
- assert isCssName(style);
- return appendAttribute("class", style);
- }
-
- private void sealElement0() {
- assert cb == sBuf;
- cb = dBuf;
- att.onto(cb, this);
- }
-
- Buffer sealElement() {
- sealElement0();
- cb.append(">");
- return cb;
- }
-
- /** Close the current element with a self closing suffix ("/ &gt;"). */
- public SafeHtmlBuilder closeSelf() {
- sealElement0();
- cb.append(" />");
- return this;
- }
-
- /** Append a closing tag for the named element. */
- public SafeHtmlBuilder closeElement(String name) {
- assert isElementName(name);
- cb.append("</");
- cb.append(name);
- cb.append(">");
- return this;
- }
-
- /** Append "&amp;nbsp;" - a non-breaking space, useful in empty table cells. */
- public SafeHtmlBuilder nbsp() {
- cb.append("&nbsp;");
- return this;
- }
-
- /** Append "&lt;br /&gt;" - a line break with no attributes */
- public SafeHtmlBuilder br() {
- cb.append("<br />");
- return this;
- }
-
- /** Append "&lt;tr&gt;"; attributes may be set if needed */
- public SafeHtmlBuilder openTr() {
- return openElement("tr");
- }
-
- /** Append "&lt;/tr&gt;" */
- public SafeHtmlBuilder closeTr() {
- return closeElement("tr");
- }
-
- /** Append "&lt;td&gt;"; attributes may be set if needed */
- public SafeHtmlBuilder openTd() {
- return openElement("td");
- }
-
- /** Append "&lt;/td&gt;" */
- public SafeHtmlBuilder closeTd() {
- return closeElement("td");
- }
-
- /** Append "&lt;th&gt;"; attributes may be set if needed */
- public SafeHtmlBuilder openTh() {
- return openElement("th");
- }
-
- /** Append "&lt;/th&gt;" */
- public SafeHtmlBuilder closeTh() {
- return closeElement("th");
- }
-
- /** Append "&lt;div&gt;"; attributes may be set if needed */
- public SafeHtmlBuilder openDiv() {
- return openElement("div");
- }
-
- /** Append "&lt;/div&gt;" */
- public SafeHtmlBuilder closeDiv() {
- return closeElement("div");
- }
-
- /** Append "&lt;span&gt;"; attributes may be set if needed */
- public SafeHtmlBuilder openSpan() {
- return openElement("span");
- }
-
- /** Append "&lt;/span&gt;" */
- public SafeHtmlBuilder closeSpan() {
- return closeElement("span");
- }
-
- /** Append "&lt;a&gt;"; attributes may be set if needed */
- public SafeHtmlBuilder openAnchor() {
- return openElement("a");
- }
-
- /** Append "&lt;/a&gt;" */
- public SafeHtmlBuilder closeAnchor() {
- return closeElement("a");
- }
-
- /** Append "&lt;param name=... value=... /&gt;". */
- public SafeHtmlBuilder paramElement(String name, String value) {
- openElement("param");
- setAttribute("name", name);
- setAttribute("value", value);
- return closeSelf();
- }
-
- /** @return an immutable {@link SafeHtml} representation of the buffer. */
- public SafeHtml toSafeHtml() {
- return new SafeHtmlString(asString());
- }
-
- @Override
- public String asString() {
- return cb.toString();
- }
-
- private static void escapeCS(SafeHtmlBuilder b, CharSequence in) {
- for (int i = 0; i < in.length(); i++) {
- b.append(in.charAt(i));
- }
- }
-
- private static boolean isElementName(String name) {
- return name.matches("^[a-zA-Z][a-zA-Z0-9_-]*$");
- }
-
- private static boolean isAttributeName(String name) {
- return isElementName(name);
- }
-
- private static boolean isCssName(String name) {
- return isElementName(name);
- }
-
- private abstract static class Impl {
- abstract void escapeStr(SafeHtmlBuilder b, String in);
- }
-
- private static class ServerImpl extends Impl {
- @Override
- void escapeStr(SafeHtmlBuilder b, String in) {
- SafeHtmlBuilder.escapeCS(b, in);
- }
- }
-
- private static class ClientImpl extends Impl {
- @Override
- void escapeStr(SafeHtmlBuilder b, String in) {
- b.cb.append(escape(in));
- }
-
- private static native String escape(String src) /*-{ return src.replace(/&/g,'&amp;')
- .replace(/>/g,'&gt;')
- .replace(/</g,'&lt;')
- .replace(/"/g,'&quot;')
- .replace(/'/g,'&#39;');
- }-*/;
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/SafeHtmlCss.java b/java/com/google/gwtexpui/safehtml/client/SafeHtmlCss.java
deleted file mode 100644
index f4b1c777d4..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/SafeHtmlCss.java
+++ /dev/null
@@ -1,25 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.resources.client.CssResource;
-
-public interface SafeHtmlCss extends CssResource {
- String wikiList();
-
- String wikiPreFormat();
-
- String wikiQuote();
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/SafeHtmlResources.java b/java/com/google/gwtexpui/safehtml/client/SafeHtmlResources.java
deleted file mode 100644
index e3f5724d61..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/SafeHtmlResources.java
+++ /dev/null
@@ -1,22 +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.gwtexpui.safehtml.client;
-
-import com.google.gwt.resources.client.ClientBundle;
-
-public interface SafeHtmlResources extends ClientBundle {
- @Source("safehtml.css")
- SafeHtmlCss css();
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/SafeHtmlString.java b/java/com/google/gwtexpui/safehtml/client/SafeHtmlString.java
deleted file mode 100644
index 5335170499..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/SafeHtmlString.java
+++ /dev/null
@@ -1,30 +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.gwtexpui.safehtml.client;
-
-class SafeHtmlString extends SafeHtml {
- private static final long serialVersionUID = 1L;
-
- private final String html;
-
- SafeHtmlString(String h) {
- html = h;
- }
-
- @Override
- public String asString() {
- return html;
- }
-}
diff --git a/java/com/google/gwtexpui/safehtml/client/safehtml.css b/java/com/google/gwtexpui/safehtml/client/safehtml.css
deleted file mode 100644
index 163c548b9a..0000000000
--- a/java/com/google/gwtexpui/safehtml/client/safehtml.css
+++ /dev/null
@@ -1,29 +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.
- */
-
-.wikiList {
-}
-
-.wikiPreFormat {
- white-space: pre;
- font-family: 'Lucida Console', 'Lucida Sans Typewriter', Monaco, monospace;
- font-size: small;
-}
-
-.wikiQuote {
- margin-left: 0;
- border-left: 1px solid #888;
- padding-left: 5px;
-}
diff --git a/java/com/google/gwtexpui/user/BUILD b/java/com/google/gwtexpui/user/BUILD
deleted file mode 100644
index 813f4330cb..0000000000
--- a/java/com/google/gwtexpui/user/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "agent",
- srcs = glob(["client/*.java"]),
- gwt_xml = "User.gwt.xml",
- resources = ["client/tooltip.css"],
- visibility = ["//visibility:public"],
- deps = ["//lib/gwt:user"],
-)
diff --git a/java/com/google/gwtexpui/user/User.gwt.xml b/java/com/google/gwtexpui/user/User.gwt.xml
deleted file mode 100644
index f4f8d51dd1..0000000000
--- a/java/com/google/gwtexpui/user/User.gwt.xml
+++ /dev/null
@@ -1,18 +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.
--->
-<module>
- <inherits name="com.google.gwt.user.User"/>
-</module>
diff --git a/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java b/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java
deleted file mode 100644
index fdaf8617f1..0000000000
--- a/java/com/google/gwtexpui/user/client/AutoCenterDialogBox.java
+++ /dev/null
@@ -1,79 +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.gwtexpui.user.client;
-
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.DialogBox;
-
-/** A DialogBox that automatically re-centers itself if the window changes */
-public class AutoCenterDialogBox extends DialogBox {
- private HandlerRegistration recenter;
-
- public AutoCenterDialogBox() {
- this(false);
- }
-
- public AutoCenterDialogBox(boolean autoHide) {
- this(autoHide, true);
- }
-
- public AutoCenterDialogBox(boolean autoHide, boolean modal) {
- super(autoHide, modal);
- }
-
- @Override
- public void show() {
- if (recenter == null) {
- recenter =
- Window.addResizeHandler(
- new ResizeHandler() {
- @Override
- public void onResize(ResizeEvent event) {
- final int w = event.getWidth();
- final int h = event.getHeight();
- AutoCenterDialogBox.this.onResize(w, h);
- }
- });
- }
- super.show();
- }
-
- @Override
- protected void onUnload() {
- if (recenter != null) {
- recenter.removeHandler();
- recenter = null;
- }
- super.onUnload();
- }
-
- /**
- * Invoked when the outer browser window resizes.
- *
- * <p>Subclasses may override (but should ensure they still call super.onResize) to implement
- * custom logic when a window resize occurs.
- *
- * @param width new browser window width
- * @param height new browser window height
- */
- protected void onResize(int width, int height) {
- if (isAttached()) {
- center();
- }
- }
-}
diff --git a/java/com/google/gwtexpui/user/client/Tooltip.java b/java/com/google/gwtexpui/user/client/Tooltip.java
deleted file mode 100644
index b5ce046f58..0000000000
--- a/java/com/google/gwtexpui/user/client/Tooltip.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2015 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.gwtexpui.user.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.CssResource;
-import com.google.gwt.user.client.ui.UIObject;
-
-/** Displays custom tooltip message below an element. */
-public class Tooltip {
- interface Resources extends ClientBundle {
- Resources I = GWT.create(Resources.class);
-
- @Source("tooltip.css")
- Css css();
- }
-
- interface Css extends CssResource {
- String tooltip();
- }
-
- static {
- Resources.I.css().ensureInjected();
- }
-
- /**
- * Add required supporting style to enable custom tooltip rendering.
- *
- * @param o widget whose element should display a tooltip on hover.
- */
- public static void addStyle(UIObject o) {
- addStyle(o.getElement());
- }
-
- /**
- * Add required supporting style to enable custom tooltip rendering.
- *
- * @param e element that should display a tooltip on hover.
- */
- public static void addStyle(Element e) {
- e.addClassName(Resources.I.css().tooltip());
- }
-
- /**
- * Set the text displayed on hover.
- *
- * @param o widget whose hover text is being set.
- * @param text message to display on hover.
- */
- public static void setLabel(UIObject o, String text) {
- setLabel(o.getElement(), text);
- }
-
- /**
- * Set the text displayed on hover.
- *
- * @param e element whose hover text is being set.
- * @param text message to display on hover.
- */
- public static void setLabel(Element e, String text) {
- e.setAttribute("aria-label", text != null ? text : "");
- }
-
- private Tooltip() {}
-}
diff --git a/java/com/google/gwtexpui/user/client/UserAgent.java b/java/com/google/gwtexpui/user/client/UserAgent.java
deleted file mode 100644
index 1660a62c27..0000000000
--- a/java/com/google/gwtexpui/user/client/UserAgent.java
+++ /dev/null
@@ -1,168 +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.gwtexpui.user.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.Window;
-
-/**
- * User agent feature tests we don't create permutations for.
- *
- * <p>Some features aren't worth creating full permutations in GWT for, as each new boolean
- * permutation (only two settings) doubles the compile time required. If the setting only affects a
- * couple of lines of JavaScript code, the slightly larger cache files for user agents that lack the
- * functionality requested is trivial compared to the time developers lose building their
- * application.
- */
-public class UserAgent {
- private static boolean jsClip = guessJavaScriptClipboard();
-
- public static boolean hasJavaScriptClipboard() {
- return jsClip;
- }
-
- public static void disableJavaScriptClipboard() {
- jsClip = false;
- }
-
- private static native boolean nativeHasCopy()
- /*-{ return $doc['queryCommandSupported'] && $doc.queryCommandSupported('copy') }-*/ ;
-
- private static boolean guessJavaScriptClipboard() {
- String ua = Window.Navigator.getUserAgent();
- int chrome = major(ua, "Chrome/");
- if (chrome > 0) {
- return 42 <= chrome;
- }
-
- int ff = major(ua, "Firefox/");
- if (ff > 0) {
- return 41 <= ff;
- }
-
- int opera = major(ua, "OPR/");
- if (opera > 0) {
- return 29 <= opera;
- }
-
- int msie = major(ua, "MSIE ");
- if (msie > 0) {
- return 9 <= msie;
- }
-
- if (nativeHasCopy()) {
- // Firefox 39.0 lies and says it supports copy, then fails.
- // So we try this after the browser specific test above.
- return true;
- }
-
- // Safari is not planning to support document.execCommand('copy').
- // Assume the browser does not have the feature.
- return false;
- }
-
- private static int major(String ua, String product) {
- int entry = ua.indexOf(product);
- if (entry >= 0) {
- String s = ua.substring(entry + product.length());
- String p = s.split("[ /;,.)]", 2)[0];
- try {
- return Integer.parseInt(p);
- } catch (NumberFormatException nan) {
- // Ignored
- }
- }
- return -1;
- }
-
- public static class Flash {
- private static boolean checked;
- private static boolean installed;
-
- /**
- * Does the browser have ShockwaveFlash plugin installed?
- *
- * <p>This method may still return true if the user has disabled Flash or set the plugin to
- * "click to run".
- */
- public static boolean isInstalled() {
- if (!checked) {
- installed = hasFlash();
- checked = true;
- }
- return installed;
- }
-
- private static native boolean hasFlash() /*-{
- if (navigator.plugins && navigator.plugins.length) {
- if (navigator.plugins['Shockwave Flash']) return true;
- if (navigator.plugins['Shockwave Flash 2.0']) return true;
-
- } else if (navigator.mimeTypes && navigator.mimeTypes.length) {
- var mimeType = navigator.mimeTypes['application/x-shockwave-flash'];
- if (mimeType && mimeType.enabledPlugin) return true;
-
- } else {
- try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7'); return true; } catch (e) {}
- try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6'); return true; } catch (e) {}
- try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); return true; } catch (e) {}
- }
- return false;
- }-*/;
- }
-
- /**
- * Test for and disallow running this application in an &lt;iframe&gt;.
- *
- * <p>If the application is running within an iframe this method requests a browser generated
- * redirect to pop the application out of the iframe into the top level window, and then aborts
- * execution by throwing an exception. This is call should be placed early within the module's
- * onLoad() method, before any real UI can be initialized that an attacking site could try to snip
- * out and present in a confusing context.
- *
- * <p>If the break out works, execution will restart automatically in a proper top level window,
- * where the script has full control over the display. If the break out fails, execution will
- * abort and stop immediately, preventing UI widgets from being created, leaving the user with an
- * empty frame.
- */
- public static void assertNotInIFrame() {
- if (GWT.isScript() && amInsideIFrame()) {
- bustOutOfIFrame(Window.Location.getHref());
- throw new RuntimeException();
- }
- }
-
- private static native boolean amInsideIFrame() /*-{ return top.location != $wnd.location; }-*/;
-
- private static native void bustOutOfIFrame(String newloc) /*-{ top.location.href = newloc }-*/;
-
- /**
- * Test if Gerrit is running on a mobile browser. This check could be incomplete, but should cover
- * most cases. Regexes shamelessly borrowed from CodeMirror.
- */
- public static native boolean isMobile() /*-{
- var ua = $wnd.navigator.userAgent;
- var ios = /AppleWebKit/.test(ua) && /Mobile\/\w+/.test(ua);
- return ios
- || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(ua);
- }-*/;
-
- /** Check if the height of the browser view is greater than its width. */
- public static boolean isPortrait() {
- return Window.getClientHeight() > Window.getClientWidth();
- }
-
- private UserAgent() {}
-}
diff --git a/java/com/google/gwtexpui/user/client/View.java b/java/com/google/gwtexpui/user/client/View.java
deleted file mode 100644
index b15d2fd997..0000000000
--- a/java/com/google/gwtexpui/user/client/View.java
+++ /dev/null
@@ -1,55 +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.gwtexpui.user.client;
-
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Widget;
-
-/**
- * Widget to display within a {@link ViewSite}.
- *
- * <p>Implementations must override {@code protected void onLoad()} and arrange for {@link
- * #display()} to be invoked once the DOM within the view is consistent for presentation to the
- * user. Typically this means that the subclass can start RPCs within {@code onLoad()} and then
- * invoke {@code display()} from within the AsyncCallback's {@code onSuccess(Object)} method.
- */
-public abstract class View extends Composite {
- ViewSite<? extends View> site;
-
- @Override
- protected void onUnload() {
- site = null;
- super.onUnload();
- }
-
- /** true if this is the current view of its parent view site */
- public final boolean isCurrentView() {
- Widget p = getParent();
- while (p != null) {
- if (p instanceof ViewSite<?>) {
- return ((ViewSite<?>) p).getView() == this;
- }
- p = p.getParent();
- }
- return false;
- }
-
- /** Replace the current view in the parent ViewSite with this view. */
- public final void display() {
- if (site != null) {
- site.swap(this);
- }
- }
-}
diff --git a/java/com/google/gwtexpui/user/client/ViewSite.java b/java/com/google/gwtexpui/user/client/ViewSite.java
deleted file mode 100644
index 4614546f92..0000000000
--- a/java/com/google/gwtexpui/user/client/ViewSite.java
+++ /dev/null
@@ -1,84 +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.gwtexpui.user.client;
-
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-
-/**
- * Hosts a single {@link View}.
- *
- * <p>View instances are attached inside of an invisible DOM node, permitting their {@code onLoad()}
- * method to be invoked and to update the DOM prior to the elements being made visible in the UI.
- *
- * <p>Complaint View instances must invoke {@link View#display()} once the DOM is ready for
- * presentation.
- */
-public class ViewSite<V extends View> extends Composite {
- private final FlowPanel main;
- private SimplePanel current;
- private SimplePanel next;
-
- public ViewSite() {
- main = new FlowPanel();
- initWidget(main);
- }
-
- /** Get the current view; null if there is no view being displayed. */
- @SuppressWarnings("unchecked")
- public V getView() {
- return current != null ? (V) current.getWidget() : null;
- }
-
- /**
- * Set the next view to display.
- *
- * <p>The view will be attached to the DOM tree within a hidden container, permitting its {@code
- * onLoad()} method to execute and update the DOM without the user seeing the result.
- *
- * @param view the next view to display.
- */
- public void setView(V view) {
- if (next != null) {
- main.remove(next);
- }
- view.site = this;
- next = new SimplePanel();
- next.setVisible(false);
- main.add(next);
- next.add(view);
- }
-
- /**
- * Invoked after the view becomes the current view and has been made visible.
- *
- * @param view the view being displayed.
- */
- protected void onShowView(V view) {}
-
- @SuppressWarnings("unchecked")
- final void swap(View v) {
- if (next != null && next.getWidget() == v) {
- if (current != null) {
- main.remove(current);
- }
- current = next;
- next = null;
- current.setVisible(true);
- onShowView((V) v);
- }
- }
-}
diff --git a/java/com/google/gwtexpui/user/client/tooltip.css b/java/com/google/gwtexpui/user/client/tooltip.css
deleted file mode 100644
index 1aeb0155a8..0000000000
--- a/java/com/google/gwtexpui/user/client/tooltip.css
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright (C) 2015 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.
- */
-
-.tooltip {
- position: relative;
-}
-
-.tooltip:hover:before {
- position: absolute;
- z-index: 51;
- border: solid;
- border-color: #333 transparent;
- border-width: 0 4px 4px 4px;
- pointer-events: none;
- content: "";
-
- top: auto;
- right: 50%;
- bottom: -5px;
- margin-right: -5px;
-}
-
-.tooltip:hover:after {
- position: absolute;
- z-index: 50;
- font: normal normal 11px/1.5 Helvetica, arial, sans-serif;
- text-align: center;
- white-space: pre;
- pointer-events: none;
- background: rgba(0,0,0,.7);
- color: #fff;
- border-radius: 3px;
- padding: 5px;
- content: attr(aria-label);
-
- top: 100%;
- right: 50%;
- margin-top: 5px;
- -webkit-transform: translateX(50%);
- -ms-transform: translateX(50%);
- transform: translateX(50%)
-}
diff --git a/java/com/google/gwtorm/BUILD b/java/com/google/gwtorm/BUILD
new file mode 100644
index 0000000000..baf7a8cb76
--- /dev/null
+++ b/java/com/google/gwtorm/BUILD
@@ -0,0 +1,7 @@
+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
new file mode 100644
index 0000000000..1c66d186c3
--- /dev/null
+++ b/java/com/google/gwtorm/client/CompoundKey.java
@@ -0,0 +1,108 @@
+// 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
new file mode 100644
index 0000000000..08c90e08f8
--- /dev/null
+++ b/java/com/google/gwtorm/client/IntKey.java
@@ -0,0 +1,80 @@
+// 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
new file mode 100644
index 0000000000..69a224864e
--- /dev/null
+++ b/java/com/google/gwtorm/client/Key.java
@@ -0,0 +1,45 @@
+// 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
new file mode 100644
index 0000000000..e236d370cb
--- /dev/null
+++ b/java/com/google/gwtorm/client/KeyUtil.java
@@ -0,0 +1,91 @@
+// 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/StandardKeyEncoder.java b/java/com/google/gwtorm/client/StandardKeyEncoder.java
new file mode 100644
index 0000000000..1ceb0dcecd
--- /dev/null
+++ b/java/com/google/gwtorm/client/StandardKeyEncoder.java
@@ -0,0 +1,111 @@
+// 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 com.google.gwtorm.client.KeyUtil.Encoder;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+public class StandardKeyEncoder extends Encoder {
+ private static final char[] hexc = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+ private static final char safe[];
+ private static final byte hexb[];
+
+ static {
+ safe = new char[256];
+ safe['-'] = '-';
+ safe['_'] = '_';
+ safe['.'] = '.';
+ safe['!'] = '!';
+ safe['~'] = '~';
+ safe['*'] = '*';
+ safe['\''] = '\'';
+ safe['('] = '(';
+ safe[')'] = ')';
+ safe['/'] = '/';
+ safe[' '] = '+';
+ for (char c = '0'; c <= '9'; c++) safe[c] = c;
+ for (char c = 'A'; c <= 'Z'; c++) safe[c] = c;
+ for (char c = 'a'; c <= 'z'; c++) safe[c] = c;
+
+ hexb = new byte['f' + 1];
+ Arrays.fill(hexb, (byte) -1);
+ for (char i = '0'; i <= '9'; i++) hexb[i] = (byte) (i - '0');
+ for (char i = 'A'; i <= 'F'; i++) hexb[i] = (byte) ((i - 'A') + 10);
+ for (char i = 'a'; i <= 'f'; i++) hexb[i] = (byte) ((i - 'a') + 10);
+ }
+
+ @Override
+ public String encode(final String e) {
+ final byte[] b;
+ try {
+ b = e.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e1) {
+ throw new RuntimeException("No UTF-8 support", e1);
+ }
+
+ final StringBuilder r = new StringBuilder(b.length);
+ for (int i = 0; i < b.length; i++) {
+ final int c = b[i] & 0xff;
+ final char s = safe[c];
+ if (s == 0) {
+ r.append('%');
+ r.append(hexc[c >> 4]);
+ r.append(hexc[c & 15]);
+ } else {
+ r.append(s);
+ }
+ }
+ return r.toString();
+ }
+
+ @Override
+ public String decode(final String e) {
+ if (e.indexOf('%') < 0) {
+ return e.replace('+', ' ');
+ }
+
+ final byte[] b = new byte[e.length()];
+ int bPtr = 0;
+ try {
+ for (int i = 0; i < e.length(); ) {
+ final char c = e.charAt(i);
+ if (c == '%' && i + 2 < e.length()) {
+ final int v = (hexb[e.charAt(i + 1)] << 4) | hexb[e.charAt(i + 2)];
+ if (v < 0) {
+ throw new IllegalArgumentException(e.substring(i, i + 3));
+ }
+ b[bPtr++] = (byte) v;
+ i += 3;
+ } else if (c == '+') {
+ b[bPtr++] = ' ';
+ i++;
+ } else {
+ b[bPtr++] = (byte) c;
+ i++;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException err) {
+ throw new IllegalArgumentException("Bad encoding" + e, err);
+ }
+ try {
+ return new String(b, 0, bPtr, "UTF-8");
+ } catch (UnsupportedEncodingException e1) {
+ throw new RuntimeException("No UTF-8 support", e1);
+ }
+ }
+}
diff --git a/java/com/google/gwtorm/client/StringKey.java b/java/com/google/gwtorm/client/StringKey.java
new file mode 100644
index 0000000000..e56661ff1c
--- /dev/null
+++ b/java/com/google/gwtorm/client/StringKey.java
@@ -0,0 +1,86 @@
+// 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/BUILD b/java/gerrit/BUILD
index 84f65dbbb2..d7e23069ba 100644
--- a/java/gerrit/BUILD
+++ b/java/gerrit/BUILD
@@ -6,10 +6,10 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/exceptions",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
- "//lib:gwtorm",
"//lib/flogger:api",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/prolog:runtime",
diff --git a/java/gerrit/PRED__load_commit_labels_1.java b/java/gerrit/PRED__load_commit_labels_1.java
index 1d0ba8a48a..693c89e4d6 100644
--- a/java/gerrit/PRED__load_commit_labels_1.java
+++ b/java/gerrit/PRED__load_commit_labels_1.java
@@ -7,8 +7,6 @@ import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -36,27 +34,22 @@ class PRED__load_commit_labels_1 extends Predicate.P1 {
Term a1 = arg1.dereference();
Term listHead = Prolog.Nil;
- try {
- ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
- LabelTypes types = cd.getLabelTypes();
+ ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
+ LabelTypes types = cd.getLabelTypes();
- for (PatchSetApproval a : cd.currentApprovals()) {
- LabelType t = types.byLabel(a.getLabelId());
- if (t == null) {
- continue;
- }
+ for (PatchSetApproval a : cd.currentApprovals()) {
+ LabelType t = types.byLabel(a.getLabelId());
+ if (t == null) {
+ continue;
+ }
- StructureTerm labelTerm =
- new StructureTerm(
- sym_label, SymbolTerm.intern(t.getName()), new IntegerTerm(a.getValue()));
+ StructureTerm labelTerm =
+ new StructureTerm(
+ sym_label, SymbolTerm.intern(t.getName()), new IntegerTerm(a.getValue()));
- StructureTerm userTerm =
- new StructureTerm(sym_user, new IntegerTerm(a.getAccountId().get()));
+ StructureTerm userTerm = new StructureTerm(sym_user, new IntegerTerm(a.getAccountId().get()));
- listHead = new ListTerm(new StructureTerm(sym_commit_label, labelTerm, userTerm), listHead);
- }
- } catch (OrmException err) {
- throw new JavaException(this, 1, err);
+ listHead = new ListTerm(new StructureTerm(sym_commit_label, labelTerm, userTerm), listHead);
}
if (!a1.unify(listHead, engine.trail)) {
diff --git a/java/gerrit/PRED_commit_edits_2.java b/java/gerrit/PRED_commit_edits_2.java
index f46a6487f0..6ca533874d 100644
--- a/java/gerrit/PRED_commit_edits_2.java
+++ b/java/gerrit/PRED_commit_edits_2.java
@@ -97,11 +97,7 @@ public class PRED_commit_edits_2 extends Predicate.P2 {
if (fileRegex.matcher(newName).find()
|| (oldName != null && fileRegex.matcher(oldName).find())) {
- // This cast still seems to be needed on JDK 8 as workaround for:
- // https://bugs.openjdk.java.net/browse/JDK-8039214
- @SuppressWarnings("cast")
- List<Edit> edits = (List<Edit>) entry.getEdits();
-
+ List<Edit> edits = entry.getEdits();
if (edits.isEmpty()) {
continue;
}
diff --git a/java/gerrit/PRED_get_legacy_label_types_1.java b/java/gerrit/PRED_get_legacy_label_types_1.java
index ef79e0584f..2f0c1eae56 100644
--- a/java/gerrit/PRED_get_legacy_label_types_1.java
+++ b/java/gerrit/PRED_get_legacy_label_types_1.java
@@ -17,8 +17,6 @@ package gerrit;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -53,12 +51,7 @@ class PRED_get_legacy_label_types_1 extends Predicate.P1 {
public Operation exec(Prolog engine) throws PrologException {
engine.setB0();
Term a1 = arg1.dereference();
- List<LabelType> list;
- try {
- list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
- } catch (OrmException err) {
- throw new JavaException(this, 1, err);
- }
+ List<LabelType> list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
Term head = Prolog.Nil;
for (int idx = list.size() - 1; 0 <= idx; idx--) {
head = new ListTerm(export(list.get(idx)), head);
diff --git a/java/gerrit/PRED_pure_revert_1.java b/java/gerrit/PRED_pure_revert_1.java
index 95a0729111..6300a668b7 100644
--- a/java/gerrit/PRED_pure_revert_1.java
+++ b/java/gerrit/PRED_pure_revert_1.java
@@ -15,8 +15,6 @@
package gerrit;
import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.Operation;
@@ -36,12 +34,7 @@ public class PRED_pure_revert_1 extends Predicate.P1 {
engine.setB0();
Term a1 = arg1.dereference();
- Boolean isPureRevert;
- try {
- isPureRevert = StoredValues.CHANGE_DATA.get(engine).isPureRevert();
- } catch (OrmException e) {
- throw new JavaException(this, 1, e);
- }
+ Boolean isPureRevert = StoredValues.CHANGE_DATA.get(engine).isPureRevert();
if (!a1.unify(new IntegerTerm(Boolean.TRUE.equals(isPureRevert) ? 1 : 0), engine.trail)) {
return engine.fail();
}
diff --git a/java/gerrit/PRED_unresolved_comments_count_1.java b/java/gerrit/PRED_unresolved_comments_count_1.java
index 5ed1525cfd..d4abcc54c2 100644
--- a/java/gerrit/PRED_unresolved_comments_count_1.java
+++ b/java/gerrit/PRED_unresolved_comments_count_1.java
@@ -15,8 +15,6 @@
package gerrit;
import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.Operation;
@@ -35,13 +33,9 @@ public class PRED_unresolved_comments_count_1 extends Predicate.P1 {
engine.setB0();
Term a1 = arg1.dereference();
- try {
- Integer count = StoredValues.CHANGE_DATA.get(engine).unresolvedCommentCount();
- if (!a1.unify(new IntegerTerm(count != null ? count : 0), engine.trail)) {
- return engine.fail();
- }
- } catch (OrmException err) {
- throw new JavaException(this, 1, err);
+ Integer count = StoredValues.CHANGE_DATA.get(engine).unresolvedCommentCount();
+ if (!a1.unify(new IntegerTerm(count != null ? count : 0), engine.trail)) {
+ return engine.fail();
}
return cont;
}
diff --git a/java/org/eclipse/jgit/BUILD b/java/org/eclipse/jgit/BUILD
deleted file mode 100644
index f90debf1b6..0000000000
--- a/java/org/eclipse/jgit/BUILD
+++ /dev/null
@@ -1,49 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//tools/bzl:genrule2.bzl", "genrule2")
-load("//tools/bzl:gwt.bzl", "gwt_module")
-
-gwt_module(
- name = "client",
- srcs = [
- "diff/Edit_JsonSerializer.java",
- "diff/ReplaceEdit.java",
- ],
- gwt_xml = "JGit.gwt.xml",
- visibility = ["//visibility:public"],
- deps = [
- ":Edit",
- "//lib:gwtjsonrpc",
- "//lib/gwt:user",
- ],
-)
-
-gwt_module(
- name = "Edit",
- srcs = [":jgit_edit_src"],
- visibility = ["//visibility:public"],
-)
-
-genrule2(
- name = "jgit_edit_src",
- outs = ["edit.srcjar"],
- cmd = " && ".join([
- "unzip -qd $$TMP $(location //lib/jgit/org.eclipse.jgit:jgit-source) " +
- "org/eclipse/jgit/diff/Edit.java",
- "cd $$TMP",
- "zip -Dq $$ROOT/$@ org/eclipse/jgit/diff/Edit.java",
- ]),
- tools = ["//lib/jgit/org.eclipse.jgit:jgit-source"],
-)
-
-java_library(
- name = "server",
- srcs = [
- "diff/EditDeserializer.java",
- "diff/ReplaceEdit.java",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//lib:gson",
- "//lib/jgit/org.eclipse.jgit:jgit",
- ],
-)
diff --git a/java/org/eclipse/jgit/JGit.gwt.xml b/java/org/eclipse/jgit/JGit.gwt.xml
deleted file mode 100644
index 5aeb936526..0000000000
--- a/java/org/eclipse/jgit/JGit.gwt.xml
+++ /dev/null
@@ -1,22 +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.
--->
-<module>
- <source path='diff' includes='
- Edit.java
- Edit_JsonSerializer.java
- ReplaceEdit.java
- '/>
-</module>
diff --git a/java/org/eclipse/jgit/diff/EditDeserializer.java b/java/org/eclipse/jgit/diff/EditDeserializer.java
deleted file mode 100644
index 9435979bf5..0000000000
--- a/java/org/eclipse/jgit/diff/EditDeserializer.java
+++ /dev/null
@@ -1,95 +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 org.eclipse.jgit.diff;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonNull;
-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;
-import java.util.ArrayList;
-import java.util.List;
-
-public class EditDeserializer implements JsonDeserializer<Edit>, JsonSerializer<Edit> {
- @Override
- public Edit deserialize(final JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- if (json.isJsonNull()) {
- return null;
- }
- if (!json.isJsonArray()) {
- throw new JsonParseException("Expected array for Edit type");
- }
-
- final JsonArray o = (JsonArray) json;
- final int cnt = o.size();
- if (cnt < 4 || cnt % 4 != 0) {
- throw new JsonParseException("Expected array of 4 for Edit type");
- }
-
- if (4 == cnt) {
- return new Edit(get(o, 0), get(o, 1), get(o, 2), get(o, 3));
- }
-
- List<Edit> l = new ArrayList<>((cnt / 4) - 1);
- for (int i = 4; i < cnt; ) {
- int as = get(o, i++);
- int ae = get(o, i++);
- int bs = get(o, i++);
- int be = get(o, i++);
- l.add(new Edit(as, ae, bs, be));
- }
- return new ReplaceEdit(get(o, 0), get(o, 1), get(o, 2), get(o, 3), l);
- }
-
- private static int get(JsonArray a, int idx) throws JsonParseException {
- final JsonElement v = a.get(idx);
- if (!v.isJsonPrimitive()) {
- throw new JsonParseException("Expected array of 4 for Edit type");
- }
- final JsonPrimitive p = (JsonPrimitive) v;
- if (!p.isNumber()) {
- throw new JsonParseException("Expected array of 4 for Edit type");
- }
- return p.getAsInt();
- }
-
- @Override
- public JsonElement serialize(final Edit src, Type typeOfSrc, JsonSerializationContext context) {
- if (src == null) {
- return JsonNull.INSTANCE;
- }
- final JsonArray a = new JsonArray();
- add(a, src);
- if (src instanceof ReplaceEdit) {
- for (Edit e : ((ReplaceEdit) src).getInternalEdits()) {
- add(a, e);
- }
- }
- return a;
- }
-
- private void add(JsonArray a, Edit src) {
- a.add(new JsonPrimitive(src.getBeginA()));
- a.add(new JsonPrimitive(src.getEndA()));
- a.add(new JsonPrimitive(src.getBeginB()));
- a.add(new JsonPrimitive(src.getEndB()));
- }
-}
diff --git a/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java b/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java
deleted file mode 100644
index 184cb36a95..0000000000
--- a/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java
+++ /dev/null
@@ -1,74 +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 org.eclipse.jgit.diff;
-
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwtjsonrpc.client.impl.JsonSerializer;
-import java.util.ArrayList;
-import java.util.List;
-
-public class Edit_JsonSerializer extends JsonSerializer<Edit> {
- public static final Edit_JsonSerializer INSTANCE = new Edit_JsonSerializer();
-
- @Override
- public Edit fromJson(Object jso) {
- if (jso == null) {
- return null;
- }
-
- final JavaScriptObject o = (JavaScriptObject) jso;
- final int cnt = length(o);
- if (4 == cnt) {
- return new Edit(get(o, 0), get(o, 1), get(o, 2), get(o, 3));
- }
-
- List<Edit> l = new ArrayList<>((cnt / 4) - 1);
- for (int i = 4; i < cnt; ) {
- int as = get(o, i++);
- int ae = get(o, i++);
- int bs = get(o, i++);
- int be = get(o, i++);
- l.add(new Edit(as, ae, bs, be));
- }
- return new ReplaceEdit(get(o, 0), get(o, 1), get(o, 2), get(o, 3), l);
- }
-
- @Override
- public void printJson(StringBuilder sb, Edit o) {
- sb.append('[');
- append(sb, o);
- if (o instanceof ReplaceEdit) {
- for (Edit e : ((ReplaceEdit) o).getInternalEdits()) {
- sb.append(',');
- append(sb, e);
- }
- }
- sb.append(']');
- }
-
- private void append(StringBuilder sb, Edit o) {
- sb.append(o.getBeginA());
- sb.append(',');
- sb.append(o.getEndA());
- sb.append(',');
- sb.append(o.getBeginB());
- sb.append(',');
- sb.append(o.getEndB());
- }
-
- private static native int length(JavaScriptObject jso) /*-{ return jso.length; }-*/;
-
- private static native int get(JavaScriptObject jso, int idx) /*-{ return jso[idx]; }-*/;
-}
diff --git a/java/org/eclipse/jgit/diff/ReplaceEdit.java b/java/org/eclipse/jgit/diff/ReplaceEdit.java
deleted file mode 100644
index 46681c6473..0000000000
--- a/java/org/eclipse/jgit/diff/ReplaceEdit.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2010 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 org.eclipse.jgit.diff;
-
-import java.util.List;
-
-public class ReplaceEdit extends Edit {
- private List<Edit> internalEdit;
-
- public ReplaceEdit(int as, int ae, int bs, int be, List<Edit> internal) {
- super(as, ae, bs, be);
- internalEdit = internal;
- }
-
- public ReplaceEdit(Edit orig, List<Edit> internal) {
- super(orig.getBeginA(), orig.getEndA(), orig.getBeginB(), orig.getEndB());
- internalEdit = internal;
- }
-
- public List<Edit> getInternalEdits() {
- return internalEdit;
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/BUILD b/javatests/com/google/gerrit/acceptance/BUILD
index 32804ef16f..54b3626b50 100644
--- a/javatests/com/google/gerrit/acceptance/BUILD
+++ b/javatests/com/google/gerrit/acceptance/BUILD
@@ -6,6 +6,7 @@ junit_tests(
deps = [
"//java/com/google/gerrit/acceptance:lib",
"//java/com/google/gerrit/server/util/time",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
diff --git a/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java b/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
index 49c23e3652..69fdc7e6ba 100644
--- a/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
+++ b/javatests/com/google/gerrit/acceptance/MergeableFileBasedConfigTest.java
@@ -18,13 +18,14 @@ import static com.google.common.truth.Truth.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 {
+public class MergeableFileBasedConfigTest extends GerritBaseTests {
@Test
public void mergeNull() throws Exception {
MergeableFileBasedConfig cfg = newConfig();
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 23fc1c0e75..7dcff20820 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -48,6 +48,7 @@ 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;
@@ -58,12 +59,16 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
+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.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.Action;
+import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.api.accounts.AccountApi;
import com.google.gerrit.extensions.api.accounts.AccountInput;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
@@ -95,17 +100,18 @@ 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.git.LockFailureException;
import com.google.gerrit.gpg.Fingerprint;
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.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountProperties;
import com.google.gerrit.server.account.AccountState;
@@ -118,11 +124,10 @@ 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.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.LockFailureException;
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.rebuild.ChangeRebuilderImpl;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.project.RefPattern;
import com.google.gerrit.server.query.account.InternalAccountQuery;
@@ -134,7 +139,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
@@ -202,36 +206,25 @@ public class AccountIT extends AbstractDaemonTest {
return cfg;
}
- @Inject private Provider<PublicKeyStore> publicKeyStoreProvider;
-
@Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
-
- @Inject private ExternalIds externalIds;
-
+ @Inject private AccountIndexer accountIndexer;
@Inject private DynamicSet<AccountIndexedListener> accountIndexedListeners;
-
@Inject private DynamicSet<GitReferenceUpdatedListener> refUpdateListeners;
-
- @Inject private Sequences seq;
-
- @Inject private Provider<InternalAccountQuery> accountQueryProvider;
-
- @Inject protected Emails emails;
-
- @Inject private StalenessChecker stalenessChecker;
-
- @Inject private AccountIndexer accountIndexer;
-
+ @Inject private ExternalIdNotes.Factory extIdNotesFactory;
+ @Inject private ExternalIds externalIds;
@Inject private GitReferenceUpdated gitReferenceUpdated;
-
- @Inject private RetryHelper.Metrics retryMetrics;
-
+ @Inject private ProjectOperations projectOperations;
+ @Inject private Provider<InternalAccountQuery> accountQueryProvider;
@Inject private Provider<MetaDataUpdate.InternalFactory> metaDataUpdateInternalFactory;
-
- @Inject private ExternalIdNotes.Factory extIdNotesFactory;
-
+ @Inject private Provider<PublicKeyStore> publicKeyStoreProvider;
+ @Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private RetryHelper.Metrics retryMetrics;
+ @Inject private Sequences seq;
+ @Inject private StalenessChecker stalenessChecker;
@Inject private VersionedAuthorizedKeys.Accessor authorizedKeys;
+ @Inject protected Emails emails;
+
@Inject
@Named("accounts")
private LoadingCache<Account.Id, Optional<AccountState>> accountsCache;
@@ -241,6 +234,8 @@ public class AccountIT extends AbstractDaemonTest {
@Inject
private DynamicSet<AccountActivationValidationListener> accountActivationValidationListeners;
+ @Inject protected GroupOperations groupOperations;
+
private AccountIndexedCounter accountIndexedCounter;
private RegistrationHandle accountIndexEventCounterHandle;
private RefUpdateCounter refUpdateCounter;
@@ -357,19 +352,19 @@ 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();
+ 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.getId(), name, null);
- return foo.getId();
+ assertUserBranch(foo.id(), name, null);
+ return foo.id();
}
@Test
public void createAnonymousCowardByAccountCreator() throws Exception {
TestAccount anonymousCoward = accountCreator.create();
accountIndexedCounter.assertReindexOf(anonymousCoward);
- assertUserBranchWithoutAccountConfig(anonymousCoward.getId());
+ assertUserBranchWithoutAccountConfig(anonymousCoward.id());
}
@Test
@@ -396,10 +391,10 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void createAccountUsernameAlreadyTaken() throws Exception {
AccountInput input = new AccountInput();
- input.username = admin.username;
+ input.username = admin.username();
exception.expect(ResourceConflictException.class);
- exception.expectMessage("username '" + admin.username + "' already exists");
+ exception.expectMessage("username '" + admin.username() + "' already exists");
gApi.accounts().create(input);
}
@@ -407,10 +402,10 @@ public class AccountIT extends AbstractDaemonTest {
public void createAccountEmailAlreadyTaken() throws Exception {
AccountInput input = new AccountInput();
input.username = "foo";
- input.email = admin.email;
+ input.email = admin.email();
exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("email '" + admin.email + "' already exists");
+ exception.expectMessage("email '" + admin.email() + "' already exists");
gApi.accounts().create(input);
}
@@ -482,18 +477,18 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void updateAccountWithoutAccountConfigNoteDb() throws Exception {
TestAccount anonymousCoward = accountCreator.create();
- assertUserBranchWithoutAccountConfig(anonymousCoward.getId());
+ assertUserBranchWithoutAccountConfig(anonymousCoward.id());
String status = "OOO";
Optional<AccountState> accountState =
accountsUpdateProvider
.get()
- .update("Set status", anonymousCoward.getId(), u -> u.setStatus(status));
+ .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);
- assertUserBranch(anonymousCoward.getId(), null, status);
+ assertUserBranch(anonymousCoward.id(), null, status);
}
private void assertUserBranchWithoutAccountConfig(Account.Id accountId) throws Exception {
@@ -510,7 +505,7 @@ public class AccountIT extends AbstractDaemonTest {
RevCommit c = rw.parseCommit(ref.getObjectId());
long timestampDiffMs =
Math.abs(c.getCommitTime() * 1000L - getAccount(accountId).getRegisteredOn().getTime());
- assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
+ assertThat(timestampDiffMs).isAtMost(SECONDS.toMillis(1));
// Check the 'account.config' file.
try (TreeWalk tw = TreeWalk.forPath(or, AccountProperties.ACCOUNT_CONFIG, c.getTree())) {
@@ -560,17 +555,49 @@ public class AccountIT extends AbstractDaemonTest {
@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);
- assertThat(gApi.accounts().id("user").getActive()).isFalse();
accountIndexedCounter.assertReindexOf(user);
- gApi.accounts().id("user").setActive(true);
+ // Inactive users may only be resolved by ID.
+ try {
+ gApi.accounts().id("user");
+ assert_().fail("expected ResourceNotFoundException");
+ } catch (ResourceNotFoundException e) {
+ assertThat(e)
+ .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();
+
+ 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);
+
+ 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);
+
+ gApi.changes().query("owner:foo").get();
+ }
+
@GerritConfig(name = "auth.type", value = "DEVELOPMENT_BECOME_ANY_ACCOUNT")
public void activeUserGetSessionCookieOnLogin() throws Exception {
Integer accountId = accountIdApi().get()._accountId;
@@ -701,16 +728,17 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void deactivateNotActive() throws Exception {
+ int id = gApi.accounts().id("user").get()._accountId;
assertThat(gApi.accounts().id("user").getActive()).isTrue();
gApi.accounts().id("user").setActive(false);
- assertThat(gApi.accounts().id("user").getActive()).isFalse();
+ assertThat(gApi.accounts().id(id).getActive()).isFalse();
try {
- gApi.accounts().id("user").setActive(false);
+ gApi.accounts().id(id).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("account not active");
}
- gApi.accounts().id("user").setActive(true);
+ gApi.accounts().id(id).setActive(true);
}
@Test
@@ -725,7 +753,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(change.stars).contains(DEFAULT_LABEL);
refUpdateCounter.assertRefUpdateFor(
RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
gApi.accounts().self().unstarChange(triplet);
change = info(triplet);
@@ -733,7 +761,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(change.stars).isNull();
refUpdateCounter.assertRefUpdateFor(
RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
accountIndexedCounter.assertNoReindex();
}
@@ -764,7 +792,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(starredChange.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
refUpdateCounter.assertRefUpdateFor(
RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
gApi.accounts()
.self()
@@ -783,14 +811,14 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(starredChange.stars).containsExactly("red", "yellow").inOrder();
refUpdateCounter.assertRefUpdateFor(
RefUpdateCounter.projectRef(
- allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+ allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
accountIndexedCounter.assertNoReindex();
- setApiUser(user);
+ 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);
+ gApi.accounts().id(Integer.toString((admin.id().get()))).getStars(triplet);
}
@Test
@@ -843,22 +871,22 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
in = new AddReviewerInput();
- in.reviewer = user2.email;
+ in.reviewer = user2.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
sender.clear();
- setApiUser(admin);
+ 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.emailAddress);
+ assertThat(messages.get(0).rcpt()).containsExactly(user2.getEmailAddress());
accountIndexedCounter.assertNoReindex();
}
@@ -866,20 +894,20 @@ public class AccountIT extends AbstractDaemonTest {
public void addReviewerToIgnoredChange() throws Exception {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
sender.clear();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ 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.emailAddress);
- assertMailReplyTo(message, admin.email);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertMailReplyTo(message, admin.email());
accountIndexedCounter.assertNoReindex();
}
@@ -891,14 +919,14 @@ public class AccountIT extends AbstractDaemonTest {
ReviewInput input = new ReviewInput();
input.reviewers = new ArrayList<>(1);
AddReviewerInput addReviewerInput = new AddReviewerInput();
- addReviewerInput.reviewer = user.email;
+ addReviewerInput.reviewer = user.email();
input.reviewers.add(addReviewerInput);
gApi.changes().id(r.getChangeId()).current().review(input);
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message message = messages.get(0);
- assertThat(message.rcpt()).containsExactly(user.emailAddress);
- assertMailReplyTo(message, admin.email);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertMailReplyTo(message, admin.email());
sender.clear();
@@ -906,20 +934,20 @@ public class AccountIT extends AbstractDaemonTest {
ReviewInput input2 = new ReviewInput();
input2.reviewers = new ArrayList<>(2);
AddReviewerInput addReviewerInput2 = new AddReviewerInput();
- addReviewerInput2.reviewer = user.email;
+ addReviewerInput2.reviewer = user.email();
input2.reviewers.add(addReviewerInput2);
AddReviewerInput addReviewerInput3 = new AddReviewerInput();
TestAccount user2 = accountCreator.user2();
- addReviewerInput3.reviewer = user2.email;
+ addReviewerInput3.reviewer = user2.email();
input2.reviewers.add(addReviewerInput3);
gApi.changes().id(r.getChangeId()).current().review(input2);
List<Message> messages2 = sender.getMessages();
assertThat(messages2).hasSize(1);
Message message2 = messages2.get(0);
- assertThat(message2.rcpt()).containsExactly(user.emailAddress, user2.emailAddress);
- assertMailReplyTo(message, admin.email);
+ assertThat(message2.rcpt()).containsExactly(user.getEmailAddress(), user2.getEmailAddress());
+ assertMailReplyTo(message, admin.email());
sender.clear();
@@ -927,11 +955,11 @@ public class AccountIT extends AbstractDaemonTest {
ReviewInput input3 = new ReviewInput();
input3.reviewers = new ArrayList<>(2);
AddReviewerInput addReviewerInput4 = new AddReviewerInput();
- addReviewerInput4.reviewer = user.email;
+ addReviewerInput4.reviewer = user.email();
input3.reviewers.add(addReviewerInput4);
AddReviewerInput addReviewerInput5 = new AddReviewerInput();
- addReviewerInput5.reviewer = user2.email;
+ addReviewerInput5.reviewer = user2.email();
input3.reviewers.add(addReviewerInput5);
gApi.changes().id(r.getChangeId()).current().review(input3);
@@ -945,32 +973,32 @@ public class AccountIT extends AbstractDaemonTest {
// First reviewer added to the change
AddReviewerInput addReviewerInput = new AddReviewerInput();
- addReviewerInput.reviewer = user.email;
+ addReviewerInput.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(addReviewerInput);
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message message = messages.get(0);
- assertThat(message.rcpt()).containsExactly(user.emailAddress);
- assertMailReplyTo(message, admin.email);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+ assertMailReplyTo(message, admin.email());
sender.clear();
// Second reviewer added to the change
TestAccount user2 = accountCreator.user2();
AddReviewerInput addReviewerInput2 = new AddReviewerInput();
- addReviewerInput2.reviewer = user2.email;
+ addReviewerInput2.reviewer = user2.email();
gApi.changes().id(r.getChangeId()).addReviewer(addReviewerInput2);
List<Message> messages2 = sender.getMessages();
assertThat(messages2).hasSize(1);
Message message2 = messages2.get(0);
- assertThat(message2.rcpt()).containsExactly(user.emailAddress, user2.emailAddress);
- assertMailReplyTo(message2, admin.email);
+ assertThat(message2.rcpt()).containsExactly(user.getEmailAddress(), user2.getEmailAddress());
+ assertMailReplyTo(message2, admin.email());
sender.clear();
// Exiting reviewer re-added to the change: no notifications
AddReviewerInput addReviewerInput3 = new AddReviewerInput();
- addReviewerInput3.reviewer = user2.email;
+ addReviewerInput3.reviewer = user2.email();
gApi.changes().id(r.getChangeId()).addReviewer(addReviewerInput3);
List<Message> messages3 = sender.getMessages();
assertThat(messages3).isEmpty();
@@ -999,20 +1027,20 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount foo = accountCreator.create(username, email, name);
String secondaryEmail = "secondary@example.com";
EmailInput input = newEmailInput(secondaryEmail);
- gApi.accounts().id(foo.id.get()).addEmail(input);
+ gApi.accounts().id(foo.id().get()).addEmail(input);
String status = "OOO";
- gApi.accounts().id(foo.id.get()).setStatus(status);
+ gApi.accounts().id(foo.id().get()).setStatus(status);
- setApiUser(foo);
- AccountDetailInfo detail = gApi.accounts().id(foo.id.get()).detail();
- assertThat(detail._accountId).isEqualTo(foo.id.get());
+ requestScopeOperations.setApiUser(foo.id());
+ AccountDetailInfo detail = gApi.accounts().id(foo.id().get()).detail();
+ assertThat(detail._accountId).isEqualTo(foo.id().get());
assertThat(detail.name).isEqualTo(name);
assertThat(detail.username).isEqualTo(username);
assertThat(detail.email).isEqualTo(email);
assertThat(detail.secondaryEmails).containsExactly(secondaryEmail);
assertThat(detail.status).isEqualTo(status);
- assertThat(detail.registeredOn).isEqualTo(getAccount(foo.getId()).getRegisteredOn());
+ assertThat(detail.registeredOn).isEqualTo(getAccount(foo.id()).getRegisteredOn());
assertThat(detail.inactive).isNull();
assertThat(detail._moreAccounts).isNull();
}
@@ -1024,10 +1052,10 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
String secondaryEmail = "secondary@example.com";
EmailInput input = newEmailInput(secondaryEmail);
- gApi.accounts().id(foo.id.get()).addEmail(input);
+ gApi.accounts().id(foo.id().get()).addEmail(input);
- setApiUser(user);
- AccountDetailInfo detail = gApi.accounts().id(foo.id.get()).detail();
+ requestScopeOperations.setApiUser(user.id());
+ AccountDetailInfo detail = gApi.accounts().id(foo.id().get()).detail();
assertThat(detail.secondaryEmails).isNull();
}
@@ -1037,9 +1065,9 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
String secondaryEmail = "secondary@example.com";
EmailInput input = newEmailInput(secondaryEmail);
- gApi.accounts().id(foo.id.get()).addEmail(input);
+ gApi.accounts().id(foo.id().get()).addEmail(input);
- AccountDetailInfo detail = gApi.accounts().id(foo.id.get()).detail();
+ AccountDetailInfo detail = gApi.accounts().id(foo.id().get()).detail();
assertThat(detail.secondaryEmails).containsExactly(secondaryEmail);
}
@@ -1048,15 +1076,15 @@ public class AccountIT extends AbstractDaemonTest {
String email = "preferred@example.com";
TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
- setApiUser(foo);
+ requestScopeOperations.setApiUser(foo.id());
assertThat(getEmails()).containsExactly(email);
- setApiUser(admin);
+ 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().hashCode()).addEmail(input);
- setApiUser(foo);
+ requestScopeOperations.setApiUser(foo.id());
assertThat(getEmails()).containsExactly(email, secondaryEmail);
}
@@ -1065,10 +1093,10 @@ public class AccountIT extends AbstractDaemonTest {
String email = "preferred2@example.com";
TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("modify account not permitted");
- gApi.accounts().id(foo.id.get()).getEmails();
+ gApi.accounts().id(foo.id().get()).getEmails();
}
@Test
@@ -1077,10 +1105,10 @@ 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().hashCode()).addEmail(input);
assertThat(
- gApi.accounts().id(foo.id.get()).getEmails().stream()
+ gApi.accounts().id(foo.id().get()).getEmails().stream()
.map(e -> e.email)
.collect(toSet()))
.containsExactly(email, secondaryEmail);
@@ -1097,8 +1125,8 @@ public class AccountIT extends AbstractDaemonTest {
accountIndexedCounter.assertReindexOf(admin);
}
- resetCurrentApiUser();
- assertThat(getEmails()).containsAllIn(emails);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).containsAtLeastElementsIn(emails);
}
@Test
@@ -1132,9 +1160,9 @@ public class AccountIT extends AbstractDaemonTest {
public void cannotAddNonConfirmedEmailWithoutModifyAccountPermission() throws Exception {
TestAccount account = accountCreator.create(name("user"));
EmailInput input = newEmailInput("test@test.com");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- gApi.accounts().id(account.username).addEmail(input);
+ gApi.accounts().id(account.username()).addEmail(input);
}
@Test
@@ -1144,7 +1172,7 @@ public class AccountIT extends AbstractDaemonTest {
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);
+ gApi.accounts().id(user.username()).addEmail(input);
}
@Test
@@ -1164,10 +1192,10 @@ public class AccountIT extends AbstractDaemonTest {
@Test
@GerritConfig(
name = "auth.registerEmailPrivateKey",
- value = "HsOc6l_2lhS9G7sE_RsnS7Z6GJjdRDX14co=")
+ value = "HsOc6l_2lhS9G7sE-RsnS7Z6GJjdRDX14co=")
public void addEmailToBeConfirmedToOwnAccount() throws Exception {
TestAccount user = accountCreator.create();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
String email = "self@example.com";
EmailInput input = newEmailInput(email, false);
@@ -1178,21 +1206,21 @@ public class AccountIT extends AbstractDaemonTest {
public void cannotAddEmailToBeConfirmedToOtherAccountWithoutModifyAccountPermission()
throws Exception {
TestAccount user = accountCreator.create();
- setApiUser(user);
+ 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));
+ gApi.accounts().id(admin.id().get()).addEmail(newEmailInput("foo@example.com", false));
}
@Test
@GerritConfig(
name = "auth.registerEmailPrivateKey",
- value = "HsOc6l_2lhS9G7sE_RsnS7Z6GJjdRDX14co=")
+ value = "HsOc6l_2lhS9G7sE-RsnS7Z6GJjdRDX14co=")
public void addEmailToBeConfirmedToOtherAccount() throws Exception {
TestAccount user = accountCreator.create();
String email = "me@example.com";
- gApi.accounts().id(user.id.get()).addEmail(newEmailInput(email, false));
+ gApi.accounts().id(user.id().get()).addEmail(newEmailInput(email, false));
}
@Test
@@ -1218,14 +1246,14 @@ public class AccountIT extends AbstractDaemonTest {
EmailInput input = newEmailInput(email);
gApi.accounts().self().addEmail(input);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).contains(email);
accountIndexedCounter.clear();
gApi.accounts().self().deleteEmail(input.email);
accountIndexedCounter.assertReindexOf(admin);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).doesNotContain(email);
}
@@ -1250,7 +1278,7 @@ public class AccountIT extends AbstractDaemonTest {
gApi.accounts().self().deleteEmail(input.email);
accountIndexedCounter.assertReindexOf(admin);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).containsExactly(previous);
assertThat(gApi.accounts().self().get().email).isNull();
}
@@ -1263,7 +1291,7 @@ public class AccountIT extends AbstractDaemonTest {
input.noConfirmation = true;
gApi.accounts().self().addEmail(input);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
Set<String> allEmails = getEmails();
assertThat(allEmails).hasSize(2);
@@ -1271,7 +1299,7 @@ public class AccountIT extends AbstractDaemonTest {
gApi.accounts().self().deleteEmail(email);
}
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).isEmpty();
assertThat(gApi.accounts().self().get().email).isNull();
}
@@ -1285,24 +1313,25 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Add External IDs",
- admin.id,
+ admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id, email))
+ ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id(), email))
.addExternalId(
- ExternalId.createWithEmail(ExternalId.Key.parse(extId2), admin.id, email)));
+ ExternalId.createWithEmail(
+ ExternalId.Key.parse(extId2), admin.id(), email)));
accountIndexedCounter.assertReindexOf(admin);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
- .containsAllOf(extId1, extId2);
+ .containsAtLeast(extId1, extId2);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).contains(email);
gApi.accounts().self().deleteEmail(email);
accountIndexedCounter.assertReindexOf(admin);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).doesNotContain(email);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
@@ -1318,17 +1347,17 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Add External IDs",
- admin.id,
+ admin.id(),
u ->
u.addExternalId(
ExternalId.createWithEmail(
- ExternalId.Key.parse(ldapExternalId), admin.id, ldapEmail)));
+ ExternalId.Key.parse(ldapExternalId), admin.id(), ldapEmail)));
accountIndexedCounter.assertReindexOf(admin);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
.contains(ldapExternalId);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).contains(ldapEmail);
ResourceConflictException exception =
@@ -1336,7 +1365,7 @@ public class AccountIT extends AbstractDaemonTest {
ResourceConflictException.class, () -> gApi.accounts().self().deleteEmail(ldapEmail));
assertThat(exception).hasMessageThat().contains(ldapEmail);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).contains(ldapEmail);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
@@ -1354,25 +1383,25 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Add External IDs",
- admin.id,
+ admin.id(),
u ->
u.addExternalId(
ExternalId.createWithEmail(
- ExternalId.Key.parse(nonLdapExternalId), admin.id, nonLdapEMail))
+ ExternalId.Key.parse(nonLdapExternalId), admin.id(), nonLdapEMail))
.addExternalId(
ExternalId.createWithEmail(
- ExternalId.Key.parse(ldapExternalId), admin.id, ldapEmail)));
+ ExternalId.Key.parse(ldapExternalId), admin.id(), ldapEmail)));
accountIndexedCounter.assertReindexOf(admin);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
- .containsAllOf(ldapExternalId, nonLdapExternalId);
+ .containsAtLeast(ldapExternalId, nonLdapExternalId);
- resetCurrentApiUser();
- assertThat(getEmails()).containsAllOf(ldapEmail, nonLdapEMail);
+ requestScopeOperations.resetCurrentApiUser();
+ assertThat(getEmails()).containsAtLeast(ldapEmail, nonLdapEMail);
gApi.accounts().self().deleteEmail(nonLdapEMail);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
assertThat(getEmails()).doesNotContain(nonLdapEMail);
assertThat(
gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
@@ -1385,30 +1414,30 @@ public class AccountIT extends AbstractDaemonTest {
EmailInput input = new EmailInput();
input.email = email;
input.noConfirmation = true;
- gApi.accounts().id(user.id.get()).addEmail(input);
+ gApi.accounts().id(user.id().get()).addEmail(input);
accountIndexedCounter.assertReindexOf(user);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(getEmails()).contains(email);
// admin can delete email of user
- setApiUser(admin);
- gApi.accounts().id(user.id.get()).deleteEmail(email);
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.accounts().id(user.id().get()).deleteEmail(email);
accountIndexedCounter.assertReindexOf(user);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(getEmails()).doesNotContain(email);
// 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);
+ gApi.accounts().id(admin.id().get()).deleteEmail(admin.email());
}
@Test
public void lookUpByEmail() throws Exception {
// exact match with scheme "mailto:"
- assertEmail(emails.getAccountFor(admin.email), admin);
+ assertEmail(emails.getAccountFor(admin.email()), admin);
// exact match with other scheme
String email = "foo.bar@example.com";
@@ -1416,26 +1445,28 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Add Email",
- admin.id,
+ admin.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email)));
+ ExternalId.createWithEmail(
+ ExternalId.Key.parse("foo:bar"), admin.id(), email)));
assertEmail(emails.getAccountFor(email), admin);
// wrong case doesn't match
- assertThat(emails.getAccountFor(admin.email.toUpperCase(Locale.US))).isEmpty();
+ assertThat(emails.getAccountFor(admin.email().toUpperCase(Locale.US))).isEmpty();
// prefix doesn't match
- assertThat(emails.getAccountFor(admin.email.substring(0, admin.email.indexOf('@')))).isEmpty();
+ assertThat(emails.getAccountFor(admin.email().substring(0, admin.email().indexOf('@'))))
+ .isEmpty();
// non-existing doesn't match
assertThat(emails.getAccountFor("non-existing@example.com")).isEmpty();
// lookup several accounts by email at once
ImmutableSetMultimap<String, Account.Id> byEmails =
- emails.getAccountsFor(admin.email, user.email);
- assertEmail(byEmails.get(admin.email), admin);
- assertEmail(byEmails.get(user.email), user);
+ emails.getAccountsFor(admin.email(), user.email());
+ assertEmail(byEmails.get(admin.email()), admin);
+ assertEmail(byEmails.get(user.email()), user);
}
@Test
@@ -1446,12 +1477,12 @@ public class AccountIT extends AbstractDaemonTest {
TestAccount foo = accountCreator.create(name("foo"));
accountsUpdateProvider
.get()
- .update("Set Preferred Email", foo.id, u -> u.setPreferredEmail(prefEmail));
+ .update("Set Preferred Email", foo.id(), u -> u.setPreferredEmail(prefEmail));
// verify that the account is still found when using the preferred email to lookup the account
ImmutableSet<Account.Id> accountsByPrefEmail = emails.getAccountFor(prefEmail);
assertThat(accountsByPrefEmail).hasSize(1);
- assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id);
+ assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id());
// look up by email prefix doesn't find the account
accountsByPrefEmail = emails.getAccountFor(prefix);
@@ -1487,31 +1518,31 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void adminCanSetNameOfOtherUser() throws Exception {
- gApi.accounts().id(user.username).setName("User McUserface");
- assertThat(gApi.accounts().id(user.username).get().name).isEqualTo("User McUserface");
+ gApi.accounts().id(user.username()).setName("User McUserface");
+ assertThat(gApi.accounts().id(user.username()).get().name).isEqualTo("User McUserface");
}
@Test
public void userCannotSetNameOfOtherUser() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- gApi.accounts().id(admin.username).setName("Admin McAdminface");
+ gApi.accounts().id(admin.username()).setName("Admin McAdminface");
}
@Test
@Sandboxed
public void userCanSetNameOfOtherUserWithModifyAccountPermission() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.MODIFY_ACCOUNT);
- gApi.accounts().id(admin.username).setName("Admin McAdminface");
- assertThat(gApi.accounts().id(admin.username).get().name).isEqualTo("Admin McAdminface");
+ 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 {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
- String userRefName = RefNames.refsUsers(user.id);
+ String userRefName = RefNames.refsUsers(user.id());
// remove default READ permissions
try (ProjectConfigUpdate u = updateProject(allUsers)) {
@@ -1554,7 +1585,7 @@ public class AccountIT extends AbstractDaemonTest {
accountIndexedCounter.assertNoReindex();
// fetching user branch of another user fails
- String otherUserRefName = RefNames.refsUsers(admin.id);
+ String otherUserRefName = RefNames.refsUsers(admin.id());
exception.expect(TransportException.class);
exception.expectMessage("Remote does not have " + otherUserRefName + " available for fetch.");
fetch(allUsersRepo, otherUserRefName + ":otherUserRef");
@@ -1563,24 +1594,24 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void pushToUserBranch() throws Exception {
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
allUsersRepo.reset("userRef");
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
- push.to(RefNames.refsUsers(admin.id)).assertOkStatus();
+ PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
+ push.to(RefNames.refsUsers(admin.id())).assertOkStatus();
accountIndexedCounter.assertReindexOf(admin);
- push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ 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);
+ String userRefName = RefNames.refsUsers(admin.id());
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, userRefName + ":userRef");
allUsersRepo.reset("userRef");
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
r.assertOkStatus();
accountIndexedCounter.assertNoReindex();
@@ -1589,7 +1620,7 @@ public class AccountIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).current().submit();
accountIndexedCounter.assertReindexOf(admin);
- push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
+ push = pushFactory.create(admin.newIdent(), allUsersRepo);
r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
r.assertOkStatus();
accountIndexedCounter.assertNoReindex();
@@ -1601,7 +1632,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void pushAccountConfigToUserBranchForReviewAndSubmit() throws Exception {
- String userRef = RefNames.refsUsers(admin.id);
+ String userRef = RefNames.refsUsers(admin.id());
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, userRef + ":userRef");
allUsersRepo.reset("userRef");
@@ -1612,8 +1643,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1628,8 +1658,8 @@ public class AccountIT extends AbstractDaemonTest {
accountIndexedCounter.assertReindexOf(admin);
AccountInfo info = gApi.accounts().self().get();
- assertThat(info.email).isEqualTo(admin.email);
- assertThat(info.name).isEqualTo(admin.fullName);
+ assertThat(info.email).isEqualTo(admin.email());
+ assertThat(info.name).isEqualTo(admin.fullName());
assertThat(info.status).isEqualTo("out-of-office");
}
@@ -1637,7 +1667,7 @@ public class AccountIT extends AbstractDaemonTest {
public void pushAccountConfigWithPrefEmailThatDoesNotExistAsExtIdToUserBranchForReviewAndSubmit()
throws Exception {
TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
- String userRef = RefNames.refsUsers(foo.id);
+ String userRef = RefNames.refsUsers(foo.id());
accountIndexedCounter.clear();
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
@@ -1651,8 +1681,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- foo.getIdent(),
+ foo.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1662,7 +1691,7 @@ public class AccountIT extends AbstractDaemonTest {
accountIndexedCounter.assertNoReindex();
assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
- setApiUser(foo);
+ requestScopeOperations.setApiUser(foo.id());
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
gApi.changes().id(r.getChangeId()).current().submit();
@@ -1670,13 +1699,13 @@ public class AccountIT extends AbstractDaemonTest {
AccountInfo info = gApi.accounts().self().get();
assertThat(info.email).isEqualTo(email);
- assertThat(info.name).isEqualTo(foo.fullName);
+ assertThat(info.name).isEqualTo(foo.fullName());
}
@Test
public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfConfigIsInvalid()
throws Exception {
- String userRef = RefNames.refsUsers(admin.id);
+ String userRef = RefNames.refsUsers(admin.id());
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, userRef + ":userRef");
allUsersRepo.reset("userRef");
@@ -1684,8 +1713,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1703,7 +1731,7 @@ public class AccountIT extends AbstractDaemonTest {
+ " Invalid config file %s in commit %s",
r.getCommit().name(),
AccountProperties.ACCOUNT_CONFIG,
- admin.id,
+ admin.id(),
AccountProperties.ACCOUNT_CONFIG,
r.getCommit().name()));
gApi.changes().id(r.getChangeId()).current().submit();
@@ -1712,7 +1740,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfPreferredEmailIsInvalid()
throws Exception {
- String userRef = RefNames.refsUsers(admin.id);
+ String userRef = RefNames.refsUsers(admin.id());
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, userRef + ":userRef");
allUsersRepo.reset("userRef");
@@ -1724,8 +1752,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1740,14 +1767,14 @@ public class AccountIT extends AbstractDaemonTest {
exception.expectMessage(
String.format(
"invalid account configuration: invalid preferred email '%s' for account '%s'",
- noEmail, admin.id));
+ noEmail, admin.id()));
gApi.changes().id(r.getChangeId()).current().submit();
}
@Test
public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfOwnAccountIsDeactivated()
throws Exception {
- String userRef = RefNames.refsUsers(admin.id);
+ String userRef = RefNames.refsUsers(admin.id());
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, userRef + ":userRef");
allUsersRepo.reset("userRef");
@@ -1758,8 +1785,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1780,8 +1806,8 @@ public class AccountIT extends AbstractDaemonTest {
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);
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
+ String userRef = RefNames.refsUsers(foo.id());
accountIndexedCounter.clear();
grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
@@ -1798,8 +1824,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1813,13 +1838,13 @@ public class AccountIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).current().submit();
accountIndexedCounter.assertReindexOf(foo);
- assertThat(gApi.accounts().id(foo.id.get()).getActive()).isFalse();
+ 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");
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
allUsersRepo.reset("userRef");
Config wc = new Config();
@@ -1830,8 +1855,7 @@ public class AccountIT extends AbstractDaemonTest {
ProjectWatches.NotifyValue.create(null, EnumSet.of(NotifyType.ALL_COMMENTS)).toString());
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Add project watch",
ProjectWatches.WATCH_CONFIG,
@@ -1844,8 +1868,7 @@ public class AccountIT extends AbstractDaemonTest {
ProjectWatches.PROJECT, project.get(), ProjectWatches.KEY_NOTIFY, invalidNotifyValue);
push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Add invalid project watch",
ProjectWatches.WATCH_CONFIG,
@@ -1855,17 +1878,17 @@ public class AccountIT extends AbstractDaemonTest {
r.assertMessage(
String.format(
"%s: Invalid project watch of account %d for project %s: %s",
- ProjectWatches.WATCH_CONFIG, admin.getId().get(), project.get(), invalidNotifyValue));
+ 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");
- setApiUser(oooUser);
+ 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");
+ fetch(allUsersRepo, RefNames.refsUsers(oooUser.id()) + ":userRef");
allUsersRepo.reset("userRef");
Config ac = getAccountConfig(allUsersRepo);
@@ -1874,34 +1897,32 @@ public class AccountIT extends AbstractDaemonTest {
accountIndexedCounter.clear();
pushFactory
.create(
- db,
- oooUser.getIdent(),
+ oooUser.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
ac.toText())
- .to(RefNames.refsUsers(oooUser.id))
+ .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.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");
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
allUsersRepo.reset("userRef");
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1914,7 +1935,7 @@ public class AccountIT extends AbstractDaemonTest {
+ " Invalid config file %s in commit %s",
r.getCommit().name(),
AccountProperties.ACCOUNT_CONFIG,
- admin.id,
+ admin.id(),
AccountProperties.ACCOUNT_CONFIG,
r.getCommit().name()));
accountIndexedCounter.assertNoReindex();
@@ -1923,7 +1944,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void pushAccountConfigToUserBranchIsRejectedIfPreferredEmailIsInvalid() throws Exception {
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
allUsersRepo.reset("userRef");
String noEmail = "no.email";
@@ -1933,8 +1954,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1942,19 +1962,19 @@ public class AccountIT extends AbstractDaemonTest {
.to(RefNames.REFS_USERS_SELF);
r.assertErrorStatus("invalid account configuration");
r.assertMessage(
- String.format("invalid preferred email '%s' for account '%s'", noEmail, admin.id));
+ 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 userRef = RefNames.refsUsers(foo.id());
String noEmail = "no.email";
accountsUpdateProvider
.get()
- .update("Set Preferred Email", foo.id, u -> u.setPreferredEmail(noEmail));
+ .update("Set Preferred Email", foo.id(), u -> u.setPreferredEmail(noEmail));
accountIndexedCounter.clear();
grant(allUsers, userRef, Permission.PUSH, false, REGISTERED_USERS);
@@ -1968,8 +1988,7 @@ public class AccountIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- foo.getIdent(),
+ foo.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -1978,16 +1997,16 @@ public class AccountIT extends AbstractDaemonTest {
.assertOkStatus();
accountIndexedCounter.assertReindexOf(foo);
- AccountInfo info = gApi.accounts().id(foo.id.get()).get();
+ AccountInfo info = gApi.accounts().id(foo.id().get()).get();
assertThat(info.email).isEqualTo(noEmail);
- assertThat(info.name).isEqualTo(foo.fullName);
+ 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);
+ String userRef = RefNames.refsUsers(foo.id());
accountIndexedCounter.clear();
grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
@@ -2002,8 +2021,7 @@ public class AccountIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- foo.getIdent(),
+ foo.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -2012,15 +2030,15 @@ public class AccountIT extends AbstractDaemonTest {
.assertOkStatus();
accountIndexedCounter.assertReindexOf(foo);
- AccountInfo info = gApi.accounts().id(foo.id.get()).get();
+ AccountInfo info = gApi.accounts().id(foo.id().get()).get();
assertThat(info.email).isEqualTo(email);
- assertThat(info.name).isEqualTo(foo.fullName);
+ assertThat(info.name).isEqualTo(foo.fullName());
}
@Test
public void pushAccountConfigToUserBranchIsRejectedIfOwnAccountIsDeactivated() throws Exception {
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+ fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
allUsersRepo.reset("userRef");
Config ac = getAccountConfig(allUsersRepo);
@@ -2029,8 +2047,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -2046,8 +2063,8 @@ public class AccountIT extends AbstractDaemonTest {
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);
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
+ String userRef = RefNames.refsUsers(foo.id());
accountIndexedCounter.clear();
grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
@@ -2061,8 +2078,7 @@ public class AccountIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
allUsersRepo,
"Update account config",
AccountProperties.ACCOUNT_CONFIG,
@@ -2071,7 +2087,7 @@ public class AccountIT extends AbstractDaemonTest {
.assertOkStatus();
accountIndexedCounter.assertReindexOf(foo);
- assertThat(gApi.accounts().id(foo.id.get()).getActive()).isFalse();
+ assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
}
@Test
@@ -2081,7 +2097,7 @@ public class AccountIT extends AbstractDaemonTest {
String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId()));
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), allUsersRepo).to(userRef);
+ PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
r.assertErrorStatus();
assertThat(r.getMessage()).contains("Not allowed to create user branch.");
@@ -2098,7 +2114,7 @@ public class AccountIT extends AbstractDaemonTest {
String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId()));
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- pushFactory.create(db, admin.getIdent(), allUsersRepo).to(userRef).assertOkStatus();
+ pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef).assertOkStatus();
try (Repository repo = repoManager.openRepository(allUsers)) {
assertThat(repo.exactRef(userRef)).isNotNull();
@@ -2114,7 +2130,7 @@ public class AccountIT extends AbstractDaemonTest {
String userRef = RefNames.REFS_USERS + "foo";
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), allUsersRepo).to(userRef);
+ 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/.");
@@ -2134,7 +2150,7 @@ public class AccountIT extends AbstractDaemonTest {
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
pushFactory
- .create(db, admin.getIdent(), allUsersRepo)
+ .create(admin.newIdent(), allUsersRepo)
.to(RefNames.REFS_USERS_DEFAULT)
.assertOkStatus();
@@ -2153,7 +2169,7 @@ public class AccountIT extends AbstractDaemonTest {
REGISTERED_USERS);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- String userRef = RefNames.refsUsers(admin.id);
+ String userRef = RefNames.refsUsers(admin.id());
PushResult r = deleteRef(allUsersRepo, userRef);
RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
@@ -2175,7 +2191,7 @@ public class AccountIT extends AbstractDaemonTest {
REGISTERED_USERS);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- String userRef = RefNames.refsUsers(admin.id);
+ String userRef = RefNames.refsUsers(admin.id());
PushResult r = deleteRef(allUsersRepo, userRef);
RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
@@ -2184,8 +2200,8 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(repo.exactRef(userRef)).isNull();
}
- assertThat(accountCache.get(admin.id)).isEmpty();
- assertThat(accountQueryProvider.get().byDefault(admin.id.toString())).isEmpty();
+ assertThat(accountCache.get(admin.id())).isEmpty();
+ assertThat(accountQueryProvider.get().byDefault(admin.id().toString())).isEmpty();
}
@Test
@@ -2200,7 +2216,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).body()).contains("new GPG keys have been added");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceNotFoundException.class);
exception.expectMessage(id);
gApi.accounts().self().gpgKey(id).get();
@@ -2212,7 +2228,7 @@ public class AccountIT extends AbstractDaemonTest {
addExternalIdEmail(user, "test1@example.com");
sender.clear();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
exception.expect(ResourceNotFoundException.class);
addGpgKey(user, key.getPublicKeyArmored());
}
@@ -2249,13 +2265,13 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Add External ID",
- user.getId(),
- u -> u.addExternalId(ExternalId.create("foo", "myId", user.getId())));
+ user.id(),
+ u -> u.addExternalId(ExternalId.create("foo", "myId", user.id())));
accountIndexedCounter.assertReindexOf(user);
TestKey key = validKeyWithSecondUserId();
addGpgKey(key.getPublicKeyArmored());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceConflictException.class);
exception.expectMessage("GPG key already associated with another account");
@@ -2270,7 +2286,7 @@ public class AccountIT extends AbstractDaemonTest {
addExternalIdEmail(admin, PushCertificateIdent.parse(key.getFirstUserId()).getEmailAddress());
toAdd.add(key.getPublicKeyArmored());
}
- gApi.accounts().self().putGpgKeys(toAdd, ImmutableList.<String>of());
+ gApi.accounts().self().putGpgKeys(toAdd, ImmutableList.of());
assertKeys(keys);
accountIndexedCounter.assertReindexOf(admin);
}
@@ -2351,13 +2367,13 @@ public class AccountIT extends AbstractDaemonTest {
assertSequenceNumbers(info);
SshKeyInfo key = info.get(0);
KeyPair keyPair = sshKeys.getKeyPair(admin);
- String initial = TestSshKeys.publicKey(keyPair, admin.email);
+ 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);
+ String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
gApi.accounts().self().addSshKey(newKey);
info = gApi.accounts().self().listSshKeys();
assertThat(info).hasSize(2);
@@ -2379,7 +2395,7 @@ public class AccountIT extends AbstractDaemonTest {
// Add another new key
sender.clear();
- String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email);
+ String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
gApi.accounts().self().addSshKey(newKey2);
info = gApi.accounts().self().listSshKeys();
assertThat(info).hasSize(3);
@@ -2402,7 +2418,7 @@ public class AccountIT extends AbstractDaemonTest {
// Mark first key as invalid
assertThat(info.get(0).valid).isTrue();
- authorizedKeys.markKeyInvalid(admin.id, 1);
+ authorizedKeys.markKeyInvalid(admin.id(), 1);
info = gApi.accounts().self().listSshKeys();
assertThat(info).hasSize(2);
assertThat(info.get(0).seq).isEqualTo(1);
@@ -2420,77 +2436,77 @@ public class AccountIT extends AbstractDaemonTest {
assertSequenceNumbers(info);
SshKeyInfo key = info.get(0);
KeyPair keyPair = sshKeys.getKeyPair(admin);
- String initial = TestSshKeys.publicKey(keyPair, admin.email);
+ 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();
+ 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.emailAddress);
+ 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();
+ 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.emailAddress);
+ assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
assertThat(message.body()).contains("SSH keys have been deleted");
}
@Test
@UseSsh
public void userCannotAddSshKeyToOtherAccount() throws Exception {
- String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email);
- setApiUser(user);
+ String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- gApi.accounts().id(admin.username).addSshKey(newKey);
+ gApi.accounts().id(admin.username()).addSshKey(newKey);
}
@Test
@UseSsh
public void userCannotDeleteSshKeyOfOtherAccount() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceNotFoundException.class);
- gApi.accounts().id(admin.username).deleteSshKey(0);
+ 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
- setApiUser(admin);
- gApi.accounts().id(user.username).index();
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.accounts().id(user.username()).index();
accountIndexedCounter.assertReindexOf(user);
// user can reindex own account
- setApiUser(user);
+ 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();
+ gApi.accounts().id(admin.username()).index();
}
@Test
public void checkConsistency() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
// Create an account with a preferred email.
String username = name("foo");
@@ -2510,13 +2526,13 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Delete External ID",
- account.getId(),
- u -> u.deleteExternalId(ExternalId.createEmail(account.getId(), email)));
+ account.id(),
+ u -> u.deleteExternalId(ExternalId.createEmail(account.id(), email)));
expectedProblems.add(
new ConsistencyProblemInfo(
ConsistencyProblemInfo.Status.ERROR,
"Account '"
- + account.getId().get()
+ + account.id().get()
+ "' has no external ID for its preferred email '"
+ email
+ "'"));
@@ -2532,11 +2548,11 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(accountQueryProvider.get().byDefault(name)).isEmpty();
TestAccount foo1 = accountCreator.create(name + "-1");
- assertThat(gApi.accounts().id(foo1.username).getActive()).isTrue();
+ assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
TestAccount foo2 = accountCreator.create(name + "-2");
- gApi.accounts().id(foo2.username).setActive(false);
- assertThat(gApi.accounts().id(foo2.username).getActive()).isFalse();
+ gApi.accounts().id(foo2.username()).setActive(false);
+ assertThat(gApi.accounts().id(foo2.id().get()).getActive()).isFalse();
assertThat(accountQueryProvider.get().byDefault(name)).hasSize(2);
}
@@ -2544,8 +2560,8 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void checkMetaId() throws Exception {
// metaId is set when account is loaded
- assertThat(accounts.get(admin.getId()).get().getAccount().getMetaId())
- .isEqualTo(getMetaId(admin.getId()));
+ assertThat(accounts.get(admin.id()).get().getAccount().getMetaId())
+ .isEqualTo(getMetaId(admin.id()));
// metaId is set when account is created
AccountsUpdate au = accountsUpdateProvider.get();
@@ -2584,7 +2600,7 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void allGroupsForAnAdminAccountCanBeRetrieved() throws Exception {
- List<GroupInfo> groups = gApi.accounts().id(admin.username).getGroups();
+ List<GroupInfo> groups = gApi.accounts().id(admin.username()).getGroups();
assertThat(groups)
.comparingElementsUsing(getGroupToNameCorrespondence())
.containsExactly("Anonymous Users", "Registered Users", "Administrators");
@@ -2626,7 +2642,9 @@ public class AccountIT extends AbstractDaemonTest {
public void allGroupsForAUserAccountCanBeRetrieved() throws Exception {
String username = name("user1");
accountOperations.newAccount().username(username).create();
- String group = createGroup("group");
+ AccountGroup.UUID groupID = groupOperations.newGroup().name("group").create();
+ String group = groupOperations.group(groupID).get().name();
+
gApi.groups().id(group).addMembers(username);
List<GroupInfo> allGroups = gApi.accounts().id(username).getGroups();
@@ -2670,17 +2688,12 @@ public class AccountIT extends AbstractDaemonTest {
new AccountsUpdate(
repoManager,
gitReferenceUpdated,
- null,
+ Optional.empty(),
allUsers,
externalIds,
metaDataUpdateInternalFactory,
new RetryHelper(
- cfg,
- retryMetrics,
- null,
- null,
- null,
- r -> r.withBlockStrategy(noSleepBlockStrategy)),
+ cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
extIdNotesFactory,
ident,
ident,
@@ -2689,20 +2702,20 @@ public class AccountIT extends AbstractDaemonTest {
try {
accountsUpdateProvider
.get()
- .update("Set Status", admin.id, u -> u.setStatus(status));
- } catch (IOException | ConfigInvalidException | OrmException e) {
+ .update("Set Status", admin.id(), u -> u.setStatus(status));
+ } catch (IOException | ConfigInvalidException | StorageException e) {
// Ignore, the successful update of the account is asserted later
}
}
},
Runnables.doNothing());
assertThat(doneBgUpdate.get()).isFalse();
- AccountInfo accountInfo = gApi.accounts().id(admin.id.get()).get();
+ AccountInfo accountInfo = gApi.accounts().id(admin.id().get()).get();
assertThat(accountInfo.status).isNull();
assertThat(accountInfo.name).isNotEqualTo(fullName);
Optional<AccountState> updatedAccountState =
- update.update("Set Full Name", admin.id, u -> u.setFullName(fullName));
+ update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName));
assertThat(doneBgUpdate.get()).isTrue();
assertThat(updatedAccountState).isPresent();
@@ -2710,7 +2723,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(updatedAccount.getStatus()).isEqualTo(status);
assertThat(updatedAccount.getFullName()).isEqualTo(fullName);
- accountInfo = gApi.accounts().id(admin.id.get()).get();
+ accountInfo = gApi.accounts().id(admin.id().get()).get();
assertThat(accountInfo.status).isEqualTo(status);
assertThat(accountInfo.name).isEqualTo(fullName);
}
@@ -2725,7 +2738,7 @@ public class AccountIT extends AbstractDaemonTest {
new AccountsUpdate(
repoManager,
gitReferenceUpdated,
- null,
+ Optional.empty(),
allUsers,
externalIds,
metaDataUpdateInternalFactory,
@@ -2733,8 +2746,6 @@ public class AccountIT extends AbstractDaemonTest {
cfg,
retryMetrics,
null,
- null,
- null,
r ->
r.withStopStrategy(StopStrategies.stopAfterAttempt(status.size()))
.withBlockStrategy(noSleepBlockStrategy)),
@@ -2747,38 +2758,38 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Set Status",
- admin.id,
+ admin.id(),
u -> u.setStatus(status.get(bgCounter.getAndAdd(1))));
- } catch (IOException | ConfigInvalidException | OrmException e) {
+ } catch (IOException | ConfigInvalidException | StorageException e) {
// Ignore, the expected exception is asserted later
}
},
Runnables.doNothing());
assertThat(bgCounter.get()).isEqualTo(0);
- AccountInfo accountInfo = gApi.accounts().id(admin.id.get()).get();
+ AccountInfo accountInfo = gApi.accounts().id(admin.id().get()).get();
assertThat(accountInfo.status).isNull();
assertThat(accountInfo.name).isNotEqualTo(fullName);
try {
- update.update("Set Full Name", admin.id, u -> u.setFullName(fullName));
+ update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName));
fail("expected LockFailureException");
} catch (LockFailureException e) {
// Ignore, expected
}
assertThat(bgCounter.get()).isEqualTo(status.size());
- Account updatedAccount = accounts.get(admin.id).get().getAccount();
+ Account updatedAccount = accounts.get(admin.id()).get().getAccount();
assertThat(updatedAccount.getStatus()).isEqualTo(Iterables.getLast(status));
- assertThat(updatedAccount.getFullName()).isEqualTo(admin.fullName);
+ assertThat(updatedAccount.getFullName()).isEqualTo(admin.fullName());
- accountInfo = gApi.accounts().id(admin.id.get()).get();
+ accountInfo = gApi.accounts().id(admin.id().get()).get();
assertThat(accountInfo.status).isEqualTo(Iterables.getLast(status));
- assertThat(accountInfo.name).isEqualTo(admin.fullName);
+ assertThat(accountInfo.name).isEqualTo(admin.fullName());
}
@Test
public void atomicReadMofifyWrite() throws Exception {
- gApi.accounts().id(admin.id.get()).setStatus("A-1");
+ gApi.accounts().id(admin.id().get()).setStatus("A-1");
AtomicInteger bgCounterA1 = new AtomicInteger(0);
AtomicInteger bgCounterA2 = new AtomicInteger(0);
@@ -2787,17 +2798,12 @@ public class AccountIT extends AbstractDaemonTest {
new AccountsUpdate(
repoManager,
gitReferenceUpdated,
- null,
+ Optional.empty(),
allUsers,
externalIds,
metaDataUpdateInternalFactory,
new RetryHelper(
- cfg,
- retryMetrics,
- null,
- null,
- null,
- r -> r.withBlockStrategy(noSleepBlockStrategy)),
+ cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
extIdNotesFactory,
ident,
ident,
@@ -2806,19 +2812,19 @@ public class AccountIT extends AbstractDaemonTest {
try {
accountsUpdateProvider
.get()
- .update("Set Status", admin.id, u -> u.setStatus("A-2"));
- } catch (IOException | ConfigInvalidException | OrmException e) {
+ .update("Set Status", admin.id(), u -> u.setStatus("A-2"));
+ } catch (IOException | ConfigInvalidException | StorageException e) {
// Ignore, the expected exception is asserted later
}
});
assertThat(bgCounterA1.get()).isEqualTo(0);
assertThat(bgCounterA2.get()).isEqualTo(0);
- assertThat(gApi.accounts().id(admin.id.get()).get().status).isEqualTo("A-1");
+ assertThat(gApi.accounts().id(admin.id().get()).get().status).isEqualTo("A-1");
Optional<AccountState> updatedAccountState =
update.update(
"Set Status",
- admin.id,
+ admin.id(),
(a, u) -> {
if ("A-1".equals(a.getAccount().getStatus())) {
bgCounterA1.getAndIncrement();
@@ -2836,8 +2842,8 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(updatedAccountState).isPresent();
assertThat(updatedAccountState.get().getAccount().getStatus()).isEqualTo("B-2");
- assertThat(accounts.get(admin.id).get().getAccount().getStatus()).isEqualTo("B-2");
- assertThat(gApi.accounts().id(admin.id.get()).get().status).isEqualTo("B-2");
+ assertThat(accounts.get(admin.id()).get().getAccount().getStatus()).isEqualTo("B-2");
+ assertThat(gApi.accounts().id(admin.id().get()).get().status).isEqualTo("B-2");
}
@Test
@@ -2858,17 +2864,12 @@ public class AccountIT extends AbstractDaemonTest {
new AccountsUpdate(
repoManager,
gitReferenceUpdated,
- null,
+ Optional.empty(),
allUsers,
externalIds,
metaDataUpdateInternalFactory,
new RetryHelper(
- cfg,
- retryMetrics,
- null,
- null,
- null,
- r -> r.withBlockStrategy(noSleepBlockStrategy)),
+ cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)),
extIdNotesFactory,
ident,
ident,
@@ -2881,7 +2882,7 @@ public class AccountIT extends AbstractDaemonTest {
"Update External ID",
accountId,
u -> u.replaceExternalId(extIdA1, extIdA2));
- } catch (IOException | ConfigInvalidException | OrmException e) {
+ } catch (IOException | ConfigInvalidException | StorageException e) {
// Ignore, the expected exception is asserted later
}
});
@@ -3008,7 +3009,7 @@ public class AccountIT extends AbstractDaemonTest {
public void deleteAllDraftComments() throws Exception {
try {
TestTimeUtil.resetWithClockStep(1, SECONDS);
- Project.NameKey project2 = createProject("project2");
+ Project.NameKey project2 = projectOperations.newProject().create();
PushOneCommit.Result r1 = createChange();
TestRepository<?> tr2 = cloneProject(project2);
@@ -3022,7 +3023,7 @@ public class AccountIT extends AbstractDaemonTest {
null);
// Create 2 drafts each on both changes for user.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
createDraft(r1, PushOneCommit.FILE_NAME, "draft 1a");
createDraft(r1, PushOneCommit.FILE_NAME, "draft 1b");
createDraft(r2, PushOneCommit.FILE_NAME, "draft 2a");
@@ -3031,12 +3032,12 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(r2.getChangeId()).current().draftsAsList()).hasSize(2);
// Create 1 draft on first change for admin.
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
createDraft(r1, PushOneCommit.FILE_NAME, "admin draft");
assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
// Delete user's draft comments; leave admin's alone.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
List<DeletedDraftCommentInfo> result =
gApi.accounts().self().deleteDraftComments(new DeleteDraftCommentsInput());
@@ -3052,7 +3053,7 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).isEmpty();
assertThat(gApi.changes().id(r2.getChangeId()).current().draftsAsList()).isEmpty();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
} finally {
cleanUpDrafts();
@@ -3089,11 +3090,11 @@ public class AccountIT extends AbstractDaemonTest {
public void deleteOtherUsersDraftCommentsDisallowed() throws Exception {
try {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
createDraft(r, PushOneCommit.FILE_NAME, "draft");
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
try {
- gApi.accounts().id(user.id.get()).deleteDraftComments(new DeleteDraftCommentsInput());
+ 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");
@@ -3110,7 +3111,7 @@ public class AccountIT extends AbstractDaemonTest {
PushOneCommit.Result r1 = createChange();
PushOneCommit.Result r2 = createChange("refs/for/secret");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
createDraft(r1, PushOneCommit.FILE_NAME, "draft a");
createDraft(r2, PushOneCommit.FILE_NAME, "draft b");
assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
@@ -3143,9 +3144,9 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void adminCanGenerateNewHttpPasswordForUser() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
- String newPassword = gApi.accounts().id(user.username).generateHttpPassword();
+ String newPassword = gApi.accounts().id(user.username()).generateHttpPassword();
assertThat(newPassword).isNotNull();
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).body()).contains("HTTP password was added or updated");
@@ -3153,28 +3154,28 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void userCannotGenerateNewHttpPasswordForOtherUser() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- gApi.accounts().id(admin.username).generateHttpPassword();
+ gApi.accounts().id(admin.username()).generateHttpPassword();
}
@Test
public void userCannotExplicitlySetHttpPassword() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.accounts().self().setHttpPassword("my-new-password");
}
@Test
public void userCannotExplicitlySetHttpPasswordForOtherUser() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- gApi.accounts().id(admin.username).setHttpPassword("my-new-password");
+ gApi.accounts().id(admin.username()).setHttpPassword("my-new-password");
}
@Test
public void userCanRemoveHttpPassword() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
sender.clear();
assertThat(gApi.accounts().self().setHttpPassword(null)).isNull();
assertThat(sender.getMessages()).hasSize(1);
@@ -3183,17 +3184,17 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void userCannotRemoveHttpPasswordForOtherUser() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- gApi.accounts().id(admin.username).setHttpPassword(null);
+ gApi.accounts().id(admin.username()).setHttpPassword(null);
}
@Test
public void adminCanExplicitlySetHttpPasswordForUser() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
String httpPassword = "new-password-for-user";
sender.clear();
- assertThat(gApi.accounts().id(user.username).setHttpPassword(httpPassword))
+ assertThat(gApi.accounts().id(user.username()).setHttpPassword(httpPassword))
.isEqualTo(httpPassword);
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).body()).contains("HTTP password was added or updated");
@@ -3201,17 +3202,17 @@ public class AccountIT extends AbstractDaemonTest {
@Test
public void adminCanRemoveHttpPasswordForUser() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
- assertThat(gApi.accounts().id(user.username).setHttpPassword(null)).isNull();
+ assertThat(gApi.accounts().id(user.username()).setHttpPassword(null)).isNull();
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).body()).contains("HTTP password was deleted");
}
@Test
public void cannotGenerateHttpPasswordWhenUsernameIsNotSet() throws Exception {
- setApiUser(admin);
- int userId = accountCreator.create().id.get();
+ 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");
@@ -3228,7 +3229,7 @@ public class AccountIT extends AbstractDaemonTest {
private void cleanUpDrafts() throws Exception {
for (TestAccount testAccount : accountCreator.getAll()) {
- setApiUser(testAccount);
+ requestScopeOperations.setApiUser(testAccount.id());
for (ChangeInfo changeInfo : gApi.changes().query("has:draft").get()) {
for (CommentInfo c :
gApi.changes().id(changeInfo.id).drafts().values().stream()
@@ -3241,18 +3242,15 @@ public class AccountIT extends AbstractDaemonTest {
}
private static Correspondence<GroupInfo, String> getGroupToNameCorrespondence() {
- return new Correspondence<GroupInfo, String>() {
- @Override
- public boolean compare(GroupInfo actualGroup, String expectedName) {
- String groupName = actualGroup == null ? null : actualGroup.name;
- return Objects.equals(groupName, expectedName);
- }
-
- @Override
- public String toString() {
- return "has name";
- }
- };
+ 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);
+ }
+ },
+ "has name");
}
private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) {
@@ -3351,12 +3349,12 @@ public class AccountIT extends AbstractDaemonTest {
.get()
.update(
"Add Email",
- account.getId(),
+ account.id(),
u ->
u.addExternalId(
- ExternalId.createWithEmail(name("test"), email, account.getId(), email)));
+ ExternalId.createWithEmail(name("test"), email, account.id(), email)));
accountIndexedCounter.assertReindexOf(account);
- setApiUser(account);
+ requestScopeOperations.setApiUser(account.id());
}
private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception {
@@ -3366,14 +3364,14 @@ 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)
+ .id(account.username())
.putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of());
- accountIndexedCounter.assertReindexOf(gApi.accounts().id(account.username).get());
+ accountIndexedCounter.assertReindexOf(gApi.accounts().id(account.username()).get());
return gpgKeys;
}
private Map<String, GpgKeyInfo> addGpgKeyNoReindex(String armored) throws Exception {
- return gApi.accounts().self().putGpgKeys(ImmutableList.of(armored), ImmutableList.<String>of());
+ return gApi.accounts().self().putGpgKeys(ImmutableList.of(armored), ImmutableList.of());
}
private void assertUser(AccountInfo info, TestAccount account) throws Exception {
@@ -3382,9 +3380,9 @@ public class AccountIT extends AbstractDaemonTest {
private void assertUser(AccountInfo info, TestAccount account, @Nullable String expectedStatus)
throws Exception {
- assertThat(info.name).isEqualTo(account.fullName);
- assertThat(info.email).isEqualTo(account.email);
- assertThat(info.username).isEqualTo(account.username);
+ assertThat(info.name).isEqualTo(account.fullName());
+ assertThat(info.email).isEqualTo(account.email());
+ assertThat(info.username).isEqualTo(account.username());
assertThat(info.status).isEqualTo(expectedStatus);
}
@@ -3394,7 +3392,7 @@ public class AccountIT extends AbstractDaemonTest {
private void assertEmail(Set<Account.Id> accounts, TestAccount expectedAccount) {
assertThat(accounts).hasSize(1);
- assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.getId());
+ assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.id());
}
private Config getAccountConfig(TestRepository<?> allUsersRepo) throws Exception {
@@ -3403,7 +3401,7 @@ public class AccountIT extends AbstractDaemonTest {
TreeWalk.forPath(
allUsersRepo.getRepository(),
AccountProperties.ACCOUNT_CONFIG,
- getHead(allUsersRepo.getRepository()).getTree())) {
+ getHead(allUsersRepo.getRepository(), "HEAD").getTree())) {
assertThat(tw).isNotNull();
ac.fromText(
new String(
@@ -3418,7 +3416,7 @@ public class AccountIT extends AbstractDaemonTest {
}
private AccountApi accountIdApi() throws RestApiException {
- return gApi.accounts().id(user.id.get());
+ return gApi.accounts().id(user.id().get());
}
private Set<String> getCookiesNames() {
@@ -3467,7 +3465,7 @@ public class AccountIT extends AbstractDaemonTest {
}
void assertReindexOf(TestAccount testAccount, int expectedCount) {
- assertThat(getCount(testAccount.id)).isEqualTo(expectedCount);
+ assertThat(getCount(testAccount.id())).isEqualTo(expectedCount);
assertThat(countsByAccount).hasSize(1);
clear();
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index 36c4da16d6..7fd6af945f 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -25,7 +25,6 @@ import com.google.gerrit.common.Nullable;
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.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
@@ -39,6 +38,7 @@ import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.db.GroupsUpdate;
+import com.google.gerrit.server.notedb.Sequences;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.inject.Inject;
import com.google.inject.util.Providers;
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index 7a4a9012b9..a4a5745e0a 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -16,13 +16,23 @@ 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 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.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.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.SubmitInput;
+import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
@@ -34,8 +44,14 @@ 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;
@@ -46,6 +62,47 @@ import org.junit.Test;
public class AgreementsIT extends AbstractDaemonTest {
private ContributorAgreement caAutoVerify;
private ContributorAgreement caNoAutoVerify;
+ @Inject private GroupOperations groupOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
+ protected void setUseContributorAgreements(InheritableBoolean value) throws Exception {
+ try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
+ ProjectConfig config = projectConfigFactory.read(md);
+ config.getProject().setBooleanConfig(BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS, value);
+ config.commit(md);
+ projectCache.evict(config.getProject());
+ }
+ }
+
+ protected ContributorAgreement configureContributorAgreement(boolean autoVerify)
+ throws Exception {
+ ContributorAgreement ca;
+ String name = autoVerify ? "cla-test-group" : "cla-test-no-auto-verify-group";
+ 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));
+ GroupReference groupRef = new GroupReference(caGroup.getGroupUUID(), caGroup.getName());
+ PermissionRule rule = new PermissionRule(groupRef);
+ rule.setAction(PermissionRule.Action.ALLOW);
+ if (autoVerify) {
+ ca = new ContributorAgreement("cla-test");
+ ca.setAutoVerify(groupRef);
+ ca.setAccepted(ImmutableList.of(rule));
+ } else {
+ ca = new ContributorAgreement("cla-test-no-auto-verify");
+ }
+ ca.setDescription("description");
+ ca.setAgreementUrl("agreement-url");
+ ca.setAccepted(ImmutableList.of(rule));
+ ca.setExcludeProjectsRegexes(ImmutableList.of("ExcludedProject"));
+
+ try (ProjectConfigUpdate u = updateProject(allProjects)) {
+ u.getConfig().replace(ca);
+ u.save();
+ return ca;
+ }
+ }
@ConfigSuite.Config
public static Config enableAgreementsConfig() {
@@ -68,7 +125,7 @@ public class AgreementsIT extends AbstractDaemonTest {
public void setUp() throws Exception {
caAutoVerify = configureContributorAgreement(true);
caNoAutoVerify = configureContributorAgreement(false);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
}
@Test
@@ -113,7 +170,7 @@ public class AgreementsIT extends AbstractDaemonTest {
gApi.accounts().self().signAgreement(caAutoVerify.getName());
// Explicitly reset the user to force a new request context
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// Verify that the agreement was signed
result = gApi.accounts().self().listAgreements();
@@ -138,7 +195,7 @@ public class AgreementsIT extends AbstractDaemonTest {
@Test
public void signAgreementAnonymous() throws Exception {
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
exception.expect(AuthException.class);
exception.expectMessage("Authentication required");
gApi.accounts().self().signAgreement(caAutoVerify.getName());
@@ -169,12 +226,12 @@ public class AgreementsIT extends AbstractDaemonTest {
ChangeInfo change = gApi.changes().create(newChangeInput()).get();
// Approve and submit it
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(change.changeId).current().review(ReviewInput.approve());
gApi.changes().id(change.changeId).current().submit(new SubmitInput());
// Revert is not allowed when CLA is required but not signed
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
setUseContributorAgreements(InheritableBoolean.TRUE);
exception.expect(AuthException.class);
exception.expectMessage("Contributor Agreement");
@@ -182,11 +239,33 @@ public class AgreementsIT extends AbstractDaemonTest {
}
@Test
+ public void revertExcludedProjectChangeWithoutCLA() throws Exception {
+ // Contributor agreements configured with excludeProjects = ExcludedProject
+ // in AbstractDaemonTest.configureContributorAgreement(...)
+ assume().that(isContributorAgreementsEnabled()).isTrue();
+
+ // Create a change succeeds when agreement is not required
+ setUseContributorAgreements(InheritableBoolean.FALSE);
+ // Project name includes test method name which contains ExcludedProject
+ ChangeInfo change = gApi.changes().create(newChangeInput()).get();
+
+ // Approve and submit it
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(change.changeId).current().review(ReviewInput.approve());
+ gApi.changes().id(change.changeId).current().submit(new SubmitInput());
+
+ // Revert in excluded project is allowed even when CLA is required but not signed
+ requestScopeOperations.setApiUser(user.id());
+ setUseContributorAgreements(InheritableBoolean.TRUE);
+ gApi.changes().id(change.changeId).revert();
+ }
+
+ @Test
public void cherrypickChangeWithoutCLA() throws Exception {
assume().that(isContributorAgreementsEnabled()).isTrue();
// Create a new branch
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
BranchInfo dest =
gApi.projects()
.name(project.get())
@@ -203,7 +282,7 @@ public class AgreementsIT extends AbstractDaemonTest {
gApi.changes().id(change.changeId).current().submit(new SubmitInput());
// Cherry-pick is not allowed when CLA is required but not signed
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
setUseContributorAgreements(InheritableBoolean.TRUE);
CherryPickInput in = new CherryPickInput();
in.destination = dest.ref;
@@ -234,12 +313,23 @@ public class AgreementsIT extends AbstractDaemonTest {
gApi.accounts().self().signAgreement(caAutoVerify.getName());
// Explicitly reset the user to force a new request context
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// Create a change succeeds after signing the agreement
gApi.changes().create(newChangeInput());
}
+ @Test
+ public void createExcludedProjectChangeIgnoresCLA() throws Exception {
+ // Contributor agreements configured with excludeProjects = ExcludedProject
+ // in AbstractDaemonTest.configureContributorAgreement(...)
+ assume().that(isContributorAgreementsEnabled()).isTrue();
+
+ // Create a change in excluded project is allowed even when CLA is required but not signed.
+ setUseContributorAgreements(InheritableBoolean.TRUE);
+ gApi.changes().create(newChangeInput());
+ }
+
private void assertAgreement(AgreementInfo info, ContributorAgreement ca) {
assertThat(info.name).isEqualTo(ca.getName());
assertThat(info.description).isEqualTo(ca.getDescription());
@@ -258,4 +348,33 @@ public class AgreementsIT extends AbstractDaemonTest {
in.project = project.get();
return in;
}
+
+ @Test
+ @GerritConfig(name = "auth.contributorAgreements", value = "true")
+ public void anonymousAccessServerInfoEvenWithCLAs() throws Exception {
+ requestScopeOperations.setApiUserAnonymous();
+ gApi.config().server().getInfo();
+ }
+
+ @Test
+ public void publishEditRestWithoutCLA() throws Exception {
+ String filename = "foo";
+ PushOneCommit push =
+ pushFactory.create(admin.newIdent(), testRepo, "subject1", filename, "contentold");
+ PushOneCommit.Result result = push.to("refs/for/master");
+ result.assertOkStatus();
+ String changeId = result.getChangeId();
+
+ gApi.changes().id(changeId).edit().create();
+ gApi.changes()
+ .id(changeId)
+ .edit()
+ .modifyFile(filename, RawInputUtil.create("newcontent".getBytes(UTF_8)));
+
+ String url = "/changes/" + changeId + "/edit:publish";
+ setUseContributorAgreements(InheritableBoolean.TRUE);
+ userRestSession.post(url).assertForbidden();
+ setUseContributorAgreements(InheritableBoolean.FALSE);
+ userRestSession.post(url).assertNoContent();
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
index ad2bfa6a77..673379dda0 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
@@ -1,3 +1,4 @@
+load("@rules_java//java:defs.bzl", "java_library")
load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
acceptance_tests(
@@ -10,6 +11,7 @@ acceptance_tests(
],
deps = [
":util",
+ "//java/com/google/gerrit/git",
"//java/com/google/gerrit/mail",
"//java/com/google/gerrit/server/util/time",
],
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
index ba340eb129..a442ddd273 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
@@ -21,7 +21,6 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.client.Theme;
import org.junit.Test;
@NoHttpd
@@ -29,7 +28,7 @@ public class DiffPreferencesIT extends AbstractDaemonTest {
@Test
public void getDiffPreferences() throws Exception {
DiffPreferencesInfo d = DiffPreferencesInfo.defaults();
- DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+ DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
assertPrefs(o, d);
}
@@ -43,7 +42,6 @@ public class DiffPreferencesIT extends AbstractDaemonTest {
i.fontSize *= -1;
i.lineLength *= -1;
i.cursorBlinkRate = 500;
- i.theme = Theme.MIDNIGHT;
i.ignoreWhitespace = Whitespace.IGNORE_ALL;
i.expandAllComments ^= true;
i.intralineDifference ^= true;
@@ -64,13 +62,13 @@ public class DiffPreferencesIT extends AbstractDaemonTest {
i.matchBrackets ^= true;
i.lineWrapping ^= true;
- DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+ DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
assertPrefs(o, i);
// Partially fill input record
i = new DiffPreferencesInfo();
i.tabSize = 42;
- DiffPreferencesInfo a = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+ DiffPreferencesInfo a = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
assertPrefs(a, o, "tabSize");
assertThat(a.tabSize).isEqualTo(42);
}
@@ -87,7 +85,7 @@ public class DiffPreferencesIT extends AbstractDaemonTest {
update.fontSize = newFontSize;
gApi.config().server().setDefaultDiffPreferences(update);
- DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+ DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
// assert configured defaults
assertThat(o.lineLength).isEqualTo(newLineLength);
@@ -106,29 +104,29 @@ public class DiffPreferencesIT extends AbstractDaemonTest {
update.lineLength = configuredDefaultLineLength;
gApi.config().server().setDefaultDiffPreferences(update);
- DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+ DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
assertThat(o.lineLength).isEqualTo(configuredDefaultLineLength);
assertPrefs(o, d, "lineLength");
int newLineLength = configuredDefaultLineLength + 10;
DiffPreferencesInfo i = new DiffPreferencesInfo();
i.lineLength = newLineLength;
- DiffPreferencesInfo a = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+ DiffPreferencesInfo a = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
assertThat(a.lineLength).isEqualTo(newLineLength);
assertPrefs(a, d, "lineLength");
- a = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+ a = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
assertThat(a.lineLength).isEqualTo(newLineLength);
assertPrefs(a, d, "lineLength");
// overwrite the configured default with original hard-coded default
i = new DiffPreferencesInfo();
i.lineLength = d.lineLength;
- a = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+ a = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
assertThat(a.lineLength).isEqualTo(d.lineLength);
assertPrefs(a, d, "lineLength");
- a = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+ a = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
assertThat(a.lineLength).isEqualTo(d.lineLength);
assertPrefs(a, d, "lineLength");
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
index c1d9bcb26e..f142c0881e 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
@@ -19,15 +19,13 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
-import com.google.gerrit.extensions.client.KeyMapType;
-import com.google.gerrit.extensions.client.Theme;
import org.junit.Test;
@NoHttpd
public class EditPreferencesIT extends AbstractDaemonTest {
@Test
public void getSetEditPreferences() throws Exception {
- EditPreferencesInfo out = gApi.accounts().id(admin.getId().toString()).getEditPreferences();
+ EditPreferencesInfo out = gApi.accounts().id(admin.id().toString()).getEditPreferences();
assertThat(out.lineLength).isEqualTo(100);
assertThat(out.indentUnit).isEqualTo(2);
@@ -43,8 +41,6 @@ public class EditPreferencesIT extends AbstractDaemonTest {
assertThat(out.indentWithTabs).isNull();
assertThat(out.autoCloseBrackets).isNull();
assertThat(out.showBase).isNull();
- assertThat(out.theme).isEqualTo(Theme.DEFAULT);
- assertThat(out.keyMapType).isEqualTo(KeyMapType.DEFAULT);
// change some default values
out.lineLength = 80;
@@ -61,10 +57,8 @@ public class EditPreferencesIT extends AbstractDaemonTest {
out.indentWithTabs = true;
out.autoCloseBrackets = true;
out.showBase = true;
- out.theme = Theme.TWILIGHT;
- out.keyMapType = KeyMapType.EMACS;
- EditPreferencesInfo info = gApi.accounts().id(admin.getId().toString()).setEditPreferences(out);
+ EditPreferencesInfo info = gApi.accounts().id(admin.id().toString()).setEditPreferences(out);
assertEditPreferences(info, out);
@@ -72,7 +66,7 @@ public class EditPreferencesIT extends AbstractDaemonTest {
EditPreferencesInfo in = new EditPreferencesInfo();
in.tabSize = 42;
- info = gApi.accounts().id(admin.getId().toString()).setEditPreferences(in);
+ info = gApi.accounts().id(admin.id().toString()).setEditPreferences(in);
out.tabSize = in.tabSize;
assertEditPreferences(info, out);
@@ -94,7 +88,5 @@ public class EditPreferencesIT extends AbstractDaemonTest {
assertThat(out.indentWithTabs).isEqualTo(in.indentWithTabs);
assertThat(out.autoCloseBrackets).isEqualTo(in.autoCloseBrackets);
assertThat(out.showBase).isEqualTo(in.showBase);
- assertThat(out.theme).isEqualTo(in.theme);
- assertThat(out.keyMapType).isEqualTo(in.keyMapType);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 24040a4136..c905d3fee0 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -24,7 +24,6 @@ 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.DownloadCommand;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.ReviewCategoryStrategy;
@@ -38,7 +37,6 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.inject.Inject;
import com.google.inject.util.Providers;
import java.util.ArrayList;
-import java.util.HashMap;
import org.junit.Before;
import org.junit.Test;
@@ -56,7 +54,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
@Test
public void getAndSetPreferences() throws Exception {
- GeneralPreferencesInfo o = gApi.accounts().id(user42.id.toString()).getPreferences();
+ GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).getPreferences();
assertPrefs(o, GeneralPreferencesInfo.defaults(), "my", "changeTable");
assertThat(o.my)
.containsExactly(
@@ -74,7 +72,6 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
i.changesPerPage *= -1;
i.showSiteHeader ^= true;
i.useFlashClipboard ^= true;
- i.downloadCommand = DownloadCommand.REPO_DOWNLOAD;
i.dateFormat = DateFormat.US;
i.timeFormat = TimeFormat.HHMM_24;
i.emailStrategy = EmailStrategy.DISABLED;
@@ -93,10 +90,8 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
i.my.add(new MenuItem("name", "url"));
i.changeTable = new ArrayList<>();
i.changeTable.add("Status");
- i.urlAliases = new HashMap<>();
- i.urlAliases.put("foo", "bar");
- o = gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+ o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
assertPrefs(o, i, "my");
assertThat(o.my).containsExactlyElementsIn(i.my);
assertThat(o.changeTable).containsExactlyElementsIn(i.changeTable);
@@ -110,7 +105,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
update.changesPerPage = newChangesPerPage;
gApi.config().server().setDefaultPreferences(update);
- GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).getPreferences();
+ GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).getPreferences();
// assert configured defaults
assertThat(o.changesPerPage).isEqualTo(newChangesPerPage);
@@ -127,29 +122,29 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
update.changesPerPage = configuredChangesPerPage;
gApi.config().server().setDefaultPreferences(update);
- GeneralPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getPreferences();
+ GeneralPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getPreferences();
assertThat(o.changesPerPage).isEqualTo(configuredChangesPerPage);
assertPrefs(o, d, "my", "changeTable", "changesPerPage");
int newChangesPerPage = configuredChangesPerPage * 2;
GeneralPreferencesInfo i = new GeneralPreferencesInfo();
i.changesPerPage = newChangesPerPage;
- GeneralPreferencesInfo a = gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+ GeneralPreferencesInfo a = gApi.accounts().id(admin.id().toString()).setPreferences(i);
assertThat(a.changesPerPage).isEqualTo(newChangesPerPage);
assertPrefs(a, d, "my", "changeTable", "changesPerPage");
- a = gApi.accounts().id(admin.getId().toString()).getPreferences();
+ a = gApi.accounts().id(admin.id().toString()).getPreferences();
assertThat(a.changesPerPage).isEqualTo(newChangesPerPage);
assertPrefs(a, d, "my", "changeTable", "changesPerPage");
// overwrite the configured default with original hard-coded default
i = new GeneralPreferencesInfo();
i.changesPerPage = d.changesPerPage;
- a = gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+ a = gApi.accounts().id(admin.id().toString()).setPreferences(i);
assertThat(a.changesPerPage).isEqualTo(d.changesPerPage);
assertPrefs(a, d, "my", "changeTable", "changesPerPage");
- a = gApi.accounts().id(admin.getId().toString()).getPreferences();
+ a = gApi.accounts().id(admin.id().toString()).getPreferences();
assertThat(a.changesPerPage).isEqualTo(d.changesPerPage);
assertPrefs(a, d, "my", "changeTable", "changesPerPage");
}
@@ -162,7 +157,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
exception.expect(BadRequestException.class);
exception.expectMessage("name for menu item is required");
- gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+ gApi.accounts().id(user42.id().toString()).setPreferences(i);
}
@Test
@@ -173,7 +168,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
exception.expect(BadRequestException.class);
exception.expectMessage("URL for menu item is required");
- gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+ gApi.accounts().id(user42.id().toString()).setPreferences(i);
}
@Test
@@ -182,7 +177,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
i.my = new ArrayList<>();
i.my.add(new MenuItem(" name\t", " url\t", " _blank\t", " id\t"));
- GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+ GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
assertThat(o.my).containsExactly(new MenuItem("name", "url", "_blank", "id"));
}
@@ -193,7 +188,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
exception.expect(BadRequestException.class);
exception.expectMessage("Unsupported download scheme: " + i.downloadScheme);
- gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+ gApi.accounts().id(user42.id().toString()).setPreferences(i);
}
@Test
@@ -206,10 +201,10 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
GeneralPreferencesInfo i = GeneralPreferencesInfo.defaults();
i.downloadScheme = schemeName;
- GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+ GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
assertThat(o.downloadScheme).isEqualTo(schemeName);
- o = gApi.accounts().id(user42.getId().toString()).getPreferences();
+ o = gApi.accounts().id(user42.id().toString()).getPreferences();
assertThat(o.downloadScheme).isEqualTo(schemeName);
} finally {
registrationHandle.remove();
@@ -222,7 +217,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
// becomes unsupported.
setDownloadScheme();
- GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).getPreferences();
+ GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).getPreferences();
assertThat(o.downloadScheme).isNull();
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
index b626ab34f9..925c66ab7b 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
@@ -26,6 +26,7 @@ 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.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -45,6 +46,7 @@ import org.junit.Test;
public class AbandonIT extends AbstractDaemonTest {
@Inject private AbandonUtil abandonUtil;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Inject private ChangeCleanupConfig cleanupConfig;
@Test
@@ -154,7 +156,7 @@ public class AbandonIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("abandon not permitted");
gApi.changes().id(changeId).abandon();
@@ -166,7 +168,7 @@ public class AbandonIT extends AbstractDaemonTest {
String changeId = r.getChangeId();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
grant(project, "refs/heads/master", Permission.ABANDON, false, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).abandon();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.ABANDONED);
gApi.changes().id(changeId).restore();
@@ -197,7 +199,7 @@ public class AbandonIT extends AbstractDaemonTest {
String changeId = r.getChangeId();
assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
gApi.changes().id(changeId).abandon();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(info(changeId).status).isEqualTo(ChangeStatus.ABANDONED);
exception.expect(AuthException.class);
exception.expectMessage("restore not permitted");
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index e6dfa1d865..fe97688b07 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -16,7 +16,6 @@ package com.google.gerrit.acceptance.api.change;
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.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_CONTENT;
@@ -40,7 +39,6 @@ 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.reviewdb.server.ReviewDbUtil.unwrapDb;
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;
@@ -59,8 +57,8 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.truth.ThrowableSubject;
import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.ChangeIndexedCounter;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.GitUtil;
@@ -68,10 +66,15 @@ import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
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.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.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
@@ -83,10 +86,12 @@ import com.google.gerrit.extensions.api.changes.NotifyHandling;
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;
+import com.google.gerrit.extensions.api.changes.ReviewerInfo;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.api.changes.StarsInput;
import com.google.gerrit.extensions.api.groups.GroupApi;
@@ -127,14 +132,14 @@ 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.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.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.ChangeMessagesUtil;
@@ -142,8 +147,12 @@ 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.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
+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.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder.ChangeOperatorFactory;
import com.google.gerrit.server.restapi.change.PostReview;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
@@ -151,17 +160,18 @@ 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 java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
@@ -182,11 +192,14 @@ import org.junit.Test;
public class ChangeIT extends AbstractDaemonTest {
private String systemTimeZone;
- @Inject private DynamicSet<ChangeMessageModifier> changeMessageModifiers;
-
- @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
-
@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;
private ChangeIndexedCounter changeIndexedCounter;
private RegistrationHandle changeIndexedCounterHandle;
@@ -231,7 +244,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(c.created).isEqualTo(c.updated);
assertThat(c._number).isEqualTo(r.getChange().getId().get());
- assertThat(c.owner._accountId).isEqualTo(admin.getId().get());
+ assertThat(c.owner._accountId).isEqualTo(admin.id().get());
assertThat(c.owner.name).isNull();
assertThat(c.owner.email).isNull();
assertThat(c.owner.username).isNull();
@@ -251,154 +264,11 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
- public void setPrivateByOwner() throws Exception {
- TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
- PushOneCommit.Result result =
- pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master");
-
- setApiUser(user);
- String changeId = result.getChangeId();
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
-
- gApi.changes().id(changeId).setPrivate(true, null);
- ChangeInfo info = gApi.changes().id(changeId).get();
- assertThat(info.isPrivate).isTrue();
- assertThat(Iterables.getLast(info.messages).message).isEqualTo("Set private");
- assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_SET_PRIVATE);
-
- gApi.changes().id(changeId).setPrivate(false, null);
- info = gApi.changes().id(changeId).get();
- assertThat(info.isPrivate).isNull();
- assertThat(Iterables.getLast(info.messages).message).isEqualTo("Unset private");
- assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_UNSET_PRIVATE);
-
- String msg = "This is a security fix that must not be public.";
- gApi.changes().id(changeId).setPrivate(true, msg);
- info = gApi.changes().id(changeId).get();
- assertThat(info.isPrivate).isTrue();
- assertThat(Iterables.getLast(info.messages).message).isEqualTo("Set private\n\n" + msg);
- assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_SET_PRIVATE);
-
- msg = "After this security fix has been released we can make it public now.";
- gApi.changes().id(changeId).setPrivate(false, msg);
- info = gApi.changes().id(changeId).get();
- assertThat(info.isPrivate).isNull();
- assertThat(Iterables.getLast(info.messages).message).isEqualTo("Unset private\n\n" + msg);
- assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_UNSET_PRIVATE);
- }
-
- @Test
- public void administratorCanSetUserChangePrivate() throws Exception {
- String changeId = createNewChange();
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
-
- gApi.changes().id(changeId).setPrivate(true, null);
- setApiUser(user);
- ChangeInfo info = gApi.changes().id(changeId).get();
- assertThat(info.isPrivate).isTrue();
- }
-
- @Test
- public void cannotSetOtherUsersChangePrivate() throws Exception {
- PushOneCommit.Result result = createChange();
- setApiUser(user);
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to mark private");
- gApi.changes().id(result.getChangeId()).setPrivate(true, null);
- }
-
- @Test
- public void accessPrivate() throws Exception {
- TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
- PushOneCommit.Result result =
- pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master");
-
- setApiUser(user);
- gApi.changes().id(result.getChangeId()).setPrivate(true, null);
- // Owner can always access its private changes.
- assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
-
- // Add admin as a reviewer.
- gApi.changes().id(result.getChangeId()).addReviewer(admin.getId().toString());
-
- // This change should be visible for admin as a reviewer.
- setApiUser(admin);
- assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
-
- // Remove admin from reviewers.
- gApi.changes().id(result.getChangeId()).reviewer(admin.getId().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());
- }
-
- @Test
- public void privateChangeOfOtherUserCanBeAccessedWithPermission() throws Exception {
- PushOneCommit.Result result = createChange();
- gApi.changes().id(result.getChangeId()).setPrivate(true, null);
-
- allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
- setApiUser(user);
- assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
- }
-
- @Test
- public void administratorCanUnmarkPrivateAfterMerging() throws Exception {
- PushOneCommit.Result result = createChange();
- String changeId = result.getChangeId();
- gApi.changes().id(changeId).setPrivate(true, null);
- assertThat(gApi.changes().id(changeId).get().isPrivate).isTrue();
- merge(result);
- gApi.changes().id(changeId).setPrivate(false, null);
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
- }
-
- @Test
- public void administratorCanMarkPrivateAfterMerging() throws Exception {
- PushOneCommit.Result result = createChange();
- String changeId = result.getChangeId();
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
- merge(result);
- gApi.changes().id(changeId).setPrivate(true, null);
- assertThat(gApi.changes().id(changeId).get().isPrivate).isTrue();
- }
-
- @Test
- public void ownerCannotMarkPrivateAfterMerging() throws Exception {
- TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
- PushOneCommit.Result result =
- pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master");
-
- String changeId = result.getChangeId();
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
-
- merge(result);
-
- setApiUser(user);
- exception.expect(AuthException.class);
- exception.expectMessage("not allowed to mark private");
- gApi.changes().id(changeId).setPrivate(true, null);
- }
-
- @Test
- public void ownerCanUnmarkPrivateAfterMerging() throws Exception {
- TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
- PushOneCommit.Result result =
- pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master");
-
- String changeId = result.getChangeId();
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
- gApi.changes().id(changeId).addReviewer(admin.getId().toString());
- gApi.changes().id(changeId).setPrivate(true, null);
- assertThat(gApi.changes().id(changeId).get().isPrivate).isTrue();
-
- merge(result);
-
- setApiUser(user);
- gApi.changes().id(changeId).setPrivate(false, null);
- assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
+ @GerritConfig(name = "change.api.excludeMergeableInChangeInfo", value = "true")
+ public void excludeMergeableInChangeInfo() throws Exception {
+ PushOneCommit.Result r = createChange();
+ ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
+ assertThat(c.mergeable).isNull();
}
@Test
@@ -406,32 +276,32 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result rwip = createChange();
String changeId = rwip.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- exception.expectMessage("not allowed to toggle work in progress");
+ exception.expectMessage("toggle work in progress state not permitted");
gApi.changes().id(changeId).setWorkInProgress();
}
@Test
public void setWorkInProgressAllowedAsAdmin() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
String changeId =
gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).setWorkInProgress();
assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
}
@Test
public void setWorkInProgressAllowedAsProjectOwner() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
String changeId =
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);
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId).setWorkInProgress();
assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
}
@@ -452,34 +322,34 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = rready.getChangeId();
gApi.changes().id(changeId).setWorkInProgress();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- exception.expectMessage("not allowed to toggle work in progress");
+ exception.expectMessage("toggle work in progress state not permitted");
gApi.changes().id(changeId).setReadyForReview();
}
@Test
public void setReadyForReviewAllowedAsAdmin() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
String changeId =
gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
gApi.changes().id(changeId).setWorkInProgress();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).setReadyForReview();
assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
}
@Test
public void setReadyForReviewAllowedAsProjectOwner() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
String changeId =
gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
gApi.changes().id(changeId).setWorkInProgress();
com.google.gerrit.acceptance.TestAccount user2 = accountCreator.user2();
grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
gApi.changes().id(changeId).setReadyForReview();
assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
}
@@ -498,8 +368,6 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void pendingReviewersInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(conf);
@@ -554,7 +422,7 @@ public class ChangeIT extends AbstractDaemonTest {
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");
+ admin.email(), 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();
@@ -566,7 +434,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(admin.email(), email2, "byemail2@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
.containsExactly(email4, "byemail4@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -577,7 +445,7 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).revision("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(admin.email(), email1, email2, "byemail2@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
.containsExactly(email4, "byemail4@example.com");
assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -588,7 +456,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(admin.email(), email1, email2, "byemail2@example.com");
assertThat(toEmails.apply(info.reviewers.get(CC)))
.containsExactly(email4, "byemail4@example.com");
assertThat(info.reviewers.get(REMOVED)).isNull();
@@ -633,6 +501,37 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void toggleWorkInProgressStateByNonOwnerWithPermission() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String changeId = r.getChangeId();
+ String refactor = "Needs some refactoring";
+ String ptal = "PTAL";
+
+ grant(
+ project,
+ "refs/heads/master",
+ Permission.TOGGLE_WORK_IN_PROGRESS_STATE,
+ false,
+ REGISTERED_USERS);
+
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(changeId).setWorkInProgress(refactor);
+
+ ChangeInfo info = gApi.changes().id(changeId).get();
+
+ assertThat(info.workInProgress).isTrue();
+ assertThat(Iterables.getLast(info.messages).message).contains(refactor);
+ assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_SET_WIP);
+
+ gApi.changes().id(changeId).setReadyForReview(ptal);
+
+ info = gApi.changes().id(changeId).get();
+ assertThat(info.workInProgress).isNull();
+ assertThat(Iterables.getLast(info.messages).message).contains(ptal);
+ assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_SET_READY);
+ }
+
+ @Test
public void reviewAndStartReview() throws Exception {
PushOneCommit.Result r = createWorkInProgressChange();
r.assertOkStatus();
@@ -665,14 +564,17 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(r.getChange().change().isWorkInProgress()).isFalse();
ReviewInput in =
- ReviewInput.approve().reviewer(user.email).label("Code-Review", 1).setWorkInProgress(true);
+ ReviewInput.approve()
+ .reviewer(user.email())
+ .label("Code-Review", 1)
+ .setWorkInProgress(true);
gApi.changes().id(r.getChangeId()).revision("current").review(in);
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
assertThat(info.workInProgress).isTrue();
assertThat(info.reviewers.get(REVIEWER).stream().map(ai -> ai._accountId).collect(toList()))
- .containsExactly(admin.id.get(), user.id.get());
- assertThat(info.labels.get("Code-Review").recommended._accountId).isEqualTo(admin.id.get());
+ .containsExactly(admin.id().get(), user.id().get());
+ assertThat(info.labels.get("Code-Review").recommended._accountId).isEqualTo(admin.id().get());
}
@Test
@@ -688,12 +590,12 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
@TestProjectInput(cloneAs = "user")
public void reviewWithWorkInProgressChangeOwner() throws Exception {
- PushOneCommit push = pushFactory.create(db, user.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
- assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+ assertThat(r.getChange().change().getOwner()).isEqualTo(user.id());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
gApi.changes().id(r.getChangeId()).current().review(in);
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -703,12 +605,12 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
@TestProjectInput(cloneAs = "user")
public void reviewWithWithWorkInProgressAdmin() throws Exception {
- PushOneCommit push = pushFactory.create(db, user.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
- assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+ assertThat(r.getChange().change().getOwner()).isEqualTo(user.id());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
gApi.changes().id(r.getChangeId()).current().review(in);
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -719,19 +621,35 @@ public class ChangeIT extends AbstractDaemonTest {
public void reviewWithWorkInProgressByNonOwnerReturnsError() throws Exception {
PushOneCommit.Result r = createChange();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- exception.expectMessage("not allowed to toggle work in progress");
+ exception.expectMessage("toggle work in progress state not permitted");
gApi.changes().id(r.getChangeId()).current().review(in);
}
@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);
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(r.getChangeId()).current().review(in);
+ ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
+ assertThat(info.workInProgress).isTrue();
+ }
+
+ @Test
public void reviewWithReadyByNonOwnerReturnsError() throws Exception {
PushOneCommit.Result r = createChange();
ReviewInput in = ReviewInput.noScore().setReady(true);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
- exception.expectMessage("not allowed to toggle work in progress");
+ exception.expectMessage("toggle work in progress state not permitted");
gApi.changes().id(r.getChangeId()).current().review(in);
}
@@ -747,8 +665,7 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit push2 =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
@@ -789,7 +706,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void revertNotifications() throws Exception {
PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+ 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();
@@ -805,7 +722,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void suppressRevertNotifications() throws Exception {
PushOneCommit.Result r = createChange();
- gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+ 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();
@@ -822,33 +739,28 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput in = ReviewInput.approve();
- in.reviewer(user.email);
- in.reviewer(accountCreator.user2().email, ReviewerState.CC, true);
+ 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);
+ 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
- setApiUser(accountCreator.admin2());
+ 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());
- if (notesMigration.readChanges()) {
- 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());
- } else {
- assertThat(reviewers)
- .containsExactly(user.id.get(), admin.id.get(), accountCreator.user2().id.get());
- }
+ 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
@@ -903,8 +815,8 @@ public class ChangeIT extends AbstractDaemonTest {
// ...and the committer and description should be correct
ChangeInfo info = gApi.changes().id(changeId).get(CURRENT_REVISION, CURRENT_COMMIT);
GitPerson committer = info.revisions.get(info.currentRevision).commit.committer;
- assertThat(committer.name).isEqualTo(admin.fullName);
- assertThat(committer.email).isEqualTo(admin.email);
+ assertThat(committer.name).isEqualTo(admin.fullName());
+ assertThat(committer.email).isEqualTo(admin.email());
String description = info.revisions.get(info.currentRevision).description;
assertThat(description).isEqualTo("Rebase");
@@ -914,17 +826,6 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(cr.all).hasSize(1);
assertThat(cr.all.get(0).value).isEqualTo(1);
- if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
- // Ensure record was actually copied under ReviewDb
- List<PatchSetApproval> psas =
- unwrapDb(db)
- .patchSetApprovals()
- .byPatchSet(new PatchSet.Id(new Change.Id(c2._number), 2))
- .toList();
- assertThat(psas).hasSize(1);
- assertThat(psas.get(0).getValue()).isEqualTo((short) 1);
- }
-
// Rebasing the second change again should fail
exception.expect(ResourceConflictException.class);
exception.expectMessage("Change is already up to date");
@@ -932,6 +833,16 @@ public class ChangeIT extends AbstractDaemonTest {
}
@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);
+ }
+
+ @Test
public void rebaseOnChangeNumber() throws Exception {
String branchTip = testRepo.getRepository().exactRef("HEAD").getObjectId().name();
PushOneCommit.Result r1 = createChange();
@@ -942,23 +853,78 @@ public class ChangeIT extends AbstractDaemonTest {
RevisionInfo ri2 = ci2.revisions.get(ci2.currentRevision);
assertThat(ri2.commit.parents.get(0).commit).isEqualTo(branchTip);
+ Change.Id id1 = r1.getChange().getId();
RebaseInput in = new RebaseInput();
- in.base = Integer.toString(r1.getChange().getId().get());
+ in.base = id1.toString();
gApi.changes().id(r2.getChangeId()).rebase(in);
+ Change.Id id2 = r2.getChange().getId();
ci2 = get(r2.getChangeId(), CURRENT_REVISION, CURRENT_COMMIT);
ri2 = ci2.revisions.get(ci2.currentRevision);
assertThat(ri2.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
+
+ List<RelatedChangeAndCommitInfo> related =
+ gApi.changes().id(id2.get()).revision(ri2._number).related().changes;
+ assertThat(related).hasSize(2);
+ assertThat(related.get(0)._changeNumber).isEqualTo(id2.get());
+ assertThat(related.get(0)._revisionNumber).isEqualTo(2);
+ assertThat(related.get(1)._changeNumber).isEqualTo(id1.get());
+ assertThat(related.get(1)._revisionNumber).isEqualTo(1);
}
@Test
- public void rebaseOnNonExistingChange() throws Exception {
- String changeId = createChange().getChangeId();
+ public void rebaseOnClosedChange() throws Exception {
+ String branchTip = testRepo.getRepository().exactRef("HEAD").getObjectId().name();
+ PushOneCommit.Result r1 = createChange();
+ testRepo.reset("HEAD~1");
+ PushOneCommit.Result r2 = createChange();
+
+ ChangeInfo ci2 = get(r2.getChangeId(), CURRENT_REVISION, CURRENT_COMMIT);
+ RevisionInfo ri2 = ci2.revisions.get(ci2.currentRevision);
+ assertThat(ri2.commit.parents.get(0).commit).isEqualTo(branchTip);
+
+ // Submit first change.
+ Change.Id id1 = r1.getChange().getId();
+ gApi.changes().id(id1.get()).current().review(ReviewInput.approve());
+ gApi.changes().id(id1.get()).current().submit();
+
+ // Rebase second change on first change.
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);
+ in.base = id1.toString();
+ gApi.changes().id(r2.getChangeId()).rebase(in);
+
+ Change.Id id2 = r2.getChange().getId();
+ ci2 = get(r2.getChangeId(), CURRENT_REVISION, CURRENT_COMMIT);
+ ri2 = ci2.revisions.get(ci2.currentRevision);
+ assertThat(ri2.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
+
+ assertThat(gApi.changes().id(id2.get()).revision(ri2._number).related().changes).isEmpty();
+ }
+
+ @Test
+ public void rebaseFromRelationChainToClosedChange() throws Exception {
+ PushOneCommit.Result r1 = createChange();
+ testRepo.reset("HEAD~1");
+
+ createChange();
+ PushOneCommit.Result r3 = createChange();
+
+ // Submit first change.
+ Change.Id id1 = r1.getChange().getId();
+ gApi.changes().id(id1.get()).current().review(ReviewInput.approve());
+ gApi.changes().id(id1.get()).current().submit();
+
+ // Rebase third change on first change.
+ RebaseInput in = new RebaseInput();
+ in.base = id1.toString();
+ gApi.changes().id(r3.getChangeId()).rebase(in);
+
+ Change.Id id3 = r3.getChange().getId();
+ ChangeInfo ci3 = get(r3.getChangeId(), CURRENT_REVISION, CURRENT_COMMIT);
+ RevisionInfo ri3 = ci3.revisions.get(ci3.currentRevision);
+ assertThat(ri3.commit.parents.get(0).commit).isEqualTo(r1.getCommit().name());
+
+ assertThat(gApi.changes().id(id3.get()).revision(ri3._number).related().changes).isEmpty();
}
@Test
@@ -975,7 +941,7 @@ public class ChangeIT extends AbstractDaemonTest {
// Rebase the second
String changeId = r2.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("rebase not permitted");
gApi.changes().id(changeId).rebase();
@@ -997,7 +963,7 @@ public class ChangeIT extends AbstractDaemonTest {
// Rebase the second
String changeId = r2.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).rebase();
}
@@ -1018,7 +984,7 @@ public class ChangeIT extends AbstractDaemonTest {
// Rebase the second
String changeId = r2.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("rebase not permitted");
gApi.changes().id(changeId).rebase();
@@ -1054,10 +1020,10 @@ public class ChangeIT extends AbstractDaemonTest {
@TestProjectInput(cloneAs = "user")
public void deleteNewChangeAsNormalUser() throws Exception {
PushOneCommit.Result changeResult =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
String changeId = changeResult.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("delete not permitted");
gApi.changes().id(changeId).delete();
@@ -1116,7 +1082,7 @@ public class ChangeIT extends AbstractDaemonTest {
com.google.gerrit.acceptance.TestAccount deleteAs)
throws Exception {
try {
- setApiUser(owner);
+ requestScopeOperations.setApiUser(owner.id());
ChangeInput in = new ChangeInput();
in.project = projectName.get();
in.branch = "refs/heads/master";
@@ -1126,16 +1092,16 @@ public class ChangeIT extends AbstractDaemonTest {
int id = changeInfo._number;
String commit = changeInfo.currentRevision;
- assertThat(gApi.changes().id(changeId).info().owner._accountId).isEqualTo(owner.id.get());
+ assertThat(gApi.changes().id(changeId).info().owner._accountId).isEqualTo(owner.id().get());
- setApiUser(deleteAs);
+ requestScopeOperations.setApiUser(deleteAs.id());
gApi.changes().id(changeId).delete();
assertThat(query(changeId)).isEmpty();
String ref = new Change.Id(id).toRefPrefix() + "1";
eventRecorder.assertRefUpdatedEvents(projectName.get(), ref, null, commit, commit, null);
- eventRecorder.assertChangeDeletedEvents(changeId, deleteAs.email);
+ eventRecorder.assertChangeDeletedEvents(changeId, deleteAs.email());
} finally {
removePermission(project, "refs/*", Permission.DELETE_OWN_CHANGES);
removePermission(project, "refs/*", Permission.DELETE_CHANGES);
@@ -1155,7 +1121,7 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result changeResult = createChange();
String changeId = changeResult.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("delete not permitted");
gApi.changes().id(changeId).delete();
@@ -1179,10 +1145,10 @@ public class ChangeIT extends AbstractDaemonTest {
@TestProjectInput(cloneAs = "user")
public void deleteAbandonedChangeAsNormalUser() throws Exception {
PushOneCommit.Result changeResult =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
String changeId = changeResult.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).abandon();
exception.expect(AuthException.class);
@@ -1194,7 +1160,7 @@ public class ChangeIT extends AbstractDaemonTest {
@TestProjectInput(cloneAs = "user")
public void deleteAbandonedChangeOfAnotherUserAsAdmin() throws Exception {
PushOneCommit.Result changeResult =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
String changeId = changeResult.getChangeId();
gApi.changes().id(changeId).abandon();
@@ -1223,12 +1189,12 @@ public class ChangeIT extends AbstractDaemonTest {
try {
PushOneCommit.Result changeResult =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
String changeId = changeResult.getChangeId();
merge(changeResult);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(MethodNotAllowedException.class);
exception.expectMessage("delete not permitted");
gApi.changes().id(changeId).delete();
@@ -1253,6 +1219,51 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void deleteChangeUpdatesIndex() throws Exception {
+ PushOneCommit.Result changeResult = createChange();
+ String changeId = changeResult.getChangeId();
+ Change.Id id = changeResult.getChange().getId();
+
+ ChangeIndex idx = changeIndexCollection.getSearchIndex();
+
+ Optional<ChangeData> result =
+ idx.get(id, IndexedChangeQuery.createOptions(indexConfig, 0, 1, ImmutableSet.of()));
+
+ assertThat(result.isPresent()).isTrue();
+ gApi.changes().id(changeId).delete();
+ result = idx.get(id, IndexedChangeQuery.createOptions(indexConfig, 0, 1, ImmutableSet.of()));
+ assertThat(result.isPresent()).isFalse();
+ }
+
+ @Test
+ public void deleteChangeRemovesDraftComment() throws Exception {
+ PushOneCommit.Result r = createChange();
+
+ requestScopeOperations.setApiUser(user.id());
+
+ DraftInput dri = new DraftInput();
+ dri.message = "hello";
+ dri.path = "a.txt";
+ dri.line = 1;
+
+ gApi.changes().id(r.getChangeId()).current().createDraft(dri);
+ Change.Id num = r.getChange().getId();
+
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftComments(num, user.id())))
+ .isNotEmpty();
+ }
+
+ requestScopeOperations.setApiUser(admin.id());
+
+ gApi.changes().id(r.getChangeId()).delete();
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ assertThat(repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftComments(num, user.id())))
+ .isEmpty();
+ }
+ }
+
+ @Test
public void rebaseUpToDateChange() throws Exception {
PushOneCommit.Result r = createChange();
exception.expect(ResourceConflictException.class);
@@ -1268,8 +1279,7 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
@@ -1425,37 +1435,37 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void pushCommitOfOtherUser() throws Exception {
// admin pushes commit of user
- PushOneCommit push = pushFactory.create(db, user.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
- assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+ assertThat(change.owner._accountId).isEqualTo(admin.id().get());
CommitInfo commit = change.revisions.get(change.currentRevision).commit;
- assertThat(commit.author.email).isEqualTo(user.email);
- assertThat(commit.committer.email).isEqualTo(user.email);
+ assertThat(commit.author.email).isEqualTo(user.email());
+ assertThat(commit.committer.email).isEqualTo(user.email());
// check that the author/committer was added as reviewer
Collection<AccountInfo> reviewers = change.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
assertThat(change.reviewers.get(CC)).isNull();
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
assertThat(m.from().getName()).isEqualTo("Administrator (Code Review)");
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("I'd like you to do a code review");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
- assertMailReplyTo(m, admin.email);
+ assertMailReplyTo(m, admin.email());
}
@Test
public void pushCommitOfOtherUserThatCannotSeeChange() throws Exception {
// create hidden project that is only visible to administrators
- Project.NameKey p = createProject("p");
+ 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/*");
@@ -1464,18 +1474,18 @@ public class ChangeIT extends AbstractDaemonTest {
// admin pushes commit of user
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
- PushOneCommit push = pushFactory.create(db, user.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), repo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
- assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+ assertThat(change.owner._accountId).isEqualTo(admin.id().get());
CommitInfo commit = change.revisions.get(change.currentRevision).commit;
- assertThat(commit.author.email).isEqualTo(user.email);
- assertThat(commit.committer.email).isEqualTo(user.email);
+ assertThat(commit.author.email).isEqualTo(user.email());
+ assertThat(commit.committer.email).isEqualTo(user.email());
// check the user cannot see the change
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
gApi.changes().id(result.getChangeId()).get();
fail("Expected ResourceNotFoundException");
@@ -1495,14 +1505,13 @@ public class ChangeIT extends AbstractDaemonTest {
// admin pushes commit that references 'user' in a footer
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT
+ "\n\n"
+ FooterConstants.REVIEWED_BY.getName()
+ ": "
- + user.getIdent().toExternalString(),
+ + user.newIdent().toExternalString(),
PushOneCommit.FILE_NAME,
PushOneCommit.FILE_CONTENT);
PushOneCommit.Result result = push.to("refs/for/master");
@@ -1513,23 +1522,23 @@ public class ChangeIT extends AbstractDaemonTest {
Collection<AccountInfo> reviewers = change.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
assertThat(change.reviewers.get(CC)).isNull();
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
- assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
assertThat(m.body()).contains("I'd like you to do a code review.");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
- assertMailReplyTo(m, admin.email);
+ assertMailReplyTo(m, admin.email());
}
@Test
public void pushCommitWithFooterOfOtherUserThatCannotSeeChange() throws Exception {
// create hidden project that is only visible to administrators
- Project.NameKey p = createProject("p");
+ 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/*");
@@ -1540,21 +1549,20 @@ public class ChangeIT extends AbstractDaemonTest {
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
repo,
PushOneCommit.SUBJECT
+ "\n\n"
+ FooterConstants.REVIEWED_BY.getName()
+ ": "
- + user.getIdent().toExternalString(),
+ + user.newIdent().toExternalString(),
PushOneCommit.FILE_NAME,
PushOneCommit.FILE_CONTENT);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
// check that 'user' cannot see the change
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
gApi.changes().id(result.getChangeId()).get();
fail("Expected ResourceNotFoundException");
@@ -1563,7 +1571,7 @@ public class ChangeIT extends AbstractDaemonTest {
}
// check that 'user' was NOT added as cc ('user' can't see the change)
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
assertThat(change.reviewers.get(REVIEWER)).isNull();
assertThat(change.reviewers.get(CC)).isNull();
@@ -1573,7 +1581,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void addReviewerThatCannotSeeChange() throws Exception {
// create hidden project that is only visible to administrators
- Project.NameKey p = createProject("p");
+ 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/*");
@@ -1582,12 +1590,12 @@ public class ChangeIT extends AbstractDaemonTest {
// create change
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
// check the user cannot see the change
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
gApi.changes().id(result.getChangeId()).get();
fail("Expected ResourceNotFoundException");
@@ -1596,12 +1604,12 @@ public class ChangeIT extends AbstractDaemonTest {
}
// try to add user as reviewer
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
- assertThat(r.input).isEqualTo(user.email);
+ assertThat(r.input).isEqualTo(user.email());
assertThat(r.error).contains("does not have permission to see this change");
assertThat(r.reviewers).isNull();
}
@@ -1611,21 +1619,49 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result result = createChange();
String username = name("new-user");
- gApi.accounts().create(username).setActive(false);
+ Account.Id id = accountOperations.newAccount().username(username).inactive().create();
AddReviewerInput in = new AddReviewerInput();
in.reviewer = username;
AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
- assertThat(r.input).isEqualTo(username);
- assertThat(r.error).contains("identifies an inactive account");
+ assertThat(r.input).isEqualTo(in.reviewer);
+ assertThat(r.error)
+ .isEqualTo(
+ "Account '"
+ + username
+ + "' only matches inactive accounts. To use an inactive account, retry with one of"
+ + " the following exact account IDs:\n"
+ + id
+ + ": Name of user not set ("
+ + id
+ + ")\n"
+ + username
+ + " does not identify a registered user or group");
assertThat(r.reviewers).isNull();
}
@Test
- public void addReviewerThatIsInactiveEmailFallback() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void addReviewerThatIsInactiveById() throws Exception {
+ PushOneCommit.Result result = createChange();
+
+ String username = name("new-user");
+ Account.Id id = accountOperations.newAccount().username(username).inactive().create();
+
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = Integer.toString(id.get());
+ AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
+ assertThat(r.input).isEqualTo(in.reviewer);
+ assertThat(r.error).isNull();
+ assertThat(r.reviewers).hasSize(1);
+ ReviewerInfo reviewer = r.reviewers.get(0);
+ assertThat(reviewer._accountId).isEqualTo(id.get());
+ assertThat(reviewer.username).isEqualTo(username);
+ }
+
+ @Test
+ public void addReviewerThatIsInactiveEmailFallback() throws Exception {
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.TRUE;
gApi.projects().name(project.get()).config(conf);
@@ -1633,7 +1669,7 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result result = createChange();
String username = "user@domain.com";
- gApi.accounts().create(username).setActive(false);
+ accountOperations.newAccount().username(username).inactive().create();
AddReviewerInput in = new AddReviewerInput();
in.reviewer = username;
@@ -1656,17 +1692,17 @@ public class ChangeIT extends AbstractDaemonTest {
Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
- assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
assertThat(m.body()).contains("I'd like you to do a code review.");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
- assertMailReplyTo(m, admin.email);
+ assertMailReplyTo(m, admin.email());
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
// When NoteDb is enabled adding a reviewer records that user as reviewer
@@ -1676,7 +1712,7 @@ public class ChangeIT extends AbstractDaemonTest {
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
@@ -1685,7 +1721,7 @@ public class ChangeIT extends AbstractDaemonTest {
// Change status of reviewer and ensure ETag is updated.
oldETag = rsrc.getETag();
- accountOperations.account(user.id).forUpdate().status("new status").update();
+ accountOperations.account(user.id()).forUpdate().status("new status").update();
rsrc = parseResource(r);
assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
}
@@ -1694,7 +1730,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void listReviewers() throws Exception {
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
assertThat(gApi.changes().id(r.getChangeId()).reviewers()).hasSize(1);
@@ -1710,13 +1746,13 @@ public class ChangeIT extends AbstractDaemonTest {
in.state = ReviewerState.CC;
gApi.changes().id(r.getChangeId()).addReviewer(in);
assertThat(gApi.changes().id(r.getChangeId()).reviewers().stream().map(a -> a.username))
- .containsExactly(user.username, username1);
+ .containsExactly(user.username(), username1);
}
@Test
public void notificationsForAddedWorkInProgressReviewers() throws Exception {
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
ReviewInput batchIn = new ReviewInput();
batchIn.reviewers = ImmutableList.of(in);
@@ -1765,10 +1801,10 @@ public class ChangeIT extends AbstractDaemonTest {
.preferredEmail(email)
.fullname(fullname)
.create();
- String testGroup = createGroupWithRealName("ab");
+ String testGroup = groupOperations.newGroup().name("ab").create().get();
GroupApi groupApi = gApi.groups().id(testGroup);
groupApi.description("test group");
- groupApi.addMembers(user.fullName);
+ groupApi.addMembers(user.fullName());
AddReviewerInput in = new AddReviewerInput();
in.reviewer = "abc";
@@ -1826,7 +1862,7 @@ public class ChangeIT extends AbstractDaemonTest {
.fullname(myGroupUserFullname)
.create();
- String testGroup = createGroupWithRealName("kobe");
+ String testGroup = groupOperations.newGroup().name("kobe").create().get();
GroupApi groupApi = gApi.groups().id(testGroup);
groupApi.description("test group");
groupApi.addMembers(myGroupUserFullname);
@@ -1864,26 +1900,6 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
- public void addReviewerWithNoteDbWhenDummyApprovalInReviewDbExists() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
- assume().that(notesMigration.changePrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
-
- PushOneCommit.Result r = createChange();
-
- // insert dummy approval in ReviewDb
- PatchSetApproval psa =
- new PatchSetApproval(
- new PatchSetApproval.Key(r.getPatchSetId(), user.id, new LabelId("Code-Review")),
- (short) 0,
- TimeUtil.nowTs());
- db.patchSetApprovals().insert(Collections.singleton(psa));
-
- AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
- gApi.changes().id(r.getChangeId()).addReviewer(in);
- }
-
- @Test
public void addSelfAsReviewer() throws Exception {
TestTimeUtil.resetWithClockStep(1, SECONDS);
PushOneCommit.Result r = createChange();
@@ -1892,8 +1908,8 @@ public class ChangeIT extends AbstractDaemonTest {
Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
- setApiUser(user);
+ in.reviewer = user.email();
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).addReviewer(in);
// There should be no email notification when adding self
@@ -1907,7 +1923,7 @@ public class ChangeIT extends AbstractDaemonTest {
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
@@ -1923,15 +1939,15 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void implicitlyCcOnNonVotingReviewForUserWithoutUserNamePgStyle() throws Exception {
com.google.gerrit.acceptance.TestAccount accountWithoutUsername = accountCreator.create();
- assertThat(accountWithoutUsername.username).isNull();
+ assertThat(accountWithoutUsername.username()).isNull();
testImplicitlyCcOnNonVotingReviewPgStyle(accountWithoutUsername);
}
private void testImplicitlyCcOnNonVotingReviewPgStyle(
com.google.gerrit.acceptance.TestAccount testAccount) throws Exception {
PushOneCommit.Result r = createChange();
- setApiUser(testAccount);
- assertThat(getReviewerState(r.getChangeId(), testAccount.id)).isEmpty();
+ requestScopeOperations.setApiUser(testAccount.id());
+ assertThat(getReviewerState(r.getChangeId(), testAccount.id())).isEmpty();
// Exact request format made by PG UI at ddc6b7160fe416fed9e7e3180489d44c82fd64f8.
ReviewInput in = new ReviewInput();
@@ -1941,45 +1957,13 @@ public class ChangeIT extends AbstractDaemonTest {
in.reviewers = ImmutableList.of();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
- // If we're not reading from NoteDb, then the CCed user will be returned in the REVIEWER state.
- assertThat(getReviewerState(r.getChangeId(), testAccount.id))
- .hasValue(notesMigration.readChanges() ? CC : REVIEWER);
- }
-
- @Test
- public void implicitlyCcOnNonVotingReviewGwtStyle() throws Exception {
- testImplicitlyCcOnNonVotingReviewGwtStyle(user);
- }
-
- @Test
- public void implicitlyCcOnNonVotingReviewForUserWithoutUserNameGwtStyle() throws Exception {
- com.google.gerrit.acceptance.TestAccount accountWithoutUsername = accountCreator.create();
- assertThat(accountWithoutUsername.username).isNull();
- testImplicitlyCcOnNonVotingReviewGwtStyle(accountWithoutUsername);
- }
-
- private void testImplicitlyCcOnNonVotingReviewGwtStyle(
- com.google.gerrit.acceptance.TestAccount testAccount) throws Exception {
- PushOneCommit.Result r = createChange();
- setApiUser(testAccount);
- assertThat(getReviewerState(r.getChangeId(), testAccount.id)).isEmpty();
-
- // Exact request format made by GWT UI at ddc6b7160fe416fed9e7e3180489d44c82fd64f8.
- ReviewInput in = new ReviewInput();
- in.labels = ImmutableMap.of("Code-Review", (short) 0);
- in.drafts = DraftHandling.PUBLISH_ALL_REVISIONS;
- in.message = "comment";
- gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
-
- // If we're not reading from NoteDb, then the CCed user will be returned in the REVIEWER state.
- assertThat(getReviewerState(r.getChangeId(), testAccount.id))
- .hasValue(notesMigration.readChanges() ? CC : REVIEWER);
+ assertThat(getReviewerState(r.getChangeId(), testAccount.id())).hasValue(CC);
}
@Test
public void implicitlyAddReviewerOnVotingReview() throws Exception {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
@@ -1987,24 +1971,23 @@ public class ChangeIT extends AbstractDaemonTest {
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
assertThat(c.reviewers.get(REVIEWER).stream().map(ai -> ai._accountId).collect(toList()))
- .containsExactly(user.id.get());
+ .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.
- setApiUser(admin);
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).remove();
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).remove();
c = gApi.changes().id(r.getChangeId()).get();
assertThat(c.reviewers.values()).isEmpty();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
.review(new ReviewInput().message("hi"));
c = gApi.changes().id(r.getChangeId()).get();
- ReviewerState state = notesMigration.readChanges() ? CC : REVIEWER;
- assertThat(c.reviewers.get(state).stream().map(ai -> ai._accountId).collect(toList()))
- .containsExactly(user.id.get());
+ assertThat(c.reviewers.get(CC).stream().map(ai -> ai._accountId).collect(toList()))
+ .containsExactly(user.id().get());
}
@Test
@@ -2016,19 +1999,19 @@ public class ChangeIT extends AbstractDaemonTest {
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(admin.getId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(admin.id().get());
assertThat(c.reviewers).doesNotContainKey(CC);
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
c = gApi.changes().id(r.getChangeId()).get();
reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).hasSize(2);
Iterator<AccountInfo> reviewerIt = reviewers.iterator();
- assertThat(reviewerIt.next()._accountId).isEqualTo(admin.getId().get());
- assertThat(reviewerIt.next()._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewerIt.next()._accountId).isEqualTo(admin.id().get());
+ assertThat(reviewerIt.next()._accountId).isEqualTo(user.id().get());
assertThat(c.reviewers).doesNotContainKey(CC);
}
@@ -2038,7 +2021,7 @@ public class ChangeIT extends AbstractDaemonTest {
ChangeResource rsrc = parseResource(r);
String oldETag = rsrc.getETag();
- gApi.accounts().id(admin.id.get()).setStatus("new status");
+ accountOperations.account(admin.id()).forUpdate().status("new status").update();
rsrc = parseResource(r);
assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
}
@@ -2048,7 +2031,7 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(changeId).addReviewer(in);
sender.clear();
@@ -2063,7 +2046,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
}
@Test
@@ -2094,15 +2077,15 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
Map<String, Short> m =
- gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).votes();
+ gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).votes();
assertThat(m).hasSize(1);
assertThat(m).containsEntry("Code-Review", Short.valueOf((short) 2));
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.dislike());
- m = gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).votes();
+ m = gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).votes();
assertThat(m).hasSize(1);
assertThat(m).containsEntry("Code-Review", Short.valueOf((short) -1));
@@ -2128,35 +2111,32 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
- gApi.changes().id(changeId).addReviewer(user.getId().toString());
+ gApi.changes().id(changeId).addReviewer(user.id().toString());
- // ReviewerState will vary between ReviewDb and NoteDb; we just care that it
- // shows up somewhere.
- Iterable<AccountInfo> reviewers =
- Iterables.concat(gApi.changes().id(changeId).get().reviewers.values());
- assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+ ChangeInfo c = gApi.changes().id(changeId).get(ListChangesOption.DETAILED_LABELS);
+ assertThat(getReviewers(c.reviewers.get(CC))).isEmpty();
+ assertThat(getReviewers(c.reviewers.get(REVIEWER))).containsExactly(user.id());
sender.clear();
- gApi.changes().id(changeId).reviewer(user.getId().toString()).remove();
+ gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
assertThat(gApi.changes().id(changeId).get().reviewers).isEmpty();
assertThat(sender.getMessages()).hasSize(1);
Message message = sender.getMessages().get(0);
- assertThat(message.body()).contains("Removed reviewer " + user.fullName + ".");
+ assertThat(message.body()).contains("Removed reviewer " + user.fullName() + ".");
assertThat(message.body()).doesNotContain("with the following votes");
// Make sure the reviewer can still be added again.
- gApi.changes().id(changeId).addReviewer(user.getId().toString());
- reviewers = Iterables.concat(gApi.changes().id(changeId).get().reviewers.values());
- assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+ gApi.changes().id(changeId).addReviewer(user.id().toString());
+ c = gApi.changes().id(changeId).get();
+ assertThat(getReviewers(c.reviewers.get(CC))).isEmpty();
+ assertThat(getReviewers(c.reviewers.get(REVIEWER))).containsExactly(user.id());
// Remove again, and then try to remove once more to verify 404 is
// returned.
- gApi.changes().id(changeId).reviewer(user.getId().toString()).remove();
+ gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
exception.expect(ResourceNotFoundException.class);
- gApi.changes().id(changeId).reviewer(user.getId().toString()).remove();
+ gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
}
@Test
@@ -2174,30 +2154,30 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = r.getChangeId();
gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.recommend());
Collection<AccountInfo> reviewers = gApi.changes().id(changeId).get().reviewers.get(REVIEWER);
assertThat(reviewers).hasSize(2);
Iterator<AccountInfo> reviewerIt = reviewers.iterator();
- assertThat(reviewerIt.next()._accountId).isEqualTo(admin.getId().get());
- assertThat(reviewerIt.next()._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewerIt.next()._accountId).isEqualTo(admin.id().get());
+ assertThat(reviewerIt.next()._accountId).isEqualTo(user.id().get());
sender.clear();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
DeleteReviewerInput input = new DeleteReviewerInput();
if (!notify) {
input.notify = NotifyHandling.NONE;
}
- gApi.changes().id(changeId).reviewer(user.getId().toString()).remove(input);
+ gApi.changes().id(changeId).reviewer(user.id().toString()).remove(input);
if (notify) {
assertThat(sender.getMessages()).hasSize(1);
Message message = sender.getMessages().get(0);
assertThat(message.body())
- .contains("Removed reviewer " + user.fullName + " with the following votes");
- assertThat(message.body()).contains("* Code-Review+1 by " + user.fullName);
+ .contains("Removed reviewer " + user.fullName() + " with the following votes");
+ assertThat(message.body()).contains("* Code-Review+1 by " + user.fullName());
} else {
assertThat(sender.getMessages()).isEmpty();
}
@@ -2205,9 +2185,9 @@ public class ChangeIT extends AbstractDaemonTest {
reviewers = gApi.changes().id(changeId).get().reviewers.get(REVIEWER);
assertThat(reviewers).hasSize(1);
reviewerIt = reviewers.iterator();
- assertThat(reviewerIt.next()._accountId).isEqualTo(admin.getId().get());
+ assertThat(reviewerIt.next()._accountId).isEqualTo(admin.id().get());
- eventRecorder.assertReviewerDeletedEvents(changeId, user.email);
+ eventRecorder.assertReviewerDeletedEvents(changeId, user.email());
}
@Test
@@ -2216,10 +2196,10 @@ public class ChangeIT extends AbstractDaemonTest {
String changeId = r.getChangeId();
gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).remove();
+ gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove();
}
@Test
@@ -2227,14 +2207,14 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(changeId);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
approve(changeId);
gApi.changes().id(changeId).revision(r.getCommit().name()).submit();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("remove reviewer not permitted");
gApi.changes().id(r.getChangeId()).reviewer("self").remove();
@@ -2245,15 +2225,15 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(changeId);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).abandon();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).reviewer("self").remove();
- eventRecorder.assertReviewerDeletedEvents(changeId, user.email);
+ eventRecorder.assertReviewerDeletedEvents(changeId, user.email());
}
@Test
@@ -2261,17 +2241,17 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(changeId);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
approve(changeId);
gApi.changes().id(changeId).abandon();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).remove();
+ gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove();
}
@Test
@@ -2279,23 +2259,23 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote("Code-Review");
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote("Code-Review");
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message msg = messages.get(0);
- assertThat(msg.rcpt()).containsExactly(user.emailAddress);
- assertThat(msg.body()).contains(admin.fullName + " has removed a vote from this change.");
+ assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(msg.body()).contains(admin.fullName() + " has removed a vote from this change.");
assertThat(msg.body())
- .contains("Removed Code-Review+1 by " + user.fullName + " <" + user.email + ">\n");
+ .contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");
Map<String, Short> m =
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).votes();
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).votes();
// Dummy 0 approval on the change to block vote copying to this patch set.
assertThat(m).containsExactly("Code-Review", Short.valueOf((short) 0));
@@ -2303,10 +2283,10 @@ public class ChangeIT extends AbstractDaemonTest {
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
ChangeMessageInfo message = Iterables.getLast(c.messages);
- assertThat(message.author._accountId).isEqualTo(admin.getId().get());
+ assertThat(message.author._accountId).isEqualTo(admin.id().get());
assertThat(message.message).isEqualTo("Removed Code-Review+1 by User <user@example.com>\n");
assertThat(getReviewers(c.reviewers.get(REVIEWER)))
- .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+ .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
}
@Test
@@ -2314,15 +2294,15 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
DeleteVoteInput in = new DeleteVoteInput();
in.label = "Code-Review";
in.notify = NotifyHandling.NONE;
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
assertThat(sender.getMessages()).isEmpty();
}
@@ -2343,33 +2323,33 @@ public class ChangeIT extends AbstractDaemonTest {
.preferredEmail(email)
.fullname("User2")
.create();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.TO, new NotifyInfo(ImmutableList.of(email)));
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
assertNotifyTo(email, "User2");
// notify unrelated account as CC
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.CC, new NotifyInfo(ImmutableList.of(email)));
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
assertNotifyCc(email, "User2");
// notify unrelated account as BCC
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.BCC, new NotifyInfo(ImmutableList.of(email)));
- gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+ gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
assertNotifyBcc(email, "User2");
}
@@ -2378,10 +2358,10 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("delete vote not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).deleteVote("Code-Review");
+ gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).deleteVote("Code-Review");
}
@Test
@@ -2409,31 +2389,31 @@ public class ChangeIT extends AbstractDaemonTest {
// Reviewers should only be "admin"
ChangeInfo c = gApi.changes().id(changeId).get();
assertThat(getReviewers(c.reviewers.get(REVIEWER)))
- .containsExactlyElementsIn(ImmutableSet.of(admin.getId()));
+ .containsExactlyElementsIn(ImmutableSet.of(admin.id()));
assertThat(c.reviewers.get(CC)).isNull();
// Add the user as reviewer
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(changeId).addReviewer(in);
c = gApi.changes().id(changeId).get();
assertThat(getReviewers(c.reviewers.get(REVIEWER)))
- .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+ .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
// Approve the change as user, then remove the approval
// (only to confirm that the user does have Code-Review+2 permission)
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).revision(commit).review(ReviewInput.approve());
gApi.changes().id(changeId).revision(commit).review(ReviewInput.noScore());
// Submit the change
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).revision(commit).submit();
// User should still be on the change
c = gApi.changes().id(changeId).get();
assertThat(getReviewers(c.reviewers.get(REVIEWER)))
- .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+ .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
}
@Test
@@ -2444,7 +2424,7 @@ public class ChangeIT extends AbstractDaemonTest {
in.project = project.get();
ChangeInfo info = gApi.changes().create(in).get();
assertThat(info.project).isEqualTo(in.project);
- assertThat(info.branch).isEqualTo(in.branch);
+ assertThat(RefNames.fullName(info.branch)).isEqualTo(RefNames.fullName(in.branch));
assertThat(info.subject).isEqualTo(in.subject);
assertThat(Iterables.getOnlyElement(info.messages).message).isEqualTo("Uploaded patch set 1.");
}
@@ -2491,6 +2471,19 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void queryChangesNoLimit() throws Exception {
+ allowGlobalCapabilities(
+ SystemGroupBackend.REGISTERED_USERS, 0, 2, GlobalCapability.QUERY_LIMIT);
+ for (int i = 0; i < 3; i++) {
+ createChange();
+ }
+ List<ChangeInfo> resultsWithDefaultLimit = gApi.changes().query().get();
+ List<ChangeInfo> resultsWithNoLimit = gApi.changes().query().withNoLimit().get();
+ assertThat(resultsWithDefaultLimit).hasSize(2);
+ assertThat(resultsWithNoLimit.size()).isAtLeast(3);
+ }
+
+ @Test
public void queryChangesStart() throws Exception {
PushOneCommit.Result r1 = createChange();
createChange();
@@ -2533,7 +2526,7 @@ public class ChangeIT extends AbstractDaemonTest {
RevisionInfo rev = Iterables.getOnlyElement(result.revisions.values());
assertThat(rev._number).isEqualTo(r.getPatchSetId().get());
assertThat(rev.created).isNotNull();
- assertThat(rev.uploader._accountId).isEqualTo(admin.getId().get());
+ assertThat(rev.uploader._accountId).isEqualTo(admin.id().get());
assertThat(rev.ref).isEqualTo(r.getPatchSetId().toRefName());
assertThat(rev.actions).isNotEmpty();
}
@@ -2544,18 +2537,59 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(
Iterables.getOnlyElement(query("project:{" + project.get() + "} owner:self")).changeId)
.isEqualTo(r.getChangeId());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(query("owner:self project:{" + project.get() + "}")).isEmpty();
}
+ private static class OperatorModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(ChangeOperatorFactory.class)
+ .annotatedWith(Exports.named("mytopic"))
+ .toInstance((cqb, value) -> new MyTopicPredicate(value));
+ }
+
+ private static class MyTopicPredicate extends PostFilterPredicate<ChangeData> {
+ MyTopicPredicate(String value) {
+ super("mytopic", value);
+ }
+
+ @Override
+ public boolean match(ChangeData cd) {
+ return Objects.equals(cd.change().getTopic(), value);
+ }
+
+ @Override
+ public int getCost() {
+ return 2;
+ }
+ }
+ }
+
+ @Test
+ public void queryChangesPluginOperator() throws Exception {
+ PushOneCommit.Result r = createChange();
+ String query = "mytopic_myplugin:foo";
+ String expectedMessage = "Unsupported operator mytopic_myplugin:foo";
+ assertThatQueryException(query).hasMessageThat().isEqualTo(expectedMessage);
+
+ try (AutoCloseable ignored = installPlugin("myplugin", OperatorModule.class)) {
+ assertThat(query(query)).isEmpty();
+ gApi.changes().id(r.getChangeId()).topic("foo");
+ assertThat(query(query).stream().map(i -> i.changeId)).containsExactly(r.getChangeId());
+ }
+
+ assertThatQueryException(query).hasMessageThat().isEqualTo(expectedMessage);
+ }
+
@Test
public void checkReviewedFlagBeforeAndAfterReview() throws Exception {
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(get(r.getChangeId(), REVIEWED).reviewed).isNull();
revision(r).review(ReviewInput.recommend());
@@ -2576,7 +2610,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void editTopicWithoutPermissionNotAllowed() throws Exception {
PushOneCommit.Result r = createChange();
assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("edit topic name not permitted");
gApi.changes().id(r.getChangeId()).topic("mytopic");
@@ -2587,7 +2621,7 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("");
grant(project, "refs/heads/master", Permission.EDIT_TOPIC_NAME, false, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).topic("mytopic");
assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("mytopic");
}
@@ -2631,7 +2665,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void submitNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result r = createChange();
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("submit not permitted");
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
@@ -2642,7 +2676,7 @@ public class ChangeIT extends AbstractDaemonTest {
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);
- setApiUser(user);
+ 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);
}
@@ -2678,8 +2712,7 @@ public class ChangeIT extends AbstractDaemonTest {
r1.assertOkStatus();
PushOneCommit.Result r2 =
pushFactory
- .create(
- db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
.to("refs/for/master");
r2.assertOkStatus();
@@ -2726,16 +2759,9 @@ public class ChangeIT extends AbstractDaemonTest {
RegistrationHandle handle =
changeMessageModifiers.add(
"gerrit",
- new ChangeMessageModifier() {
- @Override
- public String onSubmit(
- String newCommitMessage,
- RevCommit original,
- RevCommit mergeTip,
- Branch.NameKey destination) {
- assertThat(original.getName()).isNotEqualTo(mergeTip.getName());
- return newCommitMessage + "Custom: " + destination.get();
- }
+ (newCommitMessage, original, mergeTip, destination) -> {
+ assertThat(original.getName()).isNotEqualTo(mergeTip.getName());
+ return newCommitMessage + "Custom: " + destination.get();
});
ChangeInfo actual;
try {
@@ -2766,7 +2792,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void defaultSearchDoesNotTouchDatabase() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
PushOneCommit.Result r1 = createChange();
gApi.changes()
.id(r1.getChangeId())
@@ -2776,9 +2802,8 @@ public class ChangeIT extends AbstractDaemonTest {
createChange();
- setApiUser(user);
- AcceptanceTestRequestScope.Context ctx = disableDb();
- try {
+ requestScopeOperations.setApiUser(user.id());
+ try (AutoCloseable ignored = disableNoteDb()) {
assertThat(
gApi.changes()
.query()
@@ -2789,8 +2814,6 @@ public class ChangeIT extends AbstractDaemonTest {
.withOption(REVIEWED)
.get())
.hasSize(2);
- } finally {
- enableDb(ctx);
}
}
@@ -2798,12 +2821,12 @@ public class ChangeIT extends AbstractDaemonTest {
public void votable() throws Exception {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
- gApi.changes().id(triplet).addReviewer(user.username);
+ gApi.changes().id(triplet).addReviewer(user.username());
ChangeInfo c = gApi.changes().id(triplet).get(DETAILED_LABELS);
LabelInfo codeReview = c.labels.get("Code-Review");
assertThat(codeReview.all).hasSize(1);
ApprovalInfo approval = codeReview.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.id.get());
+ assertThat(approval._accountId).isEqualTo(user.id().get());
assertThat(approval.value).isEqualTo(0);
try (ProjectConfigUpdate u = updateProject(project)) {
@@ -2815,7 +2838,7 @@ public class ChangeIT extends AbstractDaemonTest {
codeReview = c.labels.get("Code-Review");
assertThat(codeReview.all).hasSize(1);
approval = codeReview.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.id.get());
+ assertThat(approval._accountId).isEqualTo(user.id().get());
assertThat(approval.value).isNull();
}
@@ -2843,7 +2866,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void anonymousRestApi() throws Exception {
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
PushOneCommit.Result r = createChange();
ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -2862,12 +2885,9 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void noteDbCommitsOnPatchSetCreation() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
PushOneCommit.Result r = createChange();
pushFactory
- .create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "4711", r.getChangeId())
+ .create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "4711", r.getChangeId())
.to("refs/for/master")
.assertOkStatus();
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
@@ -2878,7 +2898,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(commitPatchSetCreation.getShortMessage()).isEqualTo("Create patch set 2");
PersonIdent expectedAuthor =
- changeNoteUtil.newIdent(getAccount(admin.id), c.updated, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id()), c.updated, serverIdent.get());
assertThat(commitPatchSetCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commitPatchSetCreation.getCommitterIdent())
.isEqualTo(new PersonIdent(serverIdent.get(), c.updated));
@@ -2886,7 +2906,8 @@ public class ChangeIT extends AbstractDaemonTest {
RevCommit commitChangeCreation = rw.parseCommit(commitPatchSetCreation.getParent(0));
assertThat(commitChangeCreation.getShortMessage()).isEqualTo("Create change");
- expectedAuthor = changeNoteUtil.newIdent(getAccount(admin.id), c.created, serverIdent.get());
+ expectedAuthor =
+ changeNoteUtil.newIdent(getAccount(admin.id()), c.created, serverIdent.get());
assertThat(commitChangeCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commitChangeCreation.getCommitterIdent())
.isEqualTo(new PersonIdent(serverIdent.get(), c.created));
@@ -2903,7 +2924,7 @@ public class ChangeIT extends AbstractDaemonTest {
in.newBranch = true;
ChangeInfo info = gApi.changes().create(in).get();
assertThat(info.project).isEqualTo(in.project);
- assertThat(info.branch).isEqualTo(in.branch);
+ assertThat(RefNames.fullName(info.branch)).isEqualTo(RefNames.fullName(in.branch));
assertThat(info.subject).isEqualTo(in.subject);
assertThat(Iterables.getOnlyElement(info.messages).message).isEqualTo("Uploaded patch set 1.");
}
@@ -2923,7 +2944,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void createNewPatchSetWithoutPermission() throws Exception {
// Create new project with clean permissions
- Project.NameKey p = createProject("addPatchSet1");
+ Project.NameKey p = projectOperations.newProject().create();
// Clone separate repositories of the same project as admin and as user
TestRepository<InMemoryRepository> adminTestRepo = cloneProject(p, admin);
@@ -2933,7 +2954,7 @@ public class ChangeIT extends AbstractDaemonTest {
block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
// Create change as admin
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), adminTestRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
PushOneCommit.Result r1 = push.to("refs/for/master");
r1.assertOkStatus();
@@ -2953,7 +2974,7 @@ public class ChangeIT extends AbstractDaemonTest {
TestRepository<?> userTestRepo = cloneProject(project, user);
// Create change as admin
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), adminTestRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
PushOneCommit.Result r1 = push.to("refs/for/master");
r1.assertOkStatus();
@@ -2969,7 +2990,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void createNewPatchSetAsOwnerWithoutPermission() throws Exception {
// Create new project with clean permissions
- Project.NameKey p = createProject("addPatchSet2");
+ Project.NameKey p = projectOperations.newProject().create();
// Clone separate repositories of the same project as admin and as user
TestRepository<?> adminTestRepo = cloneProject(project, admin);
@@ -2977,7 +2998,7 @@ public class ChangeIT extends AbstractDaemonTest {
block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
// Create change as admin
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), adminTestRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
PushOneCommit.Result r1 = push.to("refs/for/master");
r1.assertOkStatus();
@@ -3009,7 +3030,7 @@ public class ChangeIT extends AbstractDaemonTest {
createBranch("dev");
PushOneCommit.Result changeA =
pushFactory
- .create(db, user.getIdent(), testRepo, "change A", "A.txt", "A content")
+ .create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
.to("refs/heads/dev");
changeA.assertOkStatus();
MergeInput mergeInput = new MergeInput();
@@ -3045,7 +3066,7 @@ public class ChangeIT extends AbstractDaemonTest {
createBranch("dev");
PushOneCommit.Result changeA =
pushFactory
- .create(db, user.getIdent(), testRepo, "change A", "A.txt", "A content")
+ .create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
.to("refs/heads/dev");
changeA.assertOkStatus();
MergeInput mergeInput = new MergeInput();
@@ -3081,7 +3102,7 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(baseChange).setPrivate(true, "set private");
// Create the destination change on 'master' branch.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
testRepo.reset(initialHead);
String changeId = createChange().getChangeId();
@@ -3219,8 +3240,7 @@ public class ChangeIT extends AbstractDaemonTest {
testRepo.reset("config");
PushOneCommit push2 =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"Ignore Verified",
"rules.pl",
@@ -3266,8 +3286,7 @@ public class ChangeIT extends AbstractDaemonTest {
testRepo.reset("config");
PushOneCommit push2 =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"Configure Non-Author-Code-Review",
"rules.pl",
@@ -3304,10 +3323,10 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
ChangeInfo change = gApi.changes().id(r.getChangeId()).get();
@@ -3321,7 +3340,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void checkLabelsForAutoClosedChange() throws Exception {
PushOneCommit.Result r = createChange();
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result result = push.to("refs/heads/master");
result.assertOkStatus();
@@ -3340,13 +3359,13 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
- gApi.changes().id(triplet).addReviewer(user.username);
+ gApi.changes().id(triplet).addReviewer(user.username());
ChangeInfo c = gApi.changes().id(triplet).get(DETAILED_LABELS);
LabelInfo codeReview = c.labels.get("Code-Review");
assertThat(codeReview.all).hasSize(1);
ApprovalInfo approval = codeReview.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.id.get());
+ assertThat(approval._accountId).isEqualTo(user.id().get());
assertThat(approval.permittedVotingRange).isNotNull();
// default values
assertThat(approval.permittedVotingRange.min).isEqualTo(-1);
@@ -3367,7 +3386,7 @@ public class ChangeIT extends AbstractDaemonTest {
codeReview = c.labels.get("Code-Review");
assertThat(codeReview.all).hasSize(1);
approval = codeReview.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.id.get());
+ assertThat(approval._accountId).isEqualTo(user.id().get());
assertThat(approval.permittedVotingRange).isNotNull();
assertThat(approval.permittedVotingRange.min).isEqualTo(minPermittedValue);
assertThat(approval.permittedVotingRange.max).isEqualTo(maxPermittedValue);
@@ -3383,13 +3402,13 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String triplet = project.get() + "~master~" + r.getChangeId();
- gApi.changes().id(triplet).addReviewer(user.username);
+ gApi.changes().id(triplet).addReviewer(user.username());
ChangeInfo c = gApi.changes().id(triplet).get(DETAILED_LABELS);
LabelInfo codeReview = c.labels.get("Code-Review");
assertThat(codeReview.all).hasSize(1);
ApprovalInfo approval = codeReview.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.id.get());
+ assertThat(approval._accountId).isEqualTo(user.id().get());
assertThat(approval.permittedVotingRange).isNull();
}
@@ -3401,7 +3420,8 @@ public class ChangeIT extends AbstractDaemonTest {
ReviewInput input = ReviewInput.approve().label("Code-Style", 1);
gApi.changes().id(changeId).current().review(input);
- Map<String, Short> votes = gApi.changes().id(changeId).current().reviewer(admin.email).votes();
+ Map<String, Short> votes =
+ gApi.changes().id(changeId).current().reviewer(admin.email()).votes();
assertThat(votes.keySet()).containsExactly("Code-Review");
assertThat(votes.values()).containsExactly((short) 2);
}
@@ -3414,13 +3434,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();
- if (!notesMigration.readChanges()) {
- assertThat(votes.keySet()).containsExactly("Code-Review");
- assertThat(votes.values()).containsExactly((short) 0);
- } else {
- assertThat(votes).isEmpty();
- }
+ Map<String, Short> votes =
+ gApi.changes().id(changeId).current().reviewer(admin.email()).votes();
+ assertThat(votes).isEmpty();
}
@Test
@@ -3460,10 +3476,10 @@ public class ChangeIT extends AbstractDaemonTest {
String oldHead = getRemoteHead().name();
PushOneCommit.Result result1 =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
testRepo.reset(oldHead);
PushOneCommit.Result result2 =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
addComment(result1, "comment 1", true, false, null);
addComment(result2, "comment 2", true, true, null);
@@ -3481,8 +3497,7 @@ public class ChangeIT extends AbstractDaemonTest {
addPureRevertSubmitRule();
// Create a change that is not a revert of another change
- PushOneCommit.Result r1 =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
approve(r1.getChangeId());
exception.expect(ResourceConflictException.class);
@@ -3493,8 +3508,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void pureRevertFactBlocksSubmissionOfNonPureReverts() throws Exception {
- PushOneCommit.Result r1 =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
merge(r1);
addPureRevertSubmitRule();
@@ -3513,8 +3527,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void pureRevertFactAllowsSubmissionOfPureReverts() throws Exception {
// Create a change that we can later revert
- PushOneCommit.Result r1 =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
merge(r1);
addPureRevertSubmitRule();
@@ -3535,9 +3548,9 @@ public class ChangeIT extends AbstractDaemonTest {
.isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
for (com.google.gerrit.acceptance.TestAccount acc : ImmutableList.of(admin, user)) {
- setApiUser(acc);
+ requestScopeOperations.setApiUser(acc.id());
String newMessage =
- "modified commit by " + acc.username + "\n\nChange-Id: " + r.getChangeId() + "\n";
+ "modified commit by " + acc.username() + "\n\nChange-Id: " + r.getChangeId() + "\n";
gApi.changes().id(r.getChangeId()).setMessage(newMessage);
RevisionApi rApi = gApi.changes().id(r.getChangeId()).current();
assertThat(rApi.files().keySet()).containsExactly("/COMMIT_MSG", "a.txt");
@@ -3554,7 +3567,7 @@ public class ChangeIT extends AbstractDaemonTest {
// Move the change to WIP and edit the commit message again, to observe a
// different tag. Must switch to change owner to move into WIP.
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(r.getChangeId()).setWorkInProgress();
String newMessage = "modified commit in WIP change\n\nChange-Id: " + r.getChangeId() + "\n";
gApi.changes().id(r.getChangeId()).setMessage(newMessage);
@@ -3592,6 +3605,18 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
+ public void changeCommitMessageNullNotAllowed() throws Exception {
+ 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");
+ }
+
+ @Test
public void changeCommitMessageWithWrongChangeIdFails() throws Exception {
PushOneCommit.Result otherChange = createChange();
PushOneCommit.Result r = createChange();
@@ -3607,12 +3632,12 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void changeCommitMessageWithoutPermissionFails() throws Exception {
// Create new project with clean permissions
- Project.NameKey p = createProject("addPatchSetEdit");
+ 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);
// Create change as user
- PushOneCommit push = pushFactory.create(db, user.getIdent(), userTestRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
// Try to change the commit message
@@ -3642,7 +3667,7 @@ public class ChangeIT extends AbstractDaemonTest {
String subject = "A happy change " + smile;
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), testRepo, subject, FILE_NAME, FILE_CONTENT)
+ .create(admin.newIdent(), testRepo, subject, FILE_NAME, FILE_CONTENT)
.to("refs/for/master");
r.assertOkStatus();
String id = r.getChangeId();
@@ -3764,7 +3789,7 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void pureRevertThrowsExceptionWhenChangeIsNotARevertAndNoIdProvided() throws Exception {
exception.expect(BadRequestException.class);
- exception.expectMessage("no ID was provided and change isn't a revert");
+ exception.expectMessage("revertOf not set");
gApi.changes().id(createChange().getChangeId()).pureRevert();
}
@@ -3800,7 +3825,7 @@ public class ChangeIT extends AbstractDaemonTest {
u.save();
}
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
@@ -3814,13 +3839,13 @@ public class ChangeIT extends AbstractDaemonTest {
input.label(label, 1);
gApi.changes().id(changeId).current().review(input);
- assertThat(gApi.changes().id(changeId).current().reviewer(user.email).votes().keySet())
+ assertThat(gApi.changes().id(changeId).current().reviewer(user.email()).votes().keySet())
.containsExactly(codeReviewLabel, label);
- assertThat(gApi.changes().id(changeId).current().reviewer(user.email).votes().values())
+ assertThat(gApi.changes().id(changeId).current().reviewer(user.email()).votes().values())
.containsExactly((short) 2, (short) 1);
assertThat(gApi.changes().id(changeId).get().submittable).isTrue();
- setApiUser(admin);
+ 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/*");
@@ -3832,16 +3857,16 @@ public class ChangeIT extends AbstractDaemonTest {
}
// Verify user's new permitted range.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
change = gApi.changes().id(changeId).get();
assertPermitted(change, label);
assertPermitted(change, codeReviewLabel, -1, 0, 1);
- assertThat(gApi.changes().id(changeId).current().reviewer(user.email).votes().values())
+ assertThat(gApi.changes().id(changeId).current().reviewer(user.email()).votes().values())
.containsExactly((short) 2, (short) 1);
assertThat(gApi.changes().id(changeId).get().submittable).isTrue();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).current().submit();
}
@@ -3873,7 +3898,7 @@ public class ChangeIT extends AbstractDaemonTest {
private String createNewChange() throws Exception {
TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
PushOneCommit.Result result =
- pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
String changeId = result.getChangeId();
return changeId;
}
@@ -3910,6 +3935,9 @@ public class ChangeIT extends AbstractDaemonTest {
}
private static Iterable<Account.Id> getReviewers(Collection<AccountInfo> r) {
+ if (r == null) {
+ return ImmutableList.of();
+ }
return Iterables.transform(r, a -> new Account.Id(a._accountId));
}
@@ -3931,7 +3959,7 @@ public class ChangeIT extends AbstractDaemonTest {
private void setChangeStatus(Change.Id id, Change.Status newStatus) throws Exception {
try (BatchUpdate batchUpdate =
- batchUpdateFactory.create(db, project, atrScope.get().getUser(), TimeUtil.nowTs())) {
+ batchUpdateFactory.create(project, atrScope.get().getUser(), TimeUtil.nowTs())) {
batchUpdate.addOp(id, new ChangeStatusUpdateOp(newStatus));
batchUpdate.execute();
}
@@ -3976,13 +4004,13 @@ public class ChangeIT extends AbstractDaemonTest {
}
private void modifySubmitRules(String newContent) throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> testRepo = new TestRepository<>((InMemoryRepository) repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> testRepo = new TestRepository<>(repo)) {
testRepo
.branch(RefNames.REFS_CONFIG)
.commit()
- .author(admin.getIdent())
- .committer(admin.getIdent())
+ .author(admin.newIdent())
+ .committer(admin.newIdent())
.add("rules.pl", newContent)
.message("Modify rules.pl")
.create();
@@ -3996,8 +4024,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void trackingIds() throws Exception {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT + "\n\n" + "Bug:JRA001",
PushOneCommit.FILE_NAME,
@@ -4046,20 +4073,20 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
in = new AddReviewerInput();
in.reviewer = email;
gApi.changes().id(r.getChangeId()).addReviewer(in);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).ignore(true);
assertThat(gApi.changes().id(r.getChangeId()).ignored()).isTrue();
// New patch set notification is not sent to users ignoring the change
sender.clear();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
amendChange(r.getChangeId());
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
@@ -4080,7 +4107,7 @@ public class ChangeIT extends AbstractDaemonTest {
assertThat(messages).hasSize(1);
assertThat(messages.get(0).rcpt()).containsExactly(address);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).ignore(false);
assertThat(gApi.changes().id(r.getChangeId()).ignored()).isFalse();
}
@@ -4098,7 +4125,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void cannotIgnoreStarredChange() throws Exception {
String changeId = createChange().getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.accounts().self().starChange(changeId);
assertThat(gApi.changes().id(changeId).get().starred).isTrue();
@@ -4116,7 +4143,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void cannotStarIgnoredChange() throws Exception {
String changeId = createChange().getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).ignore(true);
assertThat(gApi.changes().id(changeId).ignored()).isTrue();
@@ -4137,31 +4164,31 @@ public class ChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isNull();
gApi.changes().id(r.getChangeId()).markAsReviewed(true);
assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isTrue();
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
sender.clear();
amendChange(r.getChangeId());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isNull();
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
- assertThat(messages.get(0).rcpt()).containsExactly(user.emailAddress);
+ assertThat(messages.get(0).rcpt()).containsExactly(user.getEmailAddress());
}
@Test
public void cannotSetUnreviewedLabelForPatchSetThatAlreadyHasReviewedLabel() throws Exception {
String changeId = createChange().getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).markAsReviewed(true);
assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
@@ -4186,7 +4213,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void cannotSetReviewedLabelForPatchSetThatAlreadyHasUnreviewedLabel() throws Exception {
String changeId = createChange().getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).markAsReviewed(false);
assertThat(gApi.changes().id(changeId).get().reviewed).isNull();
@@ -4211,7 +4238,7 @@ public class ChangeIT extends AbstractDaemonTest {
public void setReviewedAndUnreviewedLabelsForDifferentPatchSets() throws Exception {
String changeId = createChange().getChangeId();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).markAsReviewed(true);
assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
@@ -4240,11 +4267,25 @@ public class ChangeIT extends AbstractDaemonTest {
@Test
public void changeDetailsDoesNotRequireIndex() throws Exception {
+ // This set of options must be kept in sync with gr-rest-api-interface.js
+ Set<ListChangesOption> options =
+ ImmutableSet.of(
+ ListChangesOption.ALL_COMMITS,
+ ListChangesOption.ALL_REVISIONS,
+ ListChangesOption.CHANGE_ACTIONS,
+ ListChangesOption.CURRENT_ACTIONS,
+ ListChangesOption.DETAILED_LABELS,
+ ListChangesOption.DOWNLOAD_COMMANDS,
+ ListChangesOption.MESSAGES,
+ ListChangesOption.SUBMITTABLE,
+ ListChangesOption.WEB_LINKS,
+ ListChangesOption.SKIP_MERGEABLE);
+
PushOneCommit.Result change = createChange();
int number = gApi.changes().id(change.getChangeId()).get()._number;
- try (AutoCloseable ctx = disableChangeIndex()) {
- assertThat(gApi.changes().id(project.get(), number).get(ImmutableSet.of()).changeId)
+ try (AutoCloseable ignored = disableChangeIndex()) {
+ assertThat(gApi.changes().id(project.get(), number).get(options).changeId)
.isEqualTo(change.getChangeId());
}
}
@@ -4256,4 +4297,13 @@ public class ChangeIT extends AbstractDaemonTest {
private BranchApi createBranch(String branch) throws Exception {
return createBranch(new Branch.NameKey(project, branch));
}
+
+ private ThrowableSubject assertThatQueryException(String query) throws Exception {
+ try {
+ query(query);
+ } catch (BadRequestException e) {
+ return assertThat(e);
+ }
+ throw new AssertionError("expected BadRequestException");
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java
index e0fc358b7f..4551f900c7 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIdIT.java
@@ -18,17 +18,20 @@ 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.testsuite.project.ProjectOperations;
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;
@NoHttpd
public class ChangeIdIT extends AbstractDaemonTest {
private ChangeInfo changeInfo;
+ @Inject private ProjectOperations projectOperations;
@Before
public void setup() throws Exception {
@@ -43,7 +46,7 @@ public class ChangeIdIT extends AbstractDaemonTest {
@Test
public void projectChangeNumberReturnsChangeWhenProjectContainsSlashes() throws Exception {
- Project.NameKey p = createProject("foo/bar");
+ Project.NameKey p = projectOperations.newProject().create();
ChangeInfo ci = gApi.changes().create(new ChangeInput(p.get(), "master", "msg")).get();
ChangeApi cApi = gApi.changes().id(p.get(), ci._number);
assertThat(cApi.get().changeId).isEqualTo(ci.changeId);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
index f087b78c07..48c46d2976 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java
@@ -29,9 +29,13 @@ 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.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
public class ChangeSubmitRequirementIT extends AbstractDaemonTest {
@@ -57,22 +61,63 @@ public class ChangeSubmitRequirementIT extends AbstractDaemonTest {
};
}
+ @Inject CustomSubmitRule rule;
+
@Test
- public void checkSubmitRequirementIsPropagated() throws Exception {
+ public void submitRequirementIsPropagated() throws Exception {
+ rule.block(false);
PushOneCommit.Result r = createChange();
ChangeInfo result = gApi.changes().id(r.getChangeId()).get();
+ assertThat(result.requirements).isEmpty();
+
+ rule.block(true);
+ result = gApi.changes().id(r.getChangeId()).get();
assertThat(result.requirements).containsExactly(reqInfo);
}
+ @Test
+ public void submitRequirementIsPropagatedInQuery() throws Exception {
+ rule.block(false);
+ PushOneCommit.Result r = createChange();
+
+ String query = "status:open project:" + project.get();
+ List<ChangeInfo> result = gApi.changes().query(query).get();
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).requirements).isEmpty();
+
+ // Submit rule behavior is changed, but the query still returns
+ // the previous result from the index
+ rule.block(true);
+ result = gApi.changes().query(query).get();
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).requirements).isEmpty();
+
+ // The submit rule result is updated after the change is reindexed
+ gApi.changes().id(r.getChangeId()).index();
+ result = gApi.changes().query(query).get();
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).requirements).containsExactly(reqInfo);
+ }
+
+ @Singleton
private static class CustomSubmitRule implements SubmitRule {
+ private final AtomicBoolean block = new AtomicBoolean(true);
+
+ public void block(boolean block) {
+ this.block.set(block);
+ }
+
@Override
public Collection<SubmitRecord> evaluate(ChangeData changeData, SubmitRuleOptions options) {
- SubmitRecord record = new SubmitRecord();
- record.labels = new ArrayList<>();
- record.status = SubmitRecord.Status.NOT_READY;
- record.requirements = ImmutableList.of(req);
- return ImmutableList.of(record);
+ 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 ImmutableList.of();
}
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java b/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
index ffb8b34c8a..ae88afdab2 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
@@ -54,7 +54,7 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
@GerritConfig(name = "change.disablePrivateChanges", value = "true")
public void pushPrivatesWithDisablePrivateChangesTrue() throws Exception {
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%private");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
result.assertErrorStatus();
}
@@ -64,11 +64,11 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
public void pushDraftsWithDisablePrivateChangesTrue() throws Exception {
RevCommit initialHead = getRemoteHead();
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%draft");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
result.assertErrorStatus();
testRepo.reset(initialHead);
- result = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/drafts/master");
+ result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
result.assertErrorStatus();
}
@@ -76,7 +76,7 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
@GerritConfig(name = "change.disablePrivateChanges", value = "true")
public void pushWithDisablePrivateChangesTrue() throws Exception {
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
result.assertOkStatus();
assertThat(result.getChange().change().isPrivate()).isFalse();
}
@@ -85,7 +85,7 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
@GerritConfig(name = "change.allowDrafts", value = "true")
public void pushPrivatesWithDisablePrivateChangesFalse() throws Exception {
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%private");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
assertThat(result.getChange().change().isPrivate()).isTrue();
}
@@ -94,11 +94,11 @@ public class DisablePrivateChangesIT extends AbstractDaemonTest {
public void pushDraftsWithDisablePrivateChangesFalse() throws Exception {
RevCommit initialHead = getRemoteHead();
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%draft");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
assertThat(result.getChange().change().isPrivate()).isTrue();
testRepo.reset(initialHead);
- result = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/drafts/master");
+ result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
assertThat(result.getChange().change().isPrivate()).isTrue();
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java b/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
index 1acd71c418..a08d417455 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
@@ -55,8 +55,7 @@ public class MergeListIT extends AbstractDaemonTest {
PushOneCommit.Result gp1 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"grand parent 1",
ImmutableMap.of("foo", "foo-1.1", "bar", "bar-1.1"))
@@ -66,8 +65,7 @@ public class MergeListIT extends AbstractDaemonTest {
PushOneCommit.Result p1 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"parent 1",
ImmutableMap.of("foo", "foo-1.2", "bar", "bar-1.2"))
@@ -80,8 +78,7 @@ public class MergeListIT extends AbstractDaemonTest {
PushOneCommit.Result gp2 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"grand parent 2",
ImmutableMap.of("foo", "foo-2.1", "bar", "bar-2.1"))
@@ -91,8 +88,7 @@ public class MergeListIT extends AbstractDaemonTest {
PushOneCommit.Result p2 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"parent 2",
ImmutableMap.of("foo", "foo-2.2", "bar", "bar-2.2"))
@@ -101,11 +97,7 @@ public class MergeListIT extends AbstractDaemonTest {
PushOneCommit m =
pushFactory.create(
- db,
- admin.getIdent(),
- testRepo,
- "merge",
- ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
+ admin.newIdent(), testRepo, "merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
PushOneCommit.Result result = m.to("refs/for/master");
result.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PluginFieldsIT.java b/javatests/com/google/gerrit/acceptance/api/change/PluginFieldsIT.java
new file mode 100644
index 0000000000..d5089ff7d6
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/change/PluginFieldsIT.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.acceptance.api.change;
+
+import com.google.gerrit.acceptance.AbstractPluginFieldsTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.server.change.ChangeAttributeFactory;
+import com.google.inject.AbstractModule;
+import org.junit.Test;
+
+@NoHttpd
+public class PluginFieldsIT extends AbstractPluginFieldsTest {
+ // No tests for /detail via the extension API, since the extension API doesn't have that method.
+
+ @Test
+ public void queryChangeWithNullAttribute() throws Exception {
+ getChangeWithNullAttribute(
+ id -> pluginInfoFromSingletonList(gApi.changes().query(id.toString()).get()));
+ }
+
+ @Test
+ public void getChangeWithNullAttribute() throws Exception {
+ getChangeWithNullAttribute(
+ id -> pluginInfoFromChangeInfo(gApi.changes().id(id.toString()).get()));
+ }
+
+ @Test
+ public void queryChangeWithSimpleAttribute() throws Exception {
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromSingletonList(gApi.changes().query(id.toString()).get()));
+ }
+
+ @Test
+ public void getChangeWithSimpleAttribute() throws Exception {
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromChangeInfo(gApi.changes().id(id.toString()).get()));
+ }
+
+ @Test
+ public void queryChangeWithOption() throws Exception {
+ getChangeWithOption(
+ id -> pluginInfoFromSingletonList(gApi.changes().query(id.toString()).get()),
+ (id, opts) ->
+ pluginInfoFromSingletonList(
+ gApi.changes().query(id.toString()).withPluginOptions(opts).get()));
+ }
+
+ @Test
+ public void getChangeWithOption() throws Exception {
+ getChangeWithOption(
+ id -> pluginInfoFromChangeInfo(gApi.changes().id(id.get()).get()),
+ (id, opts) -> pluginInfoFromChangeInfo(gApi.changes().id(id.get()).get(opts)));
+ }
+
+ static class SimpleAttributeWithExplicitExportModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(ChangeAttributeFactory.class)
+ .annotatedWith(Exports.named("simple"))
+ .toInstance((cd, bp, p) -> new MyInfo("change " + cd.getId()));
+ }
+ }
+
+ @Test
+ public void getChangeWithSimpleAttributeWithExplicitExport() throws Exception {
+ // For backwards compatibility with old plugins, allow modules to bind into the
+ // DynamicSet<ChangeAttributeFactory> as if it were a DynamicMap. We only need one variant of
+ // this test to prove that the mapping works.
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromChangeInfo(gApi.changes().id(id.toString()).get()),
+ SimpleAttributeWithExplicitExportModule.class);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
new file mode 100644
index 0000000000..bfcb1a8358
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
@@ -0,0 +1,261 @@
+// 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.server.group.SystemGroupBackend.REGISTERED_USERS;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.common.data.Permission;
+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;
+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.inject.Inject;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.junit.Test;
+
+public class PrivateChangeIT extends AbstractDaemonTest {
+
+ @Inject private RequestScopeOperations requestScopeOperations;
+
+ @Test
+ public void setPrivateByOwner() throws Exception {
+ TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
+ PushOneCommit.Result result =
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
+
+ requestScopeOperations.setApiUser(user.id());
+ String changeId = result.getChangeId();
+ assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
+
+ gApi.changes().id(changeId).setPrivate(true, null);
+ ChangeInfo info = gApi.changes().id(changeId).get();
+ assertThat(info.isPrivate).isTrue();
+ assertThat(Iterables.getLast(info.messages).message).isEqualTo("Set private");
+ assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_SET_PRIVATE);
+
+ gApi.changes().id(changeId).setPrivate(false, null);
+ info = gApi.changes().id(changeId).get();
+ assertThat(info.isPrivate).isNull();
+ assertThat(Iterables.getLast(info.messages).message).isEqualTo("Unset private");
+ assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_UNSET_PRIVATE);
+
+ String msg = "This is a security fix that must not be public.";
+ gApi.changes().id(changeId).setPrivate(true, msg);
+ info = gApi.changes().id(changeId).get();
+ assertThat(info.isPrivate).isTrue();
+ assertThat(Iterables.getLast(info.messages).message).isEqualTo("Set private\n\n" + msg);
+ assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_SET_PRIVATE);
+
+ msg = "After this security fix has been released we can make it public now.";
+ gApi.changes().id(changeId).setPrivate(false, msg);
+ info = gApi.changes().id(changeId).get();
+ assertThat(info.isPrivate).isNull();
+ assertThat(Iterables.getLast(info.messages).message).isEqualTo("Unset private\n\n" + msg);
+ assertThat(Iterables.getLast(info.messages).tag).contains(ChangeMessagesUtil.TAG_UNSET_PRIVATE);
+ }
+
+ @Test
+ public void cannotSetMergedChangePrivate() throws Exception {
+ PushOneCommit.Result result = createChange();
+ approve(result.getChangeId());
+ merge(result);
+
+ 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);
+ }
+
+ @Test
+ public void cannotSetAbandonedChangePrivate() throws Exception {
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+
+ 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);
+ }
+
+ @Test
+ public void administratorCanSetUserChangePrivate() throws Exception {
+ TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
+ PushOneCommit.Result result =
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
+
+ String changeId = result.getChangeId();
+ assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
+
+ gApi.changes().id(changeId).setPrivate(true, null);
+ requestScopeOperations.setApiUser(user.id());
+ ChangeInfo info = gApi.changes().id(changeId).get();
+ assertThat(info.isPrivate).isTrue();
+ }
+
+ @Test
+ 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);
+ }
+
+ @Test
+ public void accessPrivate() throws Exception {
+ TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
+ PushOneCommit.Result result =
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
+
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(result.getChangeId()).setPrivate(true, null);
+ // Owner can always access its private changes.
+ assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
+
+ // Add admin as a reviewer.
+ gApi.changes().id(result.getChangeId()).addReviewer(admin.id().toString());
+
+ // This change should be visible for admin as a reviewer.
+ requestScopeOperations.setApiUser(admin.id());
+ assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
+
+ // Remove admin from reviewers.
+ 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());
+ }
+
+ @Test
+ public void privateChangeOfOtherUserCanBeAccessedWithPermission() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).setPrivate(true, null);
+
+ allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
+ requestScopeOperations.setApiUser(user.id());
+ assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
+ }
+
+ @Test
+ public void administratorCanUnmarkPrivateAfterMerging() throws Exception {
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+ merge(result);
+ markMergedChangePrivate(new Change.Id(gApi.changes().id(changeId).get()._number));
+
+ gApi.changes().id(changeId).setPrivate(false, null);
+ assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
+ }
+
+ @Test
+ public void ownerCannotMarkPrivateAfterMerging() throws Exception {
+ TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
+ PushOneCommit.Result result =
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
+
+ String changeId = result.getChangeId();
+ assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
+
+ merge(result);
+
+ requestScopeOperations.setApiUser(user.id());
+ exception.expect(AuthException.class);
+ exception.expectMessage("not allowed to mark private");
+ gApi.changes().id(changeId).setPrivate(true, null);
+ }
+
+ @Test
+ public void mergingPrivateChangePublishesIt() throws Exception {
+ PushOneCommit.Result result = createChange();
+ gApi.changes().id(result.getChangeId()).setPrivate(true);
+ assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
+
+ approve(result.getChangeId());
+ merge(result);
+
+ assertThat(gApi.changes().id(result.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED);
+ assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isNull();
+ }
+
+ @Test
+ public void ownerCanUnmarkPrivateAfterMerging() throws Exception {
+ TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
+ PushOneCommit.Result result =
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
+
+ String changeId = result.getChangeId();
+ gApi.changes().id(changeId).addReviewer(admin.id().toString());
+ merge(result);
+ markMergedChangePrivate(new Change.Id(gApi.changes().id(changeId).get()._number));
+
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(changeId).setPrivate(false, null);
+ assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
+ }
+
+ @Test
+ public void mergingPrivateChangeThroughGitPublishesIt() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.changes().id(r.getChangeId()).setPrivate(true);
+
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
+ PushOneCommit.Result result = push.to("refs/heads/master");
+ result.assertOkStatus();
+
+ assertThat(gApi.changes().id(r.getChangeId()).get().isPrivate).isNull();
+ }
+
+ private void markMergedChangePrivate(Change.Id changeId) throws Exception {
+ try (BatchUpdate u =
+ batchUpdateFactory.create(
+ project, identifiedUserFactory.create(admin.id()), TimeUtil.nowTs())) {
+ u.addOp(
+ changeId,
+ new BatchUpdateOp() {
+ @Override
+ public boolean updateChange(ChangeContext ctx) {
+ ctx.getChange().setPrivate(true);
+ ChangeUpdate update = ctx.getUpdate(ctx.getChange().currentPatchSetId());
+ ctx.getChange().setPrivate(true);
+ ctx.getChange().setLastUpdatedOn(ctx.getWhen());
+ update.setPrivate(true);
+ return true;
+ }
+ })
+ .execute();
+ }
+ assertThat(gApi.changes().id(changeId.get()).get().isPrivate).isTrue();
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index d632113325..d53b305db3 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -34,6 +34,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
@@ -46,6 +47,7 @@ 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.inject.Inject;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
@@ -58,6 +60,7 @@ import org.junit.Test;
@NoHttpd
public class StickyApprovalsIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
@Before
public void setup() throws Exception {
@@ -436,8 +439,8 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
commitBuilder
.message("New subject " + System.nanoTime())
- .author(admin.getIdent())
- .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()));
+ .author(admin.newIdent())
+ .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()));
commitBuilder.create();
GitUtil.pushHead(testRepo, "refs/for/master", false);
assertThat(getChangeKind(changeId)).isEqualTo(NO_CODE_CHANGE);
@@ -451,8 +454,8 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
commitBuilder
.message(commitMessage)
- .author(admin.getIdent())
- .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()));
+ .author(admin.newIdent())
+ .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()));
commitBuilder.create();
GitUtil.pushHead(testRepo, "refs/for/master", false);
assertThat(getChangeKind(changeId)).isEqualTo(NO_CHANGE);
@@ -461,8 +464,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
private void rework(String changeId) throws Exception {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
@@ -473,12 +475,11 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
}
private void trivialRebase(String changeId) throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
testRepo.reset(getRemoteHead());
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"Other Change",
"a" + System.nanoTime() + ".txt",
@@ -504,7 +505,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
testRepo.reset(parent1.getCommit());
- PushOneCommit merge = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit merge = pushFactory.create(admin.newIdent(), testRepo);
merge.setParents(ImmutableList.of(parent1.getCommit(), parent2.getCommit()));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
@@ -521,7 +522,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
testRepo.reset(parent1);
PushOneCommit.Result newParent1 = createChange("new parent 1", "p1-1.txt", "content 1-1");
- PushOneCommit merge = pushFactory.create(db, admin.getIdent(), testRepo, changeId);
+ PushOneCommit merge = pushFactory.create(admin.newIdent(), testRepo, changeId);
merge.setParents(ImmutableList.of(newParent1.getCommit(), commitParent2));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
@@ -539,7 +540,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
testRepo.reset(parent2);
PushOneCommit.Result newParent2 = createChange("new parent 2", "p2-2.txt", "content 2-2");
- PushOneCommit merge = pushFactory.create(db, admin.getIdent(), testRepo, changeId);
+ PushOneCommit merge = pushFactory.create(admin.newIdent(), testRepo, changeId);
merge.setParents(ImmutableList.of(commitParent1, newParent2.getCommit()));
PushOneCommit.Result result = merge.to("refs/for/master");
result.assertOkStatus();
@@ -563,8 +564,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"other.txt",
@@ -591,21 +591,21 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
}
private void vote(TestAccount user, String changeId, String label, int vote) throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).current().review(new ReviewInput().label(label, vote));
}
private void vote(TestAccount user, String changeId, int codeReviewVote, int verifiedVote)
throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ReviewInput in =
new ReviewInput().label("Code-Review", codeReviewVote).label("Verified", verifiedVote);
gApi.changes().id(changeId).current().review(in);
}
private void deleteVote(TestAccount user, String changeId, String label) throws Exception {
- setApiUser(user);
- gApi.changes().id(changeId).reviewer(user.getId().toString()).deleteVote(label);
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(changeId).reviewer(user.id().toString()).deleteVote(label);
}
private void assertVotes(ChangeInfo c, TestAccount user, int codeReviewVote, int verifiedVote) {
@@ -623,7 +623,7 @@ public class StickyApprovalsIT extends AbstractDaemonTest {
Integer vote = 0;
if (c.labels.get(label) != null && c.labels.get(label).all != null) {
for (ApprovalInfo approval : c.labels.get(label).all) {
- if (approval._accountId == user.id.get()) {
+ if (approval._accountId == user.id().get()) {
vote = approval.value;
break;
}
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
index 507205d972..6c0e9ab51b 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
@@ -137,8 +137,7 @@ public class SubmitTypeRuleIT extends AbstractDaemonTest {
private PushOneCommit.Result createChange(String dest, String subject) throws Exception {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
subject,
"file" + fileCounter.incrementAndGet(),
diff --git a/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java b/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.java
new file mode 100644
index 0000000000..b6d2712539
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/config/TopMenusIT.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.acceptance.api.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.webui.TopMenu;
+import com.google.gerrit.extensions.webui.TopMenu.MenuEntry;
+import com.google.inject.AbstractModule;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+@TestPlugin(
+ name = "test-topmenus",
+ sysModule = "com.google.gerrit.acceptance.api.config.TopMenusIT$Module")
+public class TopMenusIT extends LightweightPluginDaemonTest {
+
+ static final TopMenu.MenuEntry TEST_MENU_ENTRY =
+ new TopMenu.MenuEntry("MyMenu", Collections.emptyList());
+
+ public static class Module extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ DynamicSet.bind(binder(), TopMenu.class).to(TopMenuTest.class);
+ }
+ }
+
+ public static class TopMenuTest implements TopMenu {
+
+ @Override
+ public List<MenuEntry> getEntries() {
+ return Arrays.asList(TEST_MENU_ENTRY);
+ }
+ }
+
+ @Test
+ public void topMenuShouldReturnOneEntry() throws RestApiException {
+ List<MenuEntry> topMenuItems = gApi.config().server().topMenus();
+ assertThat(topMenuItems).containsExactly(TEST_MENU_ENTRY);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/group/BUILD b/javatests/com/google/gerrit/acceptance/api/group/BUILD
index f65db0d356..1ba1138481 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/group/BUILD
@@ -21,7 +21,6 @@ java_library(
deps = [
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/server",
- "//lib:gwtorm",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
index a66486915d..d7d311ba39 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
@@ -15,11 +15,12 @@
package com.google.gerrit.acceptance.api.group;
import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.server.group.testing.InternalGroupSubject.internalGroups;
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.common.errors.NoSuchGroupException;
+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;
@@ -35,7 +36,6 @@ import com.google.gerrit.server.query.group.InternalGroupQuery;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.gerrit.truth.ListSubject;
import com.google.gerrit.truth.OptionalSubject;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -157,17 +157,17 @@ public class GroupIndexerIT {
private void updateGroupWithoutCacheOrIndex(
AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+ throws NoSuchGroupException, IOException, ConfigInvalidException {
groupsUpdate.updateGroupInNoteDb(groupUuid, groupUpdate);
}
private static OptionalSubject<InternalGroupSubject, InternalGroup> assertThatGroup(
Optional<InternalGroup> updatedGroup) {
- return assertThat(updatedGroup, InternalGroupSubject::assertThat);
+ return assertThat(updatedGroup, internalGroups());
}
private static ListSubject<InternalGroupSubject, InternalGroup> assertThatGroups(
List<InternalGroup> parentGroups) {
- return assertThat(parentGroups, InternalGroupSubject::assertThat);
+ return assertThat(parentGroups, internalGroups());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
index 87a566e237..e269e682f5 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
@@ -20,6 +20,7 @@ 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.common.data.GlobalCapability;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
@@ -30,6 +31,7 @@ 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;
+import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.RefRename;
@@ -47,6 +49,8 @@ import org.junit.Test;
@Sandboxed
@NoHttpd
public class GroupsConsistencyIT extends AbstractDaemonTest {
+
+ @Inject protected GroupOperations groupOperations;
private GroupInfo gAdmin;
private GroupInfo g1;
private GroupInfo g2;
@@ -57,11 +61,11 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
public void basicSetup() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- String name1 = createGroup("g1");
- String name2 = createGroup("g2");
+ String name1 = groupOperations.newGroup().name("g1").create().get();
+ String name2 = groupOperations.newGroup().name("g2").create().get();
- gApi.groups().id(name1).addMembers(user.fullName);
- gApi.groups().id(name2).addMembers(admin.fullName);
+ gApi.groups().id(name1).addMembers(user.fullName());
+ gApi.groups().id(name2).addMembers(admin.fullName());
gApi.groups().id(name1).addGroups(name2);
this.g1 = gApi.groups().id(name1).detail();
@@ -218,7 +222,7 @@ public class GroupsConsistencyIT extends AbstractDaemonTest {
@Test
public void cyclicSubgroup() throws Exception {
updateGroupFile(RefNames.refsGroups(new AccountGroup.UUID(g1.id)), "subgroups", g1.id + "\n");
- assertWarning("cyclic");
+ assertWarning("cycle");
}
private void assertError(String msg) 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 e2d390e6fd..47ac7a9f9c 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -28,8 +28,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.toList;
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.GerritConfig;
@@ -40,6 +42,8 @@ import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+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;
@@ -68,7 +72,6 @@ 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.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.GroupIncludeCache;
import com.google.gerrit.server.group.InternalGroup;
@@ -81,6 +84,9 @@ import com.google.gerrit.server.group.db.InternalGroupCreation;
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;
@@ -89,14 +95,13 @@ import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.sql.Timestamp;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
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;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -117,16 +122,18 @@ import org.junit.Test;
@NoHttpd
public class GroupsIT extends AbstractDaemonTest {
- @Inject private Groups groups;
@Inject @ServerInitiated private GroupsUpdate groupsUpdate;
+ @Inject private AccountOperations accountOperations;
+ @Inject private DynamicSet<GroupIndexedListener> groupIndexedListeners;
@Inject private GroupIncludeCache groupIncludeCache;
- @Inject private StalenessChecker stalenessChecker;
@Inject private GroupIndexer groupIndexer;
+ @Inject private GroupOperations groupOperations;
+ @Inject private Groups groups;
@Inject private GroupsConsistencyChecker consistencyChecker;
@Inject private PeriodicGroupIndexer slaveGroupIndexer;
- @Inject private DynamicSet<GroupIndexedListener> groupIndexedListeners;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Inject private Sequences seq;
- @Inject private AccountOperations accountOperations;
+ @Inject private StalenessChecker stalenessChecker;
@Before
public void setTimeForTesting() {
@@ -173,12 +180,14 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void addRemoveMember() throws Exception {
- String g = createGroup("users");
- gApi.groups().id(g).addMembers("user");
- assertMembers(g, user);
+ AccountGroup.UUID group = groupOperations.newGroup().create();
+
+ gApi.groups().id(group.get()).addMembers("user");
+ assertMembers(group.get(), user);
- gApi.groups().id(g).removeMembers("user");
- assertNoMembers(g);
+ gApi.groups().id(group.get()).removeMembers("user");
+ ImmutableSet<Account.Id> members = groupOperations.group(group).get().members();
+ assertThat(members).isEmpty();
}
@Test
@@ -188,16 +197,15 @@ public class GroupsIT extends AbstractDaemonTest {
// Fill the cache for the observed account.
groupIncludeCache.getGroupsWithMember(accountId);
- String groupName = createGroup("users");
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(gApi.groups().id(groupName).get().id);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
- gApi.groups().id(groupName).addMembers(username);
+ gApi.groups().id(groupUuid.get()).addMembers(username);
Collection<AccountGroup.UUID> groupsWithMemberAfterAddition =
groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groupsWithMemberAfterAddition).contains(groupUuid);
- gApi.groups().id(groupName).removeMembers(username);
+ gApi.groups().id(groupUuid.get()).removeMembers(username);
Collection<AccountGroup.UUID> groupsWithMemberAfterRemoval =
groupIncludeCache.getGroupsWithMember(accountId);
@@ -229,16 +237,16 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void addMultipleMembers() throws Exception {
- String g = createGroup("users");
+ AccountGroup.UUID group = groupOperations.newGroup().create();
String u1 = name("u1");
accountOperations.newAccount().username(u1).create();
String u2 = name("u2");
accountOperations.newAccount().username(u2).create();
- gApi.groups().id(g).addMembers(u1, u2);
+ gApi.groups().id(group.get()).addMembers(u1, u2);
- List<AccountInfo> members = gApi.groups().id(g).members();
+ List<AccountInfo> members = gApi.groups().id(group.get()).members();
assertThat(members)
.comparingElementsUsing(getAccountToUsernameCorrespondence())
.containsExactly(u1, u2);
@@ -246,13 +254,13 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void membersWithAtSignInUsernameCanBeAdded() throws Exception {
- String g = createGroup("users");
+ AccountGroup.UUID group = groupOperations.newGroup().create();
String usernameWithAt = name("u1@something");
accountOperations.newAccount().username(usernameWithAt).create();
- gApi.groups().id(g).addMembers(usernameWithAt);
+ gApi.groups().id(group.get()).addMembers(usernameWithAt);
- List<AccountInfo> members = gApi.groups().id(g).members();
+ List<AccountInfo> members = gApi.groups().id(group.get()).members();
assertThat(members)
.comparingElementsUsing(getAccountToUsernameCorrespondence())
.containsExactly(usernameWithAt);
@@ -260,7 +268,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void membersWithAtSignInUsernameAreNotConfusedWithSimilarUsernames() throws Exception {
- String g = createGroup("users");
+ AccountGroup.UUID group = groupOperations.newGroup().create();
String usernameWithAt = name("u1@something");
accountOperations.newAccount().username(usernameWithAt).create();
String usernameWithoutAt = name("u1something");
@@ -271,10 +279,10 @@ public class GroupsIT extends AbstractDaemonTest {
accountOperations.newAccount().username(usernameOnlySuffix).create();
gApi.groups()
- .id(g)
+ .id(group.get())
.addMembers(usernameWithAt, usernameWithoutAt, usernameOnlyPrefix, usernameOnlySuffix);
- List<AccountInfo> members = gApi.groups().id(g).members();
+ List<AccountInfo> members = gApi.groups().id(group.get()).members();
assertThat(members)
.comparingElementsUsing(getAccountToUsernameCorrespondence())
.containsExactly(usernameWithAt, usernameWithoutAt, usernameOnlyPrefix, usernameOnlySuffix);
@@ -282,52 +290,54 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void includeRemoveGroup() throws Exception {
- String p = createGroup("parent");
- String g = createGroup("newGroup");
- gApi.groups().id(p).addGroups(g);
- assertIncludes(p, g);
+ AccountGroup.UUID parent = groupOperations.newGroup().create();
+ AccountGroup.UUID group = groupOperations.newGroup().create();
+ gApi.groups().id(parent.get()).addGroups(group.get());
+ assertThat(groupOperations.group(parent).get().subgroups()).containsExactly(group);
- gApi.groups().id(p).removeGroups(g);
- assertNoIncludes(p);
+ gApi.groups().id(parent.get()).removeGroups(group.get());
+ assertThat(groupOperations.group(parent).get().subgroups()).isEmpty();
}
@Test
public void includeExternalGroup() throws Exception {
- String g = createGroup("group");
+ AccountGroup.UUID group = groupOperations.newGroup().create();
String subgroupUuid = SystemGroupBackend.REGISTERED_USERS.get();
- gApi.groups().id(g).addGroups(subgroupUuid);
+ gApi.groups().id(group.get()).addGroups(subgroupUuid);
- List<GroupInfo> subgroups = gApi.groups().id(g).includedGroups();
+ List<GroupInfo> subgroups = gApi.groups().id(group.get()).includedGroups();
assertThat(subgroups).hasSize(1);
assertThat(subgroups.get(0).id).isEqualTo(subgroupUuid.replace(":", "%3A"));
assertThat(subgroups.get(0).name).isEqualTo("Registered Users");
assertThat(subgroups.get(0).groupId).isNull();
- List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(g).auditLog();
+ List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(group.get()).auditLog();
assertThat(auditEvents).hasSize(1);
- assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, "Registered Users");
+ assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), "Registered Users");
}
@Test
public void includeExistingGroup_OK() throws Exception {
- String p = createGroup("parent");
- String g = createGroup("newGroup");
- gApi.groups().id(p).addGroups(g);
- assertIncludes(p, g);
- gApi.groups().id(p).addGroups(g);
- assertIncludes(p, g);
+ AccountGroup.UUID parent = groupOperations.newGroup().create();
+ AccountGroup.UUID group = groupOperations.newGroup().create();
+ groupOperations.group(parent).forUpdate().addSubgroup(group);
+
+ gApi.groups().id(parent.get()).addGroups(group.get());
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(parent).get().subgroups();
+ assertThat(subgroups).containsExactly(group);
}
@Test
public void addMultipleIncludes() throws Exception {
- String p = createGroup("parent");
- String g1 = createGroup("newGroup1");
- String g2 = createGroup("newGroup2");
- List<String> groups = new ArrayList<>();
- groups.add(g1);
- groups.add(g2);
- gApi.groups().id(p).addGroups(g1, g2);
- assertIncludes(p, g1, g2);
+ AccountGroup.UUID parent = groupOperations.newGroup().create();
+ AccountGroup.UUID group1 = groupOperations.newGroup().create();
+ AccountGroup.UUID group2 = groupOperations.newGroup().create();
+
+ gApi.groups().id(parent.get()).addGroups(group1.get(), group2.get());
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(parent).get().subgroups();
+ assertThat(subgroups).containsExactly(group1, group2);
}
@Test
@@ -403,7 +413,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void createGroupWithoutCapability_Forbidden() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.groups().create(name("newGroup"));
}
@@ -628,33 +638,39 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void listEmptyGroupIncludes() throws Exception {
- String gx = createGroup("gx");
- assertThat(gApi.groups().id(gx).includedGroups()).isEmpty();
+ AccountGroup.UUID gx = groupOperations.newGroup().create();
+ assertThat(gApi.groups().id(gx.get()).includedGroups()).isEmpty();
}
@Test
public void includeNonExistingGroup() throws Exception {
- String gx = createGroup("gx");
+ AccountGroup.UUID gx = groupOperations.newGroup().create();
exception.expect(UnprocessableEntityException.class);
- gApi.groups().id(gx).addGroups("non-existing");
+ gApi.groups().id(gx.get()).addGroups("non-existing");
}
@Test
public void listNonEmptyGroupIncludes() throws Exception {
- String gx = createGroup("gx");
- String gy = createGroup("gy");
- String gz = createGroup("gz");
- gApi.groups().id(gx).addGroups(gy);
- gApi.groups().id(gx).addGroups(gz);
- assertIncludes(gApi.groups().id(gx).includedGroups(), gy, gz);
+ AccountGroup.UUID gz = groupOperations.newGroup().create();
+ AccountGroup.UUID gy = groupOperations.newGroup().create();
+ AccountGroup.UUID gx = groupOperations.newGroup().subgroups(gy, gz).create();
+
+ List<GroupInfo> includes = gApi.groups().id(gx.get()).includedGroups();
+
+ String gyName = groupOperations.group(gy).get().name();
+ String gzName = groupOperations.group(gz).get().name();
+ assertIncludes(includes, gyName, gzName);
}
@Test
public void listOneIncludeMember() throws Exception {
- String gx = createGroup("gx");
- String gy = createGroup("gy");
- gApi.groups().id(gx).addGroups(gy);
- assertIncludes(gApi.groups().id(gx).includedGroups(), gy);
+ AccountGroup.UUID gy = groupOperations.newGroup().create();
+ AccountGroup.UUID gx = groupOperations.newGroup().subgroups(gy).create();
+
+ List<GroupInfo> includes = gApi.groups().id(gx.get()).includedGroups();
+
+ String gyName = groupOperations.group(gy).get().name();
+ assertIncludes(includes, gyName);
}
@Test
@@ -665,109 +681,109 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void listEmptyGroupMembers() throws Exception {
- String group = createGroup("empty");
- assertThat(gApi.groups().id(group).members()).isEmpty();
+ AccountGroup.UUID group = groupOperations.newGroup().create();
+ assertThat(gApi.groups().id(group.get()).members()).isEmpty();
}
@Test
public void listNonEmptyGroupMembers() throws Exception {
- String group = createGroup("group");
+ AccountGroup.UUID group = groupOperations.newGroup().create();
String user1 = name("user1");
accountOperations.newAccount().username(user1).create();
String user2 = name("user2");
accountOperations.newAccount().username(user2).create();
- gApi.groups().id(group).addMembers(user1, user2);
+ gApi.groups().id(group.get()).addMembers(user1, user2);
- assertMembers(gApi.groups().id(group).members(), user1, user2);
+ assertMembers(gApi.groups().id(group.get()).members(), user1, user2);
}
@Test
public void listOneGroupMember() throws Exception {
- String group = createGroup("group");
+ AccountGroup.UUID group = groupOperations.newGroup().create();
String user = name("user1");
accountOperations.newAccount().username(user).create();
- gApi.groups().id(group).addMembers(user);
+ gApi.groups().id(group.get()).addMembers(user);
- assertMembers(gApi.groups().id(group).members(), user);
+ assertMembers(gApi.groups().id(group.get()).members(), user);
}
@Test
public void listGroupMembersRecursively() throws Exception {
- String gx = createGroup("gx");
+ AccountGroup.UUID gx = groupOperations.newGroup().create();
String ux = name("ux");
accountOperations.newAccount().username(ux).create();
- gApi.groups().id(gx).addMembers(ux);
+ gApi.groups().id(gx.get()).addMembers(ux);
- String gy = createGroup("gy");
+ AccountGroup.UUID gy = groupOperations.newGroup().create();
String uy = name("uy");
accountOperations.newAccount().username(uy).create();
- gApi.groups().id(gy).addMembers(uy);
+ gApi.groups().id(gy.get()).addMembers(uy);
- String gz = createGroup("gz");
+ AccountGroup.UUID gz = groupOperations.newGroup().create();
String uz = name("uz");
accountOperations.newAccount().username(uz).create();
- gApi.groups().id(gz).addMembers(uz);
+ gApi.groups().id(gz.get()).addMembers(uz);
- gApi.groups().id(gx).addGroups(gy);
- gApi.groups().id(gy).addGroups(gz);
- assertMembers(gApi.groups().id(gx).members(), ux);
- assertMembers(gApi.groups().id(gx).members(true), ux, uy, uz);
+ gApi.groups().id(gx.get()).addGroups(gy.get());
+ gApi.groups().id(gy.get()).addGroups(gz.get());
+ assertMembers(gApi.groups().id(gx.get()).members(), ux);
+ assertMembers(gApi.groups().id(gx.get()).members(true), ux, uy, uz);
}
@Test
public void usersSeeTheirDirectMembershipWhenListingMembersRecursively() throws Exception {
- String group = createGroup("group");
- gApi.groups().id(group).addMembers(user.username);
+ AccountGroup.UUID group = groupOperations.newGroup().create();
+ gApi.groups().id(group.get()).addMembers(user.username());
- setApiUser(user);
- assertMembers(gApi.groups().id(group).members(true), user.fullName);
+ requestScopeOperations.setApiUser(user.id());
+ assertMembers(gApi.groups().id(group.get()).members(true), user.fullName());
}
@Test
public void usersDoNotSeeTheirIndirectMembershipWhenListingMembersRecursively() throws Exception {
- String group1 = createGroup("group1");
- String group2 = createGroup("group2");
- gApi.groups().id(group1).addGroups(group2);
- gApi.groups().id(group2).addMembers(user.username);
+ AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
+ AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
+ gApi.groups().id(group1.get()).addGroups(group2.get());
+ gApi.groups().id(group2.get()).addMembers(user.username());
- setApiUser(user);
- List<AccountInfo> listedMembers = gApi.groups().id(group1).members(true);
+ requestScopeOperations.setApiUser(user.id());
+ List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
assertMembers(listedMembers);
}
@Test
public void adminsSeeTheirIndirectMembershipWhenListingMembersRecursively() throws Exception {
- String ownerGroup = createGroup("ownerGroup", null);
- String group1 = createGroup("group1", ownerGroup);
- String group2 = createGroup("group2", ownerGroup);
- gApi.groups().id(group1).addGroups(group2);
- gApi.groups().id(group2).addMembers(admin.username);
+ AccountGroup.UUID ownerGroup = groupOperations.newGroup().create();
+ AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+ AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+ gApi.groups().id(group1.get()).addGroups(group2.get());
+ gApi.groups().id(group2.get()).addMembers(admin.username());
- List<AccountInfo> listedMembers = gApi.groups().id(group1).members(true);
+ List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
- assertMembers(listedMembers, admin.fullName);
+ assertMembers(listedMembers, admin.fullName());
}
@Test
public void ownersSeeTheirIndirectMembershipWhenListingMembersRecursively() throws Exception {
- String ownerGroup = createGroup("ownerGroup", null);
- String group1 = createGroup("group1", ownerGroup);
- String group2 = createGroup("group2", ownerGroup);
- gApi.groups().id(group1).addGroups(group2);
- gApi.groups().id(ownerGroup).addMembers(user.username);
- gApi.groups().id(group2).addMembers(user.username);
+ AccountGroup.UUID ownerGroup = groupOperations.newGroup().create();
+ AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+ AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
+ gApi.groups().id(group1.get()).addGroups(group2.get());
+ gApi.groups().id(ownerGroup.get()).addMembers(user.username());
+ gApi.groups().id(group2.get()).addMembers(user.username());
- setApiUser(user);
- List<AccountInfo> listedMembers = gApi.groups().id(group1).members(true);
+ requestScopeOperations.setApiUser(user.id());
+ List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
- assertMembers(listedMembers, user.fullName);
+ assertMembers(listedMembers, user.fullName());
}
@Test
public void defaultGroupsCreated() throws Exception {
Iterable<String> names = gApi.groups().list().getAsMap().keySet();
- assertThat(names).containsAllOf("Administrators", "Non-Interactive Users").inOrder();
+ assertThat(names).containsAtLeast("Administrators", "Non-Interactive Users").inOrder();
}
@Test
@@ -782,18 +798,21 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void getGroupsByOwner() throws Exception {
- String parent = createGroup("test-parent");
- List<String> children =
- Arrays.asList(createGroup("test-child1", parent), createGroup("test-child2", parent));
+ AccountGroup.UUID parent = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
+ List<AccountGroup.UUID> children =
+ Arrays.asList(
+ groupOperations.newGroup().ownerGroupUuid(parent).create(),
+ groupOperations.newGroup().ownerGroupUuid(parent).create());
// By UUID
- List<GroupInfo> owned = gApi.groups().list().withOwnedBy(groupUuid(parent).get()).get();
- assertThat(owned.stream().map(g -> g.name).collect(toList()))
+ List<GroupInfo> owned = gApi.groups().list().withOwnedBy(parent.get()).get();
+ assertThat(owned.stream().map(g -> new AccountGroup.UUID(g.id)).collect(toList()))
.containsExactlyElementsIn(children);
// By name
- owned = gApi.groups().list().withOwnedBy(parent).get();
- assertThat(owned.stream().map(g -> g.name).collect(toList()))
+ 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()))
.containsExactlyElementsIn(children);
// By group that does not own any others
@@ -816,13 +835,13 @@ public class GroupsIT extends AbstractDaemonTest {
in.ownerId = adminGroupUuid().get();
gApi.groups().create(in);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(gApi.groups().list().getAsMap()).doesNotContainKey(newGroupName);
- setApiUser(admin);
- gApi.groups().id(newGroupName).addMembers(user.username);
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.groups().id(newGroupName).addMembers(user.username());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(gApi.groups().list().getAsMap()).containsKey(newGroupName);
}
@@ -895,41 +914,41 @@ public class GroupsIT extends AbstractDaemonTest {
GroupApi g = gApi.groups().create(name("group"));
List<? extends GroupAuditEventInfo> auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(1);
- assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id, admin.id);
+ assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id(), admin.id());
- g.addMembers(user.username);
+ g.addMembers(user.username());
auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(2);
- assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id, user.id);
+ assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id(), user.id());
- g.removeMembers(user.username);
+ g.removeMembers(user.username());
auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(3);
- assertMemberAuditEvent(auditEvents.get(0), Type.REMOVE_USER, admin.id, user.id);
+ assertMemberAuditEvent(auditEvents.get(0), Type.REMOVE_USER, admin.id(), user.id());
String otherGroup = name("otherGroup");
gApi.groups().create(otherGroup);
g.addGroups(otherGroup);
auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(4);
- assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, otherGroup);
+ assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), otherGroup);
g.removeGroups(otherGroup);
auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(5);
- assertSubgroupAuditEvent(auditEvents.get(0), Type.REMOVE_GROUP, admin.id, otherGroup);
+ assertSubgroupAuditEvent(auditEvents.get(0), Type.REMOVE_GROUP, admin.id(), otherGroup);
// Add a removed member back again.
- g.addMembers(user.username);
+ g.addMembers(user.username());
auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(6);
- assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id, user.id);
+ assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id(), user.id());
// Add a removed group back again.
g.addGroups(otherGroup);
auditEvents = g.auditLog();
assertThat(auditEvents).hasSize(7);
- assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, otherGroup);
+ assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), otherGroup);
Timestamp lastDate = null;
for (GroupAuditEventInfo auditEvent : auditEvents) {
@@ -941,8 +960,8 @@ public class GroupsIT extends AbstractDaemonTest {
}
/**
- * @Sandboxed is used by this test because it deletes a group reference which introduces an
- * inconsistency for the group storage. Once group deletion is supported, this test should be
+ * {@code @Sandboxed} is used by this test because it deletes a group reference which introduces
+ * an inconsistency for the group storage. Once group deletion is supported, this test should be
* updated to use the API instead.
*/
@Test
@@ -961,7 +980,7 @@ public class GroupsIT extends AbstractDaemonTest {
List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(parentGroup.id).auditLog();
assertThat(auditEvents).hasSize(2);
// Verify the unavailable subgroup's name is null.
- assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, null);
+ assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), null);
}
private void deleteGroupRef(String groupId) throws Exception {
@@ -990,21 +1009,20 @@ public class GroupsIT extends AbstractDaemonTest {
TestAccount groupOwner = accountCreator.user2();
GroupInput in = new GroupInput();
in.name = name("group");
- in.members =
- Collections.singleton(groupOwner).stream().map(u -> u.id.toString()).collect(toList());
+ in.members = Stream.of(groupOwner).map(u -> u.id().toString()).collect(toList());
in.visibleToAll = true;
GroupInfo group = gApi.groups().create(in).get();
// admin can reindex any group
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.groups().id(group.id).index();
// group owner can reindex own group (group is owned by itself)
- setApiUser(groupOwner);
+ requestScopeOperations.setApiUser(groupOwner.id());
gApi.groups().id(group.id).index();
// user cannot reindex any group
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("not allowed to index group");
gApi.groups().id(group.id).index();
@@ -1034,7 +1052,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void pushToGroupsBranchForNonAllUsersRepo() throws Exception {
- assertCreateGroupBranch(project, null);
+ assertCreateGroupBranch(project);
String groupRef =
RefNames.refsGroups(new AccountGroup.UUID(gApi.groups().create(name("foo")).get().id));
createBranch(project, groupRef);
@@ -1043,7 +1061,7 @@ public class GroupsIT extends AbstractDaemonTest {
@Test
public void pushToDeletedGroupsBranchForNonAllUsersRepo() throws Exception {
- assertCreateGroupBranch(project, null);
+ assertCreateGroupBranch(project);
String groupRef =
RefNames.refsDeletedGroups(
new AccountGroup.UUID(gApi.groups().create(name("foo")).get().id));
@@ -1059,11 +1077,15 @@ public class GroupsIT extends AbstractDaemonTest {
private void assertPushToGroupBranch(
Project.NameKey project, String groupRefName, String expectedErrorOnUpdate) throws Exception {
- grant(project, RefNames.REFS_GROUPS + "*", Permission.CREATE, false, REGISTERED_USERS);
- grant(project, RefNames.REFS_GROUPS + "*", Permission.PUSH, false, REGISTERED_USERS);
- grant(project, RefNames.REFS_DELETED_GROUPS + "*", Permission.CREATE, false, REGISTERED_USERS);
- grant(project, RefNames.REFS_DELETED_GROUPS + "*", Permission.PUSH, false, REGISTERED_USERS);
- grant(project, RefNames.REFS_GROUPNAMES, Permission.PUSH, false, REGISTERED_USERS);
+ 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();
+ }
TestRepository<InMemoryRepository> repo = cloneProject(project);
@@ -1072,7 +1094,7 @@ public class GroupsIT extends AbstractDaemonTest {
repo.reset("groupRef");
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
+ .create(admin.newIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
.to(groupRefName);
if (expectedErrorOnUpdate != null) {
r.assertErrorStatus(expectedErrorOnUpdate);
@@ -1081,21 +1103,20 @@ public class GroupsIT extends AbstractDaemonTest {
}
}
- private void assertCreateGroupBranch(Project.NameKey project, String expectedErrorOnCreate)
- throws Exception {
- grant(project, RefNames.REFS_GROUPS + "*", Permission.CREATE, false, REGISTERED_USERS);
- grant(project, RefNames.REFS_GROUPS + "*", Permission.PUSH, false, REGISTERED_USERS);
+ 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();
+ }
TestRepository<InMemoryRepository> repo = cloneProject(project);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
+ .create(admin.newIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
.setParents(ImmutableList.of())
.to(RefNames.REFS_GROUPS + name("bar"));
- if (expectedErrorOnCreate != null) {
- r.assertErrorStatus(expectedErrorOnCreate);
- } else {
- r.assertOkStatus();
- }
+ r.assertOkStatus();
}
@Test
@@ -1130,7 +1151,7 @@ public class GroupsIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), repo, "Subject", "project.config", config)
+ .create(admin.newIdent(), repo, "Subject", "project.config", config)
.to(RefNames.REFS_CONFIG);
r.assertErrorStatus("invalid project configuration");
r.assertMessage("All-Users must inherit from All-Projects");
@@ -1180,7 +1201,7 @@ public class GroupsIT extends AbstractDaemonTest {
grant(allUsers, refPattern, Permission.PUSH);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
- PushOneCommit.Result r = pushFactory.create(db, admin.getIdent(), allUsersRepo).to(groupRef);
+ PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(groupRef);
r.assertErrorStatus();
assertThat(r.getMessage()).contains("Not allowed to create group branch.");
@@ -1279,12 +1300,12 @@ public class GroupsIT extends AbstractDaemonTest {
public void groupsOfUserCanBeListedInSlaveMode() throws Exception {
GroupInput groupInput = new GroupInput();
groupInput.name = name("contributors");
- groupInput.members = ImmutableList.of(user.username);
+ groupInput.members = ImmutableList.of(user.username());
gApi.groups().create(groupInput).get();
restartAsSlave();
- setApiUser(user);
- List<GroupInfo> groups = gApi.groups().list().withUser(user.username).get();
+ requestScopeOperations.setApiUser(user.id());
+ List<GroupInfo> groups = gApi.groups().list().withUser(user.username()).get();
ImmutableList<String> groupNames =
groups.stream().map(group -> group.name).collect(toImmutableList());
assertThat(groupNames).contains(groupInput.name);
@@ -1373,18 +1394,15 @@ public class GroupsIT extends AbstractDaemonTest {
}
private static Correspondence<AccountInfo, String> getAccountToUsernameCorrespondence() {
- return new Correspondence<AccountInfo, String>() {
- @Override
- public boolean compare(AccountInfo actualAccount, String expectedName) {
- String username = actualAccount == null ? null : actualAccount.username;
- return Objects.equals(username, expectedName);
- }
-
- @Override
- public String toString() {
- return "has username";
- }
- };
+ 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);
+ }
+ },
+ "has username");
}
private void assertStaleGroupAndReindex(AccountGroup.UUID groupUuid) throws IOException {
@@ -1409,8 +1427,7 @@ public class GroupsIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
- .create(
- db, admin.getIdent(), repo, "Update group config", "group.config", "some content")
+ .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);
@@ -1484,7 +1501,7 @@ public class GroupsIT extends AbstractDaemonTest {
private void assertMembers(String group, TestAccount... expectedMembers) throws Exception {
assertMembers(
gApi.groups().id(group).members(),
- TestAccount.names(expectedMembers).stream().toArray(String[]::new));
+ TestAccount.names(expectedMembers).toArray(new String[0]));
assertAccountInfos(Arrays.asList(expectedMembers), gApi.groups().id(group).members());
}
@@ -1494,22 +1511,10 @@ public class GroupsIT extends AbstractDaemonTest {
.inOrder();
}
- private void assertNoMembers(String group) throws Exception {
- assertThat(gApi.groups().id(group).members()).isEmpty();
- }
-
- private void assertIncludes(String group, String... expectedNames) throws Exception {
- assertIncludes(gApi.groups().id(group).includedGroups(), expectedNames);
- }
-
- private static void assertIncludes(Iterable<GroupInfo> includes, String... expectedNames) {
- assertThat(Iterables.transform(includes, i -> i.name))
- .containsExactlyElementsIn(Arrays.asList(expectedNames))
- .inOrder();
- }
-
- private void assertNoIncludes(String group) throws Exception {
- assertThat(gApi.groups().id(group).includedGroups()).isEmpty();
+ 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();
}
private void assertBadRequest(ListRequest req) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
index 70563124b2..5e143c0d8b 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
@@ -18,7 +18,8 @@ import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+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;
@@ -27,7 +28,6 @@ import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -92,7 +92,7 @@ public class GroupsUpdateIT {
}
private void createGroup(InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
groupsUpdateProvider.get().createGroup(groupCreation, groupUpdate);
}
@@ -138,7 +138,7 @@ public class GroupsUpdateIT {
InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().build();
try {
groupsUpdateProvider.get().createGroup(groupCreation, groupUpdate);
- } catch (OrmException | IOException | ConfigInvalidException e) {
+ } catch (StorageException | IOException | ConfigInvalidException e) {
throw new IllegalStateException(e);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java b/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
index 27f737f4f4..2e455528c3 100644
--- a/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.extensions.api.plugins.InstallPluginInput;
import com.google.gerrit.extensions.api.plugins.PluginApi;
@@ -33,7 +34,13 @@ 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.inject.Inject;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
import org.junit.Test;
@NoHttpd
@@ -47,7 +54,16 @@ public class PluginIT extends AbstractDaemonTest {
private static final ImmutableList<String> PLUGINS =
ImmutableList.of(
- "plugin-a.js", "plugin-b.html", "plugin-c.js", "plugin-d.html", "plugin_e.js");
+ "plugin-a.js",
+ "plugin-b.html",
+ "plugin-c.js",
+ "plugin-d.html",
+ "plugin-normal.jar",
+ "plugin-empty.jar",
+ "plugin-unset.jar",
+ "plugin_e.js");
+
+ @Inject private RequestScopeOperations requestScopeOperations;
@Test
@GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
@@ -60,13 +76,14 @@ public class PluginIT extends AbstractDaemonTest {
// Install all the plugins
InstallPluginInput input = new InstallPluginInput();
for (String plugin : PLUGINS) {
- input.raw = plugin.endsWith(".js") ? JS_PLUGIN_CONTENT : HTML_PLUGIN_CONTENT;
+ input.raw = pluginContent(plugin);
api = gApi.plugins().install(plugin, input);
assertThat(api).isNotNull();
PluginInfo info = api.get();
String name = pluginName(plugin);
assertThat(info.id).isEqualTo(name);
assertThat(info.version).isEqualTo(pluginVersion(plugin));
+ assertThat(info.apiVersion).isEqualTo(pluginApiVersion(plugin));
assertThat(info.indexUrl).isEqualTo(String.format("plugins/%s/", name));
assertThat(info.filename).isEqualTo(plugin);
assertThat(info.disabled).isNull();
@@ -112,7 +129,7 @@ public class PluginIT extends AbstractDaemonTest {
deprecatedInput();
// Non-admin cannot disable
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
gApi.plugins().name("plugin-a").disable();
fail("Expected AuthException");
@@ -159,12 +176,52 @@ public class PluginIT extends AbstractDaemonTest {
return plugin.substring(0, dot);
}
+ private RawInput pluginJarContent(String plugin) throws IOException {
+ ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
+ Manifest manifest = new Manifest();
+ Attributes attributes = manifest.getMainAttributes();
+ attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+ if (!plugin.endsWith("-unset.jar")) {
+ attributes.put(Attributes.Name.IMPLEMENTATION_VERSION, pluginVersion(plugin));
+ attributes.put(new Attributes.Name("Gerrit-ApiVersion"), pluginApiVersion(plugin));
+ }
+ try (JarOutputStream jarStream = new JarOutputStream(arrayStream, manifest)) {}
+ return RawInputUtil.create(arrayStream.toByteArray());
+ }
+
+ private RawInput pluginContent(String plugin) throws IOException {
+ if (plugin.endsWith(".js")) {
+ return JS_PLUGIN_CONTENT;
+ }
+ if (plugin.endsWith(".html")) {
+ return HTML_PLUGIN_CONTENT;
+ }
+ assertThat(plugin).endsWith(".jar");
+ return pluginJarContent(plugin);
+ }
+
private String pluginVersion(String plugin) {
String name = pluginName(plugin);
+ if (name.endsWith("empty")) {
+ return "";
+ }
+ if (name.endsWith("unset")) {
+ return null;
+ }
int dash = name.lastIndexOf("-");
return dash > 0 ? name.substring(dash + 1) : "";
}
+ private String pluginApiVersion(String plugin) {
+ if (plugin.endsWith("normal.jar")) {
+ return "2.16.19-SNAPSHOT";
+ }
+ if (plugin.endsWith("empty.jar")) {
+ return "";
+ }
+ return null;
+ }
+
private void assertBadRequest(ListRequest req) throws Exception {
try {
req.get();
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index e4194a3640..8cdd2f66e2 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -21,6 +21,7 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
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.extensions.api.config.AccessCheckInfo;
import com.google.gerrit.extensions.api.config.AccessCheckInput;
@@ -31,6 +32,8 @@ 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;
@@ -40,7 +43,7 @@ import org.junit.Before;
import org.junit.Test;
public class CheckAccessIT extends AbstractDaemonTest {
-
+ @Inject private ProjectOperations projectOperations;
@Inject private GroupOperations groupOperations;
private Project.NameKey normalProject;
@@ -50,35 +53,38 @@ public class CheckAccessIT extends AbstractDaemonTest {
@Before
public void setUp() throws Exception {
- normalProject = createProject("normal");
- secretProject = createProject("secret");
- secretRefProject = createProject("secretRef");
+ normalProject = projectOperations.newProject().create();
+ secretProject = projectOperations.newProject().create();
+ secretRefProject = projectOperations.newProject().create();
AccountGroup.UUID privilegedGroupUuid =
groupOperations.newGroup().name(name("privilegedGroup")).create();
privilegedUser = accountCreator.create("privilegedUser", "snowden@nsa.gov", "Ed Snowden");
- groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id).update();
+ groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id()).update();
- grant(secretProject, "refs/*", Permission.READ, false, privilegedGroupUuid);
- block(secretProject, "refs/*", Permission.READ, SystemGroupBackend.REGISTERED_USERS);
+ 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();
+ }
- deny(secretRefProject, "refs/*", Permission.READ, SystemGroupBackend.ANONYMOUS_USERS);
- grant(secretRefProject, "refs/heads/secret/*", Permission.READ, false, privilegedGroupUuid);
- block(
- secretRefProject,
- "refs/heads/secret/*",
- Permission.READ,
- SystemGroupBackend.REGISTERED_USERS);
- grant(
- secretRefProject,
- "refs/heads/*",
- Permission.READ,
- false,
- SystemGroupBackend.REGISTERED_USERS);
+ 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();
+ }
// Ref permission
- grant(normalProject, "refs/*", Permission.VIEW_PRIVATE_CHANGES, false, privilegedGroupUuid);
- grant(normalProject, "refs/*", Permission.FORGE_SERVER, false, privilegedGroupUuid);
+ 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();
+ }
}
@Test
@@ -91,7 +97,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
@Test
public void nonexistentPermission() throws Exception {
AccessCheckInput in = new AccessCheckInput();
- in.account = user.email;
+ in.account = user.email();
in.permission = "notapermission";
in.ref = "refs/heads/master";
@@ -103,7 +109,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
@Test
public void permissionLacksRef() throws Exception {
AccessCheckInput in = new AccessCheckInput();
- in.account = user.email;
+ in.account = user.email();
in.permission = "forge_author";
exception.expect(BadRequestException.class);
@@ -114,7 +120,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
@Test
public void changePermission() throws Exception {
AccessCheckInput in = new AccessCheckInput();
- in.account = user.email;
+ in.account = user.email();
in.permission = "rebase";
in.ref = "refs/heads/master";
@@ -131,7 +137,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
in.ref = "refs/heads/master";
exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("cannot find account doesnotexist@invalid.com");
+ exception.expectMessage("Account 'doesnotexist@invalid.com' not found");
gApi.projects().name(normalProject.get()).checkAccess(in);
}
@@ -181,7 +187,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
+ normalProject.get()
+ "/check.access"
+ "?ref=refs/heads/master&perm=viewPrivateChanges&account="
- + user.email);
+ + user.email());
rep.assertOK();
assertThat(rep.getEntityContent()).contains("403");
}
@@ -191,28 +197,28 @@ public class CheckAccessIT extends AbstractDaemonTest {
List<TestCase> inputs =
ImmutableList.of(
TestCase.projectRefPerm(
- user.email,
+ user.email(),
normalProject.get(),
"refs/heads/master",
Permission.VIEW_PRIVATE_CHANGES,
403),
- TestCase.project(user.email, normalProject.get(), 200),
- TestCase.project(user.email, secretProject.get(), 403),
+ TestCase.project(user.email(), normalProject.get(), 200),
+ TestCase.project(user.email(), secretProject.get(), 403),
TestCase.projectRef(
- user.email, secretRefProject.get(), "refs/heads/secret/master", 403),
+ user.email(), secretRefProject.get(), "refs/heads/secret/master", 403),
TestCase.projectRef(
- privilegedUser.email, secretRefProject.get(), "refs/heads/secret/master", 200),
- TestCase.projectRef(privilegedUser.email, normalProject.get(), null, 200),
- TestCase.projectRef(privilegedUser.email, secretProject.get(), null, 200),
- TestCase.projectRef(privilegedUser.email, secretProject.get(), null, 200),
+ privilegedUser.email(), secretRefProject.get(), "refs/heads/secret/master", 200),
+ TestCase.projectRef(privilegedUser.email(), normalProject.get(), null, 200),
+ TestCase.projectRef(privilegedUser.email(), secretProject.get(), null, 200),
+ TestCase.projectRef(privilegedUser.email(), secretProject.get(), null, 200),
TestCase.projectRefPerm(
- privilegedUser.email,
+ privilegedUser.email(),
normalProject.get(),
"refs/heads/master",
Permission.VIEW_PRIVATE_CHANGES,
200),
TestCase.projectRefPerm(
- privilegedUser.email,
+ privilegedUser.email(),
normalProject.get(),
"refs/heads/master",
Permission.FORGE_SERVER,
@@ -260,7 +266,7 @@ public class CheckAccessIT extends AbstractDaemonTest {
assertThat(u.delete()).isEqualTo(Result.FORCED);
}
AccessCheckInput input = new AccessCheckInput();
- input.account = privilegedUser.email;
+ input.account = privilegedUser.email();
AccessCheckInfo info = gApi.projects().name(normalProject.get()).checkAccess(input);
assertThat(info.status).isEqualTo(200);
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
index 3b2df797c9..388ea30a1d 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
@@ -280,8 +280,8 @@ public class CheckProjectIT extends AbstractDaemonTest {
.branch("HEAD")
.commit()
.message("A change")
- .author(admin.getIdent())
- .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()))
+ .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 39d630a6f6..a341b3c64c 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
@@ -145,7 +145,7 @@ public class CommitIT extends AbstractDaemonTest {
}
private static void assertPerson(GitPerson actual, TestAccount expected) {
- assertThat(actual.email).isEqualTo(expected.email);
- assertThat(actual.name).isEqualTo(expected.fullName);
+ assertThat(actual.email).isEqualTo(expected.email());
+ assertThat(actual.name).isEqualTo(expected.fullName());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java b/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
index e51a06967a..f597392a5a 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/DashboardIT.java
@@ -192,9 +192,9 @@ public class DashboardIT extends AbstractDaemonTest {
throw e;
}
}
- try (Repository r = repoManager.openRepository(project)) {
- TestRepository<Repository>.CommitBuilder cb =
- new TestRepository<>(r).branch(canonicalRef).commit();
+ try (Repository r = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(r)) {
+ TestRepository<Repository>.CommitBuilder cb = tr.branch(canonicalRef).commit();
StringBuilder content = new StringBuilder("[dashboard]\n");
if (info.title != null) {
content.append("title = ").append(info.title).append("\n");
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 76ca625a53..766f8537b1 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -19,6 +19,7 @@ import static com.google.gerrit.server.project.ProjectState.INHERITED_FROM_GLOBA
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 java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
@@ -29,15 +30,21 @@ 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.Permission;
+import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
+import com.google.gerrit.extensions.api.projects.ConfigValue;
import com.google.gerrit.extensions.api.projects.DescriptionInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
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.ProjectIndexedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.registration.RegistrationHandle;
@@ -47,9 +54,24 @@ 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;
+import com.google.gerrit.server.project.ProjectConfig;
+import com.google.inject.AbstractModule;
import com.google.inject.Inject;
+import com.google.inject.Module;
+import java.util.HashMap;
+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;
@@ -58,11 +80,32 @@ import org.junit.Test;
@NoHttpd
public class ProjectIT extends AbstractDaemonTest {
+ private static final String BUGZILLA = "bugzilla";
+ private static final String BUGZILLA_LINK = "http://bugzilla.example.com/?id=$2";
+ private static final String BUGZILLA_MATCH = "(bug\\\\s+#?)(\\\\d+)";
+ private static final String JIRA = "jira";
+ 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;
+ @Override
+ public Module createModule() {
+ return new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(ProjectConfigEntry.class)
+ .annotatedWith(Exports.named("test-plugin-key"))
+ .toInstance(new ProjectConfigEntry("Test Plugin Config Item", true));
+ }
+ };
+ }
+
@Before
public void addProjectIndexedCounter() {
projectIndexedCounter = new ProjectIndexedCounter();
@@ -139,6 +182,17 @@ public class ProjectIT extends AbstractDaemonTest {
}
@Test
+ public void createProjectWithPluginConfigs() throws Exception {
+ String name = name("foo");
+ ProjectInput input = new ProjectInput();
+ input.name = name;
+ input.description = "foo description";
+ input.pluginConfigValues = newPluginConfigValues();
+ ProjectInfo info = gApi.projects().create(input).get();
+ assertThat(info.description).isEqualTo(input.description);
+ }
+
+ @Test
public void createProjectWithMismatchedInput() throws Exception {
ProjectInput in = new ProjectInput();
in.name = name("foo");
@@ -199,14 +253,14 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void createAndDeleteBranch() throws Exception {
- assertThat(getRemoteHead(project.get(), "foo")).isNull();
+ 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").delete();
- assertThat(getRemoteHead(project.get(), "foo")).isNull();
+ assertThat(hasHead(project, "foo")).isFalse();
projectIndexedCounter.assertNoReindex();
}
@@ -215,7 +269,7 @@ public class ProjectIT extends AbstractDaemonTest {
grant(project, "refs/*", Permission.PUSH, true);
projectIndexedCounter.clear();
- assertThat(getRemoteHead(project.get(), "foo")).isNull();
+ assertThat(hasHead(project, "foo")).isFalse();
PushOneCommit.Result r = pushTo("refs/heads/foo");
r.assertOkStatus();
@@ -224,7 +278,7 @@ public class ProjectIT extends AbstractDaemonTest {
PushResult r2 = GitUtil.pushOne(testRepo, null, "refs/heads/foo", false, true, null);
assertThat(r2.getRemoteUpdate("refs/heads/foo").getStatus()).isEqualTo(Status.OK);
- assertThat(getRemoteHead(project.get(), "foo")).isNull();
+ assertThat(hasHead(project, "foo")).isFalse();
projectIndexedCounter.assertNoReindex();
}
@@ -330,7 +384,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void nonOwnerCannotSetConfig() throws Exception {
ConfigInput input = createTestConfigInput();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("write refs/meta/config not permitted");
gApi.projects().name(project.get()).config(input);
@@ -365,7 +419,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void setHeadNotAllowed() throws Exception {
gApi.projects().name(project.get()).branch("test").create(new BranchInput());
- setApiUser(user);
+ 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");
@@ -411,7 +465,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void reindexProject() throws Exception {
- createProject("child", project);
+ projectOperations.newProject().parent(project).create();
projectIndexedCounter.clear();
gApi.projects().name(allProjects.get()).index(false);
@@ -420,8 +474,8 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void reindexProjectWithChildren() throws Exception {
- Project.NameKey middle = createProject("middle", project);
- Project.NameKey leave = createProject("leave", middle);
+ 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);
@@ -455,7 +509,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.inheritProjectMaxObjectSizeLimit", value = "true")
public void maxObjectSizeIsInheritedFromParentProject() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("100k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
@@ -471,7 +525,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void maxObjectSizeIsNotInheritedFromParentProject() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("100k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
@@ -486,7 +540,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void maxObjectSizeOverridesParentProjectWhenNotSetOnParent() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("0");
assertThat(info.maxObjectSizeLimit.value).isNull();
@@ -501,7 +555,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
public void maxObjectSizeOverridesParentProjectWhenLower() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("200k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
@@ -517,7 +571,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.inheritProjectMaxObjectSizeLimit", value = "true")
public void maxObjectSizeDoesNotOverrideParentProjectWhenHigher() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("100k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
@@ -534,7 +588,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.maxObjectSizeLimit", value = "200k")
public void maxObjectSizeIsInheritedFromGlobalConfig() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = getConfig();
assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
@@ -559,7 +613,7 @@ public class ProjectIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.maxObjectSizeLimit", value = "300k")
public void inheritedMaxObjectSizeOverridesGlobalConfigWhenLower() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("200k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
@@ -576,7 +630,7 @@ public class ProjectIT extends AbstractDaemonTest {
@GerritConfig(name = "receive.maxObjectSizeLimit", value = "200k")
@GerritConfig(name = "receive.inheritProjectMaxObjectSizeLimit", value = "true")
public void maxObjectSizeDoesNotOverrideGlobalConfigWhenHigher() throws Exception {
- Project.NameKey child = createProject(name("child"), project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
ConfigInfo info = setMaxObjectSize("300k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
@@ -596,6 +650,95 @@ public class ProjectIT extends AbstractDaemonTest {
setMaxObjectSize("100 foo");
}
+ @Test
+ public void noCommentlinksByDefault() throws Exception {
+ assertThat(getConfig().commentlinks).isEmpty();
+ }
+
+ @Test
+ @GerritConfig(name = "commentlink.bugzilla.match", value = BUGZILLA_MATCH)
+ @GerritConfig(name = "commentlink.bugzilla.link", value = BUGZILLA_LINK)
+ @GerritConfig(name = "commentlink.jira.match", value = JIRA_MATCH)
+ @GerritConfig(name = "commentlink.jira.link", value = JIRA_LINK)
+ public void projectConfigUsesCommentlinksFromGlobalConfig() throws Exception {
+ Map<String, CommentLinkInfo> expected = new HashMap<>();
+ expected.put(BUGZILLA, commentLinkInfo(BUGZILLA, BUGZILLA_MATCH, BUGZILLA_LINK));
+ expected.put(JIRA, commentLinkInfo(JIRA, JIRA_MATCH, JIRA_LINK));
+ assertCommentLinks(getConfig(), expected);
+ }
+
+ @Test
+ public void cannotPushLabelDefinitionWithDuplicateValues() throws Exception {
+ Config cfg = readAllProjectsConfig();
+ cfg.setStringList(
+ "label",
+ "Code-Review",
+ "value",
+ ImmutableList.of("+1 LGTM", "1 LGTM", "0 No Value", "-1 Looks Bad"));
+
+ TestRepository<InMemoryRepository> repo = cloneProject(allProjects);
+ GitUtil.fetch(repo, RefNames.REFS_CONFIG + ":" + RefNames.REFS_CONFIG);
+ repo.reset(RefNames.REFS_CONFIG);
+ PushOneCommit.Result r =
+ pushFactory
+ .create(admin.newIdent(), repo, "Subject", "project.config", cfg.toText())
+ .to(RefNames.REFS_CONFIG);
+ r.assertErrorStatus("invalid project configuration");
+ r.assertMessage("project.config: duplicate value \"1 lgtm\" for label \"code-review\"");
+ }
+
+ @Test
+ public void getProjectThatHasLabelDefinitionWithDuplicateValues() throws Exception {
+ // 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();
+ cfg.setStringList(
+ "label",
+ "Code-Review",
+ "value",
+ ImmutableList.of("+1 LGTM", "1 LGTM", "0 No Value", "-1 Looks Bad"));
+
+ try (TestRepository<Repository> repo =
+ new TestRepository<>(repoManager.openRepository(allProjects))) {
+ repo.update(
+ RefNames.REFS_CONFIG,
+ repo.commit()
+ .message("Set label with duplicate value")
+ .parent(getHead(repo.getRepository(), RefNames.REFS_CONFIG))
+ .add(ProjectConfig.PROJECT_CONFIG, cfg.toText()));
+ }
+
+ // Verify that project info can be retrieved and that the label value "+1 LGTM" appears only
+ // once.
+ ProjectInfo projectInfo = gApi.projects().name(allProjects.get()).get();
+ assertThat(projectInfo.labels.keySet()).containsExactly("Code-Review");
+ assertThat(projectInfo.labels.get("Code-Review").values)
+ .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*/);
+ }
+
+ private void assertCommentLinks(ConfigInfo actual, Map<String, CommentLinkInfo> expected) {
+ assertThat(actual.commentlinks).containsExactlyEntriesIn(expected);
+ }
+
private ConfigInfo setConfig(Project.NameKey name, ConfigInput input) throws Exception {
return gApi.projects().name(name.get()).config(input);
}
@@ -635,6 +778,16 @@ public class ProjectIT extends AbstractDaemonTest {
return setConfig(name, input);
}
+ private static Map<String, Map<String, ConfigValue>> newPluginConfigValues() {
+ Map<String, Map<String, ConfigValue>> pluginConfigValues = new HashMap<>();
+ Map<String, ConfigValue> configValues = new HashMap<>();
+ ConfigValue value = new ConfigValue();
+ value.value = "true";
+ configValues.put("test-plugin-key", value);
+ pluginConfigValues.put("gerrit", configValues);
+ return pluginConfigValues;
+ }
+
private static class ProjectIndexedCounter implements ProjectIndexedListener {
private final AtomicLongMap<String> countsByProject = AtomicLongMap.create();
@@ -670,4 +823,12 @@ public class ProjectIT extends AbstractDaemonTest {
clear();
}
}
+
+ protected RevCommit getRemoteHead(String project, String branch) throws Exception {
+ return getRemoteHead(new Project.NameKey(project), branch);
+ }
+
+ boolean hasHead(Project.NameKey k, String b) {
+ return projectOperations.project(k).hasHead(b);
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
index 6fde012366..6b511f66e6 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIndexerIT.java
@@ -19,6 +19,7 @@ import static com.google.gerrit.acceptance.GitUtil.fetch;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.RefState;
@@ -45,6 +46,7 @@ public class ProjectIndexerIT extends AbstractDaemonTest {
@Inject private ProjectIndexCollection indexes;
@Inject private IndexConfig indexConfig;
@Inject private StalenessChecker stalenessChecker;
+ @Inject private ProjectOperations projectOperations;
private static final ImmutableSet<String> FIELDS =
ImmutableSet.of(ProjectField.NAME.getName(), ProjectField.REF_STATE.getName());
@@ -96,8 +98,8 @@ public class ProjectIndexerIT extends AbstractDaemonTest {
@Test
public void stalenessChecker_hierarchyChange_isStale() throws Exception {
- Project.NameKey p1 = createProject("p1", allProjects);
- Project.NameKey p2 = createProject("p2", allProjects);
+ Project.NameKey p1 = projectOperations.newProject().create();
+ Project.NameKey p2 = projectOperations.newProject().create();
try (ProjectConfigUpdate u = updateProject(project)) {
u.getConfig().getProject().setParentName(p1);
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 3295f1a1f5..3c1428d3ae 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
@@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
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.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -27,15 +29,19 @@ 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;
import org.junit.Test;
@NoHttpd
public class SetParentIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void setParentNotAllowed() throws Exception {
- String parent = createProject("parent", null, true).get();
- setApiUser(user);
+ String parent = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.projects().name(project.get()).parent(parent);
}
@@ -43,8 +49,8 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.allowProjectOwnersToChangeParent", value = "true")
public void setParentNotAllowedForNonOwners() throws Exception {
- String parent = createProject("parent", null, true).get();
- setApiUser(user);
+ String parent = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.projects().name(project.get()).parent(parent);
}
@@ -52,7 +58,7 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.allowProjectOwnersToChangeParent", value = "true")
public void setParentAllowedByAdminWhenAllowProjectOwnersEnabled() throws Exception {
- String parent = createProject("parent", null, true).get();
+ String parent = projectOperations.newProject().create().get();
gApi.projects().name(project.get()).parent(parent);
assertThat(gApi.projects().name(project.get()).parent()).isEqualTo(parent);
@@ -67,8 +73,8 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "receive.allowProjectOwnersToChangeParent", value = "true")
public void setParentAllowedForOwners() throws Exception {
- String parent = createProject("parent", null, true).get();
- setApiUser(user);
+ String parent = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
grant(project, "refs/*", Permission.OWNER, false, SystemGroupBackend.REGISTERED_USERS);
gApi.projects().name(project.get()).parent(parent);
assertThat(gApi.projects().name(project.get()).parent()).isEqualTo(parent);
@@ -76,7 +82,7 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
public void setParent() throws Exception {
- String parent = createProject("parent", null, true).get();
+ String parent = projectOperations.newProject().create().get();
gApi.projects().name(project.get()).parent(parent);
assertThat(gApi.projects().name(project.get()).parent()).isEqualTo(parent);
@@ -104,7 +110,7 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
public void setParentToOwnChildNotAllowed() throws Exception {
- String child = createProject("child", project, true).get();
+ String child = projectOperations.newProject().parent(project).create().get();
exception.expect(ResourceConflictException.class);
exception.expectMessage("cycle exists between");
gApi.projects().name(project.get()).parent(child);
@@ -112,8 +118,8 @@ public class SetParentIT extends AbstractDaemonTest {
@Test
public void setParentToGrandchildNotAllowed() throws Exception {
- Project.NameKey child = createProject("child", project, true);
- String grandchild = createProject("grandchild", child, true).get();
+ 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);
@@ -137,7 +143,7 @@ public class SetParentIT extends AbstractDaemonTest {
public void setParentForAllUsersMustBeAllProjects() throws Exception {
gApi.projects().name(allUsers.get()).parent(allProjects.get());
- String parent = createProject("parent", null, true).get();
+ String parent = projectOperations.newProject().create().get();
exception.expect(BadRequestException.class);
exception.expectMessage("All-Users must inherit from All-Projects");
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/GetBlameIT.java b/javatests/com/google/gerrit/acceptance/api/revision/GetBlameIT.java
new file mode 100644
index 0000000000..8dfebad802
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/revision/GetBlameIT.java
@@ -0,0 +1,128 @@
+// 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.revision;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.extensions.common.BlameInfo;
+import com.google.gerrit.extensions.common.RangeInfo;
+import java.util.List;
+import org.junit.Test;
+
+public class GetBlameIT extends AbstractDaemonTest {
+ @Test
+ public void forNonExistingFile() throws Exception {
+ PushOneCommit.Result r = createChange("Test Change", "foo.txt", "FOO");
+ List<BlameInfo> blameInfos =
+ gApi.changes().id(r.getChangeId()).current().file("non-existing.txt").blameRequest().get();
+
+ // File doesn't exist in commit.
+ assertThat(blameInfos).isEmpty();
+ }
+
+ @Test
+ public void forNonExistingFileFromBase() throws Exception {
+ PushOneCommit.Result r = createChange("Test Change", "foo.txt", "FOO");
+ List<BlameInfo> blameInfos =
+ gApi.changes()
+ .id(r.getChangeId())
+ .current()
+ .file("non-existing.txt")
+ .blameRequest()
+ .forBase(true)
+ .get();
+
+ // File doesn't exist in base commit.
+ assertThat(blameInfos).isEmpty();
+ }
+
+ @Test
+ public void forNewlyAddedFile() throws Exception {
+ PushOneCommit.Result r = createChange("Test Change", "foo.txt", "FOO");
+ List<BlameInfo> blameInfos =
+ gApi.changes().id(r.getChangeId()).current().file("foo.txt").blameRequest().get();
+
+ assertThat(blameInfos).hasSize(1);
+ BlameInfo blameInfo = blameInfos.get(0);
+ assertThat(blameInfo.author).isEqualTo(admin.fullName());
+ assertThat(blameInfo.id).isEqualTo(r.getCommit().getId().name());
+ assertThat(blameInfo.commitMsg).isEqualTo(r.getCommit().getFullMessage());
+ assertThat(blameInfo.time).isEqualTo(r.getCommit().getCommitTime());
+
+ assertThat(blameInfo.ranges).hasSize(1);
+ RangeInfo rangeInfo = blameInfo.ranges.get(0);
+ assertThat(rangeInfo.start).isEqualTo(1);
+ assertThat(rangeInfo.end).isEqualTo(1);
+ }
+
+ @Test
+ public void forNewlyAddedFileFromBase() throws Exception {
+ String changeId = createChange("Test Change", "foo.txt", "FOO").getChangeId();
+ List<BlameInfo> blameInfos =
+ gApi.changes().id(changeId).current().file("foo.txt").blameRequest().forBase(true).get();
+
+ // File doesn't exist in base commit.
+ assertThat(blameInfos).isEmpty();
+ }
+
+ @Test
+ public void forRecreatedFile() throws Exception {
+ // Create change that adds 'foo.txt'.
+ createChange("Change 1", "foo.txt", "FOO");
+
+ // Create change that deletes 'foo.txt'.
+ pushFactory
+ .create(admin.newIdent(), testRepo, "Change 2", "foo.txt", "FOO")
+ .rm("refs/for/master");
+
+ // Create change that recreates 'foo.txt'.
+ PushOneCommit.Result r = createChange("Change 3", "foo.txt", "FOO");
+ List<BlameInfo> blameInfos =
+ gApi.changes().id(r.getChangeId()).current().file("foo.txt").blameRequest().get();
+
+ assertThat(blameInfos).hasSize(1);
+ BlameInfo blameInfo = blameInfos.get(0);
+ assertThat(blameInfo.author).isEqualTo(admin.fullName());
+ assertThat(blameInfo.id).isEqualTo(r.getCommit().getId().name());
+ assertThat(blameInfo.commitMsg).isEqualTo(r.getCommit().getFullMessage());
+ assertThat(blameInfo.time).isEqualTo(r.getCommit().getCommitTime());
+
+ assertThat(blameInfo.ranges).hasSize(1);
+ RangeInfo rangeInfo = blameInfo.ranges.get(0);
+ assertThat(rangeInfo.start).isEqualTo(1);
+ assertThat(rangeInfo.end).isEqualTo(1);
+ }
+
+ @Test
+ public void forRecreatedFileFromBase() throws Exception {
+ // Create change that adds 'foo.txt'.
+ createChange("Change 1", "foo.txt", "FOO");
+
+ // Create change that deletes 'foo.txt'.
+ pushFactory
+ .create(admin.newIdent(), testRepo, "Change 2", "foo.txt", "FOO")
+ .rm("refs/for/master");
+
+ // Create change that recreates 'foo.txt'.
+ String changeId3 = createChange("Change 3", "foo.txt", "FOO").getChangeId();
+ List<BlameInfo> blameInfos =
+ gApi.changes().id(changeId3).current().file("foo.txt").blameRequest().forBase(true).get();
+
+ // File doesn't exist in base commit.
+ assertThat(blameInfos).isEmpty();
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index 057f837188..c27d637edf 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -19,6 +19,7 @@ import static com.google.common.truth.TruthJUnit.assume;
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 java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
@@ -40,6 +41,7 @@ import com.google.gerrit.extensions.common.ChangeType;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.restapi.BinaryResult;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.testing.ConfigSuite;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@@ -400,6 +402,24 @@ public class RevisionDiffIT extends AbstractDaemonTest {
}
@Test
+ public void diffBetweenPatchSetsOfMergeCommitCanBeRetrievedForCommitMessageAndMergeList()
+ throws Exception {
+ PushOneCommit.Result result = createMergeCommitChange("refs/for/master", "my_file.txt");
+ String changeId = result.getChangeId();
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ addModifiedPatchSet(changeId, "my_file.txt", content -> content.concat("Line I\nLine II\n"));
+
+ // Call both of them in succession to ensure that they don't share the same cache keys.
+ DiffInfo commitMessageDiffInfo =
+ getDiffRequest(changeId, CURRENT, COMMIT_MSG).withBase(previousPatchSetId).get();
+ DiffInfo mergeListDiffInfo =
+ getDiffRequest(changeId, CURRENT, MERGE_LIST).withBase(previousPatchSetId).get();
+
+ assertThat(commitMessageDiffInfo).content().hasSize(3);
+ assertThat(mergeListDiffInfo).content().hasSize(1);
+ }
+
+ @Test
public void diffOfUnmodifiedFileMarksAllLinesAsCommon() throws Exception {
String filePath = "a_new_file.txt";
String fileContent = "Line 1\nLine 2\nLine 3\n";
@@ -415,7 +435,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
.content()
.onlyElement()
.commonLines()
- .containsAllOf("Line 1", "Line 2", "Line 3")
+ .containsExactly("Line 1", "Line 2", "Line 3", "")
.inOrder();
assertThat(diffInfo).content().onlyElement().linesOfA().isNull();
assertThat(diffInfo).content().onlyElement().linesOfB().isNull();
@@ -577,6 +597,109 @@ public class RevisionDiffIT extends AbstractDaemonTest {
}
@Test
+ public void addedNewlineAtEndOfFileIsMarkedInDiffWhenOtherwiseOnlyEditsDueToRebaseExist()
+ throws Exception {
+ addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE)
+ .withBase(previousPatchSetId)
+ .get();
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
+ assertThat(diffInfo).content().element(1).isNotNull(); // Line 70 modification
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
+ assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 101");
+ assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 101", "");
+
+ assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101);
+ assertThat(diffInfo).metaB().totalLineCount().isEqualTo(102);
+ }
+
+ @Test
+ // TODO: Fix this issue. This test documents the current behavior and ensures that we at least
+ // don't run into an internal server error.
+ public void addedNewlineAtEndOfFileIsNotIdentifiedAsDueToRebaseEvenThoughItShould()
+ throws Exception {
+ String baseFileContent = FILE_CONTENT.concat("Line 101");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ // Add a comment so that file contents are not 'skipped'. To be able to add a comment, touch
+ // (= modify) the file in the change.
+ addModifiedPatchSet(
+ changeId, FILE_NAME, fileContent -> fileContent.replace("Line 2\n", "Line two\n"));
+ CommentInput comment = createCommentInput(3, 0, 4, 0, "Comment to not skip file content.");
+ addCommentTo(changeId, CURRENT, FILE_NAME, comment);
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = baseFileContent.concat("\n");
+ ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit3);
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL)
+ .withBase(previousPatchSetId)
+ .get();
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
+ assertThat(diffInfo).content().element(1).linesOfA().isNull();
+ assertThat(diffInfo).content().element(1).linesOfB().containsExactly("");
+ // This should actually be isDueToRebase().
+ assertThat(diffInfo).content().element(1).isNotDueToRebase();
+ }
+
+ @Test
+ public void
+ addedNewlineAtEndOfFileIsMarkedWhenEditDueToRebaseIncreasedLineCountAndWhitespaceConsidered()
+ throws Exception {
+ addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line 70\nLine 70.5\n");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE)
+ .withBase(previousPatchSetId)
+ .get();
+ assertThat(diffInfo).content().element(0).commonLines().isNotEmpty();
+ assertThat(diffInfo).content().element(1).isNotNull(); // Line 70.5 insertion
+ assertThat(diffInfo).content().element(2).commonLines().isNotEmpty();
+ assertThat(diffInfo).content().element(3).linesOfA().containsExactly("Line 101");
+ assertThat(diffInfo).content().element(3).linesOfB().containsExactly("Line 101", "");
+
+ assertThat(diffInfo).metaA().totalLineCount().isEqualTo(101);
+ assertThat(diffInfo).metaB().totalLineCount().isEqualTo(103);
+ }
+
+ @Test
+ // TODO: Fix this issue. This test documents the current behavior and ensures that we at least
+ // don't run into an internal server error.
+ public void
+ addedNewlineAtEndOfFileIsNotMarkedWhenEditDueToRebaseIncreasedLineCountAndWhitespaceIgnoredEvenThoughItShould()
+ throws Exception {
+ addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line 70\nLine 70.5\n");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL)
+ .withBase(previousPatchSetId)
+ .get();
+ assertThat(diffInfo).content().element(0).numberOfSkippedLines().isGreaterThan(0);
+ }
+
+ @Test
public void addedLastLineWithoutNewlineBeforeAndAfterwardsIsMarkedInDiff() throws Exception {
addModifiedPatchSet(changeId, FILE_NAME, fileContent -> fileContent.concat("Line 101"));
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
@@ -2363,7 +2486,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
.content()
.element(0)
.commonLines()
- .containsAllOf("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+ .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
.inOrder();
}
@@ -2373,9 +2496,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
- ReviewInput reviewInput = new ReviewInput();
- reviewInput.comments = ImmutableMap.of(FILE_NAME, ImmutableList.of(comment));
- gApi.changes().id(changeId).revision(previousPatchSetId).review(reviewInput);
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
addModifiedPatchSet(
changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n"));
@@ -2389,7 +2510,113 @@ public class RevisionDiffIT extends AbstractDaemonTest {
.content()
.element(0)
.commonLines()
- .containsAllOf("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+ .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+ .inOrder();
+ }
+
+ @Test
+ public void
+ diffOfFileWithOnlyRebaseHunksAndWithCommentAndConsideringWhitespaceReturnsFileContents()
+ throws Exception {
+ addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withBase(previousPatchSetId)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE)
+ .get();
+ // We don't list the full file contents here as that is not the focus of this test.
+ assertThat(diffInfo)
+ .content()
+ .element(0)
+ .commonLines()
+ .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+ .inOrder();
+ }
+
+ @Test
+ public void diffOfFileWithOnlyRebaseHunksAndWithCommentAndIgnoringWhitespaceReturnsFileContents()
+ throws Exception {
+ addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = FILE_CONTENT.replace("Line 70\n", "Line seventy\n");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withBase(previousPatchSetId)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_ALL)
+ .get();
+ // We don't list the full file contents here as that is not the focus of this test.
+ assertThat(diffInfo)
+ .content()
+ .element(0)
+ .commonLines()
+ .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+ .inOrder();
+ }
+
+ @Test
+ public void
+ diffOfFileWithMultilineRebaseHunkAddingNewlineAtEndOfFileAndWithCommentReturnsFileContents()
+ throws Exception {
+ String baseFileContent = FILE_CONTENT.concat("Line 101");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, baseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = baseFileContent.concat("\nLine 102\nLine 103\n");
+ ObjectId commit3 = addCommit(commit2, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit3);
+ CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withBase(previousPatchSetId)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE)
+ .get();
+ // We don't list the full file contents here as that is not the focus of this test.
+ assertThat(diffInfo)
+ .content()
+ .element(0)
+ .commonLines()
+ .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+ .inOrder();
+ }
+
+ @Test
+ public void
+ diffOfFileWithMultilineRebaseHunkRemovingNewlineAtEndOfFileAndWithCommentReturnsFileContents()
+ throws Exception {
+ addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ String newBaseFileContent = FILE_CONTENT.concat("Line 101\nLine 103\nLine 104");
+ ObjectId commit2 = addCommit(commit1, FILE_NAME, newBaseFileContent);
+ rebaseChangeOn(changeId, commit2);
+ CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withBase(previousPatchSetId)
+ .withWhitespace(DiffPreferencesInfo.Whitespace.IGNORE_NONE)
+ .get();
+ // We don't list the full file contents here as that is not the focus of this test.
+ assertThat(diffInfo)
+ .content()
+ .element(0)
+ .commonLines()
+ .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
.inOrder();
}
@@ -2405,6 +2632,44 @@ public class RevisionDiffIT extends AbstractDaemonTest {
assertThat(diffInfo).content().isEmpty();
}
+ // This behavior is likely a bug. A fix might not be easy as it might break syntax highlighting.
+ // TODO: Fix this issue or remove the broken parameter (at least in the documentation).
+ @Test
+ public void contextParameterIsIgnored() throws Exception {
+ addModifiedPatchSet(
+ changeId, FILE_NAME, content -> content.replace("Line 20\n", "Line twenty\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withBase(initialPatchSetId)
+ .withContext(5)
+ .get();
+ assertThat(diffInfo).content().element(0).commonLines().hasSize(19);
+ assertThat(diffInfo).content().element(1).linesOfA().containsExactly("Line 20");
+ assertThat(diffInfo).content().element(1).linesOfB().containsExactly("Line twenty");
+ assertThat(diffInfo).content().element(2).commonLines().hasSize(81);
+ }
+
+ // This behavior is likely a bug. A fix might not be easy as it might break syntax highlighting.
+ // TODO: Fix this issue or remove the broken parameter (at least in the documentation).
+ @Test
+ public void contextParameterIsIgnoredForUnmodifiedFileWithComment() throws Exception {
+ addModifiedPatchSet(
+ changeId, FILE_NAME, content -> content.replace("Line 20\n", "Line twenty\n"));
+ String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
+ CommentInput comment = createCommentInput(20, 0, 21, 0, "Should be 'Line 20'.");
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
+ addModifiedPatchSet(
+ changeId, FILE_NAME2, content -> content.replace("2nd line\n", "Second line\n"));
+
+ DiffInfo diffInfo =
+ getDiffRequest(changeId, CURRENT, FILE_NAME)
+ .withBase(previousPatchSetId)
+ .withContext(5)
+ .get();
+ assertThat(diffInfo).content().element(0).commonLines().hasSize(101);
+ }
+
@Test
public void requestingDiffForOldFileNameOfRenamedFileYieldsReasonableResult() throws Exception {
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
@@ -2435,9 +2700,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
addModifiedPatchSet(changeId, FILE_NAME, content -> content.replace("Line 2\n", "Line two\n"));
String previousPatchSetId = gApi.changes().id(changeId).get().currentRevision;
CommentInput comment = createCommentInput(2, 0, 3, 0, "Should be 'Line 2'.");
- ReviewInput reviewInput = new ReviewInput();
- reviewInput.comments = ImmutableMap.of(FILE_NAME, ImmutableList.of(comment));
- gApi.changes().id(changeId).revision(previousPatchSetId).review(reviewInput);
+ addCommentTo(changeId, previousPatchSetId, FILE_NAME, comment);
String newFilePath = "a_new_file.txt";
gApi.changes().id(changeId).edit().renameFile(FILE_NAME, newFilePath);
gApi.changes().id(changeId).edit().publish();
@@ -2466,6 +2729,14 @@ public class RevisionDiffIT extends AbstractDaemonTest {
return comment;
}
+ private void addCommentTo(
+ String changeId, String previousPatchSetId, String fileName, CommentInput comment)
+ throws RestApiException {
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.comments = ImmutableMap.of(fileName, ImmutableList.of(comment));
+ gApi.changes().id(changeId).revision(previousPatchSetId).review(reviewInput);
+ }
+
private void assertDiffForNewFile(
PushOneCommit.Result pushResult, String path, String expectedContentSideB) throws Exception {
DiffInfo diff =
@@ -2521,7 +2792,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
throws Exception {
testRepo.reset(parentCommit);
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "Adjust files of repo", files);
+ pushFactory.create(admin.newIdent(), testRepo, "Adjust files of repo", files);
PushOneCommit.Result result = push.to("refs/for/master");
return result.getCommit();
}
@@ -2545,7 +2816,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
Arrays.stream(removedFilePaths)
.collect(toMap(Function.identity(), path -> "Irrelevant content"));
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "Remove files from repo", files);
+ pushFactory.create(admin.newIdent(), testRepo, "Remove files from repo", files);
PushOneCommit.Result result = push.rm("refs/for/master");
return result.getCommit();
}
@@ -2564,7 +2835,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
private Result createEmptyChange() throws Exception {
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "Test change", ImmutableMap.of());
+ pushFactory.create(admin.newIdent(), testRepo, "Test change", ImmutableMap.of());
return push.to("refs/for/master");
}
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 8ac8537f6a..237560767b 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -42,6 +42,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
@@ -86,6 +87,7 @@ 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;
@@ -114,9 +116,10 @@ import org.junit.Test;
public class RevisionIT extends AbstractDaemonTest {
- @Inject private GetRevisionActions getRevisionActions;
- @Inject private DynamicSet<PatchSetWebLink> patchSetLinks;
@Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners;
+ @Inject private DynamicSet<PatchSetWebLink> patchSetLinks;
+ @Inject private GetRevisionActions getRevisionActions;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Test
public void reviewTriplet() throws Exception {
@@ -217,24 +220,24 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = project.get() + "~master~" + r.getChangeId();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
revision(r).review(ReviewInput.approve());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
revision(r).review(ReviewInput.recommend());
- setApiUser(admin);
- gApi.changes().id(changeId).reviewer(user.username).deleteVote("Code-Review");
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(changeId).reviewer(user.username()).deleteVote("Code-Review");
Optional<ApprovalInfo> crUser =
get(changeId, DETAILED_LABELS).labels.get("Code-Review").all.stream()
- .filter(a -> a._accountId == user.id.get())
+ .filter(a -> a._accountId == user.id().get())
.findFirst();
assertThat(crUser).isPresent();
assertThat(crUser.get().value).isEqualTo(0);
revision(r).submit();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ReviewInput in = new ReviewInput();
in.label("Code-Review", 1);
in.message = "Still LGTM";
@@ -242,7 +245,7 @@ public class RevisionIT extends AbstractDaemonTest {
ApprovalInfo cr =
gApi.changes().id(changeId).get(DETAILED_LABELS).labels.get("Code-Review").all.stream()
- .filter(a -> a._accountId == user.getId().get())
+ .filter(a -> a._accountId == user.id().get())
.findFirst()
.get();
assertThat(cr.postSubmit).isTrue();
@@ -294,7 +297,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void voteNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("is restricted");
gApi.changes().id(r.getChange().getId().get()).current().review(ReviewInput.approve());
@@ -411,7 +414,7 @@ public class RevisionIT extends AbstractDaemonTest {
String subject = "Test change\n\nChange-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
PushOneCommit push =
pushFactory.create(
- db, admin.getIdent(), testRepo, subject, "another_file.txt", "another content");
+ admin.newIdent(), testRepo, subject, "another_file.txt", "another content");
PushOneCommit.Result r2 = push.to("refs/for/master");
// Change 2's parent should be change 1
@@ -468,8 +471,7 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
@@ -495,8 +497,7 @@ public class RevisionIT extends AbstractDaemonTest {
String destContent = "some content";
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
ImmutableMap.of(PushOneCommit.FILE_NAME, destContent, "foo.txt", "foo"));
@@ -507,8 +508,7 @@ public class RevisionIT extends AbstractDaemonTest {
String changeContent = "another content";
push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
ImmutableMap.of(PushOneCommit.FILE_NAME, changeContent, "bar.txt", "bar"));
@@ -586,7 +586,7 @@ public class RevisionIT extends AbstractDaemonTest {
public void cherryPickToExistingChange() throws Exception {
PushOneCommit.Result r1 =
pushFactory
- .create(db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "a")
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "a")
.to("refs/for/master");
String t1 = project.get() + "~master~" + r1.getChangeId();
@@ -596,7 +596,7 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result r2 =
pushFactory
- .create(db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "b", r1.getChangeId())
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "b", r1.getChangeId())
.to("refs/for/foo");
String t2 = project.get() + "~foo~" + r2.getChangeId();
gApi.changes().id(t2).abandon();
@@ -730,7 +730,7 @@ public class RevisionIT extends AbstractDaemonTest {
// 'user' cherry-picks the change to a new branch, the source change's author/committer('admin')
// will be added as a reviewer of the newly created change.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
CherryPickInput input = new CherryPickInput();
input.message = "it goes to a new branch";
@@ -753,7 +753,7 @@ public class RevisionIT extends AbstractDaemonTest {
input.destination = "branch-3";
input.notify = NotifyHandling.NONE;
input.notifyDetails =
- ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email)));
+ ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email())));
sender.clear();
gApi.changes().id(changeId).current().cherryPick(input);
assertNotifyTo(userToNotify);
@@ -766,13 +766,13 @@ public class RevisionIT extends AbstractDaemonTest {
// Change is created by 'admin'.
PushOneCommit.Result r = createChange();
// Change is approved by 'admin2'. Change is CC'd to 'user'.
- setApiUser(accountCreator.admin2());
+ requestScopeOperations.setApiUser(accountCreator.admin2().id());
ReviewInput in = ReviewInput.approve();
- in.reviewer(user.email, ReviewerState.CC, true);
+ in.reviewer(user.email(), ReviewerState.CC, true);
gApi.changes().id(r.getChangeId()).current().review(in);
// Change is cherrypicked by 'user2'.
- setApiUser(accountCreator.user2());
+ requestScopeOperations.setApiUser(accountCreator.user2().id());
CherryPickInput cin = new CherryPickInput();
cin.message = "this need to go to stable";
cin.destination = "stable";
@@ -786,16 +786,11 @@ public class RevisionIT extends AbstractDaemonTest {
assertThat(result).containsKey(ReviewerState.REVIEWER);
List<Integer> reviewers =
result.get(ReviewerState.REVIEWER).stream().map(a -> a._accountId).collect(toList());
- if (notesMigration.readChanges()) {
- assertThat(result).containsKey(ReviewerState.CC);
- List<Integer> ccs =
- result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
- assertThat(ccs).containsExactly(user.id.get());
- assertThat(reviewers).containsExactly(admin.id.get(), accountCreator.admin2().id.get());
- } else {
- assertThat(reviewers)
- .containsExactly(user.id.get(), admin.id.get(), accountCreator.admin2().id.get());
- }
+ assertThat(result).containsKey(ReviewerState.CC);
+ List<Integer> ccs =
+ result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
+ assertThat(ccs).containsExactly(user.id().get());
+ assertThat(reviewers).containsExactly(admin.id().get(), accountCreator.admin2().id().get());
}
@Test
@@ -856,7 +851,7 @@ public class RevisionIT extends AbstractDaemonTest {
input.base = dstChange.getCommit().name();
input.message = srcChange.getCommit().getFullMessage();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(UnprocessableEntityException.class);
exception.expectMessage(
String.format("Commit %s does not exist on branch refs/heads/foo", input.base));
@@ -877,8 +872,8 @@ public class RevisionIT extends AbstractDaemonTest {
exception.expect(ResourceConflictException.class);
exception.expectMessage(
String.format(
- "Change %s with commit %s is %s",
- change2.getChange().getId().get(), input.base, ChangeStatus.ABANDONED));
+ "Change %s with commit %s is abandoned",
+ change2.getChange().getId().get(), input.base));
gApi.changes().id(change1.getChangeId()).current().cherryPick(input);
}
@@ -915,12 +910,48 @@ public class RevisionIT extends AbstractDaemonTest {
}
@Test
+ public void cherryPickToNonExistingBranch() throws Exception {
+ PushOneCommit.Result result = createChange();
+
+ CherryPickInput input = new CherryPickInput();
+ input.message = "foo bar";
+ input.destination = "non-existing";
+ // TODO(ekempin): This should rather result in an UnprocessableEntityException.
+ BadRequestException thrown =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(result.getChangeId()).current().cherryPick(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ String.format("Branch %s does not exist.", RefNames.REFS_HEADS + input.destination));
+ }
+
+ @Test
+ public void cherryPickToNonExistingBaseCommit() throws Exception {
+ createBranch(new Branch.NameKey(project, "foo"));
+ PushOneCommit.Result result = createChange();
+
+ CherryPickInput input = new CherryPickInput();
+ input.message = "foo bar";
+ input.destination = "foo";
+ input.base = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ UnprocessableEntityException thrown =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () -> gApi.changes().id(result.getChangeId()).current().cherryPick(input));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(String.format("Base %s doesn't exist", input.base));
+ }
+
+ @Test
public void canRebase() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r1 = push.to("refs/for/master");
merge(r1);
- push = pushFactory.create(db, admin.getIdent(), testRepo);
+ push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r2 = push.to("refs/for/master");
boolean canRebase =
gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).canRebase();
@@ -928,7 +959,7 @@ public class RevisionIT extends AbstractDaemonTest {
merge(r2);
testRepo.reset(r1.getCommit());
- push = pushFactory.create(db, admin.getIdent(), testRepo);
+ push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r3 = push.to("refs/for/master");
canRebase = gApi.changes().id(r3.getChangeId()).revision(r3.getCommit().name()).canRebase();
@@ -937,7 +968,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void setUnsetReviewedFlag() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/for/master");
gApi.changes().id(r.getChangeId()).current().setReviewed(PushOneCommit.FILE_NAME, true);
@@ -952,7 +983,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void setUnsetReviewedFlagByFileApi() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/for/master");
gApi.changes().id(r.getChangeId()).current().file(PushOneCommit.FILE_NAME).setReviewed(true);
@@ -971,8 +1002,7 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit push1 =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
@@ -987,8 +1017,7 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit push2 =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
PushOneCommit.FILE_NAME,
@@ -1004,8 +1033,8 @@ public class RevisionIT extends AbstractDaemonTest {
// Make the same change in a separate commit and update server HEAD behind Gerrit's back, which
// will not reindex any open changes.
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
String ref = "refs/heads/master";
assertThat(repo.exactRef(ref).getObjectId()).isEqualTo(r1.getCommit());
tr.update(ref, tr.getRevWalk().parseCommit(initial));
@@ -1123,7 +1152,7 @@ public class RevisionIT extends AbstractDaemonTest {
@Test
public void listFilesOnDifferentBases() throws Exception {
- RevCommit initialCommit = getHead(repo());
+ RevCommit initialCommit = getHead(repo(), "HEAD");
PushOneCommit.Result result1 = createChange();
String changeId = result1.getChangeId();
@@ -1166,7 +1195,7 @@ public class RevisionIT extends AbstractDaemonTest {
public void queryRevisionFiles() throws Exception {
Map<String, String> files = ImmutableMap.of("file1.txt", "content 1", "file2.txt", "content 2");
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo, SUBJECT, files).to("refs/for/master");
+ pushFactory.create(admin.newIdent(), testRepo, SUBJECT, files).to("refs/for/master");
result.assertOkStatus();
String changeId = result.getChangeId();
@@ -1198,7 +1227,7 @@ public class RevisionIT extends AbstractDaemonTest {
public void setDescriptionNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result r = createChange();
assertDescription(r, "");
- setApiUser(user);
+ 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");
@@ -1209,7 +1238,7 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
assertDescription(r, "");
grant(project, "refs/heads/master", Permission.OWNER, false, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).description("test");
assertDescription(r, "test");
}
@@ -1248,14 +1277,7 @@ public class RevisionIT extends AbstractDaemonTest {
public void commit() throws Exception {
WebLinkInfo expectedWebLinkInfo = new WebLinkInfo("foo", "imageUrl", "url");
RegistrationHandle handle =
- patchSetLinks.add(
- "gerrit",
- new PatchSetWebLink() {
- @Override
- public WebLinkInfo getPatchSetWebLink(String projectName, String commit) {
- return expectedWebLinkInfo;
- }
- });
+ patchSetLinks.add("gerrit", (projectName, commit) -> expectedWebLinkInfo);
try {
PushOneCommit.Result r = createChange();
@@ -1340,7 +1362,7 @@ public class RevisionIT extends AbstractDaemonTest {
.get()
.author
.email)
- .isEqualTo(admin.email);
+ .isEqualTo(admin.email());
draftApi.delete();
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).drafts())
@@ -1366,7 +1388,7 @@ public class RevisionIT extends AbstractDaemonTest {
assertThat(out).hasSize(1);
CommentInfo comment = Iterables.getOnlyElement(out.get(FILE_NAME));
assertThat(comment.message).isEqualTo(in.message);
- assertThat(comment.author.email).isEqualTo(admin.email);
+ assertThat(comment.author.email).isEqualTo(admin.email());
assertThat(comment.path).isNull();
List<CommentInfo> list =
@@ -1479,17 +1501,17 @@ public class RevisionIT extends AbstractDaemonTest {
amendChange(r.getChangeId());
// code-review
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
// check if it's blocked to delete a vote on a non-current patch set.
- setApiUser(admin);
+ 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.getId().toString())
+ .reviewer(user.id().toString())
.deleteVote("Code-Review");
}
@@ -1502,27 +1524,27 @@ public class RevisionIT extends AbstractDaemonTest {
amendChange(r.getChangeId());
// code-review
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes()
.id(r.getChangeId())
.current()
- .reviewer(user.getId().toString())
+ .reviewer(user.id().toString())
.deleteVote("Code-Review");
Map<String, Short> m =
- gApi.changes().id(r.getChangeId()).current().reviewer(user.getId().toString()).votes();
+ gApi.changes().id(r.getChangeId()).current().reviewer(user.id().toString()).votes();
assertThat(m).containsExactly("Code-Review", Short.valueOf((short) 0));
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
ChangeMessageInfo message = Iterables.getLast(c.messages);
- assertThat(message.author._accountId).isEqualTo(admin.getId().get());
+ assertThat(message.author._accountId).isEqualTo(admin.id().get());
assertThat(message.message).isEqualTo("Removed Code-Review+1 by User <user@example.com>\n");
assertThat(getReviewers(c.reviewers.get(ReviewerState.REVIEWER)))
- .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+ .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
}
@Test
@@ -1537,22 +1559,22 @@ public class RevisionIT extends AbstractDaemonTest {
List<ApprovalInfo> approvals = votes.get("Code-Review");
assertThat(approvals).hasSize(1);
ApprovalInfo approval = approvals.get(0);
- assertThat(approval._accountId).isEqualTo(admin.id.get());
- assertThat(approval.email).isEqualTo(admin.email);
- assertThat(approval.username).isEqualTo(admin.username);
+ assertThat(approval._accountId).isEqualTo(admin.id().get());
+ assertThat(approval.email).isEqualTo(admin.email());
+ assertThat(approval.username).isEqualTo(admin.username());
// Also vote on it with another user
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(changeId).current().review(ReviewInput.dislike());
// Patch set 1 has 2 votes on Code-Review
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
votes = gApi.changes().id(changeId).current().votes();
assertThat(votes.keySet()).containsExactly("Code-Review");
approvals = votes.get("Code-Review");
assertThat(approvals).hasSize(2);
assertThat(approvals.stream().map(a -> a._accountId))
- .containsExactlyElementsIn(ImmutableList.of(admin.id.get(), user.id.get()));
+ .containsExactlyElementsIn(ImmutableList.of(admin.id().get(), user.id().get()));
// Create a new patch set which does not have any votes
amendChange(changeId);
@@ -1580,7 +1602,7 @@ public class RevisionIT extends AbstractDaemonTest {
throws Exception {
PushOneCommit push =
pushFactory.create(
- db, admin.getIdent(), testRepo, "test commit", "a.txt", content, r.getChangeId());
+ admin.newIdent(), testRepo, "test commit", "a.txt", content, r.getChangeId());
return push.to("refs/for/master");
}
@@ -1597,7 +1619,7 @@ public class RevisionIT extends AbstractDaemonTest {
private PushOneCommit.Result createCherryPickableMerge(
String parent1FileName, String parent2FileName) throws Exception {
- RevCommit initialCommit = getHead(repo());
+ RevCommit initialCommit = getHead(repo(), "HEAD");
String branchAName = "branchA";
createBranch(new Branch.NameKey(project, branchAName));
@@ -1606,19 +1628,18 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result changeAResult =
pushFactory
- .create(db, admin.getIdent(), testRepo, "change a", parent1FileName, "Content of a")
+ .create(admin.newIdent(), testRepo, "change a", parent1FileName, "Content of a")
.to("refs/for/" + branchAName);
testRepo.reset(initialCommit);
PushOneCommit.Result changeBResult =
pushFactory
- .create(db, admin.getIdent(), testRepo, "change b", parent2FileName, "Content of b")
+ .create(admin.newIdent(), testRepo, "change b", parent2FileName, "Content of b")
.to("refs/for/" + branchBName);
PushOneCommit pushableMergeCommit =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"merge",
ImmutableMap.of(parent1FileName, "Content of a", parent2FileName, "Content of b"));
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
index dfd25e25cb..ba228f6e3b 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
@@ -15,7 +15,6 @@
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.acceptance.PushOneCommit.SUBJECT;
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
@@ -25,7 +24,6 @@ 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.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -38,7 +36,6 @@ import com.google.gerrit.extensions.common.FixSuggestionInfo;
import com.google.gerrit.extensions.common.RobotCommentInfo;
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.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -70,8 +67,7 @@ public class RobotCommentsIT extends AbstractDaemonTest {
public void setUp() throws Exception {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"Provide files which can be used for fixes",
ImmutableMap.of(FILE_NAME, FILE_CONTENT, FILE_NAME2, FILE_CONTENT2));
@@ -85,8 +81,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void retrievingRobotCommentsBeforeAddingAnyDoesNotRaiseAnException() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
Map<String, List<RobotCommentInfo>> robotComments =
gApi.changes().id(changeId).current().robotComments();
@@ -96,8 +90,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void addedRobotCommentsCanBeRetrieved() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
RobotCommentInput in = createRobotCommentInput();
addRobotComment(changeId, in);
@@ -110,12 +102,10 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void addedRobotCommentsCanBeRetrievedByChange() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
RobotCommentInput in = createRobotCommentInput();
addRobotComment(changeId, in);
- pushFactory.create(db, admin.getIdent(), testRepo, changeId).to("refs/for/master");
+ pushFactory.create(admin.newIdent(), testRepo, changeId).to("refs/for/master");
RobotCommentInput in2 = createRobotCommentInput();
addRobotComment(changeId, in2);
@@ -133,8 +123,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void robotCommentsCanBeRetrievedAsList() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
RobotCommentInput robotCommentInput = createRobotCommentInput();
addRobotComment(changeId, robotCommentInput);
@@ -148,8 +136,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void specificRobotCommentCanBeRetrieved() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
RobotCommentInput robotCommentInput = createRobotCommentInput();
addRobotComment(changeId, robotCommentInput);
@@ -163,8 +149,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void robotCommentWithoutOptionalFieldsCanBeAdded() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
RobotCommentInput in = createRobotCommentInputWithMandatoryFields();
addRobotComment(changeId, in);
@@ -176,8 +160,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void hugeRobotCommentIsRejected() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
int defaultSizeLimit = 1024 * 1024;
int sizeOfRest = 451;
fixReplacementInfo.replacement = getStringFor(defaultSizeLimit - sizeOfRest + 1);
@@ -189,8 +171,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void reasonablyLargeRobotCommentIsAccepted() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
int defaultSizeLimit = 1024 * 1024;
int sizeOfRest = 451;
fixReplacementInfo.replacement = getStringFor(defaultSizeLimit - sizeOfRest);
@@ -204,8 +184,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "change.robotCommentSizeLimit", value = "10k")
public void maximumAllowedSizeOfRobotCommentCanBeAdjusted() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
int sizeLimit = 10 * 1024;
fixReplacementInfo.replacement = getStringFor(sizeLimit);
@@ -217,8 +195,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
@GerritConfig(name = "change.robotCommentSizeLimit", value = "0")
public void zeroForMaximumAllowedSizeOfRobotCommentRemovesRestriction() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
int defaultSizeLimit = 1024 * 1024;
fixReplacementInfo.replacement = getStringFor(defaultSizeLimit);
@@ -232,8 +208,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@GerritConfig(name = "change.robotCommentSizeLimit", value = "-1")
public void negativeValueForMaximumAllowedSizeOfRobotCommentRemovesRestriction()
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
int defaultSizeLimit = 1024 * 1024;
fixReplacementInfo.replacement = getStringFor(defaultSizeLimit);
@@ -245,8 +219,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void addedFixSuggestionCanBeRetrieved() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -255,8 +227,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixIdIsGeneratedForFixSuggestion() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -270,8 +240,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void descriptionOfFixSuggestionIsAcceptedAsIs() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -284,8 +252,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void descriptionOfFixSuggestionIsMandatory() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixSuggestionInfo.description = null;
exception.expect(BadRequestException.class);
@@ -298,8 +264,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void addedFixReplacementCanBeRetrieved() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -312,8 +276,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixReplacementsAreMandatory() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixSuggestionInfo.replacements = Collections.emptyList();
exception.expect(BadRequestException.class);
@@ -327,8 +289,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void pathOfFixReplacementIsAcceptedAsIs() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -343,8 +303,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void pathOfFixReplacementIsMandatory() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = null;
exception.expect(BadRequestException.class);
@@ -357,8 +315,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void rangeOfFixReplacementIsAcceptedAsIs() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -373,8 +329,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void rangeOfFixReplacementIsMandatory() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.range = null;
exception.expect(BadRequestException.class);
@@ -387,8 +341,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void rangeOfFixReplacementNeedsToBeValid() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.range = createRange(13, 9, 5, 10);
exception.expect(BadRequestException.class);
exception.expectMessage("Range (13:9 - 5:10)");
@@ -398,8 +350,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void rangesOfFixReplacementsOfSameFixSuggestionForSameFileMayNotOverlap()
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -422,8 +372,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void rangesOfFixReplacementsOfSameFixSuggestionForDifferentFileMayOverlap()
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -447,8 +395,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void rangesOfFixReplacementsOfDifferentFixSuggestionsForSameFileMayOverlap()
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -472,8 +418,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixReplacementsDoNotNeedToBeOrderedAccordingToRange() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -501,8 +445,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void replacementStringOfFixReplacementIsAcceptedAsIs() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
addRobotComment(changeId, withFixRobotCommentInput);
List<RobotCommentInfo> robotCommentInfos = getRobotComments();
@@ -517,8 +459,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void replacementStringOfFixReplacementIsMandatory() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.replacement = null;
exception.expect(BadRequestException.class);
@@ -532,8 +472,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixWithinALineCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME;
fixReplacementInfo.replacement = "Modified content";
fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -557,8 +495,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixSpanningMultipleLinesCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME;
fixReplacementInfo.replacement = "Modified content\n5";
fixReplacementInfo.range = createRange(3, 2, 5, 3);
@@ -581,8 +517,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixWithTwoCloseReplacementsOnSameFileCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -615,8 +549,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void twoFixesOnSameFileCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -650,8 +582,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void twoConflictingFixesOnSameFileCannotBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 1);
@@ -679,8 +609,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void twoFixesOfSameRobotCommentCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -714,8 +642,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixReferringToDifferentFileThanRobotCommentCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME2;
fixReplacementInfo.range = createRange(2, 0, 3, 0);
fixReplacementInfo.replacement = "Modified content\n";
@@ -736,8 +662,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixInvolvingTwoFilesCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
FixReplacementInfo fixReplacementInfo1 = new FixReplacementInfo();
fixReplacementInfo1.path = FILE_NAME;
fixReplacementInfo1.range = createRange(2, 0, 3, 0);
@@ -775,8 +699,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixReferringToNonExistentFileCannotBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = "a_non_existent_file.txt";
fixReplacementInfo.range = createRange(1, 0, 2, 0);
fixReplacementInfo.replacement = "Modified content\n";
@@ -792,8 +714,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixOnPreviousPatchSetWithoutChangeEditCannotBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME;
fixReplacementInfo.replacement = "Modified content";
fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -815,8 +735,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixOnPreviousPatchSetWithExistingChangeEditCanBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
// Create an empty change edit.
gApi.changes().id(changeId).edit().create();
@@ -849,8 +767,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixOnCurrentPatchSetWithChangeEditOnPreviousPatchSetCannotBeApplied()
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
// Create an empty change edit.
gApi.changes().id(changeId).edit().create();
@@ -874,8 +790,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void fixDoesNotModifyCommitMessageOfChangeEdit() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
String changeEditCommitMessage = "This is the commit message of the change edit.\n";
gApi.changes().id(changeId).edit().modifyCommitMessage(changeEditCommitMessage);
@@ -897,8 +811,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void applyingFixTwiceIsIdempotent() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME;
fixReplacementInfo.replacement = "Modified content";
fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -922,8 +834,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void nonExistentFixCannotBeApplied() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME;
fixReplacementInfo.replacement = "Modified content";
fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -941,8 +851,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void applyingFixReturnsEditInfoForCreatedChangeEdit() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
fixReplacementInfo.path = FILE_NAME;
fixReplacementInfo.replacement = "Modified content";
fixReplacementInfo.range = createRange(3, 1, 3, 3);
@@ -964,8 +872,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void applyingFixOnTopOfChangeEditReturnsEditInfoForUpdatedChangeEdit() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
gApi.changes().id(changeId).edit().create();
fixReplacementInfo.path = FILE_NAME;
@@ -989,7 +895,6 @@ public class RobotCommentsIT extends AbstractDaemonTest {
@Test
public void createdChangeEditIsBasedOnCurrentPatchSet() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
String currentRevision = gApi.changes().id(changeId).get().currentRevision;
fixReplacementInfo.path = FILE_NAME;
@@ -1008,43 +913,22 @@ public class RobotCommentsIT extends AbstractDaemonTest {
}
@Test
- public void robotCommentsNotSupportedWithoutNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
-
- RobotCommentInput in = createRobotCommentInput();
- ReviewInput reviewInput = new ReviewInput();
- Map<String, List<RobotCommentInput>> robotComments = new HashMap<>();
- robotComments.put(in.path, ImmutableList.of(in));
- reviewInput.robotComments = robotComments;
- reviewInput.message = "comment test";
-
- exception.expect(MethodNotAllowedException.class);
- exception.expectMessage("robot comments not supported");
- gApi.changes().id(changeId).current().review(reviewInput);
- }
-
- @Test
- public void queryChangesWithUnresolvedCommentCount() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
+ public void queryChangesWithCommentCounts() throws Exception {
PushOneCommit.Result r1 = createChange();
PushOneCommit.Result r2 =
pushFactory
- .create(
- db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
.to("refs/for/master");
addRobotComment(r2.getChangeId(), createRobotCommentInputWithMandatoryFields());
- AcceptanceTestRequestScope.Context ctx = disableDb();
- try {
+ try (AutoCloseable ignored = disableNoteDb()) {
ChangeInfo result = Iterables.getOnlyElement(query(r2.getChangeId()));
// currently, we create all robot comments as 'resolved' by default.
// if we allow users to resolve a robot comment, then this test should
// be modified.
assertThat(result.unresolvedCommentCount).isEqualTo(0);
- } finally {
- enableDb(ctx);
+ assertThat(result.totalCommentCount).isEqualTo(1);
}
}
@@ -1122,7 +1006,7 @@ public class RobotCommentsIT extends AbstractDaemonTest {
assertThat(c.line).isEqualTo(expected.line);
assertThat(c.message).isEqualTo(expected.message);
- assertThat(c.author.email).isEqualTo(admin.email);
+ assertThat(c.author.email).isEqualTo(admin.email());
if (expectPath) {
assertThat(c.path).isEqualTo(expected.path);
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 7a67fba910..3a2a50d95d 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -24,6 +24,7 @@ import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assert
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;
@@ -35,6 +36,8 @@ 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.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;
@@ -43,7 +46,6 @@ 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.InheritableBoolean;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -51,12 +53,12 @@ import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.common.FileInfo;
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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.change.ChangeEdits.EditMessage;
@@ -65,7 +67,6 @@ 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.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import java.io.IOException;
import java.sql.Timestamp;
@@ -79,7 +80,6 @@ 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.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -95,7 +95,8 @@ public class ChangeEditIT extends AbstractDaemonTest {
private static final String CONTENT_NEW2_STR = "quxÄÜÖßµ";
private static final byte[] CONTENT_NEW2 = CONTENT_NEW2_STR.getBytes(UTF_8);
- @Inject private SchemaFactory<ReviewDb> reviewDbProvider;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
private String changeId;
private String changeId2;
@@ -113,17 +114,11 @@ public class ChangeEditIT extends AbstractDaemonTest {
@Before
public void setUp() throws Exception {
- db = reviewDbProvider.open();
- changeId = newChange(admin.getIdent());
+ changeId = newChange(admin.newIdent());
ps = getCurrentPatchSet(changeId);
assertThat(ps).isNotNull();
- amendChange(admin.getIdent(), changeId);
- changeId2 = newChange2(admin.getIdent());
- }
-
- @After
- public void cleanup() {
- db.close();
+ amendChange(admin.newIdent(), changeId);
+ changeId2 = newChange2(admin.newIdent());
}
@Test
@@ -147,7 +142,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
@Test
public void deleteEditOfOlderPatchSet() throws Exception {
createArbitraryEditFor(changeId2);
- amendChange(admin.getIdent(), changeId2);
+ amendChange(admin.newIdent(), changeId2);
gApi.changes().id(changeId2).edit().delete();
assertThat(getEdit(changeId2)).isAbsent();
@@ -158,7 +153,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
createArbitraryEditFor(changeId);
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(changeId).addReviewer(in);
PublishChangeEditInput publishInput = new PublishChangeEditInput();
@@ -213,7 +208,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
@Test
public void publishEditNotifyRest() throws Exception {
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(changeId).addReviewer(in);
createArbitraryEditFor(changeId);
@@ -228,7 +223,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
@Test
public void publishEditWithDefaultNotify() throws Exception {
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(changeId).addReviewer(in);
createArbitraryEditFor(changeId);
@@ -246,20 +241,11 @@ public class ChangeEditIT extends AbstractDaemonTest {
}
@Test
- public void publishEditRestWithoutCLA() throws Exception {
- createArbitraryEditFor(changeId);
- setUseContributorAgreements(InheritableBoolean.TRUE);
- adminRestSession.post(urlPublish(changeId)).assertForbidden();
- setUseContributorAgreements(InheritableBoolean.FALSE);
- adminRestSession.post(urlPublish(changeId)).assertNoContent();
- }
-
- @Test
public void rebaseEdit() throws Exception {
PatchSet previousPatchSet = getCurrentPatchSet(changeId2);
createEmptyEditFor(changeId2);
gApi.changes().id(changeId2).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
- amendChange(admin.getIdent(), changeId2);
+ amendChange(admin.newIdent(), changeId2);
PatchSet currentPatchSet = getCurrentPatchSet(changeId2);
Optional<EditInfo> originalEdit = getEdit(changeId2);
@@ -278,7 +264,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
PatchSet previousPatchSet = getCurrentPatchSet(changeId2);
createEmptyEditFor(changeId2);
gApi.changes().id(changeId2).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
- amendChange(admin.getIdent(), changeId2);
+ amendChange(admin.newIdent(), changeId2);
PatchSet currentPatchSet = getCurrentPatchSet(changeId2);
Optional<EditInfo> originalEdit = getEdit(changeId2);
@@ -301,8 +287,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
assertThat(edit).value().baseRevision().isEqualTo(currentPatchSet.getRevision().get());
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
FILE_NAME,
@@ -337,7 +322,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
public void updateRootCommitMessage() throws Exception {
// Re-clone empty repo; TestRepository doesn't let us reset to unborn head.
testRepo = cloneProject(project);
- changeId = newChange(admin.getIdent());
+ changeId = newChange(admin.newIdent());
createEmptyEditFor(changeId);
Optional<EditInfo> edit = getEdit(changeId);
@@ -471,6 +456,16 @@ public class ChangeEditIT extends AbstractDaemonTest {
}
@Test
+ public void renameExistingFileToInvalidPath() throws Exception {
+ createEmptyEditFor(changeId);
+ BadRequestException badRequest =
+ assertThrows(
+ BadRequestException.class,
+ () -> gApi.changes().id(changeId).edit().renameFile(FILE_NAME, "invalid/path/"));
+ assertThat(badRequest.getMessage()).isEqualTo("Invalid path: invalid/path/");
+ }
+
+ @Test
public void createEditByDeletingExistingFileRest() throws Exception {
adminRestSession.delete(urlEditFile(changeId, FILE_NAME)).assertNoContent();
assertThat(getFileContentOfEdit(changeId, FILE_NAME)).isAbsent();
@@ -663,11 +658,11 @@ public class ChangeEditIT extends AbstractDaemonTest {
gApi.changes().id(changeId2).edit().publish(publishInput);
assertThat(queryEdits()).isEmpty();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
createEmptyEditFor(changeId);
assertThat(queryEdits()).hasSize(1);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
assertThat(queryEdits()).isEmpty();
}
@@ -708,7 +703,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
@Test
public void createEditWithoutPushPatchSetPermission() throws Exception {
// Create new project with clean permissions
- Project.NameKey p = createProject("addPatchSetEdit");
+ Project.NameKey p = projectOperations.newProject().create();
// Clone repository as user
TestRepository<InMemoryRepository> userTestRepo = cloneProject(p, user);
@@ -716,7 +711,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
// Create change as user
- PushOneCommit push = pushFactory.create(db, user.getIdent(), userTestRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
PushOneCommit.Result r1 = push.to("refs/for/master");
r1.assertOkStatus();
@@ -771,14 +766,13 @@ public class ChangeEditIT extends AbstractDaemonTest {
private String newChange(PersonIdent ident) throws Exception {
PushOneCommit push =
pushFactory.create(
- db, ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_OLD, UTF_8));
+ ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_OLD, UTF_8));
return push.to("refs/for/master").getChangeId();
}
private String amendChange(PersonIdent ident, String changeId) throws Exception {
PushOneCommit push =
pushFactory.create(
- db,
ident,
testRepo,
PushOneCommit.SUBJECT,
@@ -791,7 +785,7 @@ public class ChangeEditIT extends AbstractDaemonTest {
private String newChange2(PersonIdent ident) throws Exception {
PushOneCommit push =
pushFactory.create(
- db, ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_OLD, UTF_8));
+ ident, testRepo, PushOneCommit.SUBJECT, FILE_NAME, new String(CONTENT_OLD, UTF_8));
return push.rm("refs/for/master").getChangeId();
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java b/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
index 9b5fd7ab8c..8ebb2bdaaf 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractForcePush.java
@@ -36,7 +36,7 @@ public abstract class AbstractForcePush extends AbstractDaemonTest {
public void forcePushNotAllowed() throws Exception {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
@@ -46,7 +46,7 @@ public abstract class AbstractForcePush extends AbstractDaemonTest {
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push2.setForce(true);
PushOneCommit.Result r2 = push2.to("refs/heads/master");
r2.assertErrorStatus("not permitted: force update");
@@ -57,7 +57,7 @@ public abstract class AbstractForcePush extends AbstractDaemonTest {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
grant(project, "refs/*", Permission.PUSH, true);
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
@@ -67,7 +67,7 @@ public abstract class AbstractForcePush extends AbstractDaemonTest {
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push2.setForce(true);
PushOneCommit.Result r2 = push2.to("refs/heads/master");
r2.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractGitOverHttpServlet.java b/javatests/com/google/gerrit/acceptance/git/AbstractGitOverHttpServlet.java
index 2f54f88508..fe1c264647 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractGitOverHttpServlet.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractGitOverHttpServlet.java
@@ -16,8 +16,15 @@ package com.google.gerrit.acceptance.git;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gerrit.server.AuditEvent;
-import java.util.Collections;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.FakeGroupAuditService;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.audit.HttpAuditEvent;
+import com.google.inject.Inject;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
@@ -25,17 +32,20 @@ import org.junit.Before;
import org.junit.Test;
public class AbstractGitOverHttpServlet extends AbstractPushForReview {
+ @Inject protected FakeGroupAuditService auditService;
@Before
public void beforeEach() throws Exception {
CredentialsProvider.setDefault(
- new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
+ new UsernamePasswordCredentialsProvider(admin.username(), admin.httpPassword()));
selectProtocol(AbstractPushForReview.Protocol.HTTP);
- auditService.clearEvents();
+ // Don't clear audit events here, since we can't guarantee all test setup has run yet.
}
@Test
+ @Sandboxed
public void receivePackAuditEventLog() throws Exception {
+ auditService.drainHttpAuditEvents();
testRepo
.git()
.push()
@@ -43,26 +53,47 @@ public class AbstractGitOverHttpServlet extends AbstractPushForReview {
.setRefSpecs(new RefSpec("HEAD:refs/for/master"))
.call();
- // Git smart protocol makes two requests:
- // https://github.com/git/git/blob/master/Documentation/technical/http-protocol.txt
- assertThat(auditService.auditEvents.size()).isEqualTo(2);
+ ImmutableList<HttpAuditEvent> auditEvents = auditService.drainHttpAuditEvents();
+ assertThat(auditEvents).hasSize(2);
- AuditEvent e = auditService.auditEvents.get(1);
- assertThat(e.who.getAccountId()).isEqualTo(admin.id);
- assertThat(e.what).endsWith("/git-receive-pack");
- assertThat(e.params).isEmpty();
+ HttpAuditEvent lsRemote = auditEvents.get(0);
+ assertThat(lsRemote.who.getAccountId()).isEqualTo(admin.id());
+ assertThat(lsRemote.what).endsWith("/info/refs?service=git-receive-pack");
+ assertThat(lsRemote.params).containsExactly("service", "git-receive-pack");
+ assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+
+ HttpAuditEvent receivePack = auditEvents.get(1);
+ assertThat(receivePack.who.getAccountId()).isEqualTo(admin.id());
+ assertThat(receivePack.what).endsWith("/git-receive-pack");
+ assertThat(receivePack.params).isEmpty();
+ assertThat(receivePack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
+ @Sandboxed
public void uploadPackAuditEventLog() throws Exception {
+ auditService.drainHttpAuditEvents();
+ // testRepo is already a clone. Make a server-side change so we have something to fetch.
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<?> testRepo = new TestRepository<>(repo)) {
+ testRepo.branch("master").commit().create();
+ }
testRepo.git().fetch().call();
- assertThat(auditService.auditEvents.size()).isEqualTo(1);
+ ImmutableList<HttpAuditEvent> auditEvents = auditService.drainHttpAuditEvents();
+ assertThat(auditEvents).hasSize(2);
+
+ HttpAuditEvent lsRemote = auditEvents.get(0);
+ // Repo URL doesn't include /a, so fetching doesn't cause authentication.
+ assertThat(lsRemote.who).isInstanceOf(AnonymousUser.class);
+ assertThat(lsRemote.what).endsWith("/info/refs?service=git-upload-pack");
+ assertThat(lsRemote.params).containsExactly("service", "git-upload-pack");
+ assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
- AuditEvent e = auditService.auditEvents.get(0);
- assertThat(e.who.toString()).isEqualTo("ANONYMOUS");
- assertThat(e.params.get("service"))
- .containsExactlyElementsIn(Collections.singletonList("git-upload-pack"));
- assertThat(e.what).endsWith("service=git-upload-pack");
+ HttpAuditEvent uploadPack = auditEvents.get(1);
+ assertThat(lsRemote.who).isInstanceOf(AnonymousUser.class);
+ assertThat(uploadPack.what).endsWith("/git-upload-pack");
+ assertThat(uploadPack.params).isEmpty();
+ assertThat(uploadPack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 2acc3cd084..216677d271 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -18,7 +18,6 @@ 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.Truth8.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.assertPushRejected;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
@@ -51,8 +50,10 @@ 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.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
@@ -132,6 +133,7 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
+@SkipProjectClone
public abstract class AbstractPushForReview extends AbstractDaemonTest {
protected enum Protocol {
// TODO(dborowitz): TEST.
@@ -139,6 +141,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
HTTP
}
+ @Inject private RequestScopeOperations requestScopeOperations;
+
+ private static String NEW_CHANGE_INDICATOR = " [NEW]";
private LabelType patchSetLock;
@Inject private DynamicSet<CommitValidationListener> commitValidators;
@@ -173,10 +178,10 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@After
public void resetPublishCommentOnPushOption() throws Exception {
- setApiUser(admin);
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+ requestScopeOperations.setApiUser(admin.id());
+ GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
prefs.publishCommentsOnPush = false;
- gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+ gApi.accounts().id(admin.id().get()).setPreferences(prefs);
}
protected void selectProtocol(Protocol p) throws Exception {
@@ -230,6 +235,60 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
@TestProjectInput(createEmptyCommit = false)
+ public void pushInitialCommitSeriesForMasterBranch() throws Exception {
+ testPushInitialCommitSeriesForMasterBranch();
+ }
+
+ @Test
+ @TestProjectInput(createEmptyCommit = false)
+ public void pushInitialCommitSeriesForMasterBranchWithCreateNewChangeForAllNotInTarget()
+ throws Exception {
+ enableCreateNewChangeForAllNotInTarget();
+ testPushInitialCommitSeriesForMasterBranch();
+ }
+
+ private void testPushInitialCommitSeriesForMasterBranch() throws Exception {
+ RevCommit c = testRepo.commit().message("Initial commit").insertChangeId().create();
+ String id = GitUtil.getChangeId(testRepo, c).get();
+ testRepo.reset(c);
+
+ RevCommit c2 = testRepo.commit().parent(c).message("Second commit").insertChangeId().create();
+ String id2 = GitUtil.getChangeId(testRepo, c2).get();
+ testRepo.reset(c2);
+
+ String r = "refs/for/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ ChangeInfo change = gApi.changes().id(id).info();
+ assertThat(change.branch).isEqualTo("master");
+ assertThat(change.status).isEqualTo(ChangeStatus.NEW);
+
+ ChangeInfo change2 = gApi.changes().id(id2).info();
+ assertThat(change2.branch).isEqualTo("master");
+ assertThat(change2.status).isEqualTo(ChangeStatus.NEW);
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ assertThat(repo.resolve("master")).isNull();
+ }
+
+ gApi.changes().id(change.id).current().review(ReviewInput.approve());
+ gApi.changes().id(change.id).current().submit();
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ assertThat(repo.resolve("master")).isEqualTo(c);
+ }
+
+ gApi.changes().id(change2.id).current().review(ReviewInput.approve());
+ gApi.changes().id(change2.id).current().submit();
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ assertThat(repo.resolve("master")).isEqualTo(c2);
+ }
+ }
+
+ @Test
+ @TestProjectInput(createEmptyCommit = false)
public void validateConnected() throws Exception {
RevCommit c = testRepo.commit().message("Initial commit").insertChangeId().create();
testRepo.reset(c);
@@ -270,8 +329,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
testRepo
.commit()
.message("Initial commit")
- .author(admin.getIdent())
- .committer(admin.getIdent())
+ .author(admin.newIdent())
+ .committer(admin.newIdent())
.insertChangeId()
.create();
String id = GitUtil.getChangeId(testRepo, c).get();
@@ -303,8 +362,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
testRepo
.commit()
.message("Initial commit")
- .author(admin.getIdent())
- .committer(admin.getIdent())
+ .author(admin.newIdent())
+ .committer(admin.newIdent())
.insertChangeId()
.create();
testRepo.reset(c);
@@ -327,7 +386,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r1.assertOkStatus();
r1.assertChange(Change.Status.NEW, null);
r1.assertMessage(
- "New changes:\n " + url + id1 + " " + r1.getCommit().getShortMessage() + "\n");
+ url + id1 + " " + r1.getCommit().getShortMessage() + NEW_CHANGE_INDICATOR + "\n");
testRepo.reset(initialHead);
String newMsg = r1.getCommit().getShortMessage() + " v2";
@@ -339,7 +398,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
.create();
PushOneCommit.Result r2 =
pushFactory
- .create(db, admin.getIdent(), testRepo, "another commit", "b.txt", "bbb")
+ .create(admin.newIdent(), testRepo, "another commit", "b.txt", "bbb")
.to("refs/for/master");
Change.Id id2 = r2.getChange().getId();
r2.assertOkStatus();
@@ -347,18 +406,17 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r2.assertMessage(
"success\n"
+ "\n"
- + "New changes:\n"
- + " "
- + url
- + id2
- + " another commit\n"
- + "\n"
- + "Updated changes:\n"
+ " "
+ url
+ id1
+ " "
+ newMsg
+ + "\n"
+ + " "
+ + url
+ + id2
+ + " another commit"
+ + NEW_CHANGE_INDICATOR
+ "\n");
}
@@ -390,8 +448,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
.branch("HEAD")
.commit()
.message("A change")
- .author(admin.getIdent())
- .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()))
+ .author(admin.newIdent())
+ .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()))
.create();
PushResult result = pushHead(testRepo, "refs/for/master");
assertThat(result.getMessages()).contains("warning: pushing without Change-Id is deprecated");
@@ -444,7 +502,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
List<String> pushOptions = new ArrayList<>();
pushOptions.add(topicOption);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
push.setPushOptions(pushOptions);
PushOneCommit.Result r = push.to("refs/for/master");
@@ -477,11 +535,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
pwi.filter = "*";
pwi.notifyNewChanges = true;
projectsToWatch.add(pwi);
- setApiUser(user3);
+ requestScopeOperations.setApiUser(user3.id());
gApi.accounts().self().setWatchedProjects(projectsToWatch);
TestAccount user2 = accountCreator.user2();
- String pushSpec = "refs/for/master%reviewer=" + user.email + ",cc=" + user2.email;
+ String pushSpec = "refs/for/master%reviewer=" + user.email() + ",cc=" + user2.email();
sender.clear();
PushOneCommit.Result r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE);
@@ -499,48 +557,44 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r.assertOkStatus();
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- if (notesMigration.readChanges()) {
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
- } else {
- // CCs are considered reviewers in the storage layer and so get notified.
- assertThat(m.rcpt()).containsExactly(user.emailAddress, user2.emailAddress);
- }
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
sender.clear();
r = pushTo(pushSpec + ",notify=" + NotifyHandling.ALL);
r.assertOkStatus();
assertThat(sender.getMessages()).hasSize(1);
m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress, user2.emailAddress, user3.emailAddress);
+ assertThat(m.rcpt())
+ .containsExactly(user.getEmailAddress(), user2.getEmailAddress(), user3.getEmailAddress());
sender.clear();
- r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + user3.email);
+ r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + user3.email());
r.assertOkStatus();
assertNotifyTo(user3);
sender.clear();
- r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + user3.email);
+ r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + user3.email());
r.assertOkStatus();
assertNotifyCc(user3);
sender.clear();
- r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + user3.email);
+ r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + user3.email());
r.assertOkStatus();
assertNotifyBcc(user3);
// request that sender gets notified as TO, CC and BCC, email should be sent
// even if the sender is the only recipient
sender.clear();
- pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + admin.email);
+ pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + admin.email());
assertNotifyTo(admin);
sender.clear();
- r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + admin.email);
+ r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + admin.email());
r.assertOkStatus();
assertNotifyCc(admin);
sender.clear();
- r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + admin.email);
+ r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + admin.email());
r.assertOkStatus();
assertNotifyBcc(admin);
}
@@ -549,7 +603,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
public void pushForMasterWithCc() throws Exception {
// cc one user
String topic = "my/topic";
- PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%cc=" + user.email);
+ PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%cc=" + user.email());
r.assertOkStatus();
r.assertChange(Change.Status.NEW, topic, ImmutableList.of(), ImmutableList.of(user));
@@ -559,11 +613,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
"refs/for/master/"
+ topic
+ "%cc="
- + admin.email
+ + admin.email()
+ ",cc="
- + user.email
+ + user.email()
+ ",cc="
- + accountCreator.user2().email);
+ + accountCreator.user2().email());
r.assertOkStatus();
// Check that admin isn't CC'd as they own the change
r.assertChange(
@@ -579,11 +633,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
"refs/for/master/"
+ topic
+ "%cc="
- + admin.email
+ + admin.email()
+ ",cc="
+ nonExistingEmail
+ ",cc="
- + user.email);
+ + user.email());
r.assertErrorStatus(nonExistingEmail + " does not identify a registered user or group");
}
@@ -595,22 +649,18 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
PushOneCommit.Result r =
pushTo("refs/for/master%cc=non.existing.1@example.com,cc=non.existing.2@example.com");
- if (notesMigration.readChanges()) {
- r.assertOkStatus();
+ r.assertOkStatus();
- ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
- ImmutableList<AccountInfo> ccs =
- firstNonNull(ci.reviewers.get(ReviewerState.CC), ImmutableList.<AccountInfo>of()).stream()
- .sorted(comparing((AccountInfo a) -> a.email))
- .collect(toImmutableList());
- assertThat(ccs).hasSize(2);
- assertThat(ccs.get(0).email).isEqualTo("non.existing.1@example.com");
- assertThat(ccs.get(0)._accountId).isNull();
- assertThat(ccs.get(1).email).isEqualTo("non.existing.2@example.com");
- assertThat(ccs.get(1)._accountId).isNull();
- } else {
- r.assertErrorStatus("non.existing.1@example.com does not identify a registered user");
- }
+ ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
+ ImmutableList<AccountInfo> ccs =
+ firstNonNull(ci.reviewers.get(ReviewerState.CC), ImmutableList.<AccountInfo>of()).stream()
+ .sorted(comparing((AccountInfo a) -> a.email))
+ .collect(toImmutableList());
+ assertThat(ccs).hasSize(2);
+ assertThat(ccs.get(0).email).isEqualTo("non.existing.1@example.com");
+ assertThat(ccs.get(0)._accountId).isNull();
+ assertThat(ccs.get(1).email).isEqualTo("non.existing.2@example.com");
+ assertThat(ccs.get(1)._accountId).isNull();
}
@Test
@@ -619,7 +669,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
String group = name("group");
GroupInput gin = new GroupInput();
gin.name = group;
- gin.members = ImmutableList.of(user.username, user2.username);
+ gin.members = ImmutableList.of(user.username(), user2.username());
gin.visibleToAll = true; // TODO(dborowitz): Shouldn't be necessary; see ReviewerAdder.
gApi.groups().create(gin);
@@ -632,7 +682,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
public void pushForMasterWithReviewer() throws Exception {
// add one reviewer
String topic = "my/topic";
- PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%r=" + user.email);
+ PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%r=" + user.email());
r.assertOkStatus();
r.assertChange(Change.Status.NEW, topic, user);
@@ -644,11 +694,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
"refs/for/master/"
+ topic
+ "%r="
- + admin.email
+ + admin.email()
+ ",r="
- + user.email
+ + user.email()
+ ",r="
- + user2.email);
+ + user2.email());
r.assertOkStatus();
// admin is the owner of the change and should not appear as reviewer
r.assertChange(Change.Status.NEW, topic, user, user2);
@@ -660,11 +710,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
"refs/for/master/"
+ topic
+ "%r="
- + admin.email
+ + admin.email()
+ ",r="
+ nonExistingEmail
+ ",r="
- + user.email);
+ + user.email());
r.assertErrorStatus(nonExistingEmail + " does not identify a registered user or group");
}
@@ -676,23 +726,19 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
PushOneCommit.Result r =
pushTo("refs/for/master%r=non.existing.1@example.com,r=non.existing.2@example.com");
- if (notesMigration.readChanges()) {
- r.assertOkStatus();
+ r.assertOkStatus();
- ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
- ImmutableList<AccountInfo> reviewers =
- firstNonNull(ci.reviewers.get(ReviewerState.REVIEWER), ImmutableList.<AccountInfo>of())
- .stream()
- .sorted(comparing((AccountInfo a) -> a.email))
- .collect(toImmutableList());
- assertThat(reviewers).hasSize(2);
- assertThat(reviewers.get(0).email).isEqualTo("non.existing.1@example.com");
- assertThat(reviewers.get(0)._accountId).isNull();
- assertThat(reviewers.get(1).email).isEqualTo("non.existing.2@example.com");
- assertThat(reviewers.get(1)._accountId).isNull();
- } else {
- r.assertErrorStatus("non.existing.1@example.com does not identify a registered user");
- }
+ ChangeInfo ci = get(r.getChangeId(), DETAILED_LABELS);
+ ImmutableList<AccountInfo> reviewers =
+ firstNonNull(ci.reviewers.get(ReviewerState.REVIEWER), ImmutableList.<AccountInfo>of())
+ .stream()
+ .sorted(comparing((AccountInfo a) -> a.email))
+ .collect(toImmutableList());
+ assertThat(reviewers).hasSize(2);
+ assertThat(reviewers.get(0).email).isEqualTo("non.existing.1@example.com");
+ assertThat(reviewers.get(0)._accountId).isNull();
+ assertThat(reviewers.get(1).email).isEqualTo("non.existing.2@example.com");
+ assertThat(reviewers.get(1)._accountId).isNull();
}
@Test
@@ -701,7 +747,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
String group = name("group");
GroupInput gin = new GroupInput();
gin.name = group;
- gin.members = ImmutableList.of(user.username, user2.username);
+ gin.members = ImmutableList.of(user.username(), user2.username());
gin.visibleToAll = true; // TODO(dborowitz): Shouldn't be necessary; see ReviewerAdder.
gApi.groups().create(gin);
@@ -807,9 +853,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
public void pushWorkInProgressChangeWhenNotOwner() throws Exception {
TestRepository<?> userRepo = cloneProject(project, user);
PushOneCommit.Result r =
- pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master%wip");
+ pushFactory.create(user.newIdent(), userRepo).to("refs/for/master%wip");
r.assertOkStatus();
- assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+ assertThat(r.getChange().change().getOwner()).isEqualTo(user.id());
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
// Admin user trying to move from WIP to ready should succeed.
@@ -824,7 +870,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(r.getChange().change().isWorkInProgress()).isTrue();
// Push as change owner to move change from WIP to ready.
- r = pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master%ready");
+ r = pushFactory.create(user.newIdent(), userRepo).to("refs/for/master%ready");
r.assertOkStatus();
assertThat(r.getChange().change().isWorkInProgress()).isFalse();
@@ -869,8 +915,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(edit).isPresent();
EditInfo editInfo = edit.get();
r.assertMessage(
- "Updated Changes:\n "
- + canonicalWebUrl.get()
+ canonicalWebUrl.get()
+ "c/"
+ project.get()
+ "/+/"
@@ -906,8 +951,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
enableCreateNewChangeForAllNotInTarget();
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
// %2C is comma; the value below tests that percent decoding happens after splitting.
// All three ways of representing space ("%20", "+", and "_" are also exercised.
PushOneCommit.Result r = push.to("refs/for/master/%m=my_test%20+_message%2Cm=");
@@ -915,8 +959,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -993,8 +1036,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1015,8 +1057,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"c.txt",
@@ -1034,8 +1075,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1063,8 +1103,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Create a commit with different forged author and committer.
RevCommit c =
commitBuilder()
- .author(user.getIdent())
- .committer(user2.getIdent())
+ .author(user.newIdent())
+ .committer(user2.newIdent())
.add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
.message(PushOneCommit.SUBJECT)
.create();
@@ -1072,13 +1112,13 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
pushHead(testRepo, "refs/for/master");
String changeId = GitUtil.getChangeId(testRepo, c).get();
- assertThat(getOwnerEmail(changeId)).isEqualTo(admin.email);
+ assertThat(getOwnerEmail(changeId)).isEqualTo(admin.email());
assertThat(getReviewerEmails(changeId, ReviewerState.REVIEWER))
- .containsExactly(user.email, user2.email);
+ .containsExactly(user.email(), user2.email());
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).rcpt())
- .containsExactly(user.emailAddress, user2.emailAddress);
+ .containsExactly(user.getEmailAddress(), user2.getEmailAddress());
}
@Test
@@ -1087,23 +1127,23 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// First patch set has author and committer matching change owner.
PushOneCommit.Result r = pushTo("refs/for/master");
- assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email);
+ assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email());
assertThat(getReviewerEmails(r.getChangeId(), ReviewerState.REVIEWER)).isEmpty();
amendBuilder()
- .author(user.getIdent())
- .committer(user2.getIdent())
+ .author(user.newIdent())
+ .committer(user2.newIdent())
.add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT + "2")
.create();
pushHead(testRepo, "refs/for/master");
- assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email);
+ assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email());
assertThat(getReviewerEmails(r.getChangeId(), ReviewerState.REVIEWER))
- .containsExactly(user.email, user2.email);
+ .containsExactly(user.email(), user2.email());
assertThat(sender.getMessages()).hasSize(1);
assertThat(sender.getMessages().get(0).rcpt())
- .containsExactly(user.emailAddress, user2.emailAddress);
+ .containsExactly(user.getEmailAddress(), user2.getEmailAddress());
}
/**
@@ -1121,8 +1161,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Create a commit with "User" as author and committer
RevCommit c =
commitBuilder()
- .author(user.getIdent())
- .committer(user.getIdent())
+ .author(user.newIdent())
+ .committer(user.newIdent())
.add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
.message(PushOneCommit.SUBJECT)
.create();
@@ -1141,11 +1181,11 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
get(GitUtil.getChangeId(testRepo, c).get(), DETAILED_LABELS, MESSAGES, DETAILED_ACCOUNTS);
LabelInfo cr = ci.labels.get("Code-Review");
assertThat(cr.all).hasSize(2);
- int indexAdmin = admin.fullName.equals(cr.all.get(0).name) ? 0 : 1;
+ int indexAdmin = admin.fullName().equals(cr.all.get(0).name) ? 0 : 1;
int indexUser = indexAdmin == 0 ? 1 : 0;
- assertThat(cr.all.get(indexAdmin).name).isEqualTo(admin.fullName);
+ assertThat(cr.all.get(indexAdmin).name).isEqualTo(admin.fullName());
assertThat(cr.all.get(indexAdmin).value.intValue()).isEqualTo(1);
- assertThat(cr.all.get(indexUser).name).isEqualTo(user.fullName);
+ assertThat(cr.all.get(indexUser).name).isEqualTo(user.fullName());
assertThat(cr.all.get(indexUser).value.intValue()).isEqualTo(0);
assertThat(Iterables.getLast(ci.messages).message)
.isEqualTo("Uploaded patch set 1: Code-Review+1.");
@@ -1167,8 +1207,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
RevCommit c =
commitBuilder()
- .author(admin.getIdent())
- .committer(admin.getIdent())
+ .author(admin.newIdent())
+ .committer(admin.newIdent())
.add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
.message(PushOneCommit.SUBJECT)
.create();
@@ -1209,8 +1249,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r.assertOkStatus();
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1225,8 +1264,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r.assertOkStatus();
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1258,9 +1296,6 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushForMasterWithHashtags() throws Exception {
- // Hashtags only work when reading from NoteDB is enabled
- assume().that(notesMigration.readChanges()).isTrue();
-
// specify a single hashtag as option
String hashtag1 = "tag1";
Set<String> expected = ImmutableSet.of(hashtag1);
@@ -1275,8 +1310,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
String hashtag2 = "tag2";
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1291,9 +1325,6 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushForMasterWithMultipleHashtags() throws Exception {
- // Hashtags only work when reading from NoteDB is enabled
- assume().that(notesMigration.readChanges()).isTrue();
-
// specify multiple hashtags as options
String hashtag1 = "tag1";
String hashtag2 = "tag2";
@@ -1311,8 +1342,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
String hashtag4 = "tag4";
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1326,18 +1356,10 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
}
@Test
- public void pushForMasterWithHashtagsNoteDbDisabled() throws Exception {
- // Push with hashtags should fail when reading from NoteDb is disabled.
- assume().that(notesMigration.readChanges()).isFalse();
- PushOneCommit.Result r = pushTo("refs/for/master%hashtag=tag1");
- r.assertErrorStatus("cannot add hashtags; noteDb is disabled");
- }
-
- @Test
public void pushCommitUsingSignedOffBy() throws Exception {
PushOneCommit push =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
@@ -1346,11 +1368,10 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT
- + String.format("\n\nSigned-off-by: %s <%s>", admin.fullName, admin.email),
+ + String.format("\n\nSigned-off-by: %s <%s>", admin.fullName(), admin.email()),
"b.txt",
"anotherContent");
r = push.to("refs/for/master");
@@ -1358,7 +1379,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
push =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
r = push.to("refs/for/master");
r.assertErrorStatus("not Signed-off-by author/committer/uploader in message footer");
}
@@ -1368,14 +1389,13 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
enableCreateNewChangeForAllNotInTarget();
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
push =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
r = push.to("refs/for/master");
r.assertOkStatus();
@@ -1393,8 +1413,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// create a change as admin
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
RevCommit commitChange1 = r.getCommit();
@@ -1405,7 +1424,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
userRepo.reset("change");
push =
pushFactory.create(
- db, user.getIdent(), userRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ user.newIdent(), userRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
r = push.to("refs/for/master");
r.assertOkStatus();
@@ -1423,14 +1442,13 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
PushOneCommit push =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
PushResult pr =
GitUtil.pushHead(testRepo, "refs/for/foo%base=" + rBase.getCommit().name(), false, false);
- assertThat(pr.getMessages()).containsMatch("changes: .*new: 1.*done");
// BatchUpdate implementations differ in how they hook into progress monitors. We mostly just
// care that there is a new change.
@@ -1443,14 +1461,13 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
enableCreateNewChangeForAllNotInTarget();
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
push =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
r = push.to("refs/for/master");
r.assertOkStatus();
@@ -1465,14 +1482,13 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
enableCreateNewChangeForAllNotInTarget();
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
push =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
r = push.to("refs/for/master");
r.assertOkStatus();
@@ -1558,6 +1574,32 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
}
@Test
+ public void pushWithChangeIdAboveFooter() throws Exception {
+ testPushWithChangeIdAboveFooter();
+ }
+
+ @Test
+ public void pushWithChangeIdAboveFooterWithCreateNewChangeForAllNotInTarget() throws Exception {
+ enableCreateNewChangeForAllNotInTarget();
+ testPushWithChangeIdAboveFooter();
+ }
+
+ private void testPushWithChangeIdAboveFooter() throws Exception {
+ RevCommit c =
+ createCommit(
+ testRepo,
+ PushOneCommit.SUBJECT
+ + "\n\n"
+ + "Change-Id: Ied70ea827f5bf968f1f6aaee6594e07c846d217a\n\n"
+ + "More text, uh oh.\n");
+ assertThat(GitUtil.getChangeId(testRepo, c)).isEmpty();
+ pushForReviewRejected(testRepo, "Change-Id must be in message footer");
+
+ setRequireChangeId(InheritableBoolean.FALSE);
+ pushForReviewRejected(testRepo, "Change-Id must be in message footer");
+ }
+
+ @Test
public void errorMessageFormat() throws Exception {
RevCommit c = createCommit(testRepo, "Message without Change-Id");
assertThat(GitUtil.getChangeId(testRepo, c)).isEmpty();
@@ -1580,8 +1622,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r.assertOkStatus();
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT
+ "\n\n"
@@ -1676,8 +1717,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushCommitWithSameChangeIdAsPredecessorChange() throws Exception {
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
RevCommit commitChange1 = r.getCommit();
@@ -1756,8 +1796,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
Change.Id id2 = r2.getChange().getId();
// Merge change 1 behind Gerrit's back.
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<?> tr = new TestRepository<>(repo)) {
tr.branch("refs/heads/master").update(r1.getCommit());
}
@@ -1786,7 +1826,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Added a new patch set and auto-closed the change.
cd = byChangeId(id);
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(cd.change().isMerged()).isTrue();
assertThat(getPatchSetRevisions(cd))
.containsExactlyEntriesIn(
ImmutableMap.of(1, ps1Rev, 2, testRepo.getRepository().resolve("HEAD").name()));
@@ -1804,7 +1844,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Change not updated.
cd = byChangeId(id);
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.NEW);
+ assertThat(cd.change().isNew()).isTrue();
assertThat(getPatchSetRevisions(cd)).containsExactlyEntriesIn(ImmutableMap.of(1, ps1Rev));
}
@@ -1812,7 +1852,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
public void forcePushAbandonedChange() throws Exception {
grant(project, "refs/*", Permission.PUSH, true);
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r = push1.to("refs/for/master");
r.assertOkStatus();
@@ -1836,12 +1876,12 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
Change c = r.getChange().change();
RevCommit ps2Commit;
- try (Repository repo = repoManager.openRepository(project)) {
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<?> tr = new TestRepository<>(repo)) {
// Create a new patch set of the change directly in Gerrit's repository,
// without pushing it. In reality it's more likely that the client would
// create and push this behind Gerrit's back (e.g. an admin accidentally
// using direct ssh access to the repo), but that's harder to do in tests.
- TestRepository<?> tr = new TestRepository<>(repo);
ps2Commit =
tr.branch("refs/heads/master")
.commit()
@@ -1854,7 +1894,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
testRepo.reset(ps2Commit);
ChangeData cd = byCommit(ps1Commit);
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.NEW);
+ assertThat(cd.change().isNew()).isTrue();
assertThat(getPatchSetRevisions(cd))
.containsExactlyEntriesIn(ImmutableMap.of(1, ps1Commit.name()));
return c.getId();
@@ -1862,12 +1902,12 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void pushWithEmailInFooter() throws Exception {
- pushWithReviewerInFooter(user.emailAddress.toString(), user);
+ pushWithReviewerInFooter(user.getEmailAddress().toString(), user);
}
@Test
public void pushWithNameInFooter() throws Exception {
- pushWithReviewerInFooter(user.fullName, user);
+ pushWithReviewerInFooter(user.fullName(), user);
}
@Test
@@ -1893,8 +1933,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
r.assertOkStatus();
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
PushOneCommit.SUBJECT,
"b.txt",
@@ -1958,8 +1997,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
gApi.changes().id(r.getChangeId()).current().submit();
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
tr.branch("refs/heads/branch").commit().message("Initial commit on branch").create();
}
@@ -2017,8 +2056,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// expecting the change to be auto-closed, but the change metadata update
// fails.
ObjectId c2;
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
RevCommit commit2 =
tr.amend(c1).message("New subject").insertChangeId(r.getChangeId().substring(1)).create();
c2 = commit2.copy();
@@ -2051,7 +2090,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(getPublishedComments(r.getChangeId())).isEmpty();
- gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+ gApi.changes().id(r.getChangeId()).addReviewer(user.email());
sender.clear();
amendChange(r.getChangeId(), "refs/for/master%publish-comments");
@@ -2165,9 +2204,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(getPublishedComments(r.getChangeId())).isEmpty();
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+ GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
prefs.publishCommentsOnPush = true;
- gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+ gApi.accounts().id(admin.id().get()).setPreferences(prefs);
r = amendChange(r.getChangeId());
assertThat(getPublishedComments(r.getChangeId()).stream().map(c -> c.message))
@@ -2179,9 +2218,9 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
addDraft(r.getChangeId(), r.getCommit().name(), newDraft(FILE_NAME, 1, "comment1"));
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+ GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
prefs.publishCommentsOnPush = true;
- gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+ gApi.accounts().id(admin.id().get()).setPreferences(prefs);
r = amendChange(r.getChangeId(), "refs/for/master%no-publish-comments");
@@ -2190,7 +2229,6 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
@Test
public void noEditAndUpdateAllUsersInSameChangeStack() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
List<RevCommit> commits = createChanges(2, "refs/for/master");
String id2 = byCommit(commits.get(1)).change().getKey().get();
addDraft(id2, commits.get(1).name(), newDraft(FILE_NAME, 1, "comment2"));
@@ -2337,7 +2375,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
try {
// Validation listener is called on normal push
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r = push.to(master);
r.assertOkStatus();
assertThat(validator.count()).isEqualTo(1);
@@ -2345,7 +2383,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Push is rejected and validation listener is not called when not allowed
// to use skip option
PushOneCommit push2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push2.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION));
r = push2.to(master);
r.assertErrorStatus("not permitted: skip validation");
@@ -2354,7 +2392,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
// Validation listener is not called when skip option is used
grantSkipValidation(project, master, SystemGroupBackend.REGISTERED_USERS);
PushOneCommit push3 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push3.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION));
r = push3.to(master);
r.assertOkStatus();
@@ -2365,7 +2403,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
TestValidator validator2 = new TestValidator(true);
handle2 = commitValidators.add("test-validator-2", validator2);
PushOneCommit push4 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push4.setPushOptions(ImmutableList.of(PUSH_OPTION_SKIP_VALIDATION));
r = push4.to(master);
r.assertOkStatus();
@@ -2382,16 +2420,6 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
}
@Test
- public void pushToPublishMagicBranchIsAllowed() throws Exception {
- // Push to "refs/publish/*" will be a synonym of "refs/for/*".
- createChange("refs/publish/master");
- PushOneCommit.Result result = pushTo("refs/publish/master");
- result.assertOkStatus();
- assertThat(result.getMessage())
- .endsWith("Pushing to refs/publish/* is deprecated, use refs/for/* instead.\n");
- }
-
- @Test
public void pushNoteDbRef() throws Exception {
String ref = "refs/changes/34/1234/meta";
RevCommit c = testRepo.commit().message("Junk NoteDb commit").create();
@@ -2438,6 +2466,181 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertPushOk(pr, "refs/heads/permitted");
}
+ @Test
+ public void pushCommitsWithSameTreeNoChanges() throws Exception {
+ RevCommit c =
+ testRepo
+ .commit()
+ .message("Foo")
+ .parent(getHead(testRepo.getRepository(), "HEAD"))
+ .insertChangeId()
+ .create();
+ testRepo.reset(c);
+
+ String r = "refs/for/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ RevCommit amended = testRepo.amend(c).create();
+ testRepo.reset(amended);
+
+ pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+ assertThat(pr.getMessages())
+ .contains(
+ "warning: no changes between prior commit "
+ + c.abbreviate(7).name()
+ + " and new commit "
+ + amended.abbreviate(7).name());
+ }
+
+ @Test
+ public void pushCommitsWithSameTreeNoFilesChangedMessageUpdated() throws Exception {
+ RevCommit c =
+ testRepo
+ .commit()
+ .message("Foo")
+ .parent(getHead(testRepo.getRepository(), "HEAD"))
+ .insertChangeId()
+ .create();
+ String id = GitUtil.getChangeId(testRepo, c).get();
+ testRepo.reset(c);
+
+ String r = "refs/for/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ RevCommit amended =
+ testRepo.amend(c).message("Foo Bar").insertChangeId(id.substring(1)).create();
+ testRepo.reset(amended);
+
+ pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+ assertThat(pr.getMessages())
+ .contains(
+ "warning: " + amended.abbreviate(7).name() + ": no files changed, message updated");
+ }
+
+ @Test
+ public void pushCommitsWithSameTreeNoFilesChangedAuthorChanged() throws Exception {
+ RevCommit c =
+ testRepo
+ .commit()
+ .message("Foo")
+ .parent(getHead(testRepo.getRepository(), "HEAD"))
+ .insertChangeId()
+ .create();
+ testRepo.reset(c);
+
+ String r = "refs/for/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ RevCommit amended = testRepo.amend(c).author(user.newIdent()).create();
+ testRepo.reset(amended);
+
+ pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+ assertThat(pr.getMessages())
+ .contains(
+ "warning: " + amended.abbreviate(7).name() + ": no files changed, author changed");
+ }
+
+ @Test
+ public void pushCommitsWithSameTreeNoFilesChangedWasRebased() throws Exception {
+ RevCommit head = getHead(testRepo.getRepository(), "HEAD");
+ RevCommit c = testRepo.commit().message("Foo").parent(head).insertChangeId().create();
+ testRepo.reset(c);
+
+ String r = "refs/for/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ testRepo.reset(head);
+ RevCommit newBase = testRepo.commit().message("Base").parent(head).insertChangeId().create();
+ testRepo.reset(newBase);
+
+ pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ testRepo.reset(c);
+ RevCommit amended = testRepo.amend(c).parent(newBase).create();
+ testRepo.reset(amended);
+
+ pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+ assertThat(pr.getMessages())
+ .contains("warning: " + amended.abbreviate(7).name() + ": no files changed, was rebased");
+ }
+
+ @Test
+ public void sequentialCommitMessages() throws Exception {
+ String url = canonicalWebUrl.get() + "c/" + project.get() + "/+/";
+ ObjectId initialHead = testRepo.getRepository().resolve("HEAD");
+
+ PushOneCommit.Result r1 = pushTo("refs/for/master");
+ Change.Id id1 = r1.getChange().getId();
+ r1.assertOkStatus();
+ r1.assertChange(Change.Status.NEW, null);
+ r1.assertMessage(
+ url + id1 + " " + r1.getCommit().getShortMessage() + NEW_CHANGE_INDICATOR + "\n");
+
+ PushOneCommit.Result r2 = pushTo("refs/for/master");
+ Change.Id id2 = r2.getChange().getId();
+ r2.assertOkStatus();
+ r2.assertChange(Change.Status.NEW, null);
+ r2.assertMessage(
+ url + id2 + " " + r2.getCommit().getShortMessage() + NEW_CHANGE_INDICATOR + "\n");
+
+ testRepo.reset(initialHead);
+
+ // rearrange the commit so that change no. 2 is the parent of change no. 1
+ String r1Message = "Position 2";
+ String r2Message = "Position 1";
+ testRepo
+ .branch("HEAD")
+ .commit()
+ .message(r2Message)
+ .insertChangeId(r2.getChangeId().substring(1))
+ .create();
+ testRepo
+ .branch("HEAD")
+ .commit()
+ .message(r1Message)
+ .insertChangeId(r1.getChangeId().substring(1))
+ .create();
+
+ PushOneCommit.Result r3 =
+ pushFactory
+ .create(admin.newIdent(), testRepo, "another commit", "b.txt", "bbb")
+ .to("refs/for/master");
+ Change.Id id3 = r3.getChange().getId();
+ r3.assertOkStatus();
+ r3.assertChange(Change.Status.NEW, null);
+ // should display commit r2, r1, r3 in that order.
+ r3.assertMessage(
+ "success\n"
+ + "\n"
+ + " "
+ + url
+ + id2
+ + " "
+ + r2Message
+ + "\n"
+ + " "
+ + url
+ + id1
+ + " "
+ + r1Message
+ + "\n"
+ + " "
+ + url
+ + id3
+ + " another commit"
+ + NEW_CHANGE_INDICATOR
+ + "\n");
+ }
+
private DraftInput newDraft(String path, int line, String message) {
DraftInput d = new DraftInput();
d.path = path;
@@ -2468,7 +2671,7 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(ci.reviewers).isNotNull();
assertThat(ci.reviewers.keySet()).containsExactly(ReviewerState.REVIEWER);
assertThat(ci.reviewers.get(ReviewerState.REVIEWER).iterator().next().email)
- .isEqualTo(reviewer.email);
+ .isEqualTo(reviewer.email());
}
private void pushWithReviewerInFooter(String nameEmail, TestAccount expectedReviewer)
@@ -2482,9 +2685,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.getId());
+ assertThat(cd.reviewers().all()).named(name).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.getId().toString()).remove();
+ gApi.changes().id(cd.getId().get()).reviewer(expectedReviewer.id().toString()).remove();
}
assertThat(byCommit(c).reviewers().all()).named(name).isEmpty();
}
@@ -2495,7 +2698,7 @@ 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.getId());
+ assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.id());
} else {
assertThat(byCommit(c).reviewers().all()).named(name).isEmpty();
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
new file mode 100644
index 0000000000..c02a5edc87
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmitOnPush.java
@@ -0,0 +1,384 @@
+// 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.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 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.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.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.inject.Inject;
+import java.util.List;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.api.errors.TransportException;
+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.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class AbstractSubmitOnPush extends AbstractDaemonTest {
+ @Inject private ApprovalsUtil approvalsUtil;
+
+ @Before
+ public void blockAnonymous() throws Exception {
+ blockAnonymousRead();
+ }
+
+ @Test
+ public void submitOnPush() throws Exception {
+ grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ PushOneCommit.Result r = pushTo("refs/for/master%submit");
+ r.assertOkStatus();
+ r.assertChange(Change.Status.MERGED, null, admin);
+ assertSubmitApproval(r.getPatchSetId());
+ assertCommit(project, "refs/heads/master");
+ }
+
+ @Test
+ public void submitOnPushToRefsMetaConfig() throws Exception {
+ grant(project, "refs/for/refs/meta/config", Permission.SUBMIT);
+
+ git().fetch().setRefSpecs(new RefSpec("refs/meta/config:refs/meta/config")).call();
+ testRepo.reset(RefNames.REFS_CONFIG);
+
+ PushOneCommit.Result r = pushTo("refs/for/refs/meta/config%submit");
+ r.assertOkStatus();
+ r.assertChange(Change.Status.MERGED, null, admin);
+ assertSubmitApproval(r.getPatchSetId());
+ assertCommit(project, RefNames.REFS_CONFIG);
+ }
+
+ @Test
+ public void submitOnPushMergeConflict() throws Exception {
+ ObjectId objectId = repo().exactRef("HEAD").getObjectId();
+ push("refs/heads/master", "one change", "a.txt", "some content");
+ testRepo.reset(objectId);
+
+ grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ PushOneCommit.Result r =
+ push("refs/for/master%submit", "other change", "a.txt", "other content");
+ r.assertErrorStatus();
+ r.assertChange(Change.Status.NEW, null);
+ r.assertMessage(
+ "Change " + r.getChange().getId() + ": change could not be merged due to a path conflict.");
+ }
+
+ @Test
+ public void submitOnPushSuccessfulMerge() throws Exception {
+ String master = "refs/heads/master";
+ ObjectId objectId = repo().exactRef("HEAD").getObjectId();
+ push(master, "one change", "a.txt", "some content");
+ testRepo.reset(objectId);
+
+ grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ PushOneCommit.Result r =
+ push("refs/for/master%submit", "other change", "b.txt", "other content");
+ r.assertOkStatus();
+ r.assertChange(Change.Status.MERGED, null, admin);
+ assertMergeCommit(master, "other change");
+ }
+
+ @Test
+ public void submitOnPushNewPatchSet() throws Exception {
+ PushOneCommit.Result r =
+ push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
+
+ grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
+ r =
+ push(
+ "refs/for/master%submit",
+ PushOneCommit.SUBJECT, "a.txt", "other content", r.getChangeId());
+ r.assertOkStatus();
+ r.assertChange(Change.Status.MERGED, null, admin);
+ ChangeData cd = Iterables.getOnlyElement(queryProvider.get().byKeyPrefix(r.getChangeId()));
+ assertThat(cd.patchSets()).hasSize(2);
+ assertSubmitApproval(r.getPatchSetId());
+ assertCommit(project, "refs/heads/master");
+ }
+
+ @Test
+ public void submitOnPushNotAllowed_Error() throws Exception {
+ PushOneCommit.Result r = pushTo("refs/for/master%submit");
+ r.assertErrorStatus("not permitted: update by submit");
+ }
+
+ @Test
+ public void submitOnPushNewPatchSetNotAllowed_Error() throws Exception {
+ PushOneCommit.Result r =
+ push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
+
+ r =
+ push(
+ "refs/for/master%submit",
+ PushOneCommit.SUBJECT, "a.txt", "other content", r.getChangeId());
+ r.assertErrorStatus("not permitted: update by submit ");
+ }
+
+ @Test
+ public void submitOnPushToNonExistingBranch_Error() throws Exception {
+ String branchName = "non-existing";
+ PushOneCommit.Result r = pushTo("refs/for/" + branchName + "%submit");
+ r.assertErrorStatus("branch " + branchName + " not found");
+ }
+
+ @Test
+ public void mergeOnPushToBranch() throws Exception {
+ grant(project, "refs/heads/master", Permission.PUSH);
+ PushOneCommit.Result r =
+ push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
+ r.assertOkStatus();
+
+ git().push().setRefSpecs(new RefSpec(r.getCommit().name() + ":refs/heads/master")).call();
+ assertCommit(project, "refs/heads/master");
+
+ ChangeData cd =
+ Iterables.getOnlyElement(queryProvider.get().byKey(new Change.Key(r.getChangeId())));
+ RevCommit c = r.getCommit();
+ PatchSet.Id psId = cd.currentPatchSet().getId();
+ 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());
+ }
+
+ @Test
+ public void correctNewRevOnMergeByPushToBranch() throws Exception {
+ grant(project, "refs/heads/master", Permission.PUSH);
+ 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());
+ }
+
+ @Test
+ public void mergeOnPushToBranchWithChangeMergedInOther() throws Exception {
+ 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();
+ pushCommitTo(masterRev, other);
+ PushOneCommit.Result r = createChange();
+ r.assertOkStatus();
+ RevCommit commit = r.getCommit();
+ pushCommitTo(commit, master);
+ assertCommit(project, master);
+ ChangeData cd =
+ Iterables.getOnlyElement(queryProvider.get().byKey(new Change.Key(r.getChangeId())));
+ assertThat(cd.change().isMerged()).isTrue();
+
+ RemoteRefUpdate.Status status = pushCommitTo(commit, "refs/for/other");
+ assertThat(status).isEqualTo(RemoteRefUpdate.Status.OK);
+
+ pushCommitTo(commit, other);
+ assertCommit(project, other);
+
+ for (ChangeData c : queryProvider.get().byKey(new Change.Key(r.getChangeId()))) {
+ if (c.change().getDest().get().equals(other)) {
+ assertThat(c.change().isMerged()).isTrue();
+ }
+ }
+ }
+
+ private RemoteRefUpdate.Status pushCommitTo(RevCommit commit, String ref)
+ throws GitAPIException, InvalidRemoteException, TransportException {
+ return Iterables.getOnlyElement(
+ git().push().setRefSpecs(new RefSpec(commit.name() + ":" + ref)).call())
+ .getRemoteUpdate(ref)
+ .getStatus();
+ }
+
+ @Test
+ public void mergeOnPushToBranchWithNewPatchset() throws Exception {
+ grant(project, "refs/heads/master", Permission.PUSH);
+ PushOneCommit.Result r = pushTo("refs/for/master");
+ r.assertOkStatus();
+ RevCommit c1 = r.getCommit();
+ PatchSet.Id psId1 = r.getPatchSetId();
+ assertThat(psId1.get()).isEqualTo(1);
+
+ PushOneCommit push =
+ pushFactory.create(
+ admin.newIdent(),
+ testRepo,
+ PushOneCommit.SUBJECT,
+ "b.txt",
+ "anotherContent",
+ r.getChangeId());
+
+ r = push.to("refs/heads/master");
+ r.assertOkStatus();
+
+ ChangeData cd = r.getChange();
+ RevCommit c2 = r.getCommit();
+ assertThat(cd.change().isMerged()).isTrue();
+ PatchSet.Id psId2 = cd.change().currentPatchSetId();
+ assertThat(psId2.get()).isEqualTo(2);
+ assertCommit(project, "refs/heads/master");
+ 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());
+ }
+
+ @Test
+ public void mergeOnPushToBranchWithOldPatchset() throws Exception {
+ grant(project, "refs/heads/master", Permission.PUSH);
+ PushOneCommit.Result r = pushTo("refs/for/master");
+ r.assertOkStatus();
+ RevCommit c1 = r.getCommit();
+ PatchSet.Id psId1 = r.getPatchSetId();
+ String changeId = r.getChangeId();
+ assertThat(psId1.get()).isEqualTo(1);
+
+ r = amendChange(changeId);
+ ChangeData cd = r.getChange();
+ PatchSet.Id psId2 = cd.change().currentPatchSetId();
+ assertThat(psId2.getParentKey()).isEqualTo(psId1.getParentKey());
+ assertThat(psId2.get()).isEqualTo(2);
+
+ testRepo.reset(c1);
+ assertPushOk(pushHead(testRepo, "refs/heads/master", false), "refs/heads/master");
+
+ cd = changeDataFactory.create(project, psId1.getParentKey());
+ Change c = cd.change();
+ assertThat(c.isMerged()).isTrue();
+ assertThat(c.currentPatchSetId()).isEqualTo(psId1);
+ assertThat(cd.patchSets().stream().map(PatchSet::getId).collect(toList()))
+ .containsExactly(psId1, psId2);
+ }
+
+ @Test
+ public void mergeMultipleOnPushToBranchWithNewPatchset() throws Exception {
+ grant(project, "refs/heads/master", Permission.PUSH);
+
+ // Create 2 changes.
+ ObjectId initialHead = getRemoteHead();
+ PushOneCommit.Result r1 = createChange("Change 1", "a", "a");
+ r1.assertOkStatus();
+ PushOneCommit.Result r2 = createChange("Change 2", "b", "b");
+ r2.assertOkStatus();
+
+ RevCommit c1_1 = r1.getCommit();
+ RevCommit c2_1 = r2.getCommit();
+ PatchSet.Id psId1_1 = r1.getPatchSetId();
+ PatchSet.Id psId2_1 = r2.getPatchSetId();
+ assertThat(c1_1.getParent(0)).isEqualTo(initialHead);
+ assertThat(c2_1.getParent(0)).isEqualTo(c1_1);
+
+ // Amend both changes.
+ testRepo.reset(initialHead);
+ RevCommit c1_2 =
+ testRepo
+ .branch("HEAD")
+ .commit()
+ .message(c1_1.getShortMessage() + "v2")
+ .insertChangeId(r1.getChangeId().substring(1))
+ .create();
+ RevCommit c2_2 = testRepo.cherryPick(c2_1);
+
+ // Push directly to branch.
+ assertPushOk(pushHead(testRepo, "refs/heads/master", false), "refs/heads/master");
+
+ ChangeData cd2 = r2.getChange();
+ 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());
+
+ 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());
+ }
+
+ private PatchSetApproval getSubmitter(PatchSet.Id patchSetId) throws Exception {
+ ChangeNotes notes = notesFactory.createChecked(project, patchSetId.getParentKey()).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());
+ }
+
+ private void assertCommit(Project.NameKey project, String branch) throws Exception {
+ try (Repository r = repoManager.openRepository(project);
+ RevWalk rw = new RevWalk(r)) {
+ RevCommit c = rw.parseCommit(r.exactRef(branch).getObjectId());
+ assertThat(c.getShortMessage()).isEqualTo(PushOneCommit.SUBJECT);
+ assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email());
+ assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo(admin.email());
+ }
+ }
+
+ private void assertMergeCommit(String branch, String subject) throws Exception {
+ try (Repository r = repoManager.openRepository(project);
+ RevWalk rw = new RevWalk(r)) {
+ RevCommit c = rw.parseCommit(r.exactRef(branch).getObjectId());
+ assertThat(c.getParentCount()).isEqualTo(2);
+ assertThat(c.getShortMessage()).isEqualTo("Merge \"" + subject + "\"");
+ assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email());
+ assertThat(c.getCommitterIdent().getEmailAddress())
+ .isEqualTo(serverIdent.get().getEmailAddress());
+ }
+ }
+
+ private PushOneCommit.Result push(String ref, String subject, String fileName, String content)
+ throws Exception {
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
+ return push.to(ref);
+ }
+
+ private PushOneCommit.Result push(
+ String ref, String subject, String fileName, String content, String changeId)
+ throws Exception {
+ PushOneCommit push =
+ pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content, changeId);
+ return push.to(ref);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
index 943b052e07..d1349d073a 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractSubmoduleSubscription.java
@@ -19,13 +19,14 @@ import static java.util.stream.Collectors.toList;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.common.Nullable;
+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.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;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.StreamSupport;
@@ -53,9 +54,16 @@ import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
+import org.junit.Before;
public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
+ 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);
}
@@ -95,34 +103,27 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
return cfg;
}
- protected TestRepository<?> createProjectWithPush(
- String name,
- @Nullable Project.NameKey parent,
- boolean createEmptyCommit,
- SubmitType submitType)
- throws Exception {
- Project.NameKey project = createProject(name, parent, createEmptyCommit, submitType);
+ protected void grantPush(Project.NameKey project) throws Exception {
grant(project, "refs/heads/*", Permission.PUSH);
grant(project, "refs/for/refs/heads/*", Permission.SUBMIT);
- return cloneProject(project);
}
- protected TestRepository<?> createProjectWithPush(String name, @Nullable Project.NameKey parent)
- throws Exception {
- return createProjectWithPush(name, parent, true, getSubmitType());
+ protected Project.NameKey createProjectForPush(SubmitType submitType) throws Exception {
+ Project.NameKey project = projectOperations.newProject().submitType(submitType).create();
+ grantPush(project);
+ return project;
}
- protected TestRepository<?> createProjectWithPush(String name, boolean createEmptyCommit)
- throws Exception {
- return createProjectWithPush(name, null, createEmptyCommit, getSubmitType());
- }
+ private static AtomicInteger contentCounter = new AtomicInteger(0);
- protected TestRepository<?> createProjectWithPush(String name) throws Exception {
- return createProjectWithPush(name, null, true, getSubmitType());
+ @Before
+ public void setUp() throws Exception {
+ superKey = createProjectForPush(getSubmitType());
+ subKey = createProjectForPush(getSubmitType());
+ superRepo = cloneProject(superKey);
+ subRepo = cloneProject(subKey);
}
- private static AtomicInteger contentCounter = new AtomicInteger(0);
-
protected ObjectId pushChangeTo(
TestRepository<?> repo, String ref, String file, String content, String message, String topic)
throws Exception {
@@ -181,19 +182,27 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
return Iterables.getLast(res).getRemoteUpdate(remoteBranch).getNewObjectId();
}
+ protected void allowMatchingSubmoduleSubscription(
+ Project.NameKey submodule, String subBranch, Project.NameKey superproject, String superBranch)
+ throws Exception {
+ allowSubmoduleSubscription(submodule, subBranch, superproject, superBranch, true);
+ }
+
protected void allowSubmoduleSubscription(
- String submodule, String subBranch, String superproject, String superBranch, boolean match)
+ Project.NameKey submodule,
+ String subBranch,
+ Project.NameKey superproject,
+ String superBranch,
+ boolean match)
throws Exception {
- Project.NameKey sub = new Project.NameKey(name(submodule));
- Project.NameKey superName = new Project.NameKey(name(superproject));
- try (MetaDataUpdate md = metaDataUpdateFactory.create(sub)) {
+ try (MetaDataUpdate md = metaDataUpdateFactory.create(submodule)) {
md.setMessage("Added superproject subscription");
SubscribeSection s;
- ProjectConfig pc = ProjectConfig.read(md);
- if (pc.getSubscribeSections().containsKey(superName)) {
- s = pc.getSubscribeSections().get(superName);
+ ProjectConfig pc = projectConfigFactory.read(md);
+ if (pc.getSubscribeSections().containsKey(superproject)) {
+ s = pc.getSubscribeSections().get(superproject);
} else {
- s = new SubscribeSection(superName);
+ s = new SubscribeSection(superproject);
}
String refspec;
if (superBranch == null) {
@@ -214,14 +223,11 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
}
}
- protected void allowMatchingSubmoduleSubscription(
- String submodule, String subBranch, String superproject, String superBranch)
- throws Exception {
- allowSubmoduleSubscription(submodule, subBranch, superproject, superBranch, true);
- }
-
protected void createSubmoduleSubscription(
- TestRepository<?> repo, String branch, String subscribeToRepo, String subscribeToBranch)
+ TestRepository<?> repo,
+ String branch,
+ Project.NameKey subscribeToRepo,
+ String subscribeToBranch)
throws Exception {
Config config = new Config();
prepareSubmoduleConfigEntry(config, subscribeToRepo, subscribeToBranch);
@@ -232,7 +238,7 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
TestRepository<?> repo,
String branch,
String subscribeToRepoPrefix,
- String subscribeToRepo,
+ Project.NameKey subscribeToRepo,
String subscribeToBranch)
throws Exception {
Config config = new Config();
@@ -244,19 +250,18 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
protected void prepareRelativeSubmoduleConfigEntry(
Config config,
String subscribeToRepoPrefix,
- String subscribeToRepo,
+ Project.NameKey subscribeToRepo,
String subscribeToBranch) {
- subscribeToRepo = name(subscribeToRepo);
- String url = subscribeToRepoPrefix + subscribeToRepo;
- config.setString("submodule", subscribeToRepo, "path", subscribeToRepo);
- config.setString("submodule", subscribeToRepo, "url", url);
+ String url = subscribeToRepoPrefix + subscribeToRepo.get();
+ config.setString("submodule", subscribeToRepo.get(), "path", subscribeToRepo.get());
+ config.setString("submodule", subscribeToRepo.get(), "url", url);
if (subscribeToBranch != null) {
- config.setString("submodule", subscribeToRepo, "branch", subscribeToBranch);
+ config.setString("submodule", subscribeToRepo.get(), "branch", subscribeToBranch);
}
}
protected void prepareSubmoduleConfigEntry(
- Config config, String subscribeToRepo, String subscribeToBranch) {
+ Config config, Project.NameKey subscribeToRepo, String subscribeToBranch) {
// The submodule subscription module checks for gerrit.canonicalWebUrl to
// detect if it's configured for automatic updates. It doesn't matter if
// it serves from that URL.
@@ -264,17 +269,18 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
}
protected void prepareSubmoduleConfigEntry(
- Config config, String subscribeToRepo, String subscribeToRepoPath, String subscribeToBranch) {
- subscribeToRepo = name(subscribeToRepo);
- subscribeToRepoPath = name(subscribeToRepoPath);
+ Config config,
+ Project.NameKey subscribeToRepo,
+ Project.NameKey subscribeToRepoPath,
+ String subscribeToBranch) {
// The submodule subscription module checks for gerrit.canonicalWebUrl to
// detect if it's configured for automatic updates. It doesn't matter if
// it serves from that URL.
String url = cfg.getString("gerrit", null, "canonicalWebUrl") + "/" + subscribeToRepo;
- config.setString("submodule", subscribeToRepoPath, "path", subscribeToRepoPath);
- config.setString("submodule", subscribeToRepoPath, "url", url);
+ config.setString("submodule", subscribeToRepoPath.get(), "path", subscribeToRepoPath.get());
+ config.setString("submodule", subscribeToRepoPath.get(), "url", url);
if (subscribeToBranch != null) {
- config.setString("submodule", subscribeToRepoPath, "branch", subscribeToBranch);
+ config.setString("submodule", subscribeToRepoPath.get(), "branch", subscribeToBranch);
}
}
@@ -298,12 +304,11 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
protected void expectToHaveSubmoduleState(
TestRepository<?> repo,
String branch,
- String submodule,
+ Project.NameKey submodule,
TestRepository<?> subRepo,
String subBranch)
throws Exception {
- submodule = name(submodule);
ObjectId commitId =
repo.git()
.fetch()
@@ -326,16 +331,14 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
rw.parseBody(c.getTree());
RevTree tree = c.getTree();
- RevObject actualId = repo.get(tree, submodule);
+ RevObject actualId = repo.get(tree, submodule.get());
assertThat(actualId).isEqualTo(subHead);
}
protected void expectToHaveSubmoduleState(
- TestRepository<?> repo, String branch, String submodule, ObjectId expectedId)
+ TestRepository<?> repo, String branch, Project.NameKey submodule, ObjectId expectedId)
throws Exception {
-
- submodule = name(submodule);
ObjectId commitId =
repo.git()
.fetch()
@@ -349,7 +352,7 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
rw.parseBody(c.getTree());
RevTree tree = c.getTree();
- RevObject actualId = repo.get(tree, submodule);
+ RevObject actualId = repo.get(tree, submodule.get());
assertThat(actualId).isEqualTo(expectedId);
}
@@ -408,10 +411,8 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
assertThat(actualId).isEqualTo(expectedId);
}
- protected boolean hasSubmodule(TestRepository<?> repo, String branch, String submodule)
+ protected boolean hasSubmodule(TestRepository<?> repo, String branch, Project.NameKey submodule)
throws Exception {
-
- submodule = name(submodule);
Ref branchTip =
repo.git().fetch().setRemote("origin").call().getAdvertisedRef("refs/heads/" + branch);
if (branchTip == null) {
@@ -426,7 +427,7 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
RevTree tree = c.getTree();
try {
- repo.get(tree, submodule);
+ repo.get(tree, submodule.get());
return true;
} catch (AssertionError e) {
return false;
@@ -446,7 +447,8 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
RevWalk rw = repo.getRevWalk();
RevCommit c = rw.parseCommit(commitId);
- assertThat(c.getFullMessage()).isEqualTo(expectedMessage);
+ String msg = c.getFullMessage();
+ assertThat(msg).isEqualTo(expectedMessage);
}
protected PersonIdent getAuthor(TestRepository<?> repo, String branch) throws Exception {
@@ -463,10 +465,10 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
return c.getAuthorIdent();
}
- protected void directUpdateSubmodule(String project, String refName, String path, AnyObjectId id)
+ protected void directUpdateSubmodule(
+ Project.NameKey project, String refName, Project.NameKey path, AnyObjectId id)
throws Exception {
- path = name(path);
- try (Repository serverRepo = repoManager.openRepository(new Project.NameKey(name(project)));
+ try (Repository serverRepo = repoManager.openRepository(project);
ObjectInserter ins = serverRepo.newObjectInserter();
RevWalk rw = new RevWalk(serverRepo)) {
Ref ref = serverRepo.exactRef(refName);
@@ -480,7 +482,7 @@ public abstract class AbstractSubmoduleSubscription extends AbstractDaemonTest {
b.finish();
DirCacheEditor e = dc.editor();
e.add(
- new PathEdit(path) {
+ new PathEdit(path.get()) {
@Override
public void apply(DirCacheEntry ent) {
ent.setFileMode(FileMode.GITLINK);
diff --git a/javatests/com/google/gerrit/acceptance/git/BUILD b/javatests/com/google/gerrit/acceptance/git/BUILD
index 1ecfd43a91..24a83e0b33 100644
--- a/javatests/com/google/gerrit/acceptance/git/BUILD
+++ b/javatests/com/google/gerrit/acceptance/git/BUILD
@@ -8,6 +8,7 @@ load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
deps = [
":push_for_review",
":submodule_util",
+ "//lib/commons:lang",
],
) for f in glob(["*IT.java"])]
diff --git a/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java b/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java
index a13c8c812d..ac0cbd83e3 100644
--- a/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/GitmodulesIT.java
@@ -47,7 +47,7 @@ public class GitmodulesIT extends AbstractDaemonTest {
.commit()
.insertChangeId()
.message("subject: adding new subscription")
- .add(".gitmodules", config.toText().toString())
+ .add(".gitmodules", config.toText())
.create();
exception.expectMessage(expectedErrorMessage);
diff --git a/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java b/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java
index 3fdc0238c9..54524a0a13 100644
--- a/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/HttpForcePushIT.java
@@ -23,7 +23,7 @@ public class HttpForcePushIT extends AbstractForcePush {
@Before
public void cloneProjectOverHttp() throws Exception {
CredentialsProvider.setDefault(
- new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
+ new UsernamePasswordCredentialsProvider(admin.username(), admin.httpPassword()));
testRepo = GitUtil.cloneProject(project, admin.getHttpUrl(server) + "/" + project.get());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java b/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
index ed17c38156..6b7adf1541 100644
--- a/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
@@ -14,15 +14,83 @@
package com.google.gerrit.acceptance.git;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.FakeGroupAuditService;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.audit.HttpAuditEvent;
+import com.google.inject.Inject;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.junit.Before;
+import org.junit.Test;
public class HttpPushForReviewIT extends AbstractPushForReview {
+ @Inject private FakeGroupAuditService auditService;
+
@Before
public void selectHttpUrl() throws Exception {
CredentialsProvider.setDefault(
- new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
+ new UsernamePasswordCredentialsProvider(admin.username(), admin.httpPassword()));
selectProtocol(Protocol.HTTP);
+ // Don't clear audit events here, since we can't guarantee all test setup has run yet.
+ }
+
+ @Test
+ public void receivePackAuditEventLog() throws Exception {
+ auditService.drainHttpAuditEvents();
+ testRepo
+ .git()
+ .push()
+ .setRemote("origin")
+ .setRefSpecs(new RefSpec("HEAD:refs/for/master"))
+ .call();
+
+ ImmutableList<HttpAuditEvent> auditEvents = auditService.drainHttpAuditEvents();
+ assertThat(auditEvents).hasSize(2);
+
+ HttpAuditEvent lsRemote = auditEvents.get(0);
+ assertThat(lsRemote.who.getAccountId()).isEqualTo(admin.id());
+ assertThat(lsRemote.what).endsWith("/info/refs?service=git-receive-pack");
+ assertThat(lsRemote.params).containsExactly("service", "git-receive-pack");
+ assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+
+ HttpAuditEvent receivePack = auditEvents.get(1);
+ assertThat(receivePack.who.getAccountId()).isEqualTo(admin.id());
+ assertThat(receivePack.what).endsWith("/git-receive-pack");
+ assertThat(receivePack.params).isEmpty();
+ assertThat(receivePack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+ }
+
+ @Test
+ public void uploadPackAuditEventLog() throws Exception {
+ auditService.drainHttpAuditEvents();
+ // testRepo is already a clone. Make a server-side change so we have something to fetch.
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch("master").commit().create();
+ }
+ testRepo.git().fetch().call();
+
+ ImmutableList<HttpAuditEvent> auditEvents = auditService.drainHttpAuditEvents();
+ assertThat(auditEvents).hasSize(2);
+
+ HttpAuditEvent lsRemote = auditEvents.get(0);
+ // Repo URL doesn't include /a, so fetching doesn't cause authentication.
+ assertThat(lsRemote.who).isInstanceOf(AnonymousUser.class);
+ assertThat(lsRemote.what).endsWith("/info/refs?service=git-upload-pack");
+ assertThat(lsRemote.params).containsExactly("service", "git-upload-pack");
+ assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
+
+ HttpAuditEvent uploadPack = auditEvents.get(1);
+ assertThat(lsRemote.who).isInstanceOf(AnonymousUser.class);
+ assertThat(uploadPack.what).endsWith("/git-upload-pack");
+ assertThat(uploadPack.params).isEmpty();
+ assertThat(uploadPack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/HttpSubmitOnPushIT.java b/javatests/com/google/gerrit/acceptance/git/HttpSubmitOnPushIT.java
new file mode 100644
index 0000000000..373341536f
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/HttpSubmitOnPushIT.java
@@ -0,0 +1,30 @@
+// 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 com.google.gerrit.acceptance.GitUtil;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.junit.Before;
+
+public class HttpSubmitOnPushIT extends AbstractSubmitOnPush {
+
+ @Before
+ public void cloneProjectOverHttp() throws Exception {
+ CredentialsProvider.setDefault(
+ new UsernamePasswordCredentialsProvider(admin.username(), admin.httpPassword()));
+ testRepo = GitUtil.cloneProject(project, admin.getHttpUrl(server) + "/a/" + project.get());
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java b/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
index 954ca8b0d9..6516b32eb5 100644
--- a/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
@@ -91,8 +91,7 @@ public class ImplicitMergeCheckIT extends AbstractDaemonTest {
private PushOneCommit.Result push(String ref, String subject, String fileName, String content)
throws Exception {
- PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
return push.to(ref);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index 30e179d31d..cc19ad2a65 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -24,6 +24,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
@@ -36,6 +37,7 @@ 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;
import org.eclipse.jgit.api.PushCommand;
@@ -52,6 +54,8 @@ import org.junit.Before;
import org.junit.Test;
public class PushPermissionsIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Before
public void setUp() throws Exception {
try (ProjectConfigUpdate u = updateProject(allProjects)) {
@@ -155,8 +159,8 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void groupRefsByMessage() throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
tr.branch("foo").commit().create();
tr.branch("bar").commit().create();
}
@@ -264,14 +268,14 @@ public class PushPermissionsIT extends AbstractDaemonTest {
@Test
public void addPatchSetDenied() throws Exception {
grant(project, "refs/for/refs/heads/*", Permission.PUSH, false, REGISTERED_USERS);
- setApiUser(user);
+ 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);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ObjectId ps1Id = forceFetch(new PatchSet.Id(id, 1).toRefName());
ObjectId ps2Id = testRepo.amend(ps1Id).add("file", "content").create();
PushResult r = push(ps2Id.name() + ":refs/for/master");
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 3f06dfb4fe..5bed77c41f 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -20,16 +20,17 @@ import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.fetch;
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;
-import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.ProjectResetter;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
+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;
@@ -44,28 +45,26 @@ 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.Sequences;
import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHook;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
+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.NoteDbMode;
-import com.google.gerrit.testing.TestChanges;
+import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.function.Predicate;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
@@ -77,22 +76,36 @@ import org.junit.Test;
@NoHttpd
public class RefAdvertisementIT extends AbstractDaemonTest {
- @Inject private PermissionBackend permissionBackend;
- @Inject private ChangeNoteUtil noteUtil;
- @Inject @AnonymousCowardName private String anonymousCowardName;
@Inject private AllUsersName allUsersName;
+ @Inject private ChangeNoteUtil noteUtil;
+ @Inject private PermissionBackend permissionBackend;
+ @Inject private RequestScopeOperations requestScopeOperations;
private AccountGroup.UUID admins;
private AccountGroup.UUID nonInteractiveUsers;
- private ChangeData c1;
- private ChangeData c2;
- private ChangeData c3;
- private ChangeData c4;
- private String r1;
- private String r2;
- private String r3;
- private String r4;
+ private ChangeData cd1;
+ private String psRef1;
+ private String metaRef1;
+
+ private ChangeData cd2;
+ private String psRef2;
+ private String metaRef2;
+
+ private ChangeData cd3;
+ private String psRef3;
+ private String metaRef3;
+
+ private ChangeData cd4;
+ private String psRef4;
+ private String metaRef4;
+
+ @ConfigSuite.Config
+ public static Config enableFullRefEvaluation() {
+ Config cfg = new Config();
+ cfg.setBoolean("auth", null, "skipFullRefEvaluationIfAllRefsAreVisible", false);
+ return cfg;
+ }
@Before
public void setUp() throws Exception {
@@ -102,9 +115,9 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
setUpChanges();
}
+ // This method is idempotent, so it is safe to call it on every test setup.
private void setUpPermissions() throws Exception {
- // Remove read permissions for all users besides admin. This method is idempotent, so is safe
- // to call on every test setup.
+ // Remove read permissions for all users besides admin.
try (ProjectConfigUpdate u = updateProject(allProjects)) {
for (AccessSection sec : u.getConfig().getAccessSections()) {
sec.removePermission(Permission.READ);
@@ -113,8 +126,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
u.save();
}
- // Remove all read permissions on All-Users. This method is idempotent, so is safe to call on
- // every test setup.
+ // Remove all read permissions on All-Users.
try (ProjectConfigUpdate u = updateProject(allUsers)) {
for (AccessSection sec : u.getConfig().getAccessSections()) {
sec.removePermission(Permission.READ);
@@ -123,11 +135,6 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
}
- private static String changeRefPrefix(Change.Id id) {
- String ps = new PatchSet.Id(id, 1).toRefName();
- return ps.substring(0, ps.length() - 1);
- }
-
private void setUpChanges() throws Exception {
gApi.projects().name(project.get()).branch("branch").create(new BranchInput());
@@ -135,25 +142,29 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
// visible.
allow("refs/for/refs/heads/*", Permission.SUBMIT, admins);
PushOneCommit.Result mr =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%submit");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%submit");
mr.assertOkStatus();
- c1 = mr.getChange();
- r1 = changeRefPrefix(c1.getId());
+ cd1 = mr.getChange();
+ psRef1 = cd1.currentPatchSet().getId().toRefName();
+ metaRef1 = RefNames.changeMetaRef(cd1.getId());
PushOneCommit.Result br =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/branch%submit");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch%submit");
br.assertOkStatus();
- c2 = br.getChange();
- r2 = changeRefPrefix(c2.getId());
+ cd2 = br.getChange();
+ psRef2 = cd2.currentPatchSet().getId().toRefName();
+ metaRef2 = RefNames.changeMetaRef(cd2.getId());
// Second 2 changes are unmerged.
- mr = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master");
+ mr = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
mr.assertOkStatus();
- c3 = mr.getChange();
- r3 = changeRefPrefix(c3.getId());
- br = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/branch");
+ cd3 = mr.getChange();
+ psRef3 = cd3.currentPatchSet().getId().toRefName();
+ metaRef3 = RefNames.changeMetaRef(cd3.getId());
+ br = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch");
br.assertOkStatus();
- c4 = br.getChange();
- r4 = changeRefPrefix(c4.getId());
+ cd4 = br.getChange();
+ psRef4 = cd4.currentPatchSet().getId().toRefName();
+ metaRef4 = RefNames.changeMetaRef(cd4.getId());
try (Repository repo = repoManager.openRepository(project)) {
// master-tag -> master
@@ -179,17 +190,17 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
u.save();
}
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
"HEAD",
- r1 + "1",
- r1 + "meta",
- r2 + "1",
- r2 + "meta",
- r3 + "1",
- r3 + "meta",
- r4 + "1",
- r4 + "meta",
+ psRef1,
+ metaRef1,
+ psRef2,
+ metaRef2,
+ psRef3,
+ metaRef3,
+ psRef4,
+ metaRef4,
"refs/heads/branch",
"refs/heads/master",
"refs/tags/branch-tag",
@@ -203,14 +214,14 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
assertUploadPackRefs(
"HEAD",
- r1 + "1",
- r1 + "meta",
- r2 + "1",
- r2 + "meta",
- r3 + "1",
- r3 + "meta",
- r4 + "1",
- r4 + "meta",
+ psRef1,
+ metaRef1,
+ psRef2,
+ metaRef2,
+ psRef3,
+ metaRef3,
+ psRef4,
+ metaRef4,
"refs/heads/branch",
"refs/heads/master",
RefNames.REFS_CONFIG,
@@ -223,15 +234,9 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
- "HEAD",
- r1 + "1",
- r1 + "meta",
- r3 + "1",
- r3 + "meta",
- "refs/heads/master",
- "refs/tags/master-tag");
+ "HEAD", psRef1, metaRef1, psRef3, metaRef3, "refs/heads/master", "refs/tags/master-tag");
}
@Test
@@ -239,12 +244,12 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
- r2 + "1",
- r2 + "meta",
- r4 + "1",
- r4 + "meta",
+ psRef2,
+ metaRef2,
+ psRef4,
+ metaRef4,
"refs/heads/branch",
"refs/tags/branch-tag",
// master branch is not visible but master-tag is reachable from branch
@@ -256,27 +261,23 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
public void uploadPackSubsetOfBranchesVisibleWithEdit() throws Exception {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
- Change c = notesFactory.createChecked(db, project, c3.getId()).getChange();
- String changeId = c.getKey().get();
-
// Admin's edit is not visible.
- setApiUser(admin);
- gApi.changes().id(changeId).edit().create();
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(cd3.getId().get()).edit().create();
// User's edit is visible.
- setApiUser(user);
- gApi.changes().id(changeId).edit().create();
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(cd3.getId().get()).edit().create();
assertUploadPackRefs(
"HEAD",
- r1 + "1",
- r1 + "meta",
- r3 + "1",
- r3 + "meta",
+ psRef1,
+ metaRef1,
+ psRef3,
+ metaRef3,
"refs/heads/master",
"refs/tags/master-tag",
- "refs/tags/branch-tag",
- "refs/users/01/1000001/edit-" + c3.getId() + "/1");
+ "refs/users/01/1000001/edit-" + cd3.getId() + "/1");
}
@Test
@@ -284,33 +285,27 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
- Change change3 = notesFactory.createChecked(db, project, c3.getId()).getChange();
- String changeId3 = change3.getKey().get();
- Change change4 = notesFactory.createChecked(db, project, c4.getId()).getChange();
- String changeId4 = change4.getKey().get();
-
// Admin's edit on change3 is visible.
- setApiUser(admin);
- gApi.changes().id(changeId3).edit().create();
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(cd3.getId().get()).edit().create();
// Admin's edit on change4 is not visible since user cannot see the change.
- gApi.changes().id(changeId4).edit().create();
+ gApi.changes().id(cd4.getId().get()).edit().create();
// User's edit is visible.
- setApiUser(user);
- gApi.changes().id(changeId3).edit().create();
+ requestScopeOperations.setApiUser(user.id());
+ gApi.changes().id(cd3.getId().get()).edit().create();
assertUploadPackRefs(
"HEAD",
- r1 + "1",
- r1 + "meta",
- r3 + "1",
- r3 + "meta",
+ psRef1,
+ metaRef1,
+ psRef3,
+ metaRef3,
"refs/heads/master",
"refs/tags/master-tag",
- "refs/tags/branch-tag",
- "refs/users/00/1000000/edit-" + c3.getId() + "/1",
- "refs/users/01/1000001/edit-" + c3.getId() + "/1");
+ "refs/users/00/1000000/edit-" + cd3.getId() + "/1",
+ "refs/users/01/1000001/edit-" + cd3.getId() + "/1");
}
@Test
@@ -319,28 +314,27 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
- String changeId = c3.change().getKey().get();
- setApiUser(admin);
- gApi.changes().id(changeId).edit().create();
- setApiUser(user);
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.changes().id(cd3.getId().get()).edit().create();
+ requestScopeOperations.setApiUser(user.id());
assertUploadPackRefs(
// Change 1 is visible due to accessDatabase capability, even though
// refs/heads/master is not.
- r1 + "1",
- r1 + "meta",
- r2 + "1",
- r2 + "meta",
- r3 + "1",
- r3 + "meta",
- r4 + "1",
- r4 + "meta",
+ psRef1,
+ metaRef1,
+ psRef2,
+ metaRef2,
+ psRef3,
+ metaRef3,
+ psRef4,
+ metaRef4,
"refs/heads/branch",
"refs/tags/branch-tag",
// See comment in subsetOfBranchesVisibleNotIncludingHead.
"refs/tags/master-tag",
// All edits are visible due to accessDatabase capability.
- "refs/users/00/1000000/edit-" + c3.getId() + "/1");
+ "refs/users/00/1000000/edit-" + cd3.getId() + "/1");
}
@Test
@@ -357,38 +351,55 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
private void uploadPackNoSearchingChangeCacheImpl() throws Exception {
allow("refs/heads/*", Permission.READ, REGISTERED_USERS);
- setApiUser(user);
- try (Repository repo = repoManager.openRepository(project)) {
- assertRefs(
- repo,
- permissionBackend.user(user(user)).project(project),
- // Can't use stored values from the index so DB must be enabled.
- false,
- "HEAD",
- r1 + "1",
- r1 + "meta",
- r2 + "1",
- r2 + "meta",
- r3 + "1",
- r3 + "meta",
- r4 + "1",
- r4 + "meta",
- "refs/heads/branch",
- "refs/heads/master",
- "refs/tags/branch-tag",
- "refs/tags/master-tag");
- }
+ requestScopeOperations.setApiUser(user.id());
+ assertRefs(
+ project,
+ user,
+ // Can't use stored values from the index so DB must be enabled.
+ false,
+ "HEAD",
+ psRef1,
+ metaRef1,
+ psRef2,
+ metaRef2,
+ psRef3,
+ metaRef3,
+ psRef4,
+ metaRef4,
+ "refs/heads/branch",
+ "refs/heads/master",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag");
}
@Test
public void uploadPackSequencesWithAccessDatabase() throws Exception {
- assume().that(notesMigration.readChangeSequence()).isTrue();
- try (Repository repo = repoManager.openRepository(allProjects)) {
- assertRefs(repo, newFilter(allProjects, user), true);
+ assertRefs(allProjects, user, true);
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- assertRefs(repo, newFilter(allProjects, user), true, "refs/sequences/changes");
- }
+ allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ assertRefs(allProjects, user, true, "refs/sequences/changes");
+ }
+
+ @Test
+ public void uploadPackAllRefsAreVisibleOrphanedTag() throws Exception {
+ allow("refs/*", Permission.READ, REGISTERED_USERS);
+ // Delete the pending change on 'branch' and 'branch' itself so that the tag gets orphaned
+ gApi.changes().id(cd4.getId().id).delete();
+ gApi.projects().name(project.get()).branch("refs/heads/branch").delete();
+
+ requestScopeOperations.setApiUser(user.id());
+ assertUploadPackRefs(
+ "HEAD",
+ "refs/meta/config",
+ psRef1,
+ metaRef1,
+ psRef2,
+ metaRef2,
+ psRef3,
+ metaRef3,
+ "refs/heads/master",
+ "refs/tags/branch-tag",
+ "refs/tags/master-tag");
}
@Test
@@ -396,78 +407,70 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
ReceiveCommitsAdvertiseRefsHook.Result r = getReceivePackRefs();
assertThat(r.allRefs().keySet())
.containsExactly(
- // meta refs are excluded even when NoteDb is enabled.
+ // meta refs are excluded
"HEAD",
"refs/heads/branch",
"refs/heads/master",
"refs/meta/config",
"refs/tags/branch-tag",
"refs/tags/master-tag");
- assertThat(r.additionalHaves()).containsExactly(obj(c3, 1), obj(c4, 1));
+ 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);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
- assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(c3, 1));
+ assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd3, 1));
}
@Test
public void receivePackListsOnlyLatestPatchSet() throws Exception {
- testRepo.reset(obj(c3, 1));
- PushOneCommit.Result r = amendChange(c3.change().getKey().get());
+ testRepo.reset(obj(cd3, 1));
+ PushOneCommit.Result r = amendChange(cd3.change().getKey().get());
r.assertOkStatus();
- c3 = r.getChange();
- assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(c3, 2), obj(c4, 1));
+ cd3 = r.getChange();
+ assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd3, 2), obj(cd4, 1));
}
@Test
public void receivePackOmitsMissingObject() throws Exception {
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
String subject = "Subject for missing commit";
- Change c = new Change(c3.change());
- PatchSet.Id psId = new PatchSet.Id(c3.getId(), 2);
+ Change c = new Change(cd3.change());
+ PatchSet.Id psId = new PatchSet.Id(cd3.getId(), 2);
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
- if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
- PatchSet ps = TestChanges.newPatchSet(psId, rev, admin.getId());
- db.patchSets().insert(Collections.singleton(ps));
- db.changes().update(Collections.singleton(c));
- }
-
- if (notesMigration.commitChangeWrites()) {
- PersonIdent committer = serverIdent.get();
- PersonIdent author =
- noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
- tr.branch(RefNames.changeMetaRef(c3.getId()))
- .commit()
- .author(author)
- .committer(committer)
- .message(
- "Update patch set "
- + psId.get()
- + "\n"
- + "\n"
- + "Patch-set: "
- + psId.get()
- + "\n"
- + "Commit: "
- + rev
- + "\n"
- + "Subject: "
- + subject
- + "\n")
- .create();
- }
- indexer.index(db, c.getProject(), c.getId());
+ PersonIdent committer = serverIdent.get();
+ PersonIdent author =
+ noteUtil.newIdent(getAccount(admin.id()), committer.getWhen(), committer);
+ tr.branch(RefNames.changeMetaRef(cd3.getId()))
+ .commit()
+ .author(author)
+ .committer(committer)
+ .message(
+ "Update patch set "
+ + psId.get()
+ + "\n"
+ + "\n"
+ + "Patch-set: "
+ + psId.get()
+ + "\n"
+ + "Commit: "
+ + rev
+ + "\n"
+ + "Subject: "
+ + subject
+ + "\n")
+ .create();
+ indexer.index(c.getProject(), c.getId());
}
- assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(c4, 1));
+ assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd4, 1));
}
@Test
@@ -484,7 +487,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
assertThat(getUserRefs(git))
- .containsExactly(RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id));
+ .containsExactly(RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id()));
}
}
@@ -495,57 +498,49 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
try (Git git = userTestRepository.git()) {
assertThat(getUserRefs(git))
.containsExactly(
- RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id), RefNames.refsUsers(admin.id));
+ RefNames.REFS_USERS_SELF,
+ RefNames.refsUsers(user.id()),
+ RefNames.refsUsers(admin.id()));
}
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesDontShowGroupBranchToOwnerWithoutRead() throws Exception {
- try (ProjectResetter resetter = resetGroups()) {
- createSelfOwnedGroup("Foos", user);
- TestRepository<?> userTestRepository = cloneProject(allUsers, user);
- try (Git git = userTestRepository.git()) {
- assertThat(getGroupRefs(git)).isEmpty();
- }
+ createSelfOwnedGroup("Foos", user);
+ TestRepository<?> userTestRepository = cloneProject(allUsers, user);
+ try (Git git = userTestRepository.git()) {
+ assertThat(getGroupRefs(git)).isEmpty();
}
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesOmitGroupBranchesOfNonOwnedGroups() throws Exception {
- try (ProjectResetter resetter = resetGroups()) {
- allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
- AccountGroup.UUID users = createGroup("Users", admins, user);
- AccountGroup.UUID foos = createGroup("Foos", users);
- AccountGroup.UUID bars = createSelfOwnedGroup("Bars", user);
- TestRepository<?> userTestRepository = cloneProject(allUsers, user);
- try (Git git = userTestRepository.git()) {
- assertThat(getGroupRefs(git))
- .containsExactly(RefNames.refsGroups(foos), RefNames.refsGroups(bars));
- }
+ allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
+ AccountGroup.UUID users = createGroup("Users", admins, user);
+ AccountGroup.UUID foos = createGroup("Foos", users);
+ AccountGroup.UUID bars = createSelfOwnedGroup("Bars", user);
+ TestRepository<?> userTestRepository = cloneProject(allUsers, user);
+ try (Git git = userTestRepository.git()) {
+ assertThat(getGroupRefs(git))
+ .containsExactly(RefNames.refsGroups(foos), RefNames.refsGroups(bars));
}
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesIncludeAllGroupBranchesWithAccessDatabase() throws Exception {
- try (ProjectResetter resetter = resetGroups()) {
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- AccountGroup.UUID users = createGroup("Users", admins);
- TestRepository<?> userTestRepository = cloneProject(allUsers, user);
- try (Git git = userTestRepository.git()) {
- assertThat(getGroupRefs(git))
- .containsExactly(
- RefNames.refsGroups(admins),
- RefNames.refsGroups(nonInteractiveUsers),
- RefNames.refsGroups(users));
- }
+ allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ AccountGroup.UUID users = createGroup("Users", admins);
+ TestRepository<?> userTestRepository = cloneProject(allUsers, user);
+ try (Git git = userTestRepository.git()) {
+ assertThat(getGroupRefs(git))
+ .containsExactly(
+ RefNames.refsGroups(admins),
+ RefNames.refsGroups(nonInteractiveUsers),
+ RefNames.refsGroups(users));
}
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesIncludeAllGroupBranchesForAdmins() throws Exception {
allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ADMINISTRATE_SERVER);
@@ -561,7 +556,6 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesOmitNoteDbNotesBranches() throws Exception {
allow(allUsersName, RefNames.REFS + "*", Permission.READ, REGISTERED_USERS);
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
@@ -576,24 +570,27 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
- String change3RefName = c3.currentPatchSet().getRefName();
+ String change3RefName = cd3.currentPatchSet().getRefName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
- gApi.changes().id(c3.getId().get()).setPrivate(true, null);
+ gApi.changes().id(cd3.getId().get()).setPrivate(true, null);
assertThat(getRefs(git)).doesNotContain(change3RefName);
}
}
@Test
public void advertisedReferencesIncludePrivateChangesWhenAllRefsMayBeRead() throws Exception {
+ assume()
+ .that(baseConfig.getBoolean("auth", "skipFullRefEvaluationIfAllRefsAreVisible", true))
+ .isTrue();
allow("refs/*", Permission.READ, REGISTERED_USERS);
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
- String change3RefName = c3.currentPatchSet().getRefName();
+ String change3RefName = cd3.currentPatchSet().getRefName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
- gApi.changes().id(c3.getId().get()).setPrivate(true, null);
+ gApi.changes().id(cd3.getId().get()).setPrivate(true, null);
assertThat(getRefs(git)).contains(change3RefName);
}
}
@@ -606,28 +603,26 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
- String change3RefName = c3.currentPatchSet().getRefName();
+ String change3RefName = cd3.currentPatchSet().getRefName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
- gApi.changes().id(c3.getId().get()).setPrivate(true, null);
+ gApi.changes().id(cd3.getId().get()).setPrivate(true, null);
assertThat(getRefs(git)).doesNotContain(change3RefName);
}
}
@Test
public void advertisedReferencesOmitDraftCommentRefsOfOtherUsers() throws Exception {
- assume().that(notesMigration.commitChangeWrites()).isTrue();
-
allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
DraftInput draftInput = new DraftInput();
draftInput.line = 1;
draftInput.message = "nit: trailing whitespace";
draftInput.path = Patch.COMMIT_MSG;
- gApi.changes().id(c3.getId().get()).current().createDraft(draftInput);
- String draftCommentRef = RefNames.refsDraftComments(c3.getId(), user.id);
+ gApi.changes().id(cd3.getId().get()).current().createDraft(draftInput);
+ String draftCommentRef = RefNames.refsDraftComments(cd3.getId(), user.id());
// user can see the draft comment ref of the own draft comment
assertThat(lsRemote(allUsersName, user)).contains(draftCommentRef);
@@ -638,14 +633,12 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
@Test
public void advertisedReferencesOmitStarredChangesRefsOfOtherUsers() throws Exception {
- assume().that(notesMigration.commitChangeWrites()).isTrue();
-
allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
- setApiUser(user);
- gApi.accounts().self().starChange(c3.getId().toString());
- String starredChangesRef = RefNames.refsStarredChanges(c3.getId(), user.id);
+ requestScopeOperations.setApiUser(user.id());
+ gApi.accounts().self().starChange(cd3.getId().toString());
+ String starredChangesRef = RefNames.refsStarredChanges(cd3.getId(), user.id());
// user can see the starred changes ref of the own star
assertThat(lsRemote(allUsersName, user)).contains(starredChangesRef);
@@ -655,7 +648,6 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void hideMetadata() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
// create change
@@ -664,15 +656,15 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
allUsersRepo.reset("userRef");
PushOneCommit.Result mr =
pushFactory
- .create(db, admin.getIdent(), allUsersRepo)
+ .create(admin.newIdent(), allUsersRepo)
.to("refs/for/" + RefNames.REFS_USERS_SELF);
mr.assertOkStatus();
List<String> expectedNonMetaRefs =
ImmutableList.of(
RefNames.REFS_USERS_SELF,
- RefNames.refsUsers(admin.id),
- RefNames.refsUsers(user.id),
+ RefNames.refsUsers(admin.id()),
+ RefNames.refsUsers(user.id()),
RefNames.REFS_EXTERNAL_IDS,
RefNames.REFS_GROUPNAMES,
RefNames.refsGroups(admins),
@@ -684,15 +676,13 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
List<String> expectedMetaRefs =
new ArrayList<>(ImmutableList.of(mr.getPatchSetId().toRefName()));
- if (NoteDbMode.get() != NoteDbMode.OFF) {
- expectedMetaRefs.add(changeRefPrefix(mr.getChange().getId()) + "meta");
- }
+ expectedMetaRefs.add(RefNames.changeMetaRef(mr.getChange().getId()));
List<String> expectedAllRefs = new ArrayList<>(expectedNonMetaRefs);
expectedAllRefs.addAll(expectedMetaRefs);
try (Repository repo = repoManager.openRepository(allUsers)) {
- Map<String, Ref> all = repo.getAllRefs();
+ Map<String, Ref> all = getAllRefs(repo);
PermissionBackend.ForProject forProject = newFilter(allUsers, admin);
assertThat(forProject.filter(all, repo, RefFilterOptions.defaults()).keySet())
@@ -705,6 +695,22 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
}
+ @Test
+ public void fetchSingleChangeWithoutIndexAccess() throws Exception {
+ PushOneCommit.Result change = createChange();
+ String patchSetRef = change.getPatchSetId().toRefName();
+ try (AutoCloseable ignored = disableChangeIndex();
+ Repository repo = repoManager.openRepository(project)) {
+ Map<String, Ref> singleRef = ImmutableMap.of(patchSetRef, repo.exactRef(patchSetRef));
+ Map<String, Ref> filteredRefs =
+ permissionBackend
+ .user(user(admin))
+ .project(project)
+ .filter(singleRef, repo, RefFilterOptions.defaults());
+ assertThat(filteredRefs).isEqualTo(singleRef);
+ }
+ }
+
private List<String> lsRemote(Project.NameKey p, TestAccount a) throws Exception {
TestRepository<?> testRepository = cloneProject(p, a);
try (Git git = testRepository.git()) {
@@ -729,43 +735,27 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
}
/**
- * Assert that refs seen by a non-admin user match expected.
+ * Assert that refs seen by a non-admin user match the expected refs.
*
- * @param expectedWithMeta expected refs, in order. If NoteDb is disabled by the configuration,
- * any NoteDb refs (i.e. ending in "/meta") are removed from the expected list before
- * comparing to the actual results.
+ * @param expectedRefs expected refs.
* @throws Exception
*/
- private void assertUploadPackRefs(String... expectedWithMeta) throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- assertRefs(repo, permissionBackend.user(user(user)).project(project), true, expectedWithMeta);
- }
+ private void assertUploadPackRefs(String... expectedRefs) throws Exception {
+ assertRefs(project, user, true, expectedRefs);
}
private void assertRefs(
- Repository repo,
- PermissionBackend.ForProject forProject,
- boolean disableDb,
- String... expectedWithMeta)
+ Project.NameKey project, TestAccount user, boolean disableDb, String... expectedRefs)
throws Exception {
- List<String> expected = new ArrayList<>(expectedWithMeta.length);
- for (String r : expectedWithMeta) {
- if (notesMigration.commitChangeWrites() || !r.endsWith(RefNames.META_SUFFIX)) {
- expected.add(r);
- }
- }
-
- AcceptanceTestRequestScope.Context ctx = null;
+ AutoCloseable ctx = null;
if (disableDb) {
- ctx = disableDb();
+ ctx = disableNoteDb();
}
try {
- Map<String, Ref> all = repo.getAllRefs();
- assertThat(forProject.filter(all, repo, RefFilterOptions.defaults()).keySet())
- .containsExactlyElementsIn(expected);
+ assertThat(lsRemote(project, user)).containsExactlyElementsIn(expectedRefs);
} finally {
if (disableDb) {
- enableDb(ctx);
+ ctx.close();
}
}
}
@@ -774,7 +764,7 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
ReceiveCommitsAdvertiseRefsHook hook =
new ReceiveCommitsAdvertiseRefsHook(queryProvider, project);
try (Repository repo = repoManager.openRepository(project)) {
- return hook.advertiseRefs(repo.getAllRefs());
+ return hook.advertiseRefs(getAllRefs(repo));
}
}
@@ -801,22 +791,12 @@ public class RefAdvertisementIT extends AbstractDaemonTest {
groupInput.name = name(name);
groupInput.ownerId = ownerGroup != null ? ownerGroup.get() : null;
groupInput.members =
- Arrays.stream(members).map(m -> String.valueOf(m.id.get())).collect(toList());
+ Arrays.stream(members).map(m -> String.valueOf(m.id().get())).collect(toList());
return new AccountGroup.UUID(gApi.groups().create(groupInput).get().id);
}
- /**
- * Create a resetter to reset the group branches in All-Users. This makes the group data between
- * ReviewDb and NoteDb inconsistent, but in the context of this test class we only care about refs
- * and hence this is not an issue. Once groups are no longer in ReviewDb and {@link
- * AbstractDaemonTest#resetProjects} takes care to reset group branches we no longer need this
- * method.
- */
- private ProjectResetter resetGroups() throws IOException {
- return projectResetter
- .builder()
- .build(
- new ProjectResetter.Config()
- .reset(allUsers, RefNames.REFS_GROUPS + "*", RefNames.REFS_GROUPNAMES));
+ private static Map<String, Ref> getAllRefs(Repository repo) throws IOException {
+ return repo.getRefDatabase().getRefs().stream()
+ .collect(toMap(Ref::getName, Function.identity()));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
index d1d4f0c359..9b823b7ad2 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefOperationValidationIT.java
@@ -91,7 +91,7 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
try (TestRefValidator validator = new TestRefValidator(CREATE)) {
grant(project, "refs/*", Permission.PUSH, true);
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
PushOneCommit.Result r2 = push1.to(TEST_REF);
@@ -127,7 +127,7 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
try (TestRefValidator validator = new TestRefValidator(UPDATE)) {
grant(project, "refs/*", Permission.PUSH, true);
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to(TEST_REF);
r1.assertErrorStatus(UPDATE.name());
}
@@ -140,7 +140,7 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
grant(project, "refs/*", Permission.PUSH, true);
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to(TEST_REF);
r1.assertOkStatus();
@@ -150,7 +150,7 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
push2.setForce(true);
PushOneCommit.Result r2 = push2.to(TEST_REF);
r2.assertErrorStatus(UPDATE_NONFASTFORWARD.name());
@@ -164,13 +164,13 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
try (TestRefValidator validator = new TestRefValidator(UPDATE_NONFASTFORWARD)) {
grant(project, "refs/*", Permission.PUSH, true);
PushOneCommit push1 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change1", "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
ObjectId push1Id = r1.getCommit();
PushOneCommit push2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change2", "b.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
PushOneCommit.Result r2 = push2.to("refs/heads/master");
r2.assertOkStatus();
ObjectId push2Id = r2.getCommit();
@@ -180,7 +180,7 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push3 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change3", "c.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change3", "c.txt", "content");
PushOneCommit.Result r3 = push3.to(TEST_REF);
r3.assertOkStatus();
@@ -189,7 +189,7 @@ public class RefOperationValidationIT extends AbstractDaemonTest {
assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
PushOneCommit push4 =
- pushFactory.create(db, admin.getIdent(), testRepo, "change4", "d.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, "change4", "d.txt", "content");
push4.setForce(true);
PushOneCommit.Result r4 = push4.to(TEST_REF);
r4.assertErrorStatus(UPDATE_NONFASTFORWARD.name());
diff --git a/javatests/com/google/gerrit/acceptance/git/SshSubmitOnPushIT.java b/javatests/com/google/gerrit/acceptance/git/SshSubmitOnPushIT.java
new file mode 100644
index 0000000000..3a18257f15
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/SshSubmitOnPushIT.java
@@ -0,0 +1,27 @@
+// 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 com.google.gerrit.acceptance.NoHttpd;
+import org.junit.Before;
+
+@NoHttpd
+public class SshSubmitOnPushIT extends AbstractSubmitOnPush {
+
+ @Before
+ public void cloneProjectOverSsh() throws Exception {
+ testRepo = cloneProject(project, admin);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java b/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
deleted file mode 100644
index 7646c457ca..0000000000
--- a/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
+++ /dev/null
@@ -1,382 +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.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 java.util.stream.Collectors.toList;
-
-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.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.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.inject.Inject;
-import java.util.List;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.InvalidRemoteException;
-import org.eclipse.jgit.api.errors.TransportException;
-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.eclipse.jgit.transport.RefSpec;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
-import org.junit.Test;
-
-@NoHttpd
-public class SubmitOnPushIT extends AbstractDaemonTest {
- @Inject private ApprovalsUtil approvalsUtil;
-
- @Test
- public void submitOnPush() throws Exception {
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
- PushOneCommit.Result r = pushTo("refs/for/master%submit");
- r.assertOkStatus();
- r.assertChange(Change.Status.MERGED, null, admin);
- assertSubmitApproval(r.getPatchSetId());
- assertCommit(project, "refs/heads/master");
- }
-
- @Test
- public void submitOnPushToRefsMetaConfig() throws Exception {
- grant(project, "refs/for/refs/meta/config", Permission.SUBMIT);
-
- git().fetch().setRefSpecs(new RefSpec("refs/meta/config:refs/meta/config")).call();
- testRepo.reset(RefNames.REFS_CONFIG);
-
- PushOneCommit.Result r = pushTo("refs/for/refs/meta/config%submit");
- r.assertOkStatus();
- r.assertChange(Change.Status.MERGED, null, admin);
- assertSubmitApproval(r.getPatchSetId());
- assertCommit(project, RefNames.REFS_CONFIG);
- }
-
- @Test
- public void submitOnPushMergeConflict() throws Exception {
- ObjectId objectId = repo().exactRef("HEAD").getObjectId();
- push("refs/heads/master", "one change", "a.txt", "some content");
- testRepo.reset(objectId);
-
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
- PushOneCommit.Result r =
- push("refs/for/master%submit", "other change", "a.txt", "other content");
- r.assertErrorStatus();
- r.assertChange(Change.Status.NEW, null);
- r.assertMessage(
- "Change " + r.getChange().getId() + ": change could not be merged due to a path conflict.");
- }
-
- @Test
- public void submitOnPushSuccessfulMerge() throws Exception {
- String master = "refs/heads/master";
- ObjectId objectId = repo().exactRef("HEAD").getObjectId();
- push(master, "one change", "a.txt", "some content");
- testRepo.reset(objectId);
-
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
- PushOneCommit.Result r =
- push("refs/for/master%submit", "other change", "b.txt", "other content");
- r.assertOkStatus();
- r.assertChange(Change.Status.MERGED, null, admin);
- assertMergeCommit(master, "other change");
- }
-
- @Test
- public void submitOnPushNewPatchSet() throws Exception {
- PushOneCommit.Result r =
- push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
-
- grant(project, "refs/for/refs/heads/master", Permission.SUBMIT);
- r =
- push(
- "refs/for/master%submit",
- PushOneCommit.SUBJECT, "a.txt", "other content", r.getChangeId());
- r.assertOkStatus();
- r.assertChange(Change.Status.MERGED, null, admin);
- ChangeData cd = Iterables.getOnlyElement(queryProvider.get().byKeyPrefix(r.getChangeId()));
- assertThat(cd.patchSets()).hasSize(2);
- assertSubmitApproval(r.getPatchSetId());
- assertCommit(project, "refs/heads/master");
- }
-
- @Test
- public void submitOnPushNotAllowed_Error() throws Exception {
- PushOneCommit.Result r = pushTo("refs/for/master%submit");
- r.assertErrorStatus("not permitted: update by submit");
- }
-
- @Test
- public void submitOnPushNewPatchSetNotAllowed_Error() throws Exception {
- PushOneCommit.Result r =
- push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
-
- r =
- push(
- "refs/for/master%submit",
- PushOneCommit.SUBJECT, "a.txt", "other content", r.getChangeId());
- r.assertErrorStatus("not permitted: update by submit ");
- }
-
- @Test
- public void submitOnPushToNonExistingBranch_Error() throws Exception {
- String branchName = "non-existing";
- PushOneCommit.Result r = pushTo("refs/for/" + branchName + "%submit");
- r.assertErrorStatus("branch " + branchName + " not found");
- }
-
- @Test
- public void mergeOnPushToBranch() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
- PushOneCommit.Result r =
- push("refs/for/master", PushOneCommit.SUBJECT, "a.txt", "some content");
- r.assertOkStatus();
-
- git().push().setRefSpecs(new RefSpec(r.getCommit().name() + ":refs/heads/master")).call();
- assertCommit(project, "refs/heads/master");
-
- ChangeData cd =
- Iterables.getOnlyElement(queryProvider.get().byKey(new Change.Key(r.getChangeId())));
- RevCommit c = r.getCommit();
- PatchSet.Id psId = cd.currentPatchSet().getId();
- assertThat(psId.get()).isEqualTo(1);
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.MERGED);
- assertSubmitApproval(psId);
-
- assertThat(cd.patchSets()).hasSize(1);
- assertThat(cd.patchSet(psId).getRevision().get()).isEqualTo(c.name());
- }
-
- @Test
- public void correctNewRevOnMergeByPushToBranch() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
- 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());
- }
-
- @Test
- public void mergeOnPushToBranchWithChangeMergedInOther() throws Exception {
- 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();
- pushCommitTo(masterRev, other);
- PushOneCommit.Result r = createChange();
- r.assertOkStatus();
- RevCommit commit = r.getCommit();
- pushCommitTo(commit, master);
- assertCommit(project, master);
- ChangeData cd =
- Iterables.getOnlyElement(queryProvider.get().byKey(new Change.Key(r.getChangeId())));
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.MERGED);
-
- RemoteRefUpdate.Status status = pushCommitTo(commit, "refs/for/other");
- assertThat(status).isEqualTo(RemoteRefUpdate.Status.OK);
-
- pushCommitTo(commit, other);
- assertCommit(project, other);
-
- for (ChangeData c : queryProvider.get().byKey(new Change.Key(r.getChangeId()))) {
- if (c.change().getDest().get().equals(other)) {
- assertThat(c.change().getStatus()).isEqualTo(Change.Status.MERGED);
- }
- }
- }
-
- private RemoteRefUpdate.Status pushCommitTo(RevCommit commit, String ref)
- throws GitAPIException, InvalidRemoteException, TransportException {
- return Iterables.getOnlyElement(
- git().push().setRefSpecs(new RefSpec(commit.name() + ":" + ref)).call())
- .getRemoteUpdate(ref)
- .getStatus();
- }
-
- @Test
- public void mergeOnPushToBranchWithNewPatchset() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
- PushOneCommit.Result r = pushTo("refs/for/master");
- r.assertOkStatus();
- RevCommit c1 = r.getCommit();
- PatchSet.Id psId1 = r.getPatchSetId();
- assertThat(psId1.get()).isEqualTo(1);
-
- PushOneCommit push =
- pushFactory.create(
- db,
- admin.getIdent(),
- testRepo,
- PushOneCommit.SUBJECT,
- "b.txt",
- "anotherContent",
- r.getChangeId());
-
- r = push.to("refs/heads/master");
- r.assertOkStatus();
-
- ChangeData cd = r.getChange();
- RevCommit c2 = r.getCommit();
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.MERGED);
- PatchSet.Id psId2 = cd.change().currentPatchSetId();
- assertThat(psId2.get()).isEqualTo(2);
- assertCommit(project, "refs/heads/master");
- 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());
- }
-
- @Test
- public void mergeOnPushToBranchWithOldPatchset() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
- PushOneCommit.Result r = pushTo("refs/for/master");
- r.assertOkStatus();
- RevCommit c1 = r.getCommit();
- PatchSet.Id psId1 = r.getPatchSetId();
- String changeId = r.getChangeId();
- assertThat(psId1.get()).isEqualTo(1);
-
- r = amendChange(changeId);
- ChangeData cd = r.getChange();
- PatchSet.Id psId2 = cd.change().currentPatchSetId();
- assertThat(psId2.getParentKey()).isEqualTo(psId1.getParentKey());
- assertThat(psId2.get()).isEqualTo(2);
-
- testRepo.reset(c1);
- assertPushOk(pushHead(testRepo, "refs/heads/master", false), "refs/heads/master");
-
- cd = changeDataFactory.create(db, project, psId1.getParentKey());
- Change c = cd.change();
- assertThat(c.getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(c.currentPatchSetId()).isEqualTo(psId1);
- assertThat(cd.patchSets().stream().map(PatchSet::getId).collect(toList()))
- .containsExactly(psId1, psId2);
- }
-
- @Test
- public void mergeMultipleOnPushToBranchWithNewPatchset() throws Exception {
- grant(project, "refs/heads/master", Permission.PUSH);
-
- // Create 2 changes.
- ObjectId initialHead = getRemoteHead();
- PushOneCommit.Result r1 = createChange("Change 1", "a", "a");
- r1.assertOkStatus();
- PushOneCommit.Result r2 = createChange("Change 2", "b", "b");
- r2.assertOkStatus();
-
- RevCommit c1_1 = r1.getCommit();
- RevCommit c2_1 = r2.getCommit();
- PatchSet.Id psId1_1 = r1.getPatchSetId();
- PatchSet.Id psId2_1 = r2.getPatchSetId();
- assertThat(c1_1.getParent(0)).isEqualTo(initialHead);
- assertThat(c2_1.getParent(0)).isEqualTo(c1_1);
-
- // Amend both changes.
- testRepo.reset(initialHead);
- RevCommit c1_2 =
- testRepo
- .branch("HEAD")
- .commit()
- .message(c1_1.getShortMessage() + "v2")
- .insertChangeId(r1.getChangeId().substring(1))
- .create();
- RevCommit c2_2 = testRepo.cherryPick(c2_1);
-
- // Push directly to branch.
- assertPushOk(pushHead(testRepo, "refs/heads/master", false), "refs/heads/master");
-
- ChangeData cd2 = r2.getChange();
- assertThat(cd2.change().getStatus()).isEqualTo(Change.Status.MERGED);
- 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());
-
- ChangeData cd1 = r1.getChange();
- assertThat(cd1.change().getStatus()).isEqualTo(Change.Status.MERGED);
- 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());
- }
-
- private PatchSetApproval getSubmitter(PatchSet.Id patchSetId) throws Exception {
- ChangeNotes notes = notesFactory.createChecked(db, project, patchSetId.getParentKey()).load();
- return approvalsUtil.getSubmitter(db, 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);
- }
-
- private void assertCommit(Project.NameKey project, String branch) throws Exception {
- try (Repository r = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(r)) {
- RevCommit c = rw.parseCommit(r.exactRef(branch).getObjectId());
- assertThat(c.getShortMessage()).isEqualTo(PushOneCommit.SUBJECT);
- assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email);
- assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo(admin.email);
- }
- }
-
- private void assertMergeCommit(String branch, String subject) throws Exception {
- try (Repository r = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(r)) {
- RevCommit c = rw.parseCommit(r.exactRef(branch).getObjectId());
- assertThat(c.getParentCount()).isEqualTo(2);
- assertThat(c.getShortMessage()).isEqualTo("Merge \"" + subject + "\"");
- assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email);
- assertThat(c.getCommitterIdent().getEmailAddress())
- .isEqualTo(serverIdent.get().getEmailAddress());
- }
- }
-
- private PushOneCommit.Result push(String ref, String subject, String fileName, String content)
- throws Exception {
- PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content);
- return push.to(ref);
- }
-
- private PushOneCommit.Result push(
- String ref, String subject, String fileName, String content, String changeId)
- throws Exception {
- PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content, changeId);
- return push.to(ref);
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
index 03b1165cf5..e090fae228 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
@@ -25,6 +25,7 @@ import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.TestTimeUtil;
+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;
@@ -47,198 +48,169 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
@Test
@GerritConfig(name = "submodule.enableSuperProjectSubscriptions", value = "false")
public void testSubscriptionWithoutGlobalServerSetting() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void subscriptionWithoutSpecificSubscription() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
-
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void subscriptionToEmptyRepo() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isTrue();
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subHEAD);
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isTrue();
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEAD);
}
@Test
public void subscriptionToExistingRepo() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isTrue();
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subHEAD);
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isTrue();
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEAD);
}
@Test
public void subscriptionWildcardACLForSingleBranch() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
+
// master is allowed to be subscribed to master branch only:
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", null);
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, null);
// create 'branch':
pushChangeTo(superRepo, "branch");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
- createSubmoduleSubscription(superRepo, "branch", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
+ createSubmoduleSubscription(superRepo, "branch", subKey, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subHEAD);
- assertThat(hasSubmodule(superRepo, "branch", "subscribed-to-project")).isFalse();
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEAD);
+ assertThat(hasSubmodule(superRepo, "branch", subKey)).isFalse();
}
@Test
public void subscriptionWildcardACLForMissingProject() throws Exception {
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
+
allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/*", "not-existing-super-project", "refs/heads/*");
+ subKey, "refs/heads/*", new Project.NameKey("not-existing-super-project"), "refs/heads/*");
pushChangeTo(subRepo, "master");
}
@Test
public void subscriptionWildcardACLForMissingBranch() throws Exception {
- createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/*", "super-project", "refs/heads/*");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/*", superKey, "refs/heads/*");
pushChangeTo(subRepo, "foo");
}
@Test
public void subscriptionWildcardACLForMissingGitmodules() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/*", "super-project", "refs/heads/*");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/*", superKey, "refs/heads/*");
pushChangeTo(superRepo, "master");
pushChangeTo(subRepo, "master");
}
@Test
public void subscriptionWildcardACLOneOnOneMapping() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
+
// any branch is allowed to be subscribed to the same superprojects branch:
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/*", "super-project", "refs/heads/*");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/*", superKey, "refs/heads/*");
// create 'branch' in both repos:
pushChangeTo(superRepo, "branch");
pushChangeTo(subRepo, "branch");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
- createSubmoduleSubscription(superRepo, "branch", "subscribed-to-project", "branch");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
+ createSubmoduleSubscription(superRepo, "branch", subKey, "branch");
ObjectId subHEAD1 = pushChangeTo(subRepo, "master");
ObjectId subHEAD2 = pushChangeTo(subRepo, "branch");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subHEAD1);
- expectToHaveSubmoduleState(superRepo, "branch", "subscribed-to-project", subHEAD2);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEAD1);
+ expectToHaveSubmoduleState(superRepo, "branch", subKey, subHEAD2);
// Now test that cross subscriptions do not work:
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "branch");
+ createSubmoduleSubscription(superRepo, "master", subKey, "branch");
ObjectId subHEAD3 = pushChangeTo(subRepo, "branch");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subHEAD1);
- expectToHaveSubmoduleState(superRepo, "branch", "subscribed-to-project", subHEAD3);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEAD1);
+ expectToHaveSubmoduleState(superRepo, "branch", subKey, subHEAD3);
}
@Test
public void subscriptionWildcardACLForManyBranches() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
// Any branch is allowed to be subscribed to any superproject branch:
- allowSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/*", "super-project", null, false);
+ allowSubmoduleSubscription(subKey, "refs/heads/*", superKey, null, false);
pushChangeTo(superRepo, "branch");
pushChangeTo(subRepo, "another-branch");
- createSubmoduleSubscription(superRepo, "branch", "subscribed-to-project", "another-branch");
+ createSubmoduleSubscription(superRepo, "branch", subKey, "another-branch");
ObjectId subHEAD = pushChangeTo(subRepo, "another-branch");
- expectToHaveSubmoduleState(superRepo, "branch", "subscribed-to-project", subHEAD);
+ expectToHaveSubmoduleState(superRepo, "branch", subKey, subHEAD);
}
@Test
public void subscriptionWildcardACLOneToManyBranches() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
// Any branch is allowed to be subscribed to any superproject branch:
- allowSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/*", false);
+ allowSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/*", false);
pushChangeTo(superRepo, "branch");
- createSubmoduleSubscription(superRepo, "branch", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "branch", subKey, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "branch", "subscribed-to-project", subHEAD);
+ expectToHaveSubmoduleState(superRepo, "branch", subKey, subHEAD);
- createSubmoduleSubscription(superRepo, "branch", "subscribed-to-project", "branch");
+ createSubmoduleSubscription(superRepo, "branch", subKey, "branch");
pushChangeTo(subRepo, "branch");
// no change expected, as only master is subscribed:
- expectToHaveSubmoduleState(superRepo, "branch", "subscribed-to-project", subHEAD);
+ expectToHaveSubmoduleState(superRepo, "branch", subKey, subHEAD);
}
@Test
@GerritConfig(name = "submodule.verboseSuperprojectUpdate", value = "false")
public void testSubmoduleShortCommitMessage() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
// The first update doesn't include any commit messages
ObjectId subRepoId = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subRepoId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepoId);
expectToHaveCommitMessage(superRepo, "master", "Update git submodules\n\n");
// Any following update also has a short message
subRepoId = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subRepoId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepoId);
expectToHaveCommitMessage(superRepo, "master", "Update git submodules\n\n");
}
@Test
@GerritConfig(name = "submodule.verboseSuperprojectUpdate", value = "SUBJECT_ONLY")
public void testSubmoduleSubjectCommitMessage() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
// The first update doesn't include the rev log
@@ -248,7 +220,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"master",
"Update git submodules\n\n"
+ "* Update "
- + name("subscribed-to-project")
+ + subKey.get()
+ " from branch 'master'\n to "
+ subHEAD.getName());
@@ -261,7 +233,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"master",
"Update git submodules\n\n"
+ "* Update "
- + name("subscribed-to-project")
+ + subKey.get()
+ " from branch 'master'\n to "
+ subHEAD.getName()
+ "\n - "
@@ -270,13 +242,11 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
@Test
public void submoduleCommitMessage() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
// The first update doesn't include the rev log
@@ -286,7 +256,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"master",
"Update git submodules\n\n"
+ "* Update "
- + name("subscribed-to-project")
+ + subKey.get()
+ " from branch 'master'\n to "
+ subHEAD.getName());
@@ -299,7 +269,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"master",
"Update git submodules\n\n"
+ "* Update "
- + name("subscribed-to-project")
+ + subKey.get()
+ " from branch 'master'\n to "
+ subHEAD.getName()
+ "\n - "
@@ -308,171 +278,154 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
@Test
public void subscriptionUnsubscribe() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
ObjectId subHEADbeforeUnsubscribing = pushChangeTo(subRepo, "master");
deleteAllSubscriptions(superRepo, "master");
- expectToHaveSubmoduleState(
- superRepo, "master", "subscribed-to-project", subHEADbeforeUnsubscribing);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEADbeforeUnsubscribing);
pushChangeTo(superRepo, "refs/heads/master", "commit after unsubscribe", "");
pushChangeTo(subRepo, "refs/heads/master", "commit after unsubscribe", "");
- expectToHaveSubmoduleState(
- superRepo, "master", "subscribed-to-project", subHEADbeforeUnsubscribing);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEADbeforeUnsubscribing);
}
@Test
public void subscriptionUnsubscribeByDeletingGitModules() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
ObjectId subHEADbeforeUnsubscribing = pushChangeTo(subRepo, "master");
deleteGitModulesFile(superRepo, "master");
- expectToHaveSubmoduleState(
- superRepo, "master", "subscribed-to-project", subHEADbeforeUnsubscribing);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEADbeforeUnsubscribing);
pushChangeTo(superRepo, "refs/heads/master", "commit after unsubscribe", "");
pushChangeTo(subRepo, "refs/heads/master", "commit after unsubscribe", "");
- expectToHaveSubmoduleState(
- superRepo, "master", "subscribed-to-project", subHEADbeforeUnsubscribing);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEADbeforeUnsubscribing);
}
@Test
public void subscriptionToDifferentBranches() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/foo", "super-project", "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "foo");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/foo", superKey, "refs/heads/master");
+
+ createSubmoduleSubscription(superRepo, "master", subKey, "foo");
ObjectId subFoo = pushChangeTo(subRepo, "foo");
pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subFoo);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subFoo);
}
@Test
public void branchCircularSubscription() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "super-project", "refs/heads/master", "subscribed-to-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(superKey, "refs/heads/master", subKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
pushChangeTo(superRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
- createSubmoduleSubscription(subRepo, "master", "super-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
+ createSubmoduleSubscription(subRepo, "master", superKey, "master");
pushChangeTo(subRepo, "master");
pushChangeTo(superRepo, "master");
- assertThat(hasSubmodule(subRepo, "master", "super-project")).isFalse();
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(subRepo, "master", superKey)).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void projectCircularSubscription() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "super-project", "refs/heads/dev", "subscribed-to-project", "refs/heads/dev");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(superKey, "refs/heads/dev", subKey, "refs/heads/dev");
pushChangeTo(subRepo, "master");
pushChangeTo(superRepo, "master");
pushChangeTo(subRepo, "dev");
pushChangeTo(superRepo, "dev");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
- createSubmoduleSubscription(subRepo, "dev", "super-project", "dev");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
+ createSubmoduleSubscription(subRepo, "dev", superKey, "dev");
ObjectId subMasterHead = pushChangeTo(subRepo, "master");
ObjectId superDevHead = pushChangeTo(superRepo, "dev");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isTrue();
- assertThat(hasSubmodule(subRepo, "dev", "super-project")).isTrue();
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subMasterHead);
- expectToHaveSubmoduleState(subRepo, "dev", "super-project", superDevHead);
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isTrue();
+ assertThat(hasSubmodule(subRepo, "dev", superKey)).isTrue();
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subMasterHead);
+ expectToHaveSubmoduleState(subRepo, "dev", superKey, superDevHead);
}
@Test
public void subscriptionFailOnMissingACL() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void subscriptionFailOnWrongProjectACL() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "wrong-super-project", "refs/heads/master");
+ subKey,
+ "refs/heads/master",
+ new Project.NameKey("wrong-super-project"),
+ "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void subscriptionFailOnWrongBranchACL() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/wrong-branch");
+ subKey, "refs/heads/master", superKey, "refs/heads/wrong-branch");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
pushChangeTo(subRepo, "master");
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void subscriptionInheritACL() throws Exception {
- createProjectWithPush("config-repo");
- createProjectWithPush("config-repo2", new Project.NameKey(name("config-repo")));
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo =
- createProjectWithPush("subscribed-to-project", new Project.NameKey(name("config-repo2")));
- allowMatchingSubmoduleSubscription(
- "config-repo", "refs/heads/*", "super-project", "refs/heads/*");
+ Project.NameKey configKey = projectOperations.newProject().submitType(getSubmitType()).create();
+ grantPush(configKey);
+ Project.NameKey config2Key =
+ projectOperations.newProject().parent(configKey).submitType(getSubmitType()).create();
+ grantPush(config2Key);
+ cloneProject(config2Key);
+
+ subKey = projectOperations.newProject().parent(config2Key).submitType(getSubmitType()).create();
+ grantPush(subKey);
+ subRepo = cloneProject(subKey);
+
+ allowMatchingSubmoduleSubscription(configKey, "refs/heads/*", superKey, "refs/heads/*");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subHEAD);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subHEAD);
}
@Test
public void allowedButNotSubscribed() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
subRepo
@@ -490,24 +443,22 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
assertThat(r.getRemoteUpdate("refs/heads/master").getStatus())
.isEqualTo(RemoteRefUpdate.Status.OK);
- assertThat(hasSubmodule(superRepo, "master", "subscribed-to-project")).isFalse();
+ assertThat(hasSubmodule(superRepo, "master", subKey)).isFalse();
}
@Test
public void subscriptionDeepRelative() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("nested/subscribed-to-project");
+ Project.NameKey nest = createProjectForPush(getSubmitType());
+ TestRepository<?> subRepo = cloneProject(nest);
// master is allowed to be subscribed to any superprojects branch:
- allowMatchingSubmoduleSubscription(
- "nested/subscribed-to-project", "refs/heads/master", "super-project", null);
+ allowMatchingSubmoduleSubscription(nest, "refs/heads/master", superKey, null);
pushChangeTo(subRepo, "master");
- createRelativeSubmoduleSubscription(
- superRepo, "master", "../", "nested/subscribed-to-project", "master");
+ createRelativeSubmoduleSubscription(superRepo, "master", "../", nest, "master");
ObjectId subHEAD = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "nested/subscribed-to-project", subHEAD);
+ expectToHaveSubmoduleState(superRepo, "master", nest, subHEAD);
}
@Test
@@ -519,7 +470,9 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
@Test
@GerritConfig(name = "submodule.verboseSuperprojectUpdate", value = "SUBJECT_ONLY")
- @GerritConfig(name = "submodule.maxCombinedCommitMessageSize", value = "220")
+ // The value 110 must tuned to the test environment, and is sensitive to the
+ // length of the uniquified repository name.
+ @GerritConfig(name = "submodule.maxCombinedCommitMessageSize", value = "110")
public void submoduleSubjectCommitMessageSizeLimit() throws Exception {
assume().that(isSubmitWholeTopicEnabled()).isFalse();
testSubmoduleSubjectCommitMessageAndExpectTruncation();
@@ -530,11 +483,9 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
// Make sure that the commit is created at an earlier timestamp than the submit timestamp.
TestTimeUtil.resetWithClockStep(1, SECONDS);
try {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ 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);
@@ -544,8 +495,8 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
// 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.getName()).isEqualTo(admin.fullName());
+ assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
assertThat(authorIdent.getWhen())
.isGreaterThan(pushResult.getCommit().getAuthorIdent().getWhen());
} finally {
@@ -561,18 +512,17 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
// is afterwards.
TestTimeUtil.resetWithClockStep(1, SECONDS);
try {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- TestRepository<?> subRepo2 = createProjectWithPush("subscribed-to-project-2");
+ Project.NameKey proj2 = createProjectForPush(getSubmitType());
+
+ TestRepository<?> subRepo2 = cloneProject(proj2);
allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project-2", "refs/heads/master", "super-project", "refs/heads/master");
+ subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "subscribed-to-project", "master");
- prepareSubmoduleConfigEntry(config, "subscribed-to-project-2", "master");
+ prepareSubmoduleConfigEntry(config, subKey, subKey, "master");
+ prepareSubmoduleConfigEntry(config, proj2, proj2, "master");
pushSubmoduleConfig(superRepo, "master", config);
String topic = "foo";
@@ -591,8 +541,8 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
// 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.getName()).isEqualTo(admin.fullName());
+ assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
assertThat(authorIdent.getWhen())
.isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen());
assertThat(authorIdent.getWhen())
@@ -610,21 +560,16 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
// is afterwards.
TestTimeUtil.resetWithClockStep(1, SECONDS);
try {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
-
- createProjectWithPush("subscribed-to-project-2");
- TestRepository<?> subRepo2 =
- cloneProject(new Project.NameKey(name("subscribed-to-project-2")), user);
+ Project.NameKey proj2 = createProjectForPush(getSubmitType());
+ TestRepository<InMemoryRepository> repo2 = cloneProject(proj2, user);
allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project-2", "refs/heads/master", "super-project", "refs/heads/master");
+ subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "subscribed-to-project", "master");
- prepareSubmoduleConfigEntry(config, "subscribed-to-project-2", "master");
+ prepareSubmoduleConfigEntry(config, subKey, subKey, "master");
+ prepareSubmoduleConfigEntry(config, proj2, proj2, "master");
pushSubmoduleConfig(superRepo, "master", config);
String topic = "foo";
@@ -636,7 +581,7 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
// Create change as user.
PushOneCommit push =
- pushFactory.create(db, user.getIdent(), subRepo2, "Change 2", "b.txt", "other content");
+ pushFactory.create(user.newIdent(), repo2, "Change 2", "b.txt", "other content");
PushOneCommit.Result pushResult2 = push.to("refs/for/master/" + name(topic));
approve(pushResult2.getChangeId());
@@ -659,71 +604,65 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
@Test
public void updateOnlyRelevantSubmodules() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo1 = createProjectWithPush("subscribed-to-project-1");
- TestRepository<?> subRepo2 = createProjectWithPush("subscribed-to-project-2");
+ Project.NameKey subkey1 = createProjectForPush(getSubmitType());
+ Project.NameKey subkey2 = createProjectForPush(getSubmitType());
+ TestRepository<?> subRepo1 = cloneProject(subkey1);
+ TestRepository<?> subRepo2 = cloneProject(subkey2);
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project-1", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project-2", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subkey1, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subkey2, "refs/heads/master", superKey, "refs/heads/master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "subscribed-to-project-1", "master");
- prepareSubmoduleConfigEntry(config, "subscribed-to-project-2", "master");
+ prepareSubmoduleConfigEntry(config, subkey1, "master");
+ prepareSubmoduleConfigEntry(config, subkey2, "master");
pushSubmoduleConfig(superRepo, "master", config);
// Push once to initialize submodules.
ObjectId subTip2 = pushChangeTo(subRepo2, "master");
ObjectId subTip1 = pushChangeTo(subRepo1, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project-1", subTip1);
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project-2", subTip2);
+ expectToHaveSubmoduleState(superRepo, "master", subkey1, subTip1);
+ expectToHaveSubmoduleState(superRepo, "master", subkey2, subTip2);
- directUpdateRef("subscribed-to-project-2", "refs/heads/master");
+ directUpdateRef(subkey2, "refs/heads/master");
subTip1 = pushChangeTo(subRepo1, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project-1", subTip1);
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project-2", subTip2);
+ expectToHaveSubmoduleState(superRepo, "master", subkey1, subTip1);
+ expectToHaveSubmoduleState(superRepo, "master", subkey2, subTip2);
}
@Test
public void skipUpdatingBrokenGitlinkPointer() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
// Push once to initialize submodule.
ObjectId subTip = pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subTip);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subTip);
// Write an invalid SHA-1 directly to the gitlink.
ObjectId badId = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- directUpdateSubmodule("super-project", "refs/heads/master", "subscribed-to-project", badId);
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", badId);
+ directUpdateSubmodule(superKey, "refs/heads/master", subKey, badId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, badId);
// Push succeeds, but gitlink update is skipped.
pushChangeTo(subRepo, "master");
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", badId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, badId);
}
- private ObjectId directUpdateRef(String project, String ref) throws Exception {
- try (Repository serverRepo = repoManager.openRepository(new Project.NameKey(name(project)))) {
- return new TestRepository<>(serverRepo).branch(ref).commit().create().copy();
+ private ObjectId directUpdateRef(Project.NameKey project, String ref) throws Exception {
+ try (Repository serverRepo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(serverRepo)) {
+ return tr.branch(ref).commit().create().copy();
}
}
private void testSubmoduleSubjectCommitMessageAndExpectTruncation() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
pushChangeTo(subRepo, "master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
// The first update doesn't include the rev log, so we ignore it
pushChangeTo(subRepo, "master");
@@ -736,6 +675,6 @@ public class SubmoduleSubscriptionsIT extends AbstractSubmoduleSubscription {
"master",
String.format(
"Update git submodules\n\n* Update %s from branch 'master'\n to %s\n - %s\n\n[...]",
- name("subscribed-to-project"), subHEAD.getName(), subCommitMsg.getShortMessage()));
+ subKey.get(), subHEAD.getName(), subCommitMsg.getShortMessage()));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
index eef3295320..dc84d13fc6 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsWholeTopicMergeIT.java
@@ -16,11 +16,11 @@ 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.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.getChangeId;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.SubmitType;
@@ -30,6 +30,7 @@ import com.google.gerrit.server.change.TestSubmitInput;
import com.google.gerrit.testing.ConfigSuite;
import java.util.ArrayDeque;
import java.util.Map;
+import org.apache.commons.lang.RandomStringUtils;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
@@ -68,12 +69,8 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void subscriptionUpdateOfManyChanges() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
-
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId subHEAD =
subRepo
@@ -150,15 +147,13 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
.getAdvertisedRef("refs/heads/master")
.getObjectId();
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subRepoId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepoId);
// 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:
- Project.NameKey p1 = new Project.NameKey(name("super-project"));
- Project.NameKey p2 = new Project.NameKey(name("subscribed-to-project"));
- assertThat(preview).containsKey(new Branch.NameKey(p1, "refs/heads/master"));
- assertThat(preview).containsKey(new Branch.NameKey(p2, "refs/heads/master"));
+ assertThat(preview).containsKey(new Branch.NameKey(superKey, "refs/heads/master"));
+ assertThat(preview).containsKey(new Branch.NameKey(subKey, "refs/heads/master"));
if ((getSubmitType() == SubmitType.CHERRY_PICK)
|| (getSubmitType() == SubmitType.REBASE_ALWAYS)) {
@@ -176,12 +171,9 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void subscriptionUpdateIncludingChangeInSuperproject() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId subHEAD =
subRepo
@@ -274,71 +266,65 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
.getAdvertisedRef("refs/heads/master")
.getObjectId();
- expectToHaveSubmoduleState(superRepo, "master", "subscribed-to-project", subRepoId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepoId);
}
@Test
public void updateManySubmodules() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> sub1 = createProjectWithPush("sub1");
- TestRepository<?> sub2 = createProjectWithPush("sub2");
- TestRepository<?> sub3 = createProjectWithPush("sub3");
-
- allowMatchingSubmoduleSubscription(
- "sub1", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "sub2", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "sub3", "refs/heads/master", "super-project", "refs/heads/master");
+ final int NUM = 3;
+ Project.NameKey subKey[] = new Project.NameKey[NUM];
+ TestRepository<?> sub[] = new TestRepository[NUM];
+ String prefix = RandomStringUtils.randomAlphabetic(8);
+ for (int i = 0; i < subKey.length; i++) {
+ subKey[i] =
+ projectOperations
+ .newProject()
+ .name(prefix + "sub" + i)
+ .submitType(getSubmitType())
+ .create();
+ grant(subKey[i], "refs/heads/*", Permission.PUSH);
+ grant(subKey[i], "refs/for/refs/heads/*", Permission.SUBMIT);
+ sub[i] = cloneProject(subKey[i]);
+ }
+
+ for (int i = 0; i < subKey.length; i++) {
+ allowMatchingSubmoduleSubscription(
+ subKey[i], "refs/heads/master", superKey, "refs/heads/master");
+ }
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "sub1", "master");
- prepareSubmoduleConfigEntry(config, "sub2", "master");
- prepareSubmoduleConfigEntry(config, "sub3", "master");
+ for (int i = 0; i < subKey.length; i++) {
+ prepareSubmoduleConfigEntry(config, subKey[i], "master");
+ }
pushSubmoduleConfig(superRepo, "master", config);
ObjectId superPreviousId = pushChangeTo(superRepo, "master");
- ObjectId sub1Id = pushChangeTo(sub1, "refs/for/master", "some message", "same-topic");
- ObjectId sub2Id = pushChangeTo(sub2, "refs/for/master", "some message", "same-topic");
- ObjectId sub3Id = pushChangeTo(sub3, "refs/for/master", "some message", "same-topic");
+ ObjectId subId[] = new ObjectId[NUM];
- approve(getChangeId(sub1, sub1Id).get());
- approve(getChangeId(sub2, sub2Id).get());
- approve(getChangeId(sub3, sub3Id).get());
-
- gApi.changes().id(getChangeId(sub1, sub1Id).get()).current().submit();
-
- expectToHaveSubmoduleState(superRepo, "master", "sub1", sub1, "master");
- expectToHaveSubmoduleState(superRepo, "master", "sub2", sub2, "master");
- expectToHaveSubmoduleState(superRepo, "master", "sub3", sub3, "master");
+ for (int i = 0; i < sub.length; i++) {
+ subId[i] = pushChangeTo(sub[i], "refs/for/master", "some message", "same-topic");
+ approve(getChangeId(sub[i], subId[i]).get());
+ }
- String sub1HEAD =
- sub1.git()
- .fetch()
- .setRemote("origin")
- .call()
- .getAdvertisedRef("refs/heads/master")
- .getObjectId()
- .name();
+ gApi.changes().id(getChangeId(sub[0], subId[0]).get()).current().submit();
- String sub2HEAD =
- sub2.git()
- .fetch()
- .setRemote("origin")
- .call()
- .getAdvertisedRef("refs/heads/master")
- .getObjectId()
- .name();
+ for (int i = 0; i < sub.length; i++) {
+ expectToHaveSubmoduleState(superRepo, "master", subKey[i], sub[i], "master");
+ }
- String sub3HEAD =
- sub3.git()
- .fetch()
- .setRemote("origin")
- .call()
- .getAdvertisedRef("refs/heads/master")
- .getObjectId()
- .name();
+ String heads[] = new String[NUM];
+ for (int i = 0; i < heads.length; i++) {
+ heads[i] =
+ sub[i]
+ .git()
+ .fetch()
+ .setRemote("origin")
+ .call()
+ .getAdvertisedRef("refs/heads/master")
+ .getObjectId()
+ .name();
+ }
if (getSubmitType() == SubmitType.MERGE_IF_NECESSARY) {
expectToHaveCommitMessage(
@@ -346,17 +332,17 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
"master",
"Update git submodules\n\n"
+ "* Update "
- + name("sub1")
+ + subKey[0].get()
+ " from branch 'master'\n to "
- + sub1HEAD
+ + heads[0]
+ "\n\n* Update "
- + name("sub2")
+ + subKey[1].get()
+ " from branch 'master'\n to "
- + sub2HEAD
+ + heads[1]
+ "\n\n* Update "
- + name("sub3")
+ + subKey[2].get()
+ " from branch 'master'\n to "
- + sub3HEAD);
+ + heads[2]);
}
superRepo
@@ -374,73 +360,98 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void doNotUseFastForward() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project", false);
- TestRepository<?> sub = createProjectWithPush("sub", false);
+ // like setup, but without empty commit
+ superKey =
+ projectOperations
+ .newProject()
+ .submitType(getSubmitType())
+ .createEmptyCommit(false)
+ .create();
+ grantPush(superKey);
+ subKey =
+ projectOperations
+ .newProject()
+ .submitType(getSubmitType())
+ .createEmptyCommit(false)
+ .create();
+ grantPush(subKey);
+ superRepo = cloneProject(superKey);
+ subRepo = cloneProject(subKey);
- allowMatchingSubmoduleSubscription(
- "sub", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "sub", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
- ObjectId subId = pushChangeTo(sub, "refs/for/master", "some message", "same-topic");
+ ObjectId subId = pushChangeTo(subRepo, "refs/for/master", "some message", "same-topic");
ObjectId superId = pushChangeTo(superRepo, "refs/for/master", "some message", "same-topic");
- String subChangeId = getChangeId(sub, subId).get();
+ String subChangeId = getChangeId(subRepo, subId).get();
approve(subChangeId);
approve(getChangeId(superRepo, superId).get());
gApi.changes().id(subChangeId).current().submit();
- expectToHaveSubmoduleState(superRepo, "master", "sub", sub, "master");
- RevCommit superHead = getRemoteHead(name("super-project"), "master");
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepo, "master");
+ RevCommit superHead = getRemoteHead(superKey, "master");
assertThat(superHead.getShortMessage()).contains("some message");
assertThat(superHead.getId()).isNotEqualTo(superId);
}
@Test
public void useFastForwardWhenNoSubmodule() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project", false);
- TestRepository<?> sub = createProjectWithPush("sub", false);
-
- ObjectId subId = pushChangeTo(sub, "refs/for/master", "some message", "same-topic");
+ // like setup, but without empty commit
+ superKey =
+ projectOperations
+ .newProject()
+ .submitType(getSubmitType())
+ .createEmptyCommit(false)
+ .create();
+ grantPush(superKey);
+ subKey =
+ projectOperations
+ .newProject()
+ .submitType(getSubmitType())
+ .createEmptyCommit(false)
+ .create();
+ grantPush(subKey);
+ superRepo = cloneProject(superKey);
+ subRepo = cloneProject(subKey);
+ ObjectId subId = pushChangeTo(subRepo, "refs/for/master", "some message", "same-topic");
ObjectId superId = pushChangeTo(superRepo, "refs/for/master", "some message", "same-topic");
- String subChangeId = getChangeId(sub, subId).get();
+ String subChangeId = getChangeId(subRepo, subId).get();
approve(subChangeId);
approve(getChangeId(superRepo, superId).get());
gApi.changes().id(subChangeId).current().submit();
- RevCommit superHead = getRemoteHead(name("super-project"), "master");
+ RevCommit superHead = getRemoteHead(superKey, "master");
assertThat(superHead.getShortMessage()).isEqualTo("some message");
assertThat(superHead.getId()).isEqualTo(superId);
}
@Test
public void sameProjectSameBranchDifferentPaths() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> sub = createProjectWithPush("sub");
-
- allowMatchingSubmoduleSubscription(
- "sub", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "sub", "master");
- prepareSubmoduleConfigEntry(config, "sub", "sub-copy", "master");
+ prepareSubmoduleConfigEntry(config, subKey, "master");
+ Project.NameKey copyKey = nameKey("sub-copy");
+ prepareSubmoduleConfigEntry(config, subKey, copyKey, "master");
pushSubmoduleConfig(superRepo, "master", config);
ObjectId superPreviousId = pushChangeTo(superRepo, "master");
- ObjectId subId = pushChangeTo(sub, "refs/for/master", "some message", "");
+ ObjectId subId = pushChangeTo(subRepo, "refs/for/master", "some message", "");
- approve(getChangeId(sub, subId).get());
+ approve(getChangeId(subRepo, subId).get());
- gApi.changes().id(getChangeId(sub, subId).get()).current().submit();
+ gApi.changes().id(getChangeId(subRepo, subId).get()).current().submit();
- expectToHaveSubmoduleState(superRepo, "master", "sub", sub, "master");
- expectToHaveSubmoduleState(superRepo, "master", "sub-copy", sub, "master");
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepo, "master");
+ expectToHaveSubmoduleState(superRepo, "master", copyKey, subRepo, "master");
superRepo
.git()
@@ -457,37 +468,33 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void sameProjectDifferentBranchDifferentPaths() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> sub = createProjectWithPush("sub");
-
- allowMatchingSubmoduleSubscription(
- "sub", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "sub", "refs/heads/dev", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/dev", superKey, "refs/heads/master");
- ObjectId devHead = pushChangeTo(sub, "dev");
+ ObjectId devHead = pushChangeTo(subRepo, "dev");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "sub", "sub-master", "master");
- prepareSubmoduleConfigEntry(config, "sub", "sub-dev", "dev");
+ prepareSubmoduleConfigEntry(config, subKey, nameKey("sub-master"), "master");
+ prepareSubmoduleConfigEntry(config, subKey, nameKey("sub-dev"), "dev");
pushSubmoduleConfig(superRepo, "master", config);
ObjectId subMasterId =
- pushChangeTo(sub, "refs/for/master", "some message", "b.txt", "content b", "same-topic");
+ pushChangeTo(
+ subRepo, "refs/for/master", "some message", "b.txt", "content b", "same-topic");
- sub.reset(devHead);
+ subRepo.reset(devHead);
ObjectId subDevId =
pushChangeTo(
- sub, "refs/for/dev", "some message in dev", "b.txt", "content b", "same-topic");
+ subRepo, "refs/for/dev", "some message in dev", "b.txt", "content b", "same-topic");
- approve(getChangeId(sub, subMasterId).get());
- approve(getChangeId(sub, subDevId).get());
+ approve(getChangeId(subRepo, subMasterId).get());
+ approve(getChangeId(subRepo, subDevId).get());
ObjectId superPreviousId = pushChangeTo(superRepo, "master");
- gApi.changes().id(getChangeId(sub, subMasterId).get()).current().submit();
+ gApi.changes().id(getChangeId(subRepo, subMasterId).get()).current().submit();
- expectToHaveSubmoduleState(superRepo, "master", "sub-master", sub, "master");
- expectToHaveSubmoduleState(superRepo, "master", "sub-dev", sub, "dev");
+ expectToHaveSubmoduleState(superRepo, "master", nameKey("sub-master"), subRepo, "master");
+ expectToHaveSubmoduleState(superRepo, "master", nameKey("sub-dev"), subRepo, "dev");
superRepo
.git()
@@ -504,29 +511,27 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void nonSubmoduleInSameTopic() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> sub = createProjectWithPush("sub");
- TestRepository<?> standAlone = createProjectWithPush("standalone");
+ Project.NameKey standaloneKey = createProjectForPush(getSubmitType());
+ TestRepository<?> standAlone = cloneProject(standaloneKey);
- allowMatchingSubmoduleSubscription(
- "sub", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
- createSubmoduleSubscription(superRepo, "master", "sub", "master");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
ObjectId superPreviousId = pushChangeTo(superRepo, "master");
- ObjectId subId = pushChangeTo(sub, "refs/for/master", "some message", "same-topic");
+ ObjectId subId = pushChangeTo(subRepo, "refs/for/master", "some message", "same-topic");
ObjectId standAloneId =
pushChangeTo(standAlone, "refs/for/master", "some message", "same-topic");
- String subChangeId = getChangeId(sub, subId).get();
+ String subChangeId = getChangeId(subRepo, subId).get();
String standAloneChangeId = getChangeId(standAlone, standAloneId).get();
approve(subChangeId);
approve(standAloneChangeId);
gApi.changes().id(subChangeId).current().submit();
- expectToHaveSubmoduleState(superRepo, "master", "sub", sub, "master");
+ expectToHaveSubmoduleState(superRepo, "master", subKey, subRepo, "master");
ChangeStatus status = gApi.changes().id(standAloneChangeId).info().status;
assertThat(status).isEqualTo(ChangeStatus.MERGED);
@@ -546,17 +551,18 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void recursiveSubmodules() throws Exception {
- TestRepository<?> topRepo = createProjectWithPush("top-project");
- TestRepository<?> midRepo = createProjectWithPush("mid-project");
- TestRepository<?> bottomRepo = createProjectWithPush("bottom-project");
+ Project.NameKey topKey = createProjectForPush(getSubmitType());
+ Project.NameKey midKey = createProjectForPush(getSubmitType());
+ Project.NameKey botKey = createProjectForPush(getSubmitType());
+ TestRepository<?> topRepo = cloneProject(topKey);
+ TestRepository<?> midRepo = cloneProject(midKey);
+ TestRepository<?> bottomRepo = cloneProject(botKey);
- allowMatchingSubmoduleSubscription(
- "mid-project", "refs/heads/master", "top-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "bottom-project", "refs/heads/master", "mid-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(midKey, "refs/heads/master", topKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(botKey, "refs/heads/master", midKey, "refs/heads/master");
- createSubmoduleSubscription(topRepo, "master", "mid-project", "master");
- createSubmoduleSubscription(midRepo, "master", "bottom-project", "master");
+ createSubmoduleSubscription(topRepo, "master", midKey, "master");
+ createSubmoduleSubscription(midRepo, "master", botKey, "master");
ObjectId bottomHead = pushChangeTo(bottomRepo, "refs/for/master", "some message", "same-topic");
ObjectId topHead = pushChangeTo(topRepo, "refs/for/master", "some message", "same-topic");
@@ -569,27 +575,27 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
gApi.changes().id(id1).current().submit();
- expectToHaveSubmoduleState(midRepo, "master", "bottom-project", bottomRepo, "master");
- expectToHaveSubmoduleState(topRepo, "master", "mid-project", midRepo, "master");
+ expectToHaveSubmoduleState(midRepo, "master", botKey, bottomRepo, "master");
+ expectToHaveSubmoduleState(topRepo, "master", midKey, midRepo, "master");
}
@Test
public void triangleSubmodules() throws Exception {
- TestRepository<?> topRepo = createProjectWithPush("top-project");
- TestRepository<?> midRepo = createProjectWithPush("mid-project");
- TestRepository<?> bottomRepo = createProjectWithPush("bottom-project");
-
- allowMatchingSubmoduleSubscription(
- "mid-project", "refs/heads/master", "top-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "bottom-project", "refs/heads/master", "mid-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "bottom-project", "refs/heads/master", "top-project", "refs/heads/master");
-
- createSubmoduleSubscription(midRepo, "master", "bottom-project", "master");
+ Project.NameKey topKey = createProjectForPush(getSubmitType());
+ Project.NameKey midKey = createProjectForPush(getSubmitType());
+ Project.NameKey botKey = createProjectForPush(getSubmitType());
+ TestRepository<?> topRepo = cloneProject(topKey);
+ TestRepository<?> midRepo = cloneProject(midKey);
+ TestRepository<?> bottomRepo = cloneProject(botKey);
+
+ allowMatchingSubmoduleSubscription(midKey, "refs/heads/master", topKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(botKey, "refs/heads/master", midKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(botKey, "refs/heads/master", topKey, "refs/heads/master");
+
+ createSubmoduleSubscription(midRepo, "master", botKey, "master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "bottom-project", "master");
- prepareSubmoduleConfigEntry(config, "mid-project", "master");
+ prepareSubmoduleConfigEntry(config, botKey, "master");
+ prepareSubmoduleConfigEntry(config, midKey, "master");
pushSubmoduleConfig(topRepo, "master", config);
ObjectId bottomHead = pushChangeTo(bottomRepo, "refs/for/master", "some message", "same-topic");
@@ -603,35 +609,35 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
gApi.changes().id(id1).current().submit();
- expectToHaveSubmoduleState(midRepo, "master", "bottom-project", bottomRepo, "master");
- expectToHaveSubmoduleState(topRepo, "master", "mid-project", midRepo, "master");
- expectToHaveSubmoduleState(topRepo, "master", "bottom-project", bottomRepo, "master");
+ expectToHaveSubmoduleState(midRepo, "master", botKey, bottomRepo, "master");
+ expectToHaveSubmoduleState(topRepo, "master", midKey, midRepo, "master");
+ expectToHaveSubmoduleState(topRepo, "master", botKey, bottomRepo, "master");
}
private String prepareBranchCircularSubscription() throws Exception {
- TestRepository<?> topRepo = createProjectWithPush("top-project");
- TestRepository<?> midRepo = createProjectWithPush("mid-project");
- TestRepository<?> bottomRepo = createProjectWithPush("bottom-project");
+ Project.NameKey topKey = createProjectForPush(getSubmitType());
+ Project.NameKey midKey = createProjectForPush(getSubmitType());
+ Project.NameKey botKey = createProjectForPush(getSubmitType());
+ TestRepository<?> topRepo = cloneProject(topKey);
+ TestRepository<?> midRepo = cloneProject(midKey);
+ TestRepository<?> bottomRepo = cloneProject(botKey);
- createSubmoduleSubscription(midRepo, "master", "bottom-project", "master");
- createSubmoduleSubscription(topRepo, "master", "mid-project", "master");
- createSubmoduleSubscription(bottomRepo, "master", "top-project", "master");
+ createSubmoduleSubscription(midRepo, "master", botKey, "master");
+ createSubmoduleSubscription(topRepo, "master", midKey, "master");
+ createSubmoduleSubscription(bottomRepo, "master", topKey, "master");
- allowMatchingSubmoduleSubscription(
- "bottom-project", "refs/heads/master", "mid-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "mid-project", "refs/heads/master", "top-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "top-project", "refs/heads/master", "bottom-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(botKey, "refs/heads/master", midKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(midKey, "refs/heads/master", topKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(topKey, "refs/heads/master", botKey, "refs/heads/master");
ObjectId bottomMasterHead = pushChangeTo(bottomRepo, "refs/for/master", "some message", "");
String changeId = getChangeId(bottomRepo, bottomMasterHead).get();
approve(changeId);
exception.expectMessage("Branch level circular subscriptions detected");
- exception.expectMessage("top-project,refs/heads/master");
- exception.expectMessage("mid-project,refs/heads/master");
- exception.expectMessage("bottom-project,refs/heads/master");
+ exception.expectMessage(topKey.get() + ",refs/heads/master");
+ exception.expectMessage(midKey.get() + ",refs/heads/master");
+ exception.expectMessage(botKey.get() + ",refs/heads/master");
return changeId;
}
@@ -649,19 +655,14 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void projectCircularSubscriptionWholeTopic() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> subRepo = createProjectWithPush("subscribed-to-project");
-
- allowMatchingSubmoduleSubscription(
- "subscribed-to-project", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "super-project", "refs/heads/dev", "subscribed-to-project", "refs/heads/dev");
+ allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(superKey, "refs/heads/dev", subKey, "refs/heads/dev");
pushChangeTo(subRepo, "dev");
pushChangeTo(superRepo, "dev");
- createSubmoduleSubscription(superRepo, "master", "subscribed-to-project", "master");
- createSubmoduleSubscription(subRepo, "dev", "super-project", "dev");
+ createSubmoduleSubscription(superRepo, "master", subKey, "master");
+ createSubmoduleSubscription(subRepo, "dev", superKey, "dev");
ObjectId subMasterHead =
pushChangeTo(
@@ -672,15 +673,18 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
approve(getChangeId(superRepo, superDevHead).get());
exception.expectMessage("Project level circular subscriptions detected");
- exception.expectMessage("subscribed-to-project");
- exception.expectMessage("super-project");
+ exception.expectMessage(subKey.get());
+ exception.expectMessage(superKey.get());
gApi.changes().id(getChangeId(subRepo, subMasterHead).get()).current().submit();
}
@Test
public void projectNoSubscriptionWholeTopic() throws Exception {
- TestRepository<?> repoA = createProjectWithPush("project-a");
- TestRepository<?> repoB = createProjectWithPush("project-b");
+ Project.NameKey keyA = createProjectForPush(getSubmitType());
+ Project.NameKey keyB = createProjectForPush(getSubmitType());
+
+ TestRepository<?> repoA = cloneProject(keyA);
+ TestRepository<?> repoB = cloneProject(keyB);
// bootstrap the dev branch
ObjectId a0 = pushChangeTo(repoA, "dev");
@@ -735,33 +739,33 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
approve(getChangeId(repoB, bDevHead).get());
gApi.changes().id(getChangeId(repoA, aDevHead).get()).current().submit();
- assertThat(getRemoteHead(name("project-a"), "refs/heads/master").getShortMessage())
+ assertThat(getRemoteHead(keyA, "refs/heads/master").getShortMessage())
.contains("some message in a master.txt");
- assertThat(getRemoteHead(name("project-a"), "refs/heads/dev").getShortMessage())
+ assertThat(getRemoteHead(keyA, "refs/heads/dev").getShortMessage())
.contains("some message in a dev.txt");
- assertThat(getRemoteHead(name("project-b"), "refs/heads/master").getShortMessage())
+ assertThat(getRemoteHead(keyB, "refs/heads/master").getShortMessage())
.contains("some message in b master.txt");
- assertThat(getRemoteHead(name("project-b"), "refs/heads/dev").getShortMessage())
+ assertThat(getRemoteHead(keyB, "refs/heads/dev").getShortMessage())
.contains("some message in b dev.txt");
}
@Test
public void twoProjectsMultipleBranchesWholeTopic() throws Exception {
- TestRepository<?> repoA = createProjectWithPush("project-a");
- TestRepository<?> repoB = createProjectWithPush("project-b");
+ Project.NameKey keyA = createProjectForPush(getSubmitType());
+ Project.NameKey keyB = createProjectForPush(getSubmitType());
+ TestRepository<?> repoA = cloneProject(keyA);
+ TestRepository<?> repoB = cloneProject(keyB);
// bootstrap the dev branch
pushChangeTo(repoA, "dev");
// bootstrap the dev branch
ObjectId b0 = pushChangeTo(repoB, "dev");
- allowMatchingSubmoduleSubscription(
- "project-b", "refs/heads/master", "project-a", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "project-b", "refs/heads/dev", "project-a", "refs/heads/dev");
+ allowMatchingSubmoduleSubscription(keyB, "refs/heads/master", keyA, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(keyB, "refs/heads/dev", keyA, "refs/heads/dev");
- createSubmoduleSubscription(repoA, "master", "project-b", "master");
- createSubmoduleSubscription(repoA, "dev", "project-b", "dev");
+ createSubmoduleSubscription(repoA, "master", keyB, "master");
+ createSubmoduleSubscription(repoA, "dev", keyB, "dev");
// create a change for master branch in repo b
ObjectId bHead =
@@ -788,26 +792,23 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
approve(getChangeId(repoB, bDevHead).get());
gApi.changes().id(getChangeId(repoB, bHead).get()).current().submit();
- expectToHaveSubmoduleState(repoA, "master", "project-b", repoB, "master");
- expectToHaveSubmoduleState(repoA, "dev", "project-b", repoB, "dev");
+ expectToHaveSubmoduleState(repoA, "master", keyB, repoB, "master");
+ expectToHaveSubmoduleState(repoA, "dev", keyB, repoB, "dev");
}
@Test
public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
-
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> sub1 = createProjectWithPush("sub1");
- TestRepository<?> sub2 = createProjectWithPush("sub2");
+ Project.NameKey subKey1 = createProjectForPush(getSubmitType());
+ TestRepository<?> sub1 = cloneProject(subKey1);
+ Project.NameKey subKey2 = createProjectForPush(getSubmitType());
+ TestRepository<?> sub2 = cloneProject(subKey2);
- allowMatchingSubmoduleSubscription(
- "sub1", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "sub2", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey1, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey2, "refs/heads/master", superKey, "refs/heads/master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "sub1", "master");
- prepareSubmoduleConfigEntry(config, "sub2", "master");
+ prepareSubmoduleConfigEntry(config, subKey1, "master");
+ prepareSubmoduleConfigEntry(config, subKey2, "master");
pushSubmoduleConfig(superRepo, "master", config);
ObjectId superPreviousId = pushChangeTo(superRepo, "master");
@@ -837,20 +838,20 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
sub1.git().fetch().call();
RevWalk rw1 = sub1.getRevWalk();
- RevCommit master1 = rw1.parseCommit(getRemoteHead(name("sub1"), "master"));
+ RevCommit master1 = rw1.parseCommit(getRemoteHead(subKey1, "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(name("sub2"), "master"));
+ RevCommit master2 = rw2.parseCommit(getRemoteHead(subKey2, "master"));
RevCommit change2Ps = parseCurrentRevision(rw2, changeId2);
assertThat(rw2.isMergedInto(change2Ps, master2)).isTrue();
assertThat(input.generateLockFailures).containsExactly(false);
- expectToHaveSubmoduleState(superRepo, "master", "sub1", sub1, "master");
- expectToHaveSubmoduleState(superRepo, "master", "sub2", sub2, "master");
+ expectToHaveSubmoduleState(superRepo, "master", subKey1, sub1, "master");
+ expectToHaveSubmoduleState(superRepo, "master", subKey2, sub2, "master");
assertWithMessage("submodule subscription update should have made one commit")
.that(superRepo.getRepository().resolve("origin/master^"))
@@ -859,24 +860,23 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
@Test
public void skipUpdatingBrokenGitlinkPointer() throws Exception {
- TestRepository<?> superRepo = createProjectWithPush("super-project");
- TestRepository<?> sub1 = createProjectWithPush("sub1");
- TestRepository<?> sub2 = createProjectWithPush("sub2");
+ Project.NameKey subKey1 = createProjectForPush(getSubmitType());
+ TestRepository<?> sub1 = cloneProject(subKey1);
+ Project.NameKey subKey2 = createProjectForPush(getSubmitType());
+ TestRepository<?> sub2 = cloneProject(subKey2);
- allowMatchingSubmoduleSubscription(
- "sub1", "refs/heads/master", "super-project", "refs/heads/master");
- allowMatchingSubmoduleSubscription(
- "sub2", "refs/heads/master", "super-project", "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey1, "refs/heads/master", superKey, "refs/heads/master");
+ allowMatchingSubmoduleSubscription(subKey2, "refs/heads/master", superKey, "refs/heads/master");
Config config = new Config();
- prepareSubmoduleConfigEntry(config, "sub1", "master");
- prepareSubmoduleConfigEntry(config, "sub2", "master");
+ prepareSubmoduleConfigEntry(config, subKey1, "master");
+ prepareSubmoduleConfigEntry(config, subKey2, "master");
pushSubmoduleConfig(superRepo, "master", config);
// Write an invalid SHA-1 directly to one of the gitlinks.
ObjectId badId = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- directUpdateSubmodule("super-project", "refs/heads/master", "sub1", badId);
- expectToHaveSubmoduleState(superRepo, "master", "sub1", badId);
+ directUpdateSubmodule(superKey, "refs/heads/master", subKey1, badId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey1, badId);
String topic = "same-topic";
ObjectId sub1Id = pushChangeTo(sub1, "refs/for/master", "some message", topic);
@@ -893,7 +893,11 @@ public class SubmoduleSubscriptionsWholeTopicMergeIT extends AbstractSubmoduleSu
assertThat(info(changeId2).status).isEqualTo(ChangeStatus.MERGED);
// sub1 was skipped but sub2 succeeded.
- expectToHaveSubmoduleState(superRepo, "master", "sub1", badId);
- expectToHaveSubmoduleState(superRepo, "master", "sub2", sub2, "master");
+ expectToHaveSubmoduleState(superRepo, "master", subKey1, badId);
+ expectToHaveSubmoduleState(superRepo, "master", subKey2, sub2, "master");
+ }
+
+ private Project.NameKey nameKey(String s) {
+ return new Project.NameKey(name(s));
}
}
diff --git a/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java b/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
index 214861c69f..7b99a55e57 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
@@ -21,7 +21,6 @@ import com.google.gerrit.elasticsearch.ElasticVersion;
import com.google.gerrit.testing.ConfigSuite;
import com.google.inject.Injector;
import org.eclipse.jgit.lib.Config;
-import org.junit.Before;
public class ElasticReindexIT extends AbstractReindexTests {
@@ -32,17 +31,11 @@ public class ElasticReindexIT extends AbstractReindexTests {
@ConfigSuite.Config
public static Config elasticsearchV7() {
- return getConfig(ElasticVersion.V7_7);
+ return getConfig(ElasticVersion.V7_8);
}
@Override
- public void configureIndex(Injector injector) throws Exception {
+ public void configureIndex(Injector injector) {
createAllIndexes(injector);
}
-
- @Before
- public void reindexFirstSinceElastic() throws Exception {
- assertServerStartupFails();
- runGerrit("reindex", "-d", sitePaths.site_path.toString(), "--show-stack-trace");
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/pgm/InitIT.java b/javatests/com/google/gerrit/acceptance/pgm/InitIT.java
index a573e35b11..7cc47c3fa8 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/InitIT.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/InitIT.java
@@ -26,7 +26,16 @@ 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;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.Comparator;
import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.eclipse.jgit.util.FS;
import org.junit.Test;
@NoHttpd
@@ -34,7 +43,7 @@ public class InitIT extends StandaloneSiteTest {
@Test
public void indexesAllProjectsAndAllUsers() throws Exception {
- runGerrit("init", "-d", sitePaths.site_path.toString(), "--show-stack-trace");
+ initSite();
try (ServerContext ctx = startServer()) {
ProjectIndexCollection projectIndex =
ctx.getInjector().getInstance(ProjectIndexCollection.class);
@@ -48,4 +57,52 @@ public class InitIT extends StandaloneSiteTest {
assertThat(allUsersData.isPresent()).isTrue();
}
}
+
+ @Test
+ public void initDoesNotReindexProjectsOnExistingSites() throws Exception {
+ initSite();
+
+ // 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();
+ setProjectsIndexLastModifiedInThePast(sitePaths.index_dir, projectsLastModified.get());
+
+ initSite();
+ Optional<Instant> projectsLastModifiedAfterInit =
+ getProjectsIndexLastModified(sitePaths.index_dir);
+
+ // Verify that projects index files haven't been updated
+ assertThat(projectsLastModified).isEqualTo(projectsLastModifiedAfterInit);
+ }
+
+ private void initSite() throws Exception {
+ runGerrit("init", "-d", sitePaths.site_path.toString(), "--show-stack-trace");
+ }
+
+ private void setProjectsIndexLastModifiedInThePast(Path indexDir, Instant time)
+ throws IOException {
+ for (Path path : getAllProjectsIndexFiles(indexDir).collect(Collectors.toList())) {
+ FS.DETECTED.setLastModified(path, time);
+ }
+ }
+
+ private Optional<Instant> getProjectsIndexLastModified(Path indexDir) throws IOException {
+ return getAllProjectsIndexFiles(indexDir)
+ .map(FS.DETECTED::lastModifiedInstant)
+ .max(Comparator.comparingLong(Instant::toEpochMilli));
+ }
+
+ private Stream<Path> getAllProjectsIndexFiles(Path indexDir) throws IOException {
+ Optional<Path> projectsPath =
+ Files.walk(indexDir, 1)
+ .filter(Files::isDirectory)
+ .filter(p -> p.getFileName().toString().startsWith("projects_"))
+ .findFirst();
+ if (!projectsPath.isPresent()) {
+ return Stream.empty();
+ }
+
+ return Files.walk(projectsPath.get(), 1, FileVisitOption.FOLLOW_LINKS);
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
deleted file mode 100644
index fd00e5444d..0000000000
--- a/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright (C) 2014 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.pgm;
-
-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 com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.StandaloneSiteTest;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.common.ChangeInput;
-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.server.ReviewDb;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
-import com.google.gerrit.server.index.GerritIndexStatus;
-import com.google.gerrit.server.index.change.ChangeIndexCollection;
-import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.stream.Stream;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Tests for NoteDb migrations where the entry point is through a program, {@code
- * migrate-to-note-db} or {@code daemon}.
- *
- * <p><strong>Note:</strong> These tests are very slow due to the repeated daemon startup. Prefer
- * adding tests to {@link com.google.gerrit.acceptance.server.notedb.OnlineNoteDbMigrationIT} if
- * possible.
- */
-@NoHttpd
-public class StandaloneNoteDbMigrationIT extends StandaloneSiteTest {
- private StoredConfig gerritConfig;
- private StoredConfig noteDbConfig;
-
- private Project.NameKey project;
- private Change.Id changeId;
-
- @Before
- public void setUp() throws Exception {
- assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
- gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
- // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
- noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
-
- // Set gc.pruneExpire=now so GC prunes all unreachable objects from All-Users, which allows us
- // to reliably test that it behaves as expected.
- Path cfgPath = sitePaths.site_path.resolve("git").resolve("All-Users.git").resolve("config");
- assertWithMessage("Expected All-Users config at %s", cfgPath)
- .that(Files.isRegularFile(cfgPath))
- .isTrue();
- FileBasedConfig cfg = new FileBasedConfig(cfgPath.toFile(), FS.detect());
- cfg.setString("gc", null, "pruneExpire", "now");
- cfg.save();
- }
-
- @Test
- public void rebuildOneChangeTrialMode() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoAutoMigrateConfig(noteDbConfig);
- assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
- setUpOneChange();
-
- migrate("--trial");
- assertNotesMigrationState(NotesMigrationState.READ_WRITE_NO_SEQUENCE);
-
- try (ServerContext ctx = startServer()) {
- GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
- ObjectId metaId;
- try (Repository repo = repoManager.openRepository(project)) {
- Ref ref = repo.exactRef(RefNames.changeMetaRef(changeId));
- assertThat(ref).isNotNull();
- metaId = ref.getObjectId();
- }
-
- try (ReviewDb db = openUnderlyingReviewDb(ctx)) {
- Change c = db.changes().get(changeId);
- assertThat(c).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- assertThat(state.getRefState()).hasValue(RefState.create(metaId, ImmutableMap.of()));
- }
- }
- }
-
- @Test
- public void migrateOneChange() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoAutoMigrateConfig(noteDbConfig);
- assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
- setUpOneChange();
-
- migrate();
- assertNotesMigrationState(NotesMigrationState.NOTE_DB);
-
- File allUsersDir;
- try (ServerContext ctx = startServer()) {
- GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
- try (Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef(RefNames.changeMetaRef(changeId))).isNotNull();
- }
- assertThat(repoManager).isInstanceOf(LocalDiskRepositoryManager.class);
- try (Repository repo =
- repoManager.openRepository(ctx.getInjector().getInstance(AllUsersName.class))) {
- allUsersDir = repo.getDirectory();
- }
-
- try (ReviewDb db = openUnderlyingReviewDb(ctx)) {
- Change c = db.changes().get(changeId);
- assertThat(c).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.NOTE_DB);
- assertThat(state.getRefState()).isEmpty();
-
- ChangeInput in = new ChangeInput(project.get(), "master", "NoteDb-only change");
- in.newBranch = true;
- GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
- Change.Id id2 = new Change.Id(gApi.changes().create(in).info()._number);
- assertThat(db.changes().get(id2)).isNull();
- }
- }
- assertNoAutoMigrateConfig(gerritConfig);
- assertAutoMigrateConfig(noteDbConfig, false);
-
- try (FileRepository repo = new FileRepository(allUsersDir)) {
- try (Stream<Path> paths = Files.walk(repo.getObjectsDirectory().toPath())) {
- assertThat(paths.filter(p -> !p.toString().contains("pack") && Files.isRegularFile(p)))
- .named("loose object files in All-Users")
- .isEmpty();
- }
- assertThat(repo.getObjectDatabase().getPacks()).named("packfiles in All-Users").hasSize(1);
- }
- }
-
- @Test
- public void migrationWithReindex() throws Exception {
- assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
- setUpOneChange();
-
- int version = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
- GerritIndexStatus status = new GerritIndexStatus(sitePaths);
- assertThat(status.getReady(ChangeSchemaDefinitions.NAME, version)).isTrue();
- status.setReady(ChangeSchemaDefinitions.NAME, version, false);
- status.save();
- assertServerStartupFails();
-
- migrate();
- assertNotesMigrationState(NotesMigrationState.NOTE_DB);
-
- status = new GerritIndexStatus(sitePaths);
- assertThat(status.getReady(ChangeSchemaDefinitions.NAME, version)).isTrue();
- }
-
- @Test
- public void onlineMigrationViaDaemon() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoAutoMigrateConfig(noteDbConfig);
-
- testOnlineMigration(u -> startServer(u.module(), "--migrate-to-note-db", "true"));
-
- assertNoAutoMigrateConfig(gerritConfig);
- assertAutoMigrateConfig(noteDbConfig, false);
- }
-
- @Test
- public void onlineMigrationViaConfig() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoAutoMigrateConfig(noteDbConfig);
-
- testOnlineMigration(
- u -> {
- gerritConfig.setBoolean("noteDb", "changes", "autoMigrate", true);
- gerritConfig.save();
- return startServer(u.module());
- });
-
- // Auto-migration is turned off in notedb.config, which takes precedence, but is still on in
- // gerrit.config. This means Puppet can continue overwriting gerrit.config without turning
- // auto-migration back on.
- assertAutoMigrateConfig(gerritConfig, true);
- assertAutoMigrateConfig(noteDbConfig, false);
- }
-
- @Test
- public void onlineMigrationMultithreaded() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoAutoMigrateConfig(noteDbConfig);
-
- testOnlineMigration(
- u -> {
- gerritConfig.setBoolean("noteDb", "changes", "autoMigrate", true);
- gerritConfig.setInt("noteDb", null, "onlineMigrationThreads", 4);
- gerritConfig.save();
- return startServer(u.module());
- });
-
- // Auto-migration is turned off in notedb.config, which takes precedence, but is still on in
- // gerrit.config. This means Puppet can continue overwriting gerrit.config without turning
- // auto-migration back on.
- assertAutoMigrateConfig(gerritConfig, true);
- assertAutoMigrateConfig(noteDbConfig, false);
- }
-
- @Test
- public void onlineMigrationTrialModeViaFlag() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoTrialConfig(gerritConfig);
-
- assertNoAutoMigrateConfig(noteDbConfig);
- assertNoTrialConfig(noteDbConfig);
-
- testOnlineMigration(
- u -> startServer(u.module(), "--migrate-to-note-db", "--trial"),
- NotesMigrationState.READ_WRITE_NO_SEQUENCE);
-
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoTrialConfig(gerritConfig);
-
- assertAutoMigrateConfig(noteDbConfig, true);
- assertTrialConfig(noteDbConfig, true);
- }
-
- @Test
- public void onlineMigrationTrialModeViaConfig() throws Exception {
- assertNoAutoMigrateConfig(gerritConfig);
- assertNoTrialConfig(gerritConfig);
-
- assertNoAutoMigrateConfig(noteDbConfig);
- assertNoTrialConfig(noteDbConfig);
-
- testOnlineMigration(
- u -> {
- gerritConfig.setBoolean("noteDb", "changes", "autoMigrate", true);
- gerritConfig.setBoolean("noteDb", "changes", "trial", true);
- gerritConfig.save();
- return startServer(u.module());
- },
- NotesMigrationState.READ_WRITE_NO_SEQUENCE);
-
- assertAutoMigrateConfig(gerritConfig, true);
- assertTrialConfig(gerritConfig, true);
-
- assertAutoMigrateConfig(noteDbConfig, true);
- assertTrialConfig(noteDbConfig, true);
- }
-
- @FunctionalInterface
- private interface StartServerWithMigration {
- ServerContext start(IndexUpgradeController u) throws Exception;
- }
-
- private void testOnlineMigration(StartServerWithMigration start) throws Exception {
- testOnlineMigration(start, NotesMigrationState.NOTE_DB);
- }
-
- private void testOnlineMigration(
- StartServerWithMigration start, NotesMigrationState expectedEndState) throws Exception {
- assertNotesMigrationState(NotesMigrationState.REVIEW_DB);
- int prevVersion = ChangeSchemaDefinitions.INSTANCE.getPrevious().getVersion();
- int currVersion = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
-
- // Before storing any changes, switch back to the previous version.
- GerritIndexStatus status = new GerritIndexStatus(sitePaths);
- status.setReady(ChangeSchemaDefinitions.NAME, currVersion, false);
- status.setReady(ChangeSchemaDefinitions.NAME, prevVersion, true);
- status.save();
-
- setOnlineUpgradeConfig(false);
- setUpOneChange();
- setOnlineUpgradeConfig(true);
-
- IndexUpgradeController u = new IndexUpgradeController(1);
- try (ServerContext ctx = start.start(u)) {
- ChangeIndexCollection indexes = ctx.getInjector().getInstance(ChangeIndexCollection.class);
- assertThat(indexes.getSearchIndex().getSchema().getVersion()).isEqualTo(prevVersion);
-
- // Index schema upgrades happen after NoteDb migration, so waiting for those to complete
- // should be sufficient.
- u.runUpgrades();
-
- assertThat(indexes.getSearchIndex().getSchema().getVersion()).isEqualTo(currVersion);
- assertNotesMigrationState(expectedEndState);
- }
- }
-
- private void setUpOneChange() throws Exception {
- project = new Project.NameKey("project");
- try (ServerContext ctx = startServer()) {
- GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
- gApi.projects().create("project");
-
- ChangeInput in = new ChangeInput(project.get(), "master", "Test change");
- in.newBranch = true;
- changeId = new Change.Id(gApi.changes().create(in).info()._number);
- }
- }
-
- private void migrate(String... additionalArgs) throws Exception {
- runGerrit(
- ImmutableList.of(
- "migrate-to-note-db", "-d", sitePaths.site_path.toString(), "--show-stack-trace"),
- ImmutableList.copyOf(additionalArgs));
- }
-
- private void assertNotesMigrationState(NotesMigrationState expected) throws Exception {
- noteDbConfig.load();
- assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected);
- }
-
- private ReviewDb openUnderlyingReviewDb(ServerContext ctx) throws Exception {
- return ctx.getInjector()
- .getInstance(Key.get(new TypeLiteral<SchemaFactory<ReviewDb>>() {}, ReviewDbFactory.class))
- .open();
- }
-
- private static void assertNoAutoMigrateConfig(StoredConfig cfg) throws Exception {
- cfg.load();
- assertThat(cfg.getString("noteDb", "changes", "autoMigrate")).isNull();
- }
-
- private static void assertAutoMigrateConfig(StoredConfig cfg, boolean expected) throws Exception {
- cfg.load();
- assertThat(cfg.getString("noteDb", "changes", "autoMigrate")).isNotNull();
- assertThat(cfg.getBoolean("noteDb", "changes", "autoMigrate", false)).isEqualTo(expected);
- }
-
- private static void assertNoTrialConfig(StoredConfig cfg) throws Exception {
- cfg.load();
- assertThat(cfg.getString("noteDb", "changes", "trial")).isNull();
- }
-
- private static void assertTrialConfig(StoredConfig cfg, boolean expected) throws Exception {
- cfg.load();
- assertThat(cfg.getString("noteDb", "changes", "trial")).isNotNull();
- assertThat(cfg.getBoolean("noteDb", "changes", "trial", false)).isEqualTo(expected);
- }
-
- private void setOnlineUpgradeConfig(boolean enable) throws Exception {
- gerritConfig.load();
- gerritConfig.setBoolean("index", null, "onlineUpgrade", enable);
- gerritConfig.save();
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java b/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java
deleted file mode 100644
index 2bb3dcaa77..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/AbstractRestApiBindingsTest.java
+++ /dev/null
@@ -1,165 +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.acceptance.rest;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.truth.Truth.assertWithMessage;
-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;
-import static org.apache.http.HttpStatus.SC_NOT_FOUND;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.RestResponse;
-import java.util.List;
-import java.util.Optional;
-import org.apache.commons.lang.StringUtils;
-import org.junit.Ignore;
-
-/**
- * Base class for testing the REST API bindings.
- *
- * <p>This test sends a request to each REST endpoint and verifies that an implementation is found
- * (no '404 Not Found' response) and that the request doesn't fail (no '500 Internal Server Error'
- * response). It doesn't verify that the REST endpoint works correctly. This is okay since the
- * purpose of the test is only to verify that the REST endpoint implementations are correctly bound.
- */
-@Ignore
-public abstract class AbstractRestApiBindingsTest extends AbstractDaemonTest {
- protected void execute(List<RestCall> restCalls, String... args) throws Exception {
- execute(restCalls, () -> {}, args);
- }
-
- protected void execute(List<RestCall> restCalls, BeforeRestCall beforeRestCall, String... args)
- throws Exception {
- for (RestCall restCall : restCalls) {
- beforeRestCall.run();
- execute(restCall, args);
- }
- }
-
- protected void execute(RestCall restCall, String... args) throws Exception {
- String method = restCall.httpMethod().name();
- String uri = restCall.uri(args);
-
- RestResponse response;
- switch (restCall.httpMethod()) {
- case GET:
- response = adminRestSession.get(uri);
- break;
- case PUT:
- response = adminRestSession.put(uri);
- break;
- case POST:
- response = adminRestSession.post(uri);
- break;
- case DELETE:
- response = adminRestSession.delete(uri);
- break;
- default:
- fail("unsupported method: %s", restCall.httpMethod().name());
- throw new IllegalStateException();
- }
-
- int status = response.getStatusCode();
- String body = response.hasContent() ? response.getEntityContent() : "";
-
- String msg = String.format("%s %s returned %d: %s", method, uri, status, body);
- if (restCall.expectedResponseCode().isPresent()) {
- assertWithMessage(msg).that(status).isEqualTo(restCall.expectedResponseCode().get());
- if (restCall.expectedMessage().isPresent()) {
- assertWithMessage(msg).that(body).contains(restCall.expectedMessage().get());
- }
- } else {
- assertWithMessage(msg)
- .that(status)
- .isNotIn(ImmutableList.of(SC_FORBIDDEN, SC_NOT_FOUND, SC_METHOD_NOT_ALLOWED));
- assertWithMessage(msg).that(status).isLessThan(SC_INTERNAL_SERVER_ERROR);
- }
- }
-
- enum Method {
- GET,
- PUT,
- POST,
- DELETE
- }
-
- @AutoValue
- abstract static class RestCall {
- static RestCall get(String uriFormat) {
- return builder(Method.GET, uriFormat).build();
- }
-
- static RestCall put(String uriFormat) {
- return builder(Method.PUT, uriFormat).build();
- }
-
- static RestCall post(String uriFormat) {
- return builder(Method.POST, uriFormat).build();
- }
-
- static RestCall delete(String uriFormat) {
- return builder(Method.DELETE, uriFormat).build();
- }
-
- static Builder builder(Method httpMethod, String uriFormat) {
- return new AutoValue_AbstractRestApiBindingsTest_RestCall.Builder()
- .httpMethod(httpMethod)
- .uriFormat(uriFormat);
- }
-
- abstract Method httpMethod();
-
- abstract String uriFormat();
-
- abstract Optional<Integer> expectedResponseCode();
-
- abstract Optional<String> expectedMessage();
-
- String uri(String... args) {
- String uriFormat = uriFormat();
- int expectedArgNum = StringUtils.countMatches(uriFormat, "%s");
- checkState(
- args.length == expectedArgNum,
- "uriFormat %s needs %s arguments, got only %s: %s",
- uriFormat,
- expectedArgNum,
- args.length,
- args);
- return String.format(uriFormat, (Object[]) args);
- }
-
- @AutoValue.Builder
- abstract static class Builder {
- abstract Builder httpMethod(Method httpMethod);
-
- abstract Builder uriFormat(String uriFormat);
-
- abstract Builder expectedResponseCode(int expectedResponseCode);
-
- abstract Builder expectedMessage(String expectedMessage);
-
- abstract RestCall build();
- }
- }
-
- @FunctionalInterface
- public interface BeforeRestCall {
- void run() throws Exception;
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java
deleted file mode 100644
index b0adba7d2b..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/AccountsRestApiBindingsIT.java
+++ /dev/null
@@ -1,191 +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.acceptance.rest;
-
-import static com.google.gerrit.acceptance.rest.AbstractRestApiBindingsTest.Method.PUT;
-import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpiration;
-import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.acceptance.GerritConfig;
-import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.gpg.testing.TestKey;
-import com.google.gerrit.server.ServerInitiated;
-import com.google.gerrit.server.account.AccountsUpdate;
-import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the accounts REST API.
- *
- * <p>These tests only verify that the account REST endpoints are correctly bound, they do no test
- * the functionality of the account REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class AccountsRestApiBindingsIT extends AbstractRestApiBindingsTest {
- @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
-
- /**
- * Account REST endpoints to be tested, each URL contains a placeholder for the account
- * identifier.
- */
- private static final ImmutableList<RestCall> ACCOUNT_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/accounts/%s"),
- RestCall.put("/accounts/%s"),
- RestCall.get("/accounts/%s/detail"),
- RestCall.get("/accounts/%s/name"),
- RestCall.put("/accounts/%s/name"),
- RestCall.delete("/accounts/%s/name"),
- RestCall.get("/accounts/%s/username"),
- RestCall.builder(PUT, "/accounts/%s/username")
- // Changing the username is not allowed.
- .expectedResponseCode(SC_METHOD_NOT_ALLOWED)
- .expectedMessage("Username cannot be changed.")
- .build(),
- RestCall.get("/accounts/%s/active"),
- RestCall.put("/accounts/%s/active"),
- RestCall.delete("/accounts/%s/active"),
- RestCall.put("/accounts/%s/password.http"),
- RestCall.delete("/accounts/%s/password.http"),
- RestCall.get("/accounts/%s/status"),
- RestCall.put("/accounts/%s/status"),
- RestCall.get("/accounts/%s/avatar"),
- RestCall.get("/accounts/%s/avatar.change.url"),
- RestCall.get("/accounts/%s/emails/"),
- RestCall.put("/accounts/%s/emails/new-email@foo.com"),
- RestCall.get("/accounts/%s/sshkeys/"),
- RestCall.post("/accounts/%s/sshkeys/"),
- RestCall.get("/accounts/%s/watched.projects"),
- RestCall.post("/accounts/%s/watched.projects"),
- RestCall.post("/accounts/%s/watched.projects:delete"),
- RestCall.get("/accounts/%s/groups"),
- RestCall.get("/accounts/%s/preferences"),
- RestCall.put("/accounts/%s/preferences"),
- RestCall.get("/accounts/%s/preferences.diff"),
- RestCall.put("/accounts/%s/preferences.diff"),
- RestCall.get("/accounts/%s/preferences.edit"),
- RestCall.put("/accounts/%s/preferences.edit"),
- RestCall.get("/accounts/%s/starred.changes"),
- RestCall.get("/accounts/%s/stars.changes"),
- RestCall.post("/accounts/%s/index"),
- RestCall.get("/accounts/%s/agreements"),
- RestCall.put("/accounts/%s/agreements"),
- RestCall.get("/accounts/%s/external.ids"),
- RestCall.post("/accounts/%s/external.ids:delete"),
- RestCall.post("/accounts/%s/drafts:delete"),
- RestCall.get("/accounts/%s/oauthtoken"),
- RestCall.get("/accounts/%s/capabilities"),
- RestCall.get("/accounts/%s/capabilities/viewPlugins"),
- RestCall.get("/accounts/%s/gpgkeys"),
- RestCall.post("/accounts/%s/gpgkeys"));
-
- /**
- * Email REST endpoints to be tested, each URL contains a placeholders for the account and email
- * identifier.
- */
- private static final ImmutableList<RestCall> EMAIL_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/accounts/%s/emails/%s"),
- RestCall.put("/accounts/%s/emails/%s"),
- RestCall.put("/accounts/%s/emails/%s/preferred"),
-
- // email deletion must be tested last
- RestCall.delete("/accounts/%s/emails/%s"));
-
- /**
- * GPG key REST endpoints to be tested, each URL contains a placeholders for the account
- * identifier and the GPG key identifier.
- */
- private static final ImmutableList<RestCall> GPG_KEY_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/accounts/%s/gpgkeys/%s"),
-
- // GPG key deletion must be tested last
- RestCall.delete("/accounts/%s/gpgkeys/%s"));
-
- /**
- * SSH key REST endpoints to be tested, each URL contains a placeholders for the account and SSH
- * key identifier.
- */
- private static final ImmutableList<RestCall> SSH_KEY_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/accounts/%s/sshkeys/%s"),
-
- // SSH key deletion must be tested last
- RestCall.delete("/accounts/%s/sshkeys/%s"));
-
- /**
- * Star REST endpoints to be tested, each URL contains a placeholders for the account and change
- * identifier.
- */
- private static final ImmutableList<RestCall> STAR_ENDPOINTS =
- ImmutableList.of(
- RestCall.put("/accounts/%s/starred.changes/%s"),
- RestCall.delete("/accounts/%s/starred.changes/%s"),
- RestCall.get("/accounts/%s/stars.changes/%s"),
- RestCall.post("/accounts/%s/stars.changes/%s"));
-
- @Test
- public void accountEndpoints() throws Exception {
- execute(ACCOUNT_ENDPOINTS, "self");
- }
-
- @Test
- public void emailEndpoints() throws Exception {
- execute(EMAIL_ENDPOINTS, "self", admin.email);
- }
-
- @Test
- @GerritConfig(name = "receive.enableSignedPush", value = "true")
- public void gpgKeyEndpoints() throws Exception {
- TestKey key = validKeyWithoutExpiration();
- String id = key.getKeyIdString();
-
- String email = "test1@example.com"; // email that is hard-coded in the test GPG key
- accountsUpdateProvider
- .get()
- .update(
- "Add Email",
- admin.getId(),
- u ->
- u.addExternalId(
- ExternalId.createWithEmail(name("test"), email, admin.getId(), email)));
-
- setApiUser(admin);
- gApi.accounts()
- .self()
- .putGpgKeys(ImmutableList.of(key.getPublicKeyArmored()), ImmutableList.of());
-
- execute(GPG_KEY_ENDPOINTS, "self", id);
- }
-
- @Test
- @UseSsh
- public void sshKeyEndpoints() throws Exception {
- String sshKeySeq = Integer.toString(gApi.accounts().self().listSshKeys().size());
- execute(SSH_KEY_ENDPOINTS, "self", sshKeySeq);
- }
-
- @Test
- public void starEndpoints() throws Exception {
- ChangeInput ci = new ChangeInput(project.get(), "master", "Test change");
- String changeId = gApi.changes().create(ci).get().id;
- execute(STAR_ENDPOINTS, "self", changeId);
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/BUILD b/javatests/com/google/gerrit/acceptance/rest/BUILD
index e746eb72d1..84887dadb3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/BUILD
@@ -1,25 +1,10 @@
-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_bindings",
+ group = "rest_bindings_collection",
labels = ["rest"],
deps = [
- ":util",
"//java/com/google/gerrit/server/logging",
],
)
-
-java_library(
- name = "util",
- testonly = True,
- srcs = [
- "AbstractRestApiBindingsTest.java",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//java/com/google/gerrit/acceptance:lib",
- "//lib/commons:lang",
- ],
-)
diff --git a/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java
deleted file mode 100644
index c00c2aad40..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/ChangesRestApiBindingsIT.java
+++ /dev/null
@@ -1,509 +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.acceptance.rest;
-
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.acceptance.rest.AbstractRestApiBindingsTest.Method.GET;
-import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
-import static java.util.stream.Collectors.toList;
-import static org.apache.http.HttpStatus.SC_NOT_FOUND;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.gerrit.extensions.api.changes.AddReviewerInput;
-import com.google.gerrit.extensions.api.changes.DraftInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
-import com.google.gerrit.extensions.api.changes.ReviewInput.RobotCommentInput;
-import com.google.gerrit.extensions.client.Comment;
-import com.google.gerrit.extensions.client.Side;
-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;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the changes REST API.
- *
- * <p>These tests only verify that the change REST endpoints are correctly bound, they do no test
- * the functionality of the change REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class ChangesRestApiBindingsIT extends AbstractRestApiBindingsTest {
- /**
- * Change REST endpoints to be tested, each URL contains a placeholder for the change identifier.
- */
- private static final ImmutableList<RestCall> CHANGE_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/changes/%s"),
- RestCall.get("/changes/%s/detail"),
- RestCall.get("/changes/%s/topic"),
- RestCall.put("/changes/%s/topic"),
- RestCall.delete("/changes/%s/topic"),
- RestCall.get("/changes/%s/in"),
- RestCall.get("/changes/%s/hashtags"),
- RestCall.get("/changes/%s/comments"),
- RestCall.get("/changes/%s/robotcomments"),
- RestCall.get("/changes/%s/drafts"),
- RestCall.get("/changes/%s/assignee"),
- RestCall.get("/changes/%s/past_assignees"),
- RestCall.put("/changes/%s/assignee"),
- RestCall.delete("/changes/%s/assignee"),
- RestCall.post("/changes/%s/private"),
- RestCall.post("/changes/%s/private.delete"),
- RestCall.delete("/changes/%s/private"),
- RestCall.post("/changes/%s/wip"),
- RestCall.post("/changes/%s/ready"),
- RestCall.put("/changes/%s/ignore"),
- RestCall.put("/changes/%s/unignore"),
- RestCall.put("/changes/%s/reviewed"),
- RestCall.put("/changes/%s/unreviewed"),
- RestCall.get("/changes/%s/messages"),
- RestCall.put("/changes/%s/message"),
- RestCall.post("/changes/%s/merge"),
- RestCall.post("/changes/%s/abandon"),
- RestCall.post("/changes/%s/move"),
- RestCall.post("/changes/%s/rebase"),
- RestCall.post("/changes/%s/restore"),
- RestCall.post("/changes/%s/revert"),
- RestCall.get("/changes/%s/pure_revert"),
- RestCall.post("/changes/%s/submit"),
- RestCall.get("/changes/%s/submitted_together"),
- RestCall.post("/changes/%s/index"),
- RestCall.get("/changes/%s/check"),
- RestCall.post("/changes/%s/check"),
- RestCall.get("/changes/%s/reviewers"),
- RestCall.post("/changes/%s/reviewers"),
- RestCall.get("/changes/%s/suggest_reviewers"),
- RestCall.builder(GET, "/changes/%s/revisions")
- // GET /changes/<change-id>/revisions is not implemented
- .expectedResponseCode(SC_NOT_FOUND)
- .build(),
- RestCall.get("/changes/%s/edit"),
- RestCall.post("/changes/%s/edit"),
- RestCall.post("/changes/%s/edit:rebase"),
- RestCall.get("/changes/%s/edit:message"),
- RestCall.put("/changes/%s/edit:message"),
-
- // Publish edit and create a new edit
- RestCall.post("/changes/%s/edit:publish"),
- RestCall.put("/changes/%s/edit/a.txt"),
-
- // Deletion of change edit and change must be tested last
- RestCall.delete("/changes/%s/edit"),
- RestCall.delete("/changes/%s"));
-
- /**
- * Change REST endpoints to be tested with NoteDb, each URL contains a placeholder for the change
- * identifier.
- */
- private static final ImmutableList<RestCall> CHANGE_ENDPOINTS_NOTEDB =
- ImmutableList.of(
- RestCall.post("/changes/%s/hashtags"), RestCall.post("/changes/%s/rebuild.notedb"));
-
- /**
- * Reviewer REST endpoints to be tested, each URL contains placeholders for the change identifier
- * and the reviewer identifier.
- */
- private static final ImmutableList<RestCall> REVIEWER_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/changes/%s/reviewers/%s"),
- RestCall.get("/changes/%s/reviewers/%s/votes"),
- RestCall.post("/changes/%s/reviewers/%s/delete"),
- RestCall.delete("/changes/%s/reviewers/%s"));
-
- /**
- * Vote REST endpoints to be tested, each URL contains placeholders for the change identifier, the
- * reviewer identifier and the label identifier.
- */
- private static final ImmutableList<RestCall> VOTE_ENDPOINTS =
- ImmutableList.of(
- RestCall.post("/changes/%s/reviewers/%s/votes/%s/delete"),
- RestCall.delete("/changes/%s/reviewers/%s/votes/%s"));
-
- /**
- * Revision REST endpoints to be tested, each URL contains placeholders for the change identifier
- * and the revision identifier.
- */
- private static final ImmutableList<RestCall> REVISION_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/changes/%s/revisions/%s/actions"),
- RestCall.post("/changes/%s/revisions/%s/cherrypick"),
- RestCall.get("/changes/%s/revisions/%s/commit"),
- RestCall.get("/changes/%s/revisions/%s/mergeable"),
- RestCall.get("/changes/%s/revisions/%s/related"),
- RestCall.get("/changes/%s/revisions/%s/review"),
- RestCall.post("/changes/%s/revisions/%s/review"),
- RestCall.get("/changes/%s/revisions/%s/preview_submit"),
- RestCall.post("/changes/%s/revisions/%s/submit"),
- RestCall.get("/changes/%s/revisions/%s/submit_type"),
- RestCall.post("/changes/%s/revisions/%s/test.submit_rule"),
- RestCall.post("/changes/%s/revisions/%s/test.submit_type"),
- RestCall.post("/changes/%s/revisions/%s/rebase"),
- RestCall.get("/changes/%s/revisions/%s/description"),
- RestCall.put("/changes/%s/revisions/%s/description"),
- RestCall.get("/changes/%s/revisions/%s/patch"),
- RestCall.get("/changes/%s/revisions/%s/archive"),
- RestCall.get("/changes/%s/revisions/%s/mergelist"),
- RestCall.get("/changes/%s/revisions/%s/reviewers"),
- RestCall.get("/changes/%s/revisions/%s/drafts"),
- RestCall.put("/changes/%s/revisions/%s/drafts"),
- RestCall.get("/changes/%s/revisions/%s/comments"),
- RestCall.get("/changes/%s/revisions/%s/robotcomments"),
- RestCall.builder(GET, "/changes/%s/revisions/%s/fixes")
- // GET /changes/<change>/revisions/<revision>/fixes is not implemented
- .expectedResponseCode(SC_NOT_FOUND)
- .build(),
- RestCall.get("/changes/%s/revisions/%s/files"));
-
- /**
- * Revision reviewer REST endpoints to be tested, each URL contains placeholders for the change
- * identifier, the revision identifier and the reviewer identifier.
- */
- private static final ImmutableList<RestCall> REVISION_REVIEWER_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/changes/%s/revisions/%s/reviewers/%s"),
- RestCall.get("/changes/%s/revisions/%s/reviewers/%s/votes"),
- RestCall.post("/changes/%s/revisions/%s/reviewers/%s/delete"),
- RestCall.delete("/changes/%s/revisions/%s/reviewers/%s"));
-
- /**
- * Revision vote REST endpoints to be tested, each URL contains placeholders for the change
- * identifier, the revision identifier, the reviewer identifier and the label identifier.
- */
- private static final ImmutableList<RestCall> REVISION_VOTE_ENDPOINTS =
- ImmutableList.of(
- RestCall.post("/changes/%s/revisions/%s/reviewers/%s/votes/%s/delete"),
- RestCall.delete("/changes/%s/revisions/%s/reviewers/%s/votes/%s"));
-
- /**
- * Draft comment REST endpoints to be tested, each URL contains placeholders for the change
- * identifier, the revision identifier and the draft comment identifier.
- */
- private static final ImmutableList<RestCall> DRAFT_COMMENT_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/changes/%s/revisions/%s/drafts/%s"),
- RestCall.put("/changes/%s/revisions/%s/drafts/%s"),
- RestCall.delete("/changes/%s/revisions/%s/drafts/%s"));
-
- /**
- * Comment REST endpoints to be tested, each URL contains placeholders for the change identifier,
- * the revision identifier and the comment identifier.
- */
- private static final ImmutableList<RestCall> COMMENT_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/changes/%s/revisions/%s/comments/%s"),
- RestCall.delete("/changes/%s/revisions/%s/comments/%s"),
- RestCall.post("/changes/%s/revisions/%s/comments/%s/delete"));
-
- /**
- * Robot comment REST endpoints to be tested, each URL contains placeholders for the change
- * identifier, the revision identifier and the robot comment identifier.
- */
- private static final ImmutableList<RestCall> ROBOT_COMMENT_ENDPOINTS =
- ImmutableList.of(RestCall.get("/changes/%s/revisions/%s/robotcomments/%s"));
-
- /**
- * Fix REST endpoints to be tested, each URL contains placeholders for the change identifier, the
- * revision identifier and the fix identifier.
- */
- private static final ImmutableList<RestCall> FIX_ENDPOINTS =
- ImmutableList.of(RestCall.post("/changes/%s/revisions/%s/fixes/%s/apply"));
-
- /**
- * Revision file REST endpoints to be tested, each URL contains placeholders for the change
- * identifier, the revision identifier and the file identifier.
- */
- private static final ImmutableList<RestCall> REVISION_FILE_ENDPOINTS =
- ImmutableList.of(
- RestCall.put("/changes/%s/revisions/%s/files/%s/reviewed"),
- RestCall.delete("/changes/%s/revisions/%s/files/%s/reviewed"),
- RestCall.get("/changes/%s/revisions/%s/files/%s/content"),
- RestCall.get("/changes/%s/revisions/%s/files/%s/download"),
- RestCall.get("/changes/%s/revisions/%s/files/%s/diff"),
- RestCall.get("/changes/%s/revisions/%s/files/%s/blame"));
-
- /**
- * Change message REST endpoints to be tested, each URL contains placeholders for the change
- * identifier and the change message identifier.
- */
- private static final ImmutableList<RestCall> CHANGE_MESSAGE_ENDPOINTS =
- ImmutableList.of(RestCall.get("/changes/%s/messages/%s"));
-
- /**
- * Change edit REST endpoints that create an edit to be tested, each URL contains placeholders for
- * the change identifier and the change edit identifier.
- */
- private static final ImmutableList<RestCall> CHANGE_EDIT_CREATE_ENDPOINTS =
- ImmutableList.of(
- // Create change edit by editing an existing file.
- RestCall.put("/changes/%s/edit/%s"),
-
- // Create change edit by deleting an existing file.
- RestCall.delete("/changes/%s/edit/%s"));
-
- /**
- * Change edit REST endpoints to be tested, each URL contains placeholders for the change
- * identifier and the change edit identifier.
- */
- private static final ImmutableList<RestCall> CHANGE_EDIT_ENDPOINTS =
- ImmutableList.of(
- // Calls on existing change edit.
- RestCall.get("/changes/%s/edit/%s"),
- RestCall.put("/changes/%s/edit/%s"),
- RestCall.get("/changes/%s/edit/%s/meta"),
-
- // Delete content of a file in an existing change edit.
- RestCall.delete("/changes/%s/edit/%s"));
-
- private static final String FILENAME = "test.txt";
-
- @Test
- public void changeEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
- gApi.changes().id(changeId).edit().create();
- execute(CHANGE_ENDPOINTS, changeId);
- }
-
- @Test
- public void changeEndpointsNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
- String changeId = createChange().getChangeId();
- execute(CHANGE_ENDPOINTS_NOTEDB, changeId);
- }
-
- @Test
- public void reviewerEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- AddReviewerInput addReviewerInput = new AddReviewerInput();
- addReviewerInput.reviewer = user.email;
-
- execute(
- REVIEWER_ENDPOINTS,
- () -> gApi.changes().id(changeId).addReviewer(addReviewerInput),
- changeId,
- addReviewerInput.reviewer);
- }
-
- @Test
- public void voteEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- execute(
- VOTE_ENDPOINTS,
- () -> gApi.changes().id(changeId).current().review(ReviewInput.approve()),
- changeId,
- admin.email,
- "Code-Review");
- }
-
- @Test
- public void revisionEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
- execute(REVISION_ENDPOINTS, changeId, "current");
- }
-
- @Test
- public void revisionReviewerEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- AddReviewerInput addReviewerInput = new AddReviewerInput();
- addReviewerInput.reviewer = user.email;
-
- execute(
- REVISION_REVIEWER_ENDPOINTS,
- () -> gApi.changes().id(changeId).addReviewer(addReviewerInput),
- changeId,
- "current",
- addReviewerInput.reviewer);
- }
-
- @Test
- public void revisionVoteEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- execute(
- REVISION_VOTE_ENDPOINTS,
- () -> gApi.changes().id(changeId).current().review(ReviewInput.approve()),
- changeId,
- "current",
- admin.email,
- "Code-Review");
- }
-
- @Test
- public void draftCommentEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- for (RestCall restCall : DRAFT_COMMENT_ENDPOINTS) {
- DraftInput draftInput = new DraftInput();
- draftInput.path = Patch.COMMIT_MSG;
- draftInput.side = Side.REVISION;
- draftInput.line = 1;
- draftInput.message = "draft comment";
- CommentInfo draftInfo = gApi.changes().id(changeId).current().createDraft(draftInput).get();
-
- execute(restCall, changeId, "current", draftInfo.id);
- }
- }
-
- @Test
- public void commentEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- for (RestCall restCall : COMMENT_ENDPOINTS) {
- DraftInput draftInput = new DraftInput();
- draftInput.path = Patch.COMMIT_MSG;
- draftInput.side = Side.REVISION;
- draftInput.line = 1;
- draftInput.message = "draft comment";
- CommentInfo commentInfo = gApi.changes().id(changeId).current().createDraft(draftInput).get();
-
- ReviewInput reviewInput = new ReviewInput();
- reviewInput.drafts = DraftHandling.PUBLISH;
- gApi.changes().id(changeId).current().review(reviewInput);
-
- execute(restCall, changeId, "current", commentInfo.id);
- }
- }
-
- @Test
- public void robotCommentEndpoints() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
- String changeId = createChange().getChangeId();
-
- RobotCommentInput robotCommentInput = new RobotCommentInput();
- robotCommentInput.robotId = "happyRobot";
- robotCommentInput.robotRunId = "1";
- robotCommentInput.line = 1;
- robotCommentInput.message = "nit: trailing whitespace";
- robotCommentInput.path = Patch.COMMIT_MSG;
-
- ReviewInput reviewInput = new ReviewInput();
- reviewInput.robotComments =
- Collections.singletonMap(robotCommentInput.path, ImmutableList.of(robotCommentInput));
- reviewInput.message = "robot comment test";
- gApi.changes().id(changeId).current().review(reviewInput);
-
- List<RobotCommentInfo> robotCommentInfos =
- gApi.changes().id(changeId).current().robotCommentsAsList();
- RobotCommentInfo robotCommentInfo = Iterables.getOnlyElement(robotCommentInfos);
-
- execute(ROBOT_COMMENT_ENDPOINTS, changeId, "current", robotCommentInfo.id);
- }
-
- @Test
- public void fixEndpoints() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
- String changeId = createChange("Subject", FILENAME, "content").getChangeId();
-
- RobotCommentInput robotCommentInput = new RobotCommentInput();
- robotCommentInput.robotId = "happyRobot";
- robotCommentInput.robotRunId = "1";
- robotCommentInput.line = 1;
- robotCommentInput.message = "nit: trailing whitespace";
- robotCommentInput.path = FILENAME;
-
- FixReplacementInfo fixReplacementInfo = new FixReplacementInfo();
- fixReplacementInfo.path = FILENAME;
- fixReplacementInfo.replacement = "some replacement code";
- fixReplacementInfo.range = createRange(1, 1, 1, 2);
-
- FixSuggestionInfo fixSuggestionInfo = new FixSuggestionInfo();
- fixSuggestionInfo.fixId = "An ID which must be overwritten.";
- fixSuggestionInfo.description = "A description for a suggested fix.";
- fixSuggestionInfo.replacements = ImmutableList.of(fixReplacementInfo);
-
- robotCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo);
-
- ReviewInput reviewInput = new ReviewInput();
- reviewInput.robotComments =
- Collections.singletonMap(robotCommentInput.path, ImmutableList.of(robotCommentInput));
- reviewInput.message = "robot comment test";
- gApi.changes().id(changeId).current().review(reviewInput);
-
- List<RobotCommentInfo> robotCommentInfos =
- gApi.changes().id(changeId).current().robotCommentsAsList();
-
- List<String> fixIds = getFixIds(robotCommentInfos);
- String fixId = Iterables.getOnlyElement(fixIds);
-
- execute(FIX_ENDPOINTS, changeId, "current", fixId);
- }
-
- @Test
- public void revisionFileEndpoints() throws Exception {
- String changeId = createChange("Subject", FILENAME, "content").getChangeId();
- execute(REVISION_FILE_ENDPOINTS, changeId, "current", FILENAME);
- }
-
- @Test
- public void changeMessageEndpoints() throws Exception {
- String changeId = createChange().getChangeId();
-
- // A change message is created on change creation.
- String changeMessageId = Iterables.getOnlyElement(gApi.changes().id(changeId).messages()).id;
-
- execute(CHANGE_MESSAGE_ENDPOINTS, changeId, changeMessageId);
- }
-
- @Test
- public void changeEditCreateEndpoints() throws Exception {
- String changeId = createChange("Subject", FILENAME, "content").getChangeId();
-
- // Each of the REST calls creates the change edit newly.
- execute(
- CHANGE_EDIT_CREATE_ENDPOINTS,
- () -> adminRestSession.delete("/changes/" + changeId + "/edit"),
- changeId,
- FILENAME);
- }
-
- @Test
- public void changeEditEndpoints() throws Exception {
- String changeId = createChange("Subject", FILENAME, "content").getChangeId();
- gApi.changes().id(changeId).edit().create();
- execute(CHANGE_EDIT_ENDPOINTS, changeId, FILENAME);
- }
-
- private static Comment.Range createRange(
- int startLine, int startCharacter, int endLine, int endCharacter) {
- Comment.Range range = new Comment.Range();
- range.startLine = startLine;
- range.startCharacter = startCharacter;
- range.endLine = endLine;
- range.endCharacter = endCharacter;
- return range;
- }
-
- private static List<String> getFixIds(List<RobotCommentInfo> robotComments) {
- assertThatList(robotComments).isNotNull();
- return robotComments.stream()
- .map(robotCommentInfo -> robotCommentInfo.fixSuggestions)
- .filter(Objects::nonNull)
- .flatMap(List::stream)
- .map(fixSuggestionInfo -> fixSuggestionInfo.fixId)
- .collect(toList());
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
deleted file mode 100644
index 2e5ea00553..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/ConfigRestApiBindingsIT.java
+++ /dev/null
@@ -1,111 +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.acceptance.rest;
-
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.acceptance.RestResponse;
-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 java.util.List;
-import java.util.Optional;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the config REST API.
- *
- * <p>These tests only verify that the config REST endpoints are correctly bound, they do no test
- * the functionality of the config REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class ConfigRestApiBindingsIT extends AbstractRestApiBindingsTest {
- /**
- * Config REST endpoints to be tested, the URLs contain no placeholders since the only supported
- * config identifier ('server') can be hard-coded.
- */
- private static final ImmutableList<RestCall> CONFIG_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/config/server/version"),
- RestCall.get("/config/server/info"),
- RestCall.get("/config/server/preferences"),
- RestCall.put("/config/server/preferences"),
- RestCall.get("/config/server/preferences.diff"),
- RestCall.put("/config/server/preferences.diff"),
- RestCall.get("/config/server/preferences.edit"),
- RestCall.put("/config/server/preferences.edit"),
- RestCall.get("/config/server/top-menus"),
- RestCall.put("/config/server/email.confirm"),
- RestCall.post("/config/server/check.consistency"),
- RestCall.post("/config/server/reload"),
- RestCall.get("/config/server/summary"),
- RestCall.get("/config/server/capabilities"),
- RestCall.get("/config/server/caches"),
- RestCall.post("/config/server/caches"),
- RestCall.get("/config/server/tasks"),
- RestCall.post("/config/server/index.changes"));
-
- /**
- * Cache REST endpoints to be tested, the URLs contain a placeholder for the cache identifier.
- * Since there is only supported a single supported config identifier ('server') it can be
- * hard-coded.
- */
- private static final ImmutableList<RestCall> CACHE_ENDPOINTS =
- ImmutableList.of(RestCall.get("/config/server/caches/%s"));
-
- /**
- * Task REST endpoints to be tested, the URLs contain a placeholder for the task identifier. Since
- * there is only supported a single supported config identifier ('server') it can be hard-coded.
- */
- private static final ImmutableList<RestCall> TASK_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/config/server/tasks/%s"),
-
- // Task deletion must be tested last
- RestCall.delete("/config/server/tasks/%s"));
-
- @Test
- public void configEndpoints() throws Exception {
- // 'Access Database' is needed for the '/config/server/check.consistency' REST endpoint
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
-
- execute(CONFIG_ENDPOINTS);
- }
-
- @Test
- public void cacheEndpoints() throws Exception {
- execute(CACHE_ENDPOINTS, ProjectCacheImpl.CACHE_NAME);
- }
-
- @Test
- public void taskEndpoints() throws Exception {
- RestResponse r = adminRestSession.get("/config/server/tasks/");
- List<TaskInfo> result =
- newGson().fromJson(r.getReader(), new TypeToken<List<TaskInfo>>() {}.getType());
- r.consume();
-
- Optional<String> id =
- result.stream()
- .filter(t -> "Log File Compressor".equals(t.command))
- .map(t -> t.id)
- .findFirst();
- assertThat(id).isPresent();
-
- execute(TASK_ENDPOINTS, id.get());
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/DeleteOnCollectionIT.java b/javatests/com/google/gerrit/acceptance/rest/DeleteOnCollectionIT.java
index 4de436ae4a..d891d6ea70 100644
--- a/javatests/com/google/gerrit/acceptance/rest/DeleteOnCollectionIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/DeleteOnCollectionIT.java
@@ -36,13 +36,8 @@ public class DeleteOnCollectionIT extends AbstractDaemonTest {
public void configure() {
deleteOnCollection(BRANCH_KIND)
.toInstance(
- new RestCollectionModifyView<ProjectResource, BranchResource, Object>() {
- @Override
- public Object apply(ProjectResource parentResource, Object input)
- throws Exception {
- return Response.none();
- }
- });
+ (RestCollectionModifyView<ProjectResource, BranchResource, Object>)
+ (parentResource, input) -> Response.none());
}
};
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/GroupsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/GroupsRestApiBindingsIT.java
deleted file mode 100644
index 4538f753bd..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/GroupsRestApiBindingsIT.java
+++ /dev/null
@@ -1,100 +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.acceptance.rest;
-
-import com.google.common.collect.ImmutableList;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the groups REST API.
- *
- * <p>These tests only verify that the group REST endpoints are correctly bound, they do no test the
- * functionality of the group REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class GroupsRestApiBindingsIT extends AbstractRestApiBindingsTest {
- /**
- * Group REST endpoints to be tested, each URL contains a placeholder for the group identifier.
- */
- private static final ImmutableList<RestCall> GROUP_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/groups/%s"),
- RestCall.put("/groups/%s"),
- RestCall.get("/groups/%s/detail"),
- RestCall.get("/groups/%s/name"),
- RestCall.put("/groups/%s/name"),
- RestCall.get("/groups/%s/description"),
- RestCall.put("/groups/%s/description"),
- RestCall.delete("/groups/%s/description"),
- RestCall.get("/groups/%s/owner"),
- RestCall.put("/groups/%s/owner"),
- RestCall.get("/groups/%s/options"),
- RestCall.put("/groups/%s/options"),
- RestCall.post("/groups/%s/members"),
- RestCall.post("/groups/%s/members.add"),
- RestCall.post("/groups/%s/members.delete"),
- RestCall.post("/groups/%s/groups"),
- RestCall.post("/groups/%s/groups.add"),
- RestCall.post("/groups/%s/groups.delete"),
- RestCall.get("/groups/%s/log.audit"),
- RestCall.post("/groups/%s/index"),
- RestCall.get("/groups/%s/members"),
- RestCall.get("/groups/%s/groups"));
-
- /**
- * Member REST endpoints to be tested, each URL contains placeholders for the group identifier and
- * the member identifier.
- */
- private static final ImmutableList<RestCall> MEMBER_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/groups/%s/members/%s"),
- RestCall.put("/groups/%s/members/%s"),
-
- // Member deletion must be tested last
- RestCall.delete("/groups/%s/members/%s"));
-
- /**
- * Subgroup REST endpoints to be tested, each URL contains placeholders for the group identifier
- * and the subgroup identifier.
- */
- private static final ImmutableList<RestCall> SUBGROUP_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/groups/%s/groups/%s"),
- RestCall.put("/groups/%s/groups/%s"),
-
- // Subgroup deletion must be tested last
- RestCall.delete("/groups/%s/groups/%s"));
-
- @Test
- public void groupEndpoints() throws Exception {
- String group = gApi.groups().create("test-group").get().name;
- execute(GROUP_ENDPOINTS, group);
- }
-
- @Test
- public void memberEndpoints() throws Exception {
- String group = gApi.groups().create("test-group").get().name;
- gApi.groups().id(group).addMembers(admin.email);
- execute(MEMBER_ENDPOINTS, group, admin.email);
- }
-
- @Test
- public void subgroupEndpoints() throws Exception {
- String group = gApi.groups().create("test-group").get().name;
- String subgroup = gApi.groups().create("test-subgroup").get().name;
- gApi.groups().id(group).addGroups(subgroup);
- execute(SUBGROUP_ENDPOINTS, group, subgroup);
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java
deleted file mode 100644
index 07ea3d02ac..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/PluginsRestApiBindingsIT.java
+++ /dev/null
@@ -1,68 +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.acceptance.rest;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.acceptance.GerritConfig;
-import com.google.gerrit.common.RawInputUtil;
-import com.google.gerrit.extensions.api.plugins.InstallPluginInput;
-import com.google.gerrit.extensions.restapi.RawInput;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the plugins REST API.
- *
- * <p>These tests only verify that the plugin REST endpoints are correctly bound, they do no test
- * the functionality of the plugin REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class PluginsRestApiBindingsIT extends AbstractRestApiBindingsTest {
- /**
- * Plugin REST endpoints to be tested, each URL contains a placeholder for the plugin identifier.
- */
- private static final ImmutableList<RestCall> PLUGIN_ENDPOINTS =
- ImmutableList.of(
- RestCall.put("/plugins/%s"),
-
- // For GET requests prefixing the view name with 'gerrit~' is required.
- RestCall.get("/plugins/%s/gerrit~status"),
-
- // POST (and PUT) requests don't require the 'gerrit~' prefix in front of the view name.
- RestCall.post("/plugins/%s/gerrit~enable"),
- RestCall.post("/plugins/%s/gerrit~disable"),
- RestCall.post("/plugins/%s/gerrit~reload"),
-
- // Plugin deletion must be tested last
- RestCall.delete("/plugins/%s"));
-
- private static final String JS_PLUGIN = "Gerrit.install(function(self){});\n";
- private static final RawInput JS_PLUGIN_CONTENT = RawInputUtil.create(JS_PLUGIN.getBytes(UTF_8));
-
- @Test
- @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
- public void pluginEndpoints() throws Exception {
- String pluginName = "my-plugin";
- installPlugin(pluginName);
- execute(PLUGIN_ENDPOINTS, pluginName);
- }
-
- private void installPlugin(String pluginName) throws Exception {
- InstallPluginInput input = new InstallPluginInput();
- input.raw = JS_PLUGIN_CONTENT;
- gApi.plugins().install(pluginName + ".js", input);
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java
deleted file mode 100644
index 6563de32da..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/ProjectsRestApiBindingsIT.java
+++ /dev/null
@@ -1,248 +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.acceptance.rest;
-
-import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
-import static com.google.gerrit.acceptance.GitUtil.pushHead;
-import static com.google.gerrit.acceptance.rest.AbstractRestApiBindingsTest.Method.GET;
-import static com.google.gerrit.reviewdb.client.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;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.acceptance.GitUtil;
-import com.google.gerrit.common.data.Permission;
-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 org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.transport.PushResult;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the projects REST API.
- *
- * <p>These tests only verify that the project REST endpoints are correctly bound, they do no test
- * the functionality of the project REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class ProjectsRestApiBindingsIT extends AbstractRestApiBindingsTest {
- private static final ImmutableList<RestCall> PROJECT_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/projects/%s"),
- RestCall.put("/projects/%s"),
- RestCall.get("/projects/%s/description"),
- RestCall.put("/projects/%s/description"),
- RestCall.delete("/projects/%s/description"),
- RestCall.get("/projects/%s/parent"),
- RestCall.put("/projects/%s/parent"),
- RestCall.get("/projects/%s/config"),
- RestCall.put("/projects/%s/config"),
- RestCall.get("/projects/%s/HEAD"),
- RestCall.put("/projects/%s/HEAD"),
- RestCall.get("/projects/%s/access"),
- RestCall.post("/projects/%s/access"),
- RestCall.put("/projects/%s/access:review"),
- RestCall.get("/projects/%s/check.access"),
- RestCall.post("/projects/%s/check.access"),
- RestCall.put("/projects/%s/ban"),
- RestCall.get("/projects/%s/statistics.git"),
- RestCall.post("/projects/%s/index"),
- RestCall.post("/projects/%s/gc"),
- RestCall.get("/projects/%s/children"),
- RestCall.get("/projects/%s/branches"),
- RestCall.post("/projects/%s/branches:delete"),
- RestCall.put("/projects/%s/branches/new-branch"),
- RestCall.get("/projects/%s/tags"),
- RestCall.post("/projects/%s/tags:delete"),
- RestCall.put("/projects/%s/tags/new-tag"),
- RestCall.builder(GET, "/projects/%s/commits")
- // GET /projects/<project>/branches/<branch>/commits is not implemented
- .expectedResponseCode(SC_NOT_FOUND)
- .build(),
- RestCall.get("/projects/%s/dashboards"));
-
- /**
- * Child project REST endpoints to be tested, each URL contains placeholders for the parent
- * project identifier and the child project identifier.
- */
- private static final ImmutableList<RestCall> CHILD_PROJECT_ENDPOINTS =
- ImmutableList.of(RestCall.get("/projects/%s/children/%s"));
-
- /**
- * Branch REST endpoints to be tested, each URL contains placeholders for the project identifier
- * and the branch identifier.
- */
- private static final ImmutableList<RestCall> BRANCH_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/projects/%s/branches/%s"),
- RestCall.put("/projects/%s/branches/%s"),
- RestCall.get("/projects/%s/branches/%s/mergeable"),
- RestCall.builder(GET, "/projects/%s/branches/%s/reflog")
- // The tests use DfsRepository which does not support getting the reflog.
- .expectedResponseCode(SC_METHOD_NOT_ALLOWED)
- .expectedMessage("reflog not supported on")
- .build(),
- RestCall.builder(GET, "/projects/%s/branches/%s/files")
- // GET /projects/<project>/branches/<branch>/files is not implemented
- .expectedResponseCode(SC_NOT_FOUND)
- .build(),
-
- // Branch deletion must be tested last
- RestCall.delete("/projects/%s/branches/%s"));
-
- /**
- * Branch file REST endpoints to be tested, each URL contains placeholders for the project
- * identifier, the branch identifier and the file identifier.
- */
- private static final ImmutableList<RestCall> BRANCH_FILE_ENDPOINTS =
- ImmutableList.of(RestCall.get("/projects/%s/branches/%s/files/%s/content"));
-
- /**
- * Dashboard REST endpoints to be tested, each URL contains placeholders for the project
- * identifier and the dashboard identifier.
- */
- private static final ImmutableList<RestCall> DASHBOARD_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/projects/%s/dashboards/%s"),
- RestCall.put("/projects/%s/dashboards/%s"),
-
- // Dashboard deletion must be tested last
- RestCall.delete("/projects/%s/dashboards/%s"));
-
- /**
- * Tag REST endpoints to be tested, each URL contains placeholders for the project identifier and
- * the tag identifier.
- */
- private static final ImmutableList<RestCall> TAG_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/projects/%s/tags/%s"),
- RestCall.put("/projects/%s/tags/%s"),
- RestCall.delete("/projects/%s/tags/%s"));
-
- /**
- * Commit REST endpoints to be tested, each URL contains placeholders for the project identifier
- * and the commit identifier.
- */
- private static final ImmutableList<RestCall> COMMIT_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/projects/%s/commits/%s"),
- RestCall.get("/projects/%s/commits/%s/in"),
- RestCall.get("/projects/%s/commits/%s/files"),
- RestCall.post("/projects/%s/commits/%s/cherrypick"));
-
- /**
- * Commit file REST endpoints to be tested, each URL contains placeholders for the project
- * identifier, the commit identifier and the file identifier.
- */
- private static final ImmutableList<RestCall> COMMIT_FILE_ENDPOINTS =
- ImmutableList.of(RestCall.get("/projects/%s/commits/%s/files/%s/content"));
-
- private static final String FILENAME = "test.txt";
-
- @Test
- public void projectEndpoints() throws Exception {
- execute(PROJECT_ENDPOINTS, project.get());
- }
-
- @Test
- public void childProjectEndpoints() throws Exception {
- Project.NameKey childProject = createProject("test-child-repo", project);
- execute(CHILD_PROJECT_ENDPOINTS, project.get(), childProject.get());
- }
-
- @Test
- public void branchEndpoints() throws Exception {
- execute(BRANCH_ENDPOINTS, project.get(), "master");
- }
-
- @Test
- public void branchFileEndpoints() throws Exception {
- createAndSubmitChange(FILENAME);
- execute(BRANCH_FILE_ENDPOINTS, project.get(), "master", FILENAME);
- }
-
- @Test
- public void dashboardEndpoints() throws Exception {
- createDefaultDashboard();
- execute(DASHBOARD_ENDPOINTS, project.get(), DEFAULT_DASHBOARD_NAME);
- }
-
- @Test
- public void tagEndpoints() throws Exception {
- String tag = "test-tag";
- gApi.projects().name(project.get()).tag(tag).create(new TagInput());
- execute(TAG_ENDPOINTS, project.get(), tag);
- }
-
- @Test
- public void commitEndpoints() throws Exception {
- String commit = createAndSubmitChange(FILENAME);
- execute(COMMIT_ENDPOINTS, project.get(), commit);
- }
-
- @Test
- public void commitFileEndpoints() throws Exception {
- String commit = createAndSubmitChange(FILENAME);
- execute(COMMIT_FILE_ENDPOINTS, project.get(), commit, FILENAME);
- }
-
- private String createAndSubmitChange(String filename) throws Exception {
- RevCommit c =
- testRepo
- .commit()
- .message("A change")
- .parent(getRemoteHead())
- .add(filename, "content")
- .insertChangeId()
- .create();
- String id = GitUtil.getChangeId(testRepo, c).get();
- testRepo.reset(c);
-
- String r = "refs/for/master";
- PushResult pr = pushHead(testRepo, r, false);
- assertPushOk(pr, r);
-
- gApi.changes().id(id).current().review(ReviewInput.approve());
- gApi.changes().id(id).current().submit();
- return c.name();
- }
-
- private void createDefaultDashboard() throws Exception {
- String dashboardRef = REFS_DASHBOARDS + "team";
- grant(project, "refs/meta/*", Permission.CREATE);
- gApi.projects().name(project.get()).branch(dashboardRef).create(new BranchInput());
-
- try (Repository r = repoManager.openRepository(project)) {
- TestRepository<Repository>.CommitBuilder cb =
- new TestRepository<>(r).branch(dashboardRef).commit();
- StringBuilder content = new StringBuilder("[dashboard]\n");
- content.append("title = ").append("Open Changes").append("\n");
- content.append("[section \"").append("open").append("\"]\n");
- content.append("query = ").append("is:open").append("\n");
- cb.add("overview", content.toString());
- cb.create();
- }
-
- try (ProjectConfigUpdate u = updateProject(project)) {
- u.getConfig().getProject().setLocalDefaultDashboard(dashboardRef + ":overview");
- u.save();
- }
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java
deleted file mode 100644
index a2c4ea697b..0000000000
--- a/javatests/com/google/gerrit/acceptance/rest/RootCollectionsRestApiBindingsIT.java
+++ /dev/null
@@ -1,56 +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.acceptance.rest;
-
-import static com.google.gerrit.acceptance.rest.AbstractRestApiBindingsTest.Method.GET;
-import static org.apache.http.HttpStatus.SC_NOT_FOUND;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.acceptance.GerritConfig;
-import org.junit.Test;
-
-/**
- * Tests for checking the bindings of the root REST API.
- *
- * <p>These tests only verify that the root REST endpoints are correctly bound, they do no test the
- * functionality of the root REST endpoints (for details see JavaDoc on {@link
- * AbstractRestApiBindingsTest}).
- */
-public class RootCollectionsRestApiBindingsIT extends AbstractRestApiBindingsTest {
- /** Root REST endpoints to be tested, the URLs contain no placeholders. */
- private static final ImmutableList<RestCall> ROOT_ENDPOINTS =
- ImmutableList.of(
- RestCall.get("/access/"),
- RestCall.get("/accounts/"),
- RestCall.put("/accounts/new-account"),
- RestCall.builder(GET, "/config/")
- // GET /config/ is not implemented
- .expectedResponseCode(SC_NOT_FOUND)
- .build(),
- RestCall.get("/changes/"),
- RestCall.post("/changes/"),
- RestCall.get("/groups/"),
- RestCall.put("/groups/new-group"),
- RestCall.get("/plugins/"),
- RestCall.put("/plugins/new-plugin"),
- RestCall.get("/projects/"),
- RestCall.put("/projects/new-project"));
-
- @Test
- @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
- public void rootEndpoints() throws Exception {
- execute(ROOT_ENDPOINTS);
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index 137dc21e5d..b30dc41290 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -171,7 +171,7 @@ public class TraceIT extends AbstractDaemonTest {
@Test
public void pushWithoutTrace() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isNull();
@@ -180,7 +180,7 @@ public class TraceIT extends AbstractDaemonTest {
@Test
public void pushWithTrace() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
push.setPushOptions(ImmutableList.of("trace"));
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
@@ -190,7 +190,7 @@ public class TraceIT extends AbstractDaemonTest {
@Test
public void pushWithTraceAndProvidedTraceId() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
push.setPushOptions(ImmutableList.of("trace=issue/123"));
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
@@ -200,7 +200,7 @@ public class TraceIT extends AbstractDaemonTest {
@Test
public void pushForReviewWithoutTrace() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
assertThat(commitValidationListener.traceId).isNull();
@@ -209,7 +209,7 @@ public class TraceIT extends AbstractDaemonTest {
@Test
public void pushForReviewWithTrace() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
push.setPushOptions(ImmutableList.of("trace"));
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
@@ -219,7 +219,7 @@ public class TraceIT extends AbstractDaemonTest {
@Test
public void pushForReviewWithTraceAndProvidedTraceId() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
push.setPushOptions(ImmutableList.of("trace=issue/123"));
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
index 2baaef8366..5e652c0c18 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -25,9 +25,9 @@ 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);
+ assertThat(a.id().get()).isEqualTo(ai._accountId);
+ assertThat(a.fullName()).isEqualTo(ai.name);
+ assertThat(a.email()).isEqualTo(ai.email);
}
public static void assertAccountInfos(List<TestAccount> expected, List<AccountInfo> actual) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/BUILD b/javatests/com/google/gerrit/acceptance/rest/account/BUILD
index 6bd232a221..66ea6f33c3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/account/BUILD
@@ -19,7 +19,6 @@ java_library(
deps = [
"//java/com/google/gerrit/acceptance:lib",
"//java/com/google/gerrit/reviewdb:server",
- "//lib:gwtorm",
"//lib:junit",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
index f3fe68ad1c..84f218d091 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
@@ -23,6 +23,7 @@ import com.google.common.collect.Multimap;
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.extensions.api.accounts.EmailApi;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.common.EmailInfo;
@@ -30,7 +31,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.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
@@ -43,9 +43,8 @@ import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.DisableReverseDnsLookup;
+import com.google.gerrit.server.config.EnableReverseDnsLookup;
import com.google.gson.reflect.TypeToken;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.List;
@@ -54,15 +53,15 @@ import java.util.Set;
import org.junit.Test;
public class EmailIT extends AbstractDaemonTest {
- @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
- @Inject private ExternalIds externalIds;
- @Inject private SchemaFactory<ReviewDb> reviewDbProvider;
- @Inject private AuthConfig authConfig;
@Inject private @AnonymousCowardName String anonymousCowardName;
@Inject private @CanonicalWebUrl Provider<String> canonicalUrl;
- @Inject private @DisableReverseDnsLookup Boolean disableReverseDnsLookup;
+ @Inject private @EnableReverseDnsLookup boolean enableReverseDnsLookup;
+ @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
+ @Inject private AuthConfig authConfig;
@Inject private EmailExpander emailExpander;
+ @Inject private ExternalIds externalIds;
@Inject private Provider<Emails> emails;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Test
public void addEmail() throws Exception {
@@ -123,7 +122,7 @@ public class EmailIT extends AbstractDaemonTest {
createEmail(email);
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
gApi.accounts().self().email(email).setPreferred();
assertThat(gApi.accounts().self().get().email).isEqualTo(email);
}
@@ -135,14 +134,14 @@ public class EmailIT extends AbstractDaemonTest {
.get()
.update(
"Add External ID",
- admin.id,
+ admin.id(),
u ->
u.addExternalId(
ExternalId.createWithEmail(
- ExternalId.SCHEME_EXTERNAL, "foo", admin.id, email)));
+ ExternalId.SCHEME_EXTERNAL, "foo", admin.id(), email)));
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
gApi.accounts().self().email(email).setPreferred();
assertThat(gApi.accounts().self().get().email).isEqualTo(email);
}
@@ -158,8 +157,8 @@ public class EmailIT extends AbstractDaemonTest {
@Test
public void setPreferredEmailToEmailOfOtherAccount() throws Exception {
exception.expect(ResourceNotFoundException.class);
- exception.expectMessage("Not found: " + user.email);
- gApi.accounts().self().email(user.email).setPreferred();
+ exception.expectMessage("Not found: " + user.email());
+ gApi.accounts().self().email(user.email()).setPreferred();
}
@Test
@@ -168,7 +167,7 @@ public class EmailIT extends AbstractDaemonTest {
createEmail(email);
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
String emailOtherCase = email.toUpperCase();
gApi.accounts().self().email(emailOtherCase).setPreferred();
assertThat(gApi.accounts().self().get().email).isEqualTo(email);
@@ -182,12 +181,12 @@ public class EmailIT extends AbstractDaemonTest {
assertThat(externalIds.get(mailtoExtIdKey)).isEmpty();
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
- Context oldCtx = createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id, email));
+ Context oldCtx = createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id(), email));
try {
gApi.accounts().self().email(email).setPreferred();
Optional<ExternalId> mailtoExtId = externalIds.get(mailtoExtIdKey);
assertThat(mailtoExtId).isPresent();
- assertThat(mailtoExtId.get().accountId()).isEqualTo(admin.id);
+ assertThat(mailtoExtId.get().accountId()).isEqualTo(admin.id());
assertThat(gApi.accounts().self().get().email).isEqualTo(email);
} finally {
atrScope.set(oldCtx);
@@ -196,15 +195,15 @@ public class EmailIT extends AbstractDaemonTest {
@Test
public void setPreferredEmailToEmailFromCustomRealmThatBelongsToOtherAccount() throws Exception {
- ExternalId mailToExtId = ExternalId.createEmail(user.id, user.email);
+ ExternalId mailToExtId = ExternalId.createEmail(user.id(), user.email());
assertThat(externalIds.get(mailToExtId.key())).isPresent();
Context oldCtx =
- createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id, user.email));
+ 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();
+ gApi.accounts().self().email(user.email()).setPreferred();
} finally {
atrScope.set(oldCtx);
}
@@ -224,7 +223,7 @@ public class EmailIT extends AbstractDaemonTest {
assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
// Get email
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
EmailApi emailApi = gApi.accounts().self().email(email);
EmailInfo emailInfo = emailApi.get();
assertThat(emailInfo.email).isEqualTo(email);
@@ -236,7 +235,7 @@ public class EmailIT extends AbstractDaemonTest {
assertThat(gApi.accounts().self().get().email).isEqualTo(email);
// Get email again (now it's the preferred email)
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
emailApi = gApi.accounts().self().email(email);
emailInfo = emailApi.get();
assertThat(emailInfo.email).isEqualTo(email);
@@ -248,7 +247,7 @@ public class EmailIT extends AbstractDaemonTest {
assertThat(getEmails()).doesNotContain(email);
// Now the email is no longer found
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
emailApi = gApi.accounts().self().email(email);
exception.expect(ResourceNotFoundException.class);
emailApi.get();
@@ -276,10 +275,10 @@ public class EmailIT extends AbstractDaemonTest {
realm,
anonymousCowardName,
canonicalUrl,
- disableReverseDnsLookup,
+ enableReverseDnsLookup,
accountCache,
groupBackend);
- return atrScope.set(atrScope.newContext(reviewDbProvider, null, userFactory.create(admin.id)));
+ return atrScope.set(atrScope.newContext(null, userFactory.create(admin.id())));
}
private class RealmWithAdditionalEmails extends DefaultRealm {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index 57e0afa709..f08fa447b0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -33,8 +33,10 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.exceptions.DuplicateKeyException;
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;
@@ -53,7 +55,6 @@ 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.gson.reflect.TypeToken;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -90,10 +91,11 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Inject private ExternalIds externalIds;
@Inject private ExternalIdReader externalIdReader;
@Inject private ExternalIdNotes.Factory externalIdNotesFactory;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Test
public void getExternalIds() throws Exception {
- Collection<ExternalId> expectedIds = getAccountState(user.getId()).getExternalIds();
+ Collection<ExternalId> expectedIds = getAccountState(user.id()).getExternalIds();
List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
RestResponse response = userRestSession.get("/accounts/self/external.ids");
@@ -109,20 +111,20 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void getExternalIdsOfOtherUserNotAllowed() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("access database not permitted");
- gApi.accounts().id(admin.id.get()).getExternalIds();
+ gApi.accounts().id(admin.id().get()).getExternalIds();
}
@Test
public void getExternalIdsOfOtherUserWithAccessDatabase() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- Collection<ExternalId> expectedIds = getAccountState(admin.getId()).getExternalIds();
+ Collection<ExternalId> expectedIds = getAccountState(admin.id()).getExternalIds();
List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
- RestResponse response = userRestSession.get("/accounts/" + admin.id + "/external.ids");
+ RestResponse response = userRestSession.get("/accounts/" + admin.id() + "/external.ids");
response.assertOK();
List<AccountExternalIdInfo> results =
@@ -135,7 +137,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void deleteExternalIds() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
List<AccountExternalIdInfo> externalIds = gApi.accounts().self().getExternalIds();
List<String> toDelete = new ArrayList<>();
@@ -162,18 +164,18 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void deleteExternalIdsOfOtherUserNotAllowed() throws Exception {
List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("access database not permitted");
gApi.accounts()
- .id(admin.id.get())
+ .id(admin.id().get())
.deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList()));
}
@Test
public void deleteExternalIdOfOtherUserUnderOwnAccount_UnprocessableEntity() throws Exception {
List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(UnprocessableEntityException.class);
exception.expectMessage(String.format("External id %s does not exist", extIds.get(0).identity));
gApi.accounts()
@@ -199,11 +201,11 @@ public class ExternalIdIT extends AbstractDaemonTest {
assertThat(toDelete).hasSize(1);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
RestResponse response =
- userRestSession.post("/accounts/" + admin.id + "/external.ids:delete", toDelete);
+ userRestSession.post("/accounts/" + admin.id() + "/external.ids:delete", toDelete);
response.assertNoContent();
- List<AccountExternalIdInfo> results = gApi.accounts().id(admin.id.get()).getExternalIds();
+ List<AccountExternalIdInfo> results = gApi.accounts().id(admin.id().get()).getExternalIds();
// The external ID in WebSession will not be set for tests, resulting that
// "mailto:user@example.com" can be deleted while "username:user" can't.
assertThat(results).hasSize(1);
@@ -225,7 +227,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void deleteExternalIds_Conflict() throws Exception {
List<String> toDelete = new ArrayList<>();
- String externalIdStr = "username:" + user.username;
+ String externalIdStr = "username:" + user.username();
toDelete.add(externalIdStr);
RestResponse response = userRestSession.post("/accounts/self/external.ids:delete", toDelete);
response.assertConflict();
@@ -402,7 +404,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void readExternalIdsWhenInvalidExternalIdsExist() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
insertValidExternalIds();
insertInvalidButParsableExternalIds();
@@ -423,7 +425,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void checkConsistency() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
insertValidExternalIds();
@@ -461,10 +463,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
insertExtId(
ExternalId.createWithPassword(
ExternalId.Key.parse(nextId(scheme, i)),
- admin.id,
+ admin.id(),
"admin.other@example.com",
"secret-password"));
- insertExtId(ExternalId.createEmail(admin.id, "admin.other@example.com"));
+ insertExtId(ExternalId.createEmail(admin.id(), "admin.other@example.com"));
insertExtId(createExternalIdWithOtherCaseEmail(nextId(scheme, i)));
}
@@ -562,7 +564,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
private ExternalId createExternalIdWithOtherCaseEmail(String externalId) {
return ExternalId.createWithPassword(
- ExternalId.Key.parse(externalId), admin.id, admin.email.toUpperCase(Locale.US), "password");
+ ExternalId.Key.parse(externalId),
+ admin.id(),
+ admin.email().toUpperCase(Locale.US),
+ "password");
}
private String insertExternalIdWithoutAccountId(Repository repo, RevWalk rw, String externalId)
@@ -571,7 +576,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
repo,
rw,
(ins, noteMap) -> {
- ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id);
+ ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id());
ObjectId noteId = extId.key().sha1();
Config c = new Config();
extId.writeToConfig(c);
@@ -589,7 +594,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
repo,
rw,
(ins, noteMap) -> {
- ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id);
+ 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);
@@ -639,8 +644,8 @@ public class ExternalIdIT extends AbstractDaemonTest {
CommitBuilder cb = new CommitBuilder();
cb.setMessage("Update external IDs");
cb.setTreeId(noteMap.writeTree(ins));
- cb.setAuthor(admin.getIdent());
- cb.setCommitter(admin.getIdent());
+ cb.setAuthor(admin.newIdent());
+ cb.setCommitter(admin.newIdent());
if (!rev.equals(ObjectId.zeroId())) {
cb.setParentId(rev);
} else {
@@ -687,17 +692,18 @@ public class ExternalIdIT extends AbstractDaemonTest {
}
private ExternalId createExternalIdWithInvalidEmail(String externalId) {
- return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), admin.id, "invalid-email");
+ return ExternalId.createWithEmail(
+ ExternalId.Key.parse(externalId), admin.id(), "invalid-email");
}
private ExternalId createExternalIdWithDuplicateEmail(String externalId) {
- return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), user.id, admin.email);
+ return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), user.id(), admin.email());
}
private ExternalId createExternalIdWithBadPassword(String username) {
return ExternalId.create(
ExternalId.Key.create(SCHEME_USERNAME, username),
- admin.id,
+ admin.id(),
null,
"non-hashed-password-is-not-allowed");
}
@@ -722,29 +728,30 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void checkNoReloadAfterUpdate() throws Exception {
- Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id));
+ Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id()));
try (AutoCloseable ctx = createFailOnLoadContext()) {
// insert external ID
- ExternalId extId = ExternalId.create("foo", "bar", admin.id);
+ ExternalId extId = ExternalId.create("foo", "bar", admin.id());
insertExtId(extId);
expectedExtIds.add(extId);
- assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
+ assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
// update external ID
expectedExtIds.remove(extId);
- ExternalId extId2 = ExternalId.createWithEmail("foo", "bar", admin.id, "foo.bar@example.com");
+ ExternalId extId2 =
+ ExternalId.createWithEmail("foo", "bar", admin.id(), "foo.bar@example.com");
accountsUpdateProvider
.get()
- .update("Update External ID", admin.id, u -> u.updateExternalId(extId2));
+ .update("Update External ID", admin.id(), u -> u.updateExternalId(extId2));
expectedExtIds.add(extId2);
- assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
+ assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
// delete external ID
accountsUpdateProvider
.get()
- .update("Delete External ID", admin.id, u -> u.deleteExternalId(extId));
+ .update("Delete External ID", admin.id(), u -> u.deleteExternalId(extId));
expectedExtIds.remove(extId2);
- assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
+ assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
}
}
@@ -752,10 +759,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
public void byAccountFailIfReadingExternalIdsFails() throws Exception {
try (AutoCloseable ctx = createFailOnLoadContext()) {
// update external ID branch so that external IDs need to be reloaded
- insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
+ insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
exception.expect(IOException.class);
- externalIds.byAccount(admin.id);
+ externalIds.byAccount(admin.id());
}
}
@@ -763,28 +770,28 @@ public class ExternalIdIT extends AbstractDaemonTest {
public void byEmailFailIfReadingExternalIdsFails() throws Exception {
try (AutoCloseable ctx = createFailOnLoadContext()) {
// update external ID branch so that external IDs need to be reloaded
- insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
+ insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
exception.expect(IOException.class);
- externalIds.byEmail(admin.email);
+ externalIds.byEmail(admin.email());
}
}
@Test
public void byAccountUpdateExternalIdsBehindGerritsBack() throws Exception {
- Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id));
- ExternalId newExtId = ExternalId.create("foo", "bar", admin.id);
+ Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id()));
+ ExternalId newExtId = ExternalId.create("foo", "bar", admin.id());
insertExtIdBehindGerritsBack(newExtId);
expectedExternalIds.add(newExtId);
- assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExternalIds);
+ assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExternalIds);
}
@Test
public void unsetEmail() throws Exception {
- ExternalId extId = ExternalId.createWithEmail("x", "1", user.id, "x@example.com");
+ ExternalId extId = ExternalId.createWithEmail("x", "1", user.id(), "x@example.com");
insertExtId(extId);
- ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id);
+ ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id());
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -798,10 +805,10 @@ public class ExternalIdIT extends AbstractDaemonTest {
@Test
public void unsetHttpPassword() throws Exception {
ExternalId extId =
- ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id, null, "secret");
+ ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id(), null, "secret");
insertExtId(extId);
- ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id);
+ ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id());
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -817,22 +824,22 @@ public class ExternalIdIT extends AbstractDaemonTest {
// Insert external ID for different accounts
TestAccount user1 = accountCreator.create("user1");
TestAccount user2 = accountCreator.create("user2");
- ExternalId extId1 = ExternalId.create("foo", "1", user1.id);
- ExternalId extId2 = ExternalId.create("foo", "2", user1.id);
- ExternalId extId3 = ExternalId.create("foo", "3", user2.id);
+ ExternalId extId1 = ExternalId.create("foo", "1", user1.id());
+ ExternalId extId2 = ExternalId.create("foo", "2", user1.id());
+ ExternalId extId3 = ExternalId.create("foo", "3", user2.id());
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
extIdNotes.insert(ImmutableSet.of(extId1, extId2, extId3));
RevCommit c = extIdNotes.commit(md);
assertThat(getFooters(c))
- .containsExactly("Account: " + user1.getId(), "Account: " + user2.getId())
+ .containsExactly("Account: " + user1.id(), "Account: " + user2.id())
.inOrder();
}
// Insert external ID with different emails
- ExternalId extId4 = ExternalId.createWithEmail("foo", "4", user1.id, "foo4@example.com");
- ExternalId extId5 = ExternalId.createWithEmail("foo", "5", user2.id, "foo5@example.com");
+ ExternalId extId4 = ExternalId.createWithEmail("foo", "4", user1.id(), "foo4@example.com");
+ ExternalId extId5 = ExternalId.createWithEmail("foo", "5", user2.id(), "foo5@example.com");
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -840,22 +847,22 @@ public class ExternalIdIT extends AbstractDaemonTest {
RevCommit c = extIdNotes.commit(md);
assertThat(getFooters(c))
.containsExactly(
- "Account: " + user1.getId(),
- "Account: " + user2.getId(),
+ "Account: " + user1.id(),
+ "Account: " + user2.id(),
"Email: foo4@example.com",
"Email: foo5@example.com")
.inOrder();
}
// Update external ID - Add Email
- ExternalId extId1a = ExternalId.createWithEmail("foo", "1", user1.id, "foo1@example.com");
+ ExternalId extId1a = ExternalId.createWithEmail("foo", "1", user1.id(), "foo1@example.com");
try (Repository allUsersRepo = repoManager.openRepository(allUsers);
MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
extIdNotes.upsert(extId1a);
RevCommit c = extIdNotes.commit(md);
assertThat(getFooters(c))
- .containsExactly("Account: " + user1.getId(), "Email: foo1@example.com")
+ .containsExactly("Account: " + user1.id(), "Email: foo1@example.com")
.inOrder();
}
@@ -866,7 +873,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
extIdNotes.upsert(extId1);
RevCommit c = extIdNotes.commit(md);
assertThat(getFooters(c))
- .containsExactly("Account: " + user1.getId(), "Email: foo1@example.com")
+ .containsExactly("Account: " + user1.id(), "Email: foo1@example.com")
.inOrder();
}
@@ -878,7 +885,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
RevCommit c = extIdNotes.commit(md);
assertThat(getFooters(c))
.containsExactly(
- "Account: " + user1.getId(), "Account: " + user2.getId(), "Email: foo5@example.com")
+ "Account: " + user1.id(), "Account: " + user2.id(), "Email: foo5@example.com")
.inOrder();
}
@@ -888,7 +895,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
extIdNotes.delete(extId2.accountId(), extId2.key());
RevCommit c = extIdNotes.commit(md);
- assertThat(getFooters(c)).containsExactly("Account: " + user1.getId()).inOrder();
+ assertThat(getFooters(c)).containsExactly("Account: " + user1.id()).inOrder();
}
// Delete external ID by key with email
@@ -898,7 +905,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
extIdNotes.delete(extId4.accountId(), extId4.key());
RevCommit c = extIdNotes.commit(md);
assertThat(getFooters(c))
- .containsExactly("Account: " + user1.getId(), "Email: foo4@example.com")
+ .containsExactly("Account: " + user1.id(), "Email: foo4@example.com")
.inOrder();
}
}
@@ -927,21 +934,21 @@ public class ExternalIdIT extends AbstractDaemonTest {
extIdNotes.insert(extId);
try (MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, repo)) {
- metaDataUpdate.getCommitBuilder().setAuthor(admin.getIdent());
- metaDataUpdate.getCommitBuilder().setCommitter(admin.getIdent());
+ metaDataUpdate.getCommitBuilder().setAuthor(admin.newIdent());
+ metaDataUpdate.getCommitBuilder().setCommitter(admin.newIdent());
extIdNotes.commit(metaDataUpdate);
}
}
}
private void addExtId(TestRepository<?> testRepo, ExternalId... extIds)
- throws IOException, OrmDuplicateKeyException, ConfigInvalidException {
+ throws IOException, DuplicateKeyException, ConfigInvalidException {
ExternalIdNotes extIdNotes = externalIdNotesFactory.load(testRepo.getRepository());
extIdNotes.insert(Arrays.asList(extIds));
try (MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, testRepo.getRepository())) {
- metaDataUpdate.getCommitBuilder().setAuthor(admin.getIdent());
- metaDataUpdate.getCommitBuilder().setCommitter(admin.getIdent());
+ metaDataUpdate.getCommitBuilder().setAuthor(admin.newIdent());
+ metaDataUpdate.getCommitBuilder().setCommitter(admin.newIdent());
extIdNotes.commit(metaDataUpdate);
extIdNotes.updateCaches();
}
@@ -981,12 +988,7 @@ public class ExternalIdIT extends AbstractDaemonTest {
private AutoCloseable createFailOnLoadContext() {
externalIdReader.setFailOnLoad(true);
- return new AutoCloseable() {
- @Override
- public void close() {
- externalIdReader.setFailOnLoad(false);
- }
- };
+ return () -> externalIdReader.setFailOnLoad(false);
}
@FunctionalInterface
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
index 07bc3941f2..27ae8b1295 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
@@ -26,10 +26,10 @@ import org.junit.Test;
public class GetAccountDetailIT extends AbstractDaemonTest {
@Test
public void getDetail() throws Exception {
- RestResponse r = adminRestSession.get("/accounts/" + admin.username + "/detail/");
+ RestResponse r = adminRestSession.get("/accounts/" + admin.username() + "/detail/");
AccountDetailInfo info = newGson().fromJson(r.getReader(), AccountDetailInfo.class);
assertAccountInfo(admin, info);
- Account account = getAccount(admin.getId());
+ Account account = getAccount(admin.id());
assertThat(info.registeredOn).isEqualTo(account.getRegisteredOn());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
index ed7abd2b6a..11f7c0f0af 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
@@ -32,19 +32,19 @@ public class GetAccountIT extends AbstractDaemonTest {
@Test
public void getAccount() throws Exception {
// by formatted string
- testGetAccount(admin.fullName + " <" + admin.email + ">", admin);
+ testGetAccount(admin.fullName() + " <" + admin.email() + ">", admin);
// by email
- testGetAccount(admin.email, admin);
+ testGetAccount(admin.email(), admin);
// by full name
- testGetAccount(admin.fullName, admin);
+ testGetAccount(admin.fullName(), admin);
// by account ID
- testGetAccount(Integer.toString(admin.id.get()), admin);
+ testGetAccount(Integer.toString(admin.id().get()), admin);
// by user name
- testGetAccount(admin.username, admin);
+ testGetAccount(admin.username(), admin);
// by 'self'
testGetAccount("self", admin);
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index 65c95f85f9..4dec505066 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -15,7 +15,6 @@
package com.google.gerrit.acceptance.rest.account;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
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;
@@ -29,6 +28,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
@@ -50,7 +50,6 @@ 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.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.reviewdb.client.Patch;
@@ -71,12 +70,10 @@ import org.junit.Test;
public class ImpersonationIT extends AbstractDaemonTest {
@Inject private AccountControl.Factory accountControlFactory;
-
@Inject private ApprovalsUtil approvalsUtil;
-
@Inject private ChangeMessagesUtil cmUtil;
-
@Inject private CommentsUtil commentsUtil;
+ @Inject private RequestScopeOperations requestScopeOperations;
private RestSession anonRestSession;
private TestAccount admin2;
@@ -88,7 +85,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
admin2 = accountCreator.admin2();
GroupInput gi = new GroupInput();
gi.name = name("New-Group");
- gi.members = ImmutableList.of(user.id.toString());
+ gi.members = ImmutableList.of(user.id().toString());
newGroup = gApi.groups().create(gi).get();
}
@@ -104,22 +101,22 @@ public class ImpersonationIT extends AbstractDaemonTest {
RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
ReviewInput in = ReviewInput.recommend();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.message = "Message on behalf of";
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.getAccountId()).isEqualTo(user.id());
assertThat(psa.getValue()).isEqualTo(1);
- assertThat(psa.getRealAccountId()).isEqualTo(admin.id);
+ assertThat(psa.getRealAccountId()).isEqualTo(admin.id());
ChangeData cd = r.getChange();
- ChangeMessage m = Iterables.getLast(cmUtil.byChange(db, cd.notes()));
+ ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
assertThat(m.getMessage()).endsWith(in.message);
- assertThat(m.getAuthor()).isEqualTo(user.id);
- assertThat(m.getRealAuthor()).isEqualTo(admin.id);
+ assertThat(m.getAuthor()).isEqualTo(user.id());
+ assertThat(m.getRealAuthor()).isEqualTo(admin.id());
}
@Test
@@ -129,7 +126,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.message = "Message on behalf of";
exception.expect(AuthException.class);
@@ -144,7 +141,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
ReviewInput in = new ReviewInput().label("Not-A-Label", 5);
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
exception.expect(BadRequestException.class);
exception.expectMessage("label \"Not-A-Label\" is not a configured label");
@@ -157,7 +154,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
ReviewInput in = new ReviewInput().label("Code-Review", 1).label("Not-A-Label", 5);
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
gApi.changes().id(changeId).current().review(in);
assertThat(gApi.changes().id(changeId).get().labels).doesNotContainKey("Not-A-Label");
@@ -175,7 +172,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.label("Verified", 1);
exception.expect(AuthException.class);
@@ -191,7 +188,6 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void voteOnBehalfOfWithCommentWritingJson() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
testVoteOnBehalfOfWithComment();
}
@@ -200,7 +196,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.label("Code-Review", 1);
CommentInput ci = new CommentInput();
ci.path = Patch.COMMIT_MSG;
@@ -213,25 +209,24 @@ public class ImpersonationIT extends AbstractDaemonTest {
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.getAccountId()).isEqualTo(user.id());
assertThat(psa.getValue()).isEqualTo(1);
- assertThat(psa.getRealAccountId()).isEqualTo(admin.id);
+ assertThat(psa.getRealAccountId()).isEqualTo(admin.id());
ChangeData cd = r.getChange();
- Comment c = Iterables.getOnlyElement(commentsUtil.publishedByChange(db, cd.notes()));
+ Comment c = Iterables.getOnlyElement(commentsUtil.publishedByChange(cd.notes()));
assertThat(c.message).isEqualTo(ci.message);
- assertThat(c.author.getId()).isEqualTo(user.id);
- assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id);
+ assertThat(c.author.getId()).isEqualTo(user.id());
+ assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id());
}
@Test
public void voteOnBehalfOfWithRobotComment() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
allowCodeReviewOnBehalfOf();
PushOneCommit.Result r = createChange();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.label("Code-Review", 1);
RobotCommentInput ci = new RobotCommentInput();
ci.robotId = "my-robot";
@@ -248,8 +243,8 @@ public class ImpersonationIT extends AbstractDaemonTest {
assertThat(c.message).isEqualTo(ci.message);
assertThat(c.robotId).isEqualTo(ci.robotId);
assertThat(c.robotRunId).isEqualTo(ci.robotRunId);
- assertThat(c.author.getId()).isEqualTo(user.id);
- assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id);
+ assertThat(c.author.getId()).isEqualTo(user.id());
+ assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id());
}
@Test
@@ -257,7 +252,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
allowCodeReviewOnBehalfOf();
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
DraftInput di = new DraftInput();
di.path = Patch.COMMIT_MSG;
di.side = Side.REVISION;
@@ -265,9 +260,9 @@ public class ImpersonationIT extends AbstractDaemonTest {
di.message = "message";
gApi.changes().id(r.getChangeId()).current().createDraft(di);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.label("Code-Review", 1);
in.drafts = DraftHandling.PUBLISH;
@@ -301,11 +296,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ 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");
+ exception.expectMessage("on_behalf_of account " + user.id() + " cannot see change");
revision.review(in);
}
@@ -313,14 +308,14 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void voteOnBehalfOfInvisibleUserNotAllowed() throws Exception {
allowCodeReviewOnBehalfOf();
- setApiUser(accountCreator.user2());
- assertThat(accountControlFactory.get().canSee(user.id)).isFalse();
+ requestScopeOperations.setApiUser(accountCreator.user2().id());
+ assertThat(accountControlFactory.get().canSee(user.id())).isFalse();
PushOneCommit.Result r = createChange();
RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.label("Code-Review", 1);
exception.expect(UnprocessableEntityException.class);
@@ -336,15 +331,15 @@ public class ImpersonationIT extends AbstractDaemonTest {
String changeId = project.get() + "~master~" + r.getChangeId();
gApi.changes().id(changeId).current().review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
- in.onBehalfOf = admin2.email;
+ in.onBehalfOf = admin2.email();
gApi.changes().id(changeId).current().submit(in);
ChangeData cd = r.getChange();
- assertThat(cd.change().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(cd.change().isMerged()).isTrue();
PatchSetApproval submitter =
- approvalsUtil.getSubmitter(db, cd.notes(), cd.change().currentPatchSetId());
- assertThat(submitter.getAccountId()).isEqualTo(admin2.id);
- assertThat(submitter.getRealAccountId()).isEqualTo(admin.id);
+ approvalsUtil.getSubmitter(cd.notes(), cd.change().currentPatchSetId());
+ assertThat(submitter.getAccountId()).isEqualTo(admin2.id());
+ assertThat(submitter.getRealAccountId()).isEqualTo(admin.id());
}
@Test
@@ -369,7 +364,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
.current()
.review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
- in.onBehalfOf = admin2.email;
+ 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);
@@ -384,9 +379,9 @@ public class ImpersonationIT extends AbstractDaemonTest {
String changeId = project.get() + "~master~" + r.getChangeId();
gApi.changes().id(changeId).current().review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
- in.onBehalfOf = user.email;
+ in.onBehalfOf = user.email();
exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("on_behalf_of account " + user.id + " cannot see change");
+ exception.expectMessage("on_behalf_of account " + user.id() + " cannot see change");
gApi.changes().id(changeId).current().submit(in);
}
@@ -394,14 +389,14 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void submitOnBehalfOfInvisibleUserNotAllowed() throws Exception {
allowSubmitOnBehalfOf();
- setApiUser(accountCreator.user2());
- assertThat(accountControlFactory.get().canSee(user.id)).isFalse();
+ requestScopeOperations.setApiUser(accountCreator.user2().id());
+ assertThat(accountControlFactory.get().canSee(user.id())).isFalse();
PushOneCommit.Result r = createChange();
String changeId = project.get() + "~master~" + r.getChangeId();
gApi.changes().id(changeId).current().review(ReviewInput.approve());
SubmitInput in = new SubmitInput();
- in.onBehalfOf = user.email;
+ in.onBehalfOf = user.email();
exception.expect(UnprocessableEntityException.class);
exception.expectMessage("not found");
exception.expectMessage(in.onBehalfOf);
@@ -411,17 +406,17 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void runAsValidUser() throws Exception {
allowRunAs();
- RestResponse res = adminRestSession.getWithHeader("/accounts/self", runAsHeader(user.id));
+ RestResponse res = adminRestSession.getWithHeader("/accounts/self", runAsHeader(user.id()));
res.assertOK();
AccountInfo account = newGson().fromJson(res.getEntityContent(), AccountInfo.class);
- assertThat(account._accountId).isEqualTo(user.id.get());
+ assertThat(account._accountId).isEqualTo(user.id().get());
}
@GerritConfig(name = "auth.enableRunAs", value = "false")
@Test
public void runAsDisabledByConfig() throws Exception {
allowRunAs();
- RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id));
+ RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id()));
res.assertForbidden();
assertThat(res.getEntityContent())
.isEqualTo("X-Gerrit-RunAs disabled by auth.enableRunAs = false");
@@ -429,7 +424,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void runAsNotPermitted() throws Exception {
- RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id));
+ RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id()));
res.assertForbidden();
assertThat(res.getEntityContent()).isEqualTo("not permitted to use X-Gerrit-RunAs");
}
@@ -437,7 +432,7 @@ public class ImpersonationIT extends AbstractDaemonTest {
@Test
public void runAsNeverPermittedForAnonymousUsers() throws Exception {
allowRunAs();
- RestResponse res = anonRestSession.getWithHeader("/changes/", runAsHeader(user.id));
+ RestResponse res = anonRestSession.getWithHeader("/changes/", runAsHeader(user.id()));
res.assertForbidden();
assertThat(res.getEntityContent()).isEqualTo("not permitted to use X-Gerrit-RunAs");
}
@@ -455,14 +450,14 @@ public class ImpersonationIT extends AbstractDaemonTest {
allowRunAs();
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
DraftInput di = new DraftInput();
di.path = Patch.COMMIT_MSG;
di.side = Side.REVISION;
di.line = 1;
di.message = "inline comment";
gApi.changes().id(r.getChangeId()).current().createDraft(di);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
// Things that aren't allowed with on_behalf_of:
// - no labels.
@@ -472,19 +467,21 @@ public class ImpersonationIT extends AbstractDaemonTest {
in.drafts = DraftHandling.PUBLISH;
RestResponse res =
adminRestSession.postWithHeader(
- "/changes/" + r.getChangeId() + "/revisions/current/review", runAsHeader(user.id), in);
+ "/changes/" + r.getChangeId() + "/revisions/current/review",
+ runAsHeader(user.id()),
+ in);
res.assertOK();
ChangeMessageInfo m = Iterables.getLast(gApi.changes().id(r.getChangeId()).get().messages);
assertThat(m.message).endsWith(in.message);
- assertThat(m.author._accountId).isEqualTo(user.id.get());
+ assertThat(m.author._accountId).isEqualTo(user.id().get());
CommentInfo c =
Iterables.getOnlyElement(gApi.changes().id(r.getChangeId()).comments().get(di.path));
- assertThat(c.author._accountId).isEqualTo(user.id.get());
+ assertThat(c.author._accountId).isEqualTo(user.id().get());
assertThat(c.message).isEqualTo(di.message);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThat(gApi.changes().id(r.getChangeId()).drafts()).isEmpty();
}
@@ -500,30 +497,30 @@ public class ImpersonationIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.message = "Message on behalf of";
String endpoint = "/changes/" + r.getChangeId() + "/revisions/current/review";
- RestResponse res = adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id), in);
+ RestResponse res = adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id()), in);
res.assertForbidden();
assertThat(res.getEntityContent())
.isEqualTo("label required to post review on behalf of \"" + in.onBehalfOf + '"');
in.label("Code-Review", 1);
- adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id), in).assertOK();
+ 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.getAccountId()).isEqualTo(user.id());
assertThat(psa.getValue()).isEqualTo(1);
- assertThat(psa.getRealAccountId()).isEqualTo(admin.id); // not user2
+ assertThat(psa.getRealAccountId()).isEqualTo(admin.id()); // not user2
ChangeData cd = r.getChange();
- ChangeMessage m = Iterables.getLast(cmUtil.byChange(db, cd.notes()));
+ ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
assertThat(m.getMessage()).endsWith(in.message);
- assertThat(m.getAuthor()).isEqualTo(user.id);
- assertThat(m.getRealAuthor()).isEqualTo(admin.id); // not user2
+ assertThat(m.getAuthor()).isEqualTo(user.id());
+ assertThat(m.getRealAuthor()).isEqualTo(admin.id()); // not user2
}
@Test
@@ -532,11 +529,11 @@ public class ImpersonationIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
+ in.onBehalfOf = user.id().toString();
in.message = "Message on behalf of";
in.label("Code-Review", 1);
- setApiUser(accountCreator.user2());
+ requestScopeOperations.setApiUser(accountCreator.user2().id());
gApi.changes().id(r.getChangeId()).revision(r.getPatchSetId().getId()).review(in);
ChangeInfo info = gApi.changes().id(r.getChangeId()).get(MESSAGES);
@@ -544,7 +541,8 @@ public class ImpersonationIT extends AbstractDaemonTest {
ChangeMessageInfo changeMessageInfo = Iterables.getLast(info.messages);
assertThat(changeMessageInfo.realAuthor).isNotNull();
- assertThat(changeMessageInfo.realAuthor._accountId).isEqualTo(accountCreator.user2().id.get());
+ assertThat(changeMessageInfo.realAuthor._accountId)
+ .isEqualTo(accountCreator.user2().id().get());
}
private void allowCodeReviewOnBehalfOf() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java b/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
index ea7128177a..e05d0db795 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
@@ -27,7 +27,7 @@ public class PutUsernameIT extends AbstractDaemonTest {
UsernameInput in = new UsernameInput();
in.username = "myUsername";
RestResponse r =
- adminRestSession.put("/accounts/" + accountCreator.create().id.get() + "/username", in);
+ adminRestSession.put("/accounts/" + accountCreator.create().id().get() + "/username", in);
r.assertOK();
assertThat(newGson().fromJson(r.getReader(), String.class)).isEqualTo(in.username);
}
@@ -35,9 +35,9 @@ public class PutUsernameIT extends AbstractDaemonTest {
@Test
public void setExisting_Conflict() throws Exception {
UsernameInput in = new UsernameInput();
- in.username = admin.username;
+ in.username = admin.username();
adminRestSession
- .put("/accounts/" + accountCreator.create().id.get() + "/username", in)
+ .put("/accounts/" + accountCreator.create().id().get() + "/username", in)
.assertConflict();
}
@@ -45,11 +45,13 @@ public class PutUsernameIT extends AbstractDaemonTest {
public void setNew_MethodNotAllowed() throws Exception {
UsernameInput in = new UsernameInput();
in.username = "newUsername";
- adminRestSession.put("/accounts/" + admin.username + "/username", in).assertMethodNotAllowed();
+ adminRestSession
+ .put("/accounts/" + admin.username() + "/username", in)
+ .assertMethodNotAllowed();
}
@Test
public void delete_MethodNotAllowed() throws Exception {
- adminRestSession.put("/accounts/" + admin.username + "/username").assertMethodNotAllowed();
+ adminRestSession.put("/accounts/" + admin.username() + "/username").assertMethodNotAllowed();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
index bc84593a9f..d0c1fa4ff0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
@@ -18,21 +18,27 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.Lists;
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.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class WatchedProjectsIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
private static final String NEW_PROJECT_NAME = "newProjectAccess";
@Test
public void setAndGetWatchedProjects() throws Exception {
- String projectName1 = createProject(NEW_PROJECT_NAME).get();
- String projectName2 = createProject(NEW_PROJECT_NAME + "2").get();
+ String projectName1 = projectOperations.newProject().name(NEW_PROJECT_NAME).create().get();
+ String projectName2 =
+ projectOperations.newProject().name(NEW_PROJECT_NAME + "2").create().get();
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>(2);
@@ -52,13 +58,13 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
List<ProjectWatchInfo> persistedWatchedProjects =
gApi.accounts().self().setWatchedProjects(projectsToWatch);
- assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch).inOrder();
+ assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch).inOrder();
}
@Test
public void setAndDeleteWatchedProjects() throws Exception {
- String projectName1 = createProject(NEW_PROJECT_NAME).get();
- String projectName2 = createProject(NEW_PROJECT_NAME + "2").get();
+ String projectName1 = projectOperations.newProject().create().get();
+ String projectName2 = projectOperations.newProject().create().get();
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
@@ -86,12 +92,12 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
List<ProjectWatchInfo> persistedWatchedProjects = gApi.accounts().self().getWatchedProjects();
assertThat(persistedWatchedProjects).doesNotContain(pwi);
- assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+ assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch);
}
@Test
public void setConflictingWatches() throws Exception {
- String projectName = createProject(NEW_PROJECT_NAME).get();
+ String projectName = projectOperations.newProject().create().get();
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
@@ -115,7 +121,7 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
@Test
public void setAndGetEmptyWatch() throws Exception {
- String projectName = createProject(NEW_PROJECT_NAME).get();
+ String projectName = projectOperations.newProject().create().get();
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
@@ -125,7 +131,7 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
gApi.accounts().self().setWatchedProjects(projectsToWatch);
List<ProjectWatchInfo> persistedWatchedProjects = gApi.accounts().self().getWatchedProjects();
- assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+ assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch);
}
@Test
@@ -150,7 +156,7 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
String projectName = project.get();
// Let another user watch a project
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
ProjectWatchInfo pwi = new ProjectWatchInfo();
@@ -167,7 +173,7 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
gApi.accounts().self().deleteWatchedProjects(d);
// Check that trying to delete a non-existing watch doesn't fail
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.accounts().self().deleteWatchedProjects(d);
}
@@ -176,7 +182,7 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
String projectName = project.get();
// Let another user watch a project
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
ProjectWatchInfo pwi = new ProjectWatchInfo();
@@ -199,7 +205,7 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
List<ProjectWatchInfo> watchedProjects = gApi.accounts().self().getWatchedProjects();
- assertThat(watchedProjects).containsAllIn(projectsToWatch);
+ assertThat(watchedProjects).containsAtLeastElementsIn(projectsToWatch);
}
@Test
@@ -233,11 +239,11 @@ public class WatchedProjectsIT extends AbstractDaemonTest {
List<ProjectWatchInfo> persistedWatchedProjects = gApi.accounts().self().getWatchedProjects();
assertThat(persistedWatchedProjects).doesNotContain(pwi);
- assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+ assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch);
}
@Test
public void postWithoutBody() throws Exception {
- adminRestSession.post("/accounts/" + admin.username + "/watched.projects").assertOK();
+ adminRestSession.post("/accounts/" + admin.username() + "/watched.projects").assertOK();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
new file mode 100644
index 0000000000..8e5eaa44b4
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
@@ -0,0 +1,195 @@
+// 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.binding;
+
+import static com.google.gerrit.acceptance.rest.util.RestApiCallHelper.execute;
+import static com.google.gerrit.acceptance.rest.util.RestCall.Method.PUT;
+import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpiration;
+import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.acceptance.rest.util.RestCall;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.extensions.common.ChangeInput;
+import com.google.gerrit.gpg.testing.TestKey;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import org.junit.Test;
+
+/**
+ * Tests for checking the bindings of the accounts REST API.
+ *
+ * <p>These tests only verify that the account REST endpoints are correctly bound, they do no test
+ * the functionality of the account REST endpoints.
+ */
+public class AccountsRestApiBindingsIT extends AbstractDaemonTest {
+ @Inject private @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
+ /**
+ * Account REST endpoints to be tested, each URL contains a placeholder for the account
+ * identifier.
+ */
+ private static final ImmutableList<RestCall> ACCOUNT_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/accounts/%s"),
+ RestCall.put("/accounts/%s"),
+ RestCall.get("/accounts/%s/detail"),
+ RestCall.get("/accounts/%s/name"),
+ RestCall.put("/accounts/%s/name"),
+ RestCall.delete("/accounts/%s/name"),
+ RestCall.get("/accounts/%s/username"),
+ RestCall.builder(PUT, "/accounts/%s/username")
+ // Changing the username is not allowed.
+ .expectedResponseCode(SC_METHOD_NOT_ALLOWED)
+ .expectedMessage("Username cannot be changed.")
+ .build(),
+ RestCall.get("/accounts/%s/active"),
+ RestCall.put("/accounts/%s/active"),
+ RestCall.delete("/accounts/%s/active"),
+ RestCall.put("/accounts/%s/password.http"),
+ RestCall.delete("/accounts/%s/password.http"),
+ RestCall.get("/accounts/%s/status"),
+ RestCall.put("/accounts/%s/status"),
+ RestCall.get("/accounts/%s/avatar"),
+ RestCall.get("/accounts/%s/avatar.change.url"),
+ RestCall.get("/accounts/%s/emails/"),
+ RestCall.put("/accounts/%s/emails/new-email@foo.com"),
+ RestCall.get("/accounts/%s/sshkeys/"),
+ RestCall.post("/accounts/%s/sshkeys/"),
+ RestCall.get("/accounts/%s/watched.projects"),
+ RestCall.post("/accounts/%s/watched.projects"),
+ RestCall.post("/accounts/%s/watched.projects:delete"),
+ RestCall.get("/accounts/%s/groups"),
+ RestCall.get("/accounts/%s/preferences"),
+ RestCall.put("/accounts/%s/preferences"),
+ RestCall.get("/accounts/%s/preferences.diff"),
+ RestCall.put("/accounts/%s/preferences.diff"),
+ RestCall.get("/accounts/%s/preferences.edit"),
+ RestCall.put("/accounts/%s/preferences.edit"),
+ RestCall.get("/accounts/%s/starred.changes"),
+ RestCall.get("/accounts/%s/stars.changes"),
+ RestCall.post("/accounts/%s/index"),
+ RestCall.get("/accounts/%s/agreements"),
+ RestCall.put("/accounts/%s/agreements"),
+ RestCall.get("/accounts/%s/external.ids"),
+ RestCall.post("/accounts/%s/external.ids:delete"),
+ RestCall.post("/accounts/%s/drafts:delete"),
+ RestCall.get("/accounts/%s/oauthtoken"),
+ RestCall.get("/accounts/%s/capabilities"),
+ RestCall.get("/accounts/%s/capabilities/viewPlugins"),
+ RestCall.get("/accounts/%s/gpgkeys"),
+ RestCall.post("/accounts/%s/gpgkeys"));
+
+ /**
+ * Email REST endpoints to be tested, each URL contains a placeholders for the account and email
+ * identifier.
+ */
+ private static final ImmutableList<RestCall> EMAIL_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/accounts/%s/emails/%s"),
+ RestCall.put("/accounts/%s/emails/%s"),
+ RestCall.put("/accounts/%s/emails/%s/preferred"),
+
+ // email deletion must be tested last
+ RestCall.delete("/accounts/%s/emails/%s"));
+
+ /**
+ * GPG key REST endpoints to be tested, each URL contains a placeholders for the account
+ * identifier and the GPG key identifier.
+ */
+ private static final ImmutableList<RestCall> GPG_KEY_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/accounts/%s/gpgkeys/%s"),
+
+ // GPG key deletion must be tested last
+ RestCall.delete("/accounts/%s/gpgkeys/%s"));
+
+ /**
+ * SSH key REST endpoints to be tested, each URL contains a placeholders for the account and SSH
+ * key identifier.
+ */
+ private static final ImmutableList<RestCall> SSH_KEY_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/accounts/%s/sshkeys/%s"),
+
+ // SSH key deletion must be tested last
+ RestCall.delete("/accounts/%s/sshkeys/%s"));
+
+ /**
+ * Star REST endpoints to be tested, each URL contains a placeholders for the account and change
+ * identifier.
+ */
+ private static final ImmutableList<RestCall> STAR_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.put("/accounts/%s/starred.changes/%s"),
+ RestCall.delete("/accounts/%s/starred.changes/%s"),
+ RestCall.get("/accounts/%s/stars.changes/%s"),
+ RestCall.post("/accounts/%s/stars.changes/%s"));
+
+ @Test
+ public void accountEndpoints() throws Exception {
+ execute(adminRestSession, ACCOUNT_ENDPOINTS, "self");
+ }
+
+ @Test
+ public void emailEndpoints() throws Exception {
+ execute(adminRestSession, EMAIL_ENDPOINTS, "self", admin.email());
+ }
+
+ @Test
+ @GerritConfig(name = "receive.enableSignedPush", value = "true")
+ public void gpgKeyEndpoints() throws Exception {
+ TestKey key = validKeyWithoutExpiration();
+ String id = key.getKeyIdString();
+
+ String email = "test1@example.com"; // email that is hard-coded in the test GPG key
+ accountsUpdateProvider
+ .get()
+ .update(
+ "Add Email",
+ admin.id(),
+ u ->
+ u.addExternalId(
+ ExternalId.createWithEmail(name("test"), email, admin.id(), email)));
+
+ requestScopeOperations.setApiUser(admin.id());
+ gApi.accounts()
+ .self()
+ .putGpgKeys(ImmutableList.of(key.getPublicKeyArmored()), ImmutableList.of());
+
+ execute(adminRestSession, GPG_KEY_ENDPOINTS, "self", id);
+ }
+
+ @Test
+ @UseSsh
+ public void sshKeyEndpoints() throws Exception {
+ String sshKeySeq = Integer.toString(gApi.accounts().self().listSshKeys().size());
+ execute(adminRestSession, SSH_KEY_ENDPOINTS, "self", sshKeySeq);
+ }
+
+ @Test
+ public void starEndpoints() throws Exception {
+ ChangeInput ci = new ChangeInput(project.get(), "master", "Test change");
+ String changeId = gApi.changes().create(ci).get().id;
+ execute(adminRestSession, STAR_ENDPOINTS, "self", changeId);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/BUILD b/javatests/com/google/gerrit/acceptance/rest/binding/BUILD
new file mode 100644
index 0000000000..e4242a9d6b
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/BUILD
@@ -0,0 +1,11 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "rest_bindings",
+ labels = ["rest"],
+ deps = [
+ "//java/com/google/gerrit/server/logging",
+ "//javatests/com/google/gerrit/acceptance/rest/util",
+ ],
+)
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
new file mode 100644
index 0000000000..55744ccb8e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
@@ -0,0 +1,498 @@
+// 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.binding;
+
+import static com.google.gerrit.acceptance.rest.util.RestCall.Method.GET;
+import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
+import static java.util.stream.Collectors.toList;
+import static org.apache.http.HttpStatus.SC_NOT_FOUND;
+
+import com.google.common.collect.ImmutableList;
+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.extensions.api.changes.AddReviewerInput;
+import com.google.gerrit.extensions.api.changes.DraftInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
+import com.google.gerrit.extensions.api.changes.ReviewInput.RobotCommentInput;
+import com.google.gerrit.extensions.client.Comment;
+import com.google.gerrit.extensions.client.Side;
+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;
+import org.junit.Test;
+
+/**
+ * Tests for checking the bindings of the changes REST API.
+ *
+ * <p>These tests only verify that the change REST endpoints are correctly bound, they do no test
+ * the functionality of the change REST endpoints.
+ */
+public class ChangesRestApiBindingsIT extends AbstractDaemonTest {
+ /**
+ * Change REST endpoints to be tested, each URL contains a placeholder for the change identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/changes/%s"),
+ RestCall.get("/changes/%s/detail"),
+ RestCall.get("/changes/%s/topic"),
+ RestCall.put("/changes/%s/topic"),
+ RestCall.delete("/changes/%s/topic"),
+ RestCall.get("/changes/%s/in"),
+ RestCall.get("/changes/%s/hashtags"),
+ RestCall.get("/changes/%s/comments"),
+ RestCall.get("/changes/%s/robotcomments"),
+ RestCall.get("/changes/%s/drafts"),
+ RestCall.get("/changes/%s/assignee"),
+ RestCall.get("/changes/%s/past_assignees"),
+ RestCall.put("/changes/%s/assignee"),
+ RestCall.delete("/changes/%s/assignee"),
+ RestCall.post("/changes/%s/private"),
+ RestCall.post("/changes/%s/private.delete"),
+ RestCall.delete("/changes/%s/private"),
+ RestCall.post("/changes/%s/wip"),
+ RestCall.post("/changes/%s/ready"),
+ RestCall.put("/changes/%s/ignore"),
+ RestCall.put("/changes/%s/unignore"),
+ RestCall.put("/changes/%s/reviewed"),
+ RestCall.put("/changes/%s/unreviewed"),
+ RestCall.get("/changes/%s/messages"),
+ RestCall.put("/changes/%s/message"),
+ RestCall.post("/changes/%s/merge"),
+ RestCall.post("/changes/%s/abandon"),
+ RestCall.post("/changes/%s/move"),
+ RestCall.post("/changes/%s/rebase"),
+ RestCall.post("/changes/%s/restore"),
+ RestCall.post("/changes/%s/revert"),
+ RestCall.get("/changes/%s/pure_revert"),
+ RestCall.post("/changes/%s/submit"),
+ RestCall.get("/changes/%s/submitted_together"),
+ RestCall.post("/changes/%s/index"),
+ RestCall.get("/changes/%s/check"),
+ RestCall.post("/changes/%s/check"),
+ RestCall.get("/changes/%s/reviewers"),
+ RestCall.post("/changes/%s/reviewers"),
+ RestCall.get("/changes/%s/suggest_reviewers"),
+ RestCall.builder(GET, "/changes/%s/revisions")
+ // GET /changes/<change-id>/revisions is not implemented
+ .expectedResponseCode(SC_NOT_FOUND)
+ .build(),
+ RestCall.get("/changes/%s/edit"),
+ RestCall.post("/changes/%s/edit"),
+ RestCall.post("/changes/%s/edit:rebase"),
+ RestCall.get("/changes/%s/edit:message"),
+ RestCall.put("/changes/%s/edit:message"),
+
+ // Publish edit and create a new edit
+ RestCall.post("/changes/%s/edit:publish"),
+ RestCall.put("/changes/%s/edit/a.txt"),
+
+ // Deletion of change edit and change must be tested last
+ RestCall.delete("/changes/%s/edit"),
+ RestCall.delete("/changes/%s"));
+
+ /**
+ * Reviewer REST endpoints to be tested, each URL contains placeholders for the change identifier
+ * and the reviewer identifier.
+ */
+ private static final ImmutableList<RestCall> REVIEWER_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/changes/%s/reviewers/%s"),
+ RestCall.get("/changes/%s/reviewers/%s/votes"),
+ RestCall.post("/changes/%s/reviewers/%s/delete"),
+ RestCall.delete("/changes/%s/reviewers/%s"));
+
+ /**
+ * Vote REST endpoints to be tested, each URL contains placeholders for the change identifier, the
+ * reviewer identifier and the label identifier.
+ */
+ private static final ImmutableList<RestCall> VOTE_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.post("/changes/%s/reviewers/%s/votes/%s/delete"),
+ RestCall.delete("/changes/%s/reviewers/%s/votes/%s"));
+
+ /**
+ * Revision REST endpoints to be tested, each URL contains placeholders for the change identifier
+ * and the revision identifier.
+ */
+ private static final ImmutableList<RestCall> REVISION_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/changes/%s/revisions/%s/actions"),
+ RestCall.post("/changes/%s/revisions/%s/cherrypick"),
+ RestCall.get("/changes/%s/revisions/%s/commit"),
+ RestCall.get("/changes/%s/revisions/%s/mergeable"),
+ RestCall.get("/changes/%s/revisions/%s/related"),
+ RestCall.get("/changes/%s/revisions/%s/review"),
+ RestCall.post("/changes/%s/revisions/%s/review"),
+ RestCall.get("/changes/%s/revisions/%s/preview_submit"),
+ RestCall.post("/changes/%s/revisions/%s/submit"),
+ RestCall.get("/changes/%s/revisions/%s/submit_type"),
+ RestCall.post("/changes/%s/revisions/%s/test.submit_rule"),
+ RestCall.post("/changes/%s/revisions/%s/test.submit_type"),
+ RestCall.post("/changes/%s/revisions/%s/rebase"),
+ RestCall.get("/changes/%s/revisions/%s/description"),
+ RestCall.put("/changes/%s/revisions/%s/description"),
+ RestCall.get("/changes/%s/revisions/%s/patch"),
+ RestCall.get("/changes/%s/revisions/%s/archive"),
+ RestCall.get("/changes/%s/revisions/%s/mergelist"),
+ RestCall.get("/changes/%s/revisions/%s/reviewers"),
+ RestCall.get("/changes/%s/revisions/%s/drafts"),
+ RestCall.put("/changes/%s/revisions/%s/drafts"),
+ RestCall.get("/changes/%s/revisions/%s/comments"),
+ RestCall.get("/changes/%s/revisions/%s/robotcomments"),
+ RestCall.builder(GET, "/changes/%s/revisions/%s/fixes")
+ // GET /changes/<change>/revisions/<revision>/fixes is not implemented
+ .expectedResponseCode(SC_NOT_FOUND)
+ .build(),
+ RestCall.get("/changes/%s/revisions/%s/files"));
+
+ /**
+ * Revision reviewer REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier, the revision identifier and the reviewer identifier.
+ */
+ private static final ImmutableList<RestCall> REVISION_REVIEWER_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/changes/%s/revisions/%s/reviewers/%s"),
+ RestCall.get("/changes/%s/revisions/%s/reviewers/%s/votes"),
+ RestCall.post("/changes/%s/revisions/%s/reviewers/%s/delete"),
+ RestCall.delete("/changes/%s/revisions/%s/reviewers/%s"));
+
+ /**
+ * Revision vote REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier, the revision identifier, the reviewer identifier and the label identifier.
+ */
+ private static final ImmutableList<RestCall> REVISION_VOTE_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.post("/changes/%s/revisions/%s/reviewers/%s/votes/%s/delete"),
+ RestCall.delete("/changes/%s/revisions/%s/reviewers/%s/votes/%s"));
+
+ /**
+ * Draft comment REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier, the revision identifier and the draft comment identifier.
+ */
+ private static final ImmutableList<RestCall> DRAFT_COMMENT_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/changes/%s/revisions/%s/drafts/%s"),
+ RestCall.put("/changes/%s/revisions/%s/drafts/%s"),
+ RestCall.delete("/changes/%s/revisions/%s/drafts/%s"));
+
+ /**
+ * Comment REST endpoints to be tested, each URL contains placeholders for the change identifier,
+ * the revision identifier and the comment identifier.
+ */
+ private static final ImmutableList<RestCall> COMMENT_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/changes/%s/revisions/%s/comments/%s"),
+ RestCall.delete("/changes/%s/revisions/%s/comments/%s"),
+ RestCall.post("/changes/%s/revisions/%s/comments/%s/delete"));
+
+ /**
+ * Robot comment REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier, the revision identifier and the robot comment identifier.
+ */
+ private static final ImmutableList<RestCall> ROBOT_COMMENT_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/changes/%s/revisions/%s/robotcomments/%s"));
+
+ /**
+ * Fix REST endpoints to be tested, each URL contains placeholders for the change identifier, the
+ * revision identifier and the fix identifier.
+ */
+ private static final ImmutableList<RestCall> FIX_ENDPOINTS =
+ ImmutableList.of(RestCall.post("/changes/%s/revisions/%s/fixes/%s/apply"));
+
+ /**
+ * Revision file REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier, the revision identifier and the file identifier.
+ */
+ private static final ImmutableList<RestCall> REVISION_FILE_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.put("/changes/%s/revisions/%s/files/%s/reviewed"),
+ RestCall.delete("/changes/%s/revisions/%s/files/%s/reviewed"),
+ RestCall.get("/changes/%s/revisions/%s/files/%s/content"),
+ RestCall.get("/changes/%s/revisions/%s/files/%s/download"),
+ RestCall.get("/changes/%s/revisions/%s/files/%s/diff"),
+ RestCall.get("/changes/%s/revisions/%s/files/%s/blame"));
+
+ /**
+ * Change message REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier and the change message identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_MESSAGE_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/changes/%s/messages/%s"));
+
+ /**
+ * Change edit REST endpoints that create an edit to be tested, each URL contains placeholders for
+ * the change identifier and the change edit identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_EDIT_CREATE_ENDPOINTS =
+ ImmutableList.of(
+ // Create change edit by editing an existing file.
+ RestCall.put("/changes/%s/edit/%s"),
+
+ // Create change edit by deleting an existing file.
+ RestCall.delete("/changes/%s/edit/%s"));
+
+ /**
+ * Change edit REST endpoints to be tested, each URL contains placeholders for the change
+ * identifier and the change edit identifier.
+ */
+ private static final ImmutableList<RestCall> CHANGE_EDIT_ENDPOINTS =
+ ImmutableList.of(
+ // Calls on existing change edit.
+ RestCall.get("/changes/%s/edit/%s"),
+ RestCall.put("/changes/%s/edit/%s"),
+ RestCall.get("/changes/%s/edit/%s/meta"),
+
+ // Delete content of a file in an existing change edit.
+ RestCall.delete("/changes/%s/edit/%s"));
+
+ private static final String FILENAME = "test.txt";
+
+ @Test
+ public void changeEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+ gApi.changes().id(changeId).edit().create();
+ RestApiCallHelper.execute(adminRestSession, CHANGE_ENDPOINTS, changeId);
+ }
+
+ @Test
+ public void reviewerEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ AddReviewerInput addReviewerInput = new AddReviewerInput();
+ addReviewerInput.reviewer = user.email();
+
+ RestApiCallHelper.execute(
+ adminRestSession,
+ REVIEWER_ENDPOINTS,
+ () -> gApi.changes().id(changeId).addReviewer(addReviewerInput),
+ changeId,
+ addReviewerInput.reviewer);
+ }
+
+ @Test
+ public void voteEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ RestApiCallHelper.execute(
+ adminRestSession,
+ VOTE_ENDPOINTS,
+ () -> gApi.changes().id(changeId).current().review(ReviewInput.approve()),
+ changeId,
+ admin.email(),
+ "Code-Review");
+ }
+
+ @Test
+ public void revisionEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+ RestApiCallHelper.execute(adminRestSession, REVISION_ENDPOINTS, changeId, "current");
+ }
+
+ @Test
+ public void revisionReviewerEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ AddReviewerInput addReviewerInput = new AddReviewerInput();
+ addReviewerInput.reviewer = user.email();
+
+ RestApiCallHelper.execute(
+ adminRestSession,
+ REVISION_REVIEWER_ENDPOINTS,
+ () -> gApi.changes().id(changeId).addReviewer(addReviewerInput),
+ changeId,
+ "current",
+ addReviewerInput.reviewer);
+ }
+
+ @Test
+ public void revisionVoteEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ RestApiCallHelper.execute(
+ adminRestSession,
+ REVISION_VOTE_ENDPOINTS,
+ () -> gApi.changes().id(changeId).current().review(ReviewInput.approve()),
+ changeId,
+ "current",
+ admin.email(),
+ "Code-Review");
+ }
+
+ @Test
+ public void draftCommentEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ for (RestCall restCall : DRAFT_COMMENT_ENDPOINTS) {
+ DraftInput draftInput = new DraftInput();
+ draftInput.path = Patch.COMMIT_MSG;
+ draftInput.side = Side.REVISION;
+ draftInput.line = 1;
+ draftInput.message = "draft comment";
+ CommentInfo draftInfo = gApi.changes().id(changeId).current().createDraft(draftInput).get();
+
+ RestApiCallHelper.execute(adminRestSession, restCall, changeId, "current", draftInfo.id);
+ }
+ }
+
+ @Test
+ public void commentEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ for (RestCall restCall : COMMENT_ENDPOINTS) {
+ DraftInput draftInput = new DraftInput();
+ draftInput.path = Patch.COMMIT_MSG;
+ draftInput.side = Side.REVISION;
+ draftInput.line = 1;
+ draftInput.message = "draft comment";
+ CommentInfo commentInfo = gApi.changes().id(changeId).current().createDraft(draftInput).get();
+
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.drafts = DraftHandling.PUBLISH;
+ gApi.changes().id(changeId).current().review(reviewInput);
+
+ RestApiCallHelper.execute(adminRestSession, restCall, changeId, "current", commentInfo.id);
+ }
+ }
+
+ @Test
+ public void robotCommentEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ RobotCommentInput robotCommentInput = new RobotCommentInput();
+ robotCommentInput.robotId = "happyRobot";
+ robotCommentInput.robotRunId = "1";
+ robotCommentInput.line = 1;
+ robotCommentInput.message = "nit: trailing whitespace";
+ robotCommentInput.path = Patch.COMMIT_MSG;
+
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.robotComments =
+ Collections.singletonMap(robotCommentInput.path, ImmutableList.of(robotCommentInput));
+ reviewInput.message = "robot comment test";
+ gApi.changes().id(changeId).current().review(reviewInput);
+
+ List<RobotCommentInfo> robotCommentInfos =
+ gApi.changes().id(changeId).current().robotCommentsAsList();
+ RobotCommentInfo robotCommentInfo = Iterables.getOnlyElement(robotCommentInfos);
+
+ RestApiCallHelper.execute(
+ adminRestSession, ROBOT_COMMENT_ENDPOINTS, changeId, "current", robotCommentInfo.id);
+ }
+
+ @Test
+ public void fixEndpoints() throws Exception {
+ String changeId = createChange("Subject", FILENAME, "content").getChangeId();
+
+ RobotCommentInput robotCommentInput = new RobotCommentInput();
+ robotCommentInput.robotId = "happyRobot";
+ robotCommentInput.robotRunId = "1";
+ robotCommentInput.line = 1;
+ robotCommentInput.message = "nit: trailing whitespace";
+ robotCommentInput.path = FILENAME;
+
+ FixReplacementInfo fixReplacementInfo = new FixReplacementInfo();
+ fixReplacementInfo.path = FILENAME;
+ fixReplacementInfo.replacement = "some replacement code";
+ fixReplacementInfo.range = createRange(1, 1, 1, 2);
+
+ FixSuggestionInfo fixSuggestionInfo = new FixSuggestionInfo();
+ fixSuggestionInfo.fixId = "An ID which must be overwritten.";
+ fixSuggestionInfo.description = "A description for a suggested fix.";
+ fixSuggestionInfo.replacements = ImmutableList.of(fixReplacementInfo);
+
+ robotCommentInput.fixSuggestions = ImmutableList.of(fixSuggestionInfo);
+
+ ReviewInput reviewInput = new ReviewInput();
+ reviewInput.robotComments =
+ Collections.singletonMap(robotCommentInput.path, ImmutableList.of(robotCommentInput));
+ reviewInput.message = "robot comment test";
+ gApi.changes().id(changeId).current().review(reviewInput);
+
+ List<RobotCommentInfo> robotCommentInfos =
+ gApi.changes().id(changeId).current().robotCommentsAsList();
+
+ List<String> fixIds = getFixIds(robotCommentInfos);
+ String fixId = Iterables.getOnlyElement(fixIds);
+
+ RestApiCallHelper.execute(adminRestSession, FIX_ENDPOINTS, changeId, "current", fixId);
+ }
+
+ @Test
+ public void revisionFileEndpoints() throws Exception {
+ String changeId = createChange("Subject", FILENAME, "content").getChangeId();
+ RestApiCallHelper.execute(
+ adminRestSession, REVISION_FILE_ENDPOINTS, changeId, "current", FILENAME);
+ }
+
+ @Test
+ public void changeMessageEndpoints() throws Exception {
+ String changeId = createChange().getChangeId();
+
+ // A change message is created on change creation.
+ String changeMessageId = Iterables.getOnlyElement(gApi.changes().id(changeId).messages()).id;
+
+ RestApiCallHelper.execute(
+ adminRestSession, CHANGE_MESSAGE_ENDPOINTS, changeId, changeMessageId);
+ }
+
+ @Test
+ public void changeEditCreateEndpoints() throws Exception {
+ String changeId = createChange("Subject", FILENAME, "content").getChangeId();
+
+ // Each of the REST calls creates the change edit newly.
+ RestApiCallHelper.execute(
+ adminRestSession,
+ CHANGE_EDIT_CREATE_ENDPOINTS,
+ () -> adminRestSession.delete("/changes/" + changeId + "/edit"),
+ changeId,
+ FILENAME);
+ }
+
+ @Test
+ public void changeEditEndpoints() throws Exception {
+ String changeId = createChange("Subject", FILENAME, "content").getChangeId();
+ gApi.changes().id(changeId).edit().create();
+ RestApiCallHelper.execute(adminRestSession, CHANGE_EDIT_ENDPOINTS, changeId, FILENAME);
+ }
+
+ private static Comment.Range createRange(
+ int startLine, int startCharacter, int endLine, int endCharacter) {
+ Comment.Range range = new Comment.Range();
+ range.startLine = startLine;
+ range.startCharacter = startCharacter;
+ range.endLine = endLine;
+ range.endCharacter = endCharacter;
+ return range;
+ }
+
+ private static List<String> getFixIds(List<RobotCommentInfo> robotComments) {
+ assertThatList(robotComments).isNotNull();
+ return robotComments.stream()
+ .map(robotCommentInfo -> robotCommentInfo.fixSuggestions)
+ .filter(Objects::nonNull)
+ .flatMap(List::stream)
+ .map(fixSuggestionInfo -> fixSuggestionInfo.fixId)
+ .collect(toList());
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
new file mode 100644
index 0000000000..5f210cc6d5
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ConfigRestApiBindingsIT.java
@@ -0,0 +1,113 @@
+// 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.binding;
+
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+
+import com.google.common.collect.ImmutableList;
+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.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 java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+
+/**
+ * Tests for checking the bindings of the config REST API.
+ *
+ * <p>These tests only verify that the config REST endpoints are correctly bound, they do no test
+ * the functionality of the config REST endpoints.
+ */
+public class ConfigRestApiBindingsIT extends AbstractDaemonTest {
+ /**
+ * Config REST endpoints to be tested, the URLs contain no placeholders since the only supported
+ * config identifier ('server') can be hard-coded.
+ */
+ private static final ImmutableList<RestCall> CONFIG_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/config/server/version"),
+ RestCall.get("/config/server/info"),
+ RestCall.get("/config/server/preferences"),
+ RestCall.put("/config/server/preferences"),
+ RestCall.get("/config/server/preferences.diff"),
+ RestCall.put("/config/server/preferences.diff"),
+ RestCall.get("/config/server/preferences.edit"),
+ RestCall.put("/config/server/preferences.edit"),
+ RestCall.get("/config/server/top-menus"),
+ RestCall.put("/config/server/email.confirm"),
+ RestCall.post("/config/server/check.consistency"),
+ RestCall.post("/config/server/reload"),
+ RestCall.get("/config/server/summary"),
+ RestCall.get("/config/server/capabilities"),
+ RestCall.get("/config/server/caches"),
+ RestCall.post("/config/server/caches"),
+ RestCall.get("/config/server/tasks"),
+ RestCall.post("/config/server/index.changes"));
+
+ /**
+ * Cache REST endpoints to be tested, the URLs contain a placeholder for the cache identifier.
+ * Since there is only supported a single supported config identifier ('server') it can be
+ * hard-coded.
+ */
+ private static final ImmutableList<RestCall> CACHE_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/config/server/caches/%s"));
+
+ /**
+ * Task REST endpoints to be tested, the URLs contain a placeholder for the task identifier. Since
+ * there is only supported a single supported config identifier ('server') it can be hard-coded.
+ */
+ private static final ImmutableList<RestCall> TASK_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/config/server/tasks/%s"),
+
+ // Task deletion must be tested last
+ RestCall.delete("/config/server/tasks/%s"));
+
+ @Test
+ public void configEndpoints() throws Exception {
+ // 'Access Database' is needed for the '/config/server/check.consistency' REST endpoint
+ allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+
+ RestApiCallHelper.execute(adminRestSession, CONFIG_ENDPOINTS);
+ }
+
+ @Test
+ public void cacheEndpoints() throws Exception {
+ RestApiCallHelper.execute(adminRestSession, CACHE_ENDPOINTS, ProjectCacheImpl.CACHE_NAME);
+ }
+
+ @Test
+ public void taskEndpoints() throws Exception {
+ RestResponse r = adminRestSession.get("/config/server/tasks/");
+ List<TaskInfo> result =
+ newGson().fromJson(r.getReader(), new TypeToken<List<TaskInfo>>() {}.getType());
+ r.consume();
+
+ Optional<String> id =
+ result.stream()
+ .filter(t -> "Log File Compressor".equals(t.command))
+ .map(t -> t.id)
+ .findFirst();
+ assertThat(id).isPresent();
+
+ RestApiCallHelper.execute(adminRestSession, TASK_ENDPOINTS, id.get());
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java
new file mode 100644
index 0000000000..bb12172766
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java
@@ -0,0 +1,102 @@
+// 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.binding;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.rest.util.RestApiCallHelper;
+import com.google.gerrit.acceptance.rest.util.RestCall;
+import org.junit.Test;
+
+/**
+ * Tests for checking the bindings of the groups REST API.
+ *
+ * <p>These tests only verify that the group REST endpoints are correctly bound, they do no test the
+ * functionality of the group REST endpoints.
+ */
+public class GroupsRestApiBindingsIT extends AbstractDaemonTest {
+ /**
+ * Group REST endpoints to be tested, each URL contains a placeholder for the group identifier.
+ */
+ private static final ImmutableList<RestCall> GROUP_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/groups/%s"),
+ RestCall.put("/groups/%s"),
+ RestCall.get("/groups/%s/detail"),
+ RestCall.get("/groups/%s/name"),
+ RestCall.put("/groups/%s/name"),
+ RestCall.get("/groups/%s/description"),
+ RestCall.put("/groups/%s/description"),
+ RestCall.delete("/groups/%s/description"),
+ RestCall.get("/groups/%s/owner"),
+ RestCall.put("/groups/%s/owner"),
+ RestCall.get("/groups/%s/options"),
+ RestCall.put("/groups/%s/options"),
+ RestCall.post("/groups/%s/members"),
+ RestCall.post("/groups/%s/members.add"),
+ RestCall.post("/groups/%s/members.delete"),
+ RestCall.post("/groups/%s/groups"),
+ RestCall.post("/groups/%s/groups.add"),
+ RestCall.post("/groups/%s/groups.delete"),
+ RestCall.get("/groups/%s/log.audit"),
+ RestCall.post("/groups/%s/index"),
+ RestCall.get("/groups/%s/members"),
+ RestCall.get("/groups/%s/groups"));
+
+ /**
+ * Member REST endpoints to be tested, each URL contains placeholders for the group identifier and
+ * the member identifier.
+ */
+ private static final ImmutableList<RestCall> MEMBER_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/groups/%s/members/%s"),
+ RestCall.put("/groups/%s/members/%s"),
+
+ // Member deletion must be tested last
+ RestCall.delete("/groups/%s/members/%s"));
+
+ /**
+ * Subgroup REST endpoints to be tested, each URL contains placeholders for the group identifier
+ * and the subgroup identifier.
+ */
+ private static final ImmutableList<RestCall> SUBGROUP_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/groups/%s/groups/%s"),
+ RestCall.put("/groups/%s/groups/%s"),
+
+ // Subgroup deletion must be tested last
+ RestCall.delete("/groups/%s/groups/%s"));
+
+ @Test
+ public void groupEndpoints() throws Exception {
+ String group = gApi.groups().create("test-group").get().name;
+ RestApiCallHelper.execute(adminRestSession, GROUP_ENDPOINTS, group);
+ }
+
+ @Test
+ public void memberEndpoints() throws Exception {
+ String group = gApi.groups().create("test-group").get().name;
+ gApi.groups().id(group).addMembers(admin.email());
+ RestApiCallHelper.execute(adminRestSession, MEMBER_ENDPOINTS, group, admin.email());
+ }
+
+ @Test
+ public void subgroupEndpoints() throws Exception {
+ String group = gApi.groups().create("test-group").get().name;
+ String subgroup = gApi.groups().create("test-subgroup").get().name;
+ gApi.groups().id(group).addGroups(subgroup);
+ RestApiCallHelper.execute(adminRestSession, SUBGROUP_ENDPOINTS, group, subgroup);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
new file mode 100644
index 0000000000..27df565c75
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedChildRestApiBindingsIT.java
@@ -0,0 +1,160 @@
+// 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.rest.binding;
+
+import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND;
+
+import com.google.common.collect.ImmutableList;
+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.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.ChildCollection;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestApiModule;
+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.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;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import org.junit.Test;
+
+/**
+ * Tests for checking plugin-provided REST API bindings nested under a core collection.
+ *
+ * <p>These tests only verify that the plugin-provided REST endpoints are correctly bound, they do
+ * not test the functionality of the plugin REST endpoints.
+ */
+public class PluginProvidedChildRestApiBindingsIT extends AbstractDaemonTest {
+
+ /** Resource to bind a child collection. */
+ public static final TypeLiteral<RestView<TestPluginResource>> TEST_KIND =
+ new TypeLiteral<RestView<TestPluginResource>>() {};
+
+ private static final String PLUGIN_NAME = "my-plugin";
+
+ private static final ImmutableSet<RestCall> TEST_CALLS =
+ ImmutableSet.of(
+ // Calls that have the plugin name as part of the collection name
+ RestCall.get("/changes/%s/revisions/%s/" + PLUGIN_NAME + "~test-collection/"),
+ RestCall.get("/changes/%s/revisions/%s/" + PLUGIN_NAME + "~test-collection/1/detail"),
+ RestCall.post("/changes/%s/revisions/%s/" + PLUGIN_NAME + "~test-collection/"),
+ RestCall.post("/changes/%s/revisions/%s/" + PLUGIN_NAME + "~test-collection/1/update"),
+ // Same tests but without the plugin name as part of the collection name. This works as
+ // long as there is no core collection with the same name (which takes precedence) and no
+ // other plugin binds a collection with the same name. We highly encourage plugin authors
+ // to use the fully qualified collection name instead.
+ RestCall.get("/changes/%s/revisions/%s/test-collection/"),
+ RestCall.get("/changes/%s/revisions/%s/test-collection/1/detail"),
+ RestCall.post("/changes/%s/revisions/%s/test-collection/"),
+ RestCall.post("/changes/%s/revisions/%s/test-collection/1/update"));
+
+ /**
+ * Module for all sys bindings.
+ *
+ * <p>TODO: This should actually just move into MyPluginHttpModule. However, that doesn't work
+ * currently. This TODO is for fixing this bug.
+ */
+ static class MyPluginSysModule extends AbstractModule {
+ @Override
+ public void configure() {
+ install(
+ new RestApiModule() {
+ @Override
+ public void configure() {
+ DynamicMap.mapOf(binder(), TEST_KIND);
+ child(REVISION_KIND, "test-collection").to(TestChildCollection.class);
+
+ postOnCollection(TEST_KIND).to(TestPostOnCollection.class);
+ post(TEST_KIND, "update").to(TestPost.class);
+ get(TEST_KIND, "detail").to(TestGet.class);
+ }
+ });
+ }
+ }
+
+ static class TestPluginResource implements RestResource {}
+
+ @Singleton
+ static class TestChildCollection
+ implements ChildCollection<RevisionResource, TestPluginResource> {
+ private final DynamicMap<RestView<TestPluginResource>> views;
+
+ @Inject
+ TestChildCollection(DynamicMap<RestView<TestPluginResource>> views) {
+ this.views = views;
+ }
+
+ @Override
+ public RestView<RevisionResource> list() throws RestApiException {
+ return (RestReadView<RevisionResource>) resource -> ImmutableList.of("one", "two");
+ }
+
+ @Override
+ public TestPluginResource parse(RevisionResource parent, IdString id) throws Exception {
+ return new TestPluginResource();
+ }
+
+ @Override
+ public DynamicMap<RestView<TestPluginResource>> views() {
+ return views;
+ }
+ }
+
+ @Singleton
+ static class TestPostOnCollection
+ implements RestCollectionModifyView<RevisionResource, TestPluginResource, String> {
+ @Override
+ public Object apply(RevisionResource parentResource, String input) throws Exception {
+ return "test";
+ }
+ }
+
+ @Singleton
+ static class TestPost implements RestModifyView<TestPluginResource, String> {
+ @Override
+ public String apply(TestPluginResource resource, String input) throws Exception {
+ return "test";
+ }
+ }
+
+ @Singleton
+ static class TestGet implements RestReadView<TestPluginResource> {
+ @Override
+ public String apply(TestPluginResource resource) throws Exception {
+ return "test";
+ }
+ }
+
+ @Test
+ public void testEndpoints() throws Exception {
+ PatchSet.Id patchSetId = createChange().getPatchSetId();
+ try (AutoCloseable ignored = installPlugin(PLUGIN_NAME, MyPluginSysModule.class, null, null)) {
+ RestApiCallHelper.execute(
+ adminRestSession,
+ TEST_CALLS.asList(),
+ String.valueOf(patchSetId.changeId.id),
+ String.valueOf(patchSetId.patchSetId));
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
new file mode 100644
index 0000000000..178a32656e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginProvidedRootRestApiBindingsIT.java
@@ -0,0 +1,205 @@
+// 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.rest.binding;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+
+import com.google.common.collect.ImmutableList;
+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.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.RestApiException;
+import com.google.gerrit.extensions.restapi.RestApiModule;
+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.RestResource;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.httpd.restapi.RestApiServlet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.servlet.ServletModule;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Test;
+
+/**
+ * Tests for checking plugin-provided REST API bindings directly under {@code /}.
+ *
+ * <p>These tests only verify that the plugin-provided REST endpoints are correctly bound, they do
+ * not test the functionality of the plugin REST endpoints.
+ */
+public class PluginProvidedRootRestApiBindingsIT extends AbstractDaemonTest {
+
+ /** Resource to bind a child collection. */
+ public static final TypeLiteral<RestView<TestPluginResource>> TEST_KIND =
+ new TypeLiteral<RestView<TestPluginResource>>() {};
+
+ private static final String PLUGIN_NAME = "my-plugin";
+
+ private static final ImmutableSet<RestCall> TEST_CALLS =
+ ImmutableSet.of(
+ RestCall.get("/plugins/" + PLUGIN_NAME + "/test-collection/"),
+ RestCall.get("/plugins/" + PLUGIN_NAME + "/test-collection/1/detail"),
+ RestCall.post("/plugins/" + PLUGIN_NAME + "/test-collection/"),
+ RestCall.post("/plugins/" + PLUGIN_NAME + "/test-collection/1/update"),
+ RestCall.builder(Method.GET, "/plugins/" + PLUGIN_NAME + "/not-found")
+ .expectedResponseCode(SC_NOT_FOUND)
+ .build());
+
+ /** Module for all HTTP bindings. */
+ static class MyPluginHttpModule extends ServletModule {
+ @Override
+ public void configureServlets() {
+ bind(TestRootCollection.class);
+
+ install(
+ new RestApiModule() {
+ @Override
+ public void configure() {
+ DynamicMap.mapOf(binder(), TEST_KIND);
+
+ postOnCollection(TEST_KIND).to(TestPostOnCollection.class);
+ post(TEST_KIND, "update").to(TestPost.class);
+ get(TEST_KIND, "detail").to(TestGet.class);
+ }
+ });
+
+ serveRegex("/(?:a/)?test-collection/(.*)$").with(TestRestApiServlet.class);
+ }
+ }
+
+ @Singleton
+ static class TestRestApiServlet extends RestApiServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Inject
+ TestRestApiServlet(RestApiServlet.Globals globals, Provider<TestRootCollection> collection) {
+ super(globals, collection);
+ }
+
+ @Override
+ public void service(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws ServletException, IOException {
+ // This is...unfortunate. HttpPluginServlet (and/or ContextMapper) doesn't properly set the
+ // servlet path on the wrapped request. Based on what RestApiServlet produces for non-plugin
+ // requests, it should be:
+ // contextPath = "/plugins/checks"
+ // servletPath = "/checkers/"
+ // pathInfo = checkerUuid
+ // Instead it does:
+ // contextPath = "/plugins/checks"
+ // servletPath = ""
+ // pathInfo = "/checkers/" + checkerUuid
+ // This results in RestApiServlet splitting the pathInfo into ["", "checkers", checkerUuid],
+ // and it passes the "" to CheckersCollection#parse, which understandably, but unfortunately,
+ // fails.
+ //
+ // This frankly seems like a bug that should be fixed, but it would quite likely break
+ // existing plugins in confusing ways. So, we work around it by introducing our own request
+ // wrapper with the correct paths.
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+ String pathInfo = req.getPathInfo();
+ String correctServletPath = "/test-collection/";
+ String fixedPathInfo = pathInfo.substring(correctServletPath.length());
+ HttpServletRequestWrapper wrapped =
+ new HttpServletRequestWrapper(req) {
+ @Override
+ public String getServletPath() {
+ return correctServletPath;
+ }
+
+ @Override
+ public String getPathInfo() {
+ return fixedPathInfo;
+ }
+ };
+
+ super.service(wrapped, (HttpServletResponse) servletResponse);
+ }
+ }
+
+ static class TestPluginResource implements RestResource {}
+
+ @Singleton
+ static class TestRootCollection implements ChildCollection<TopLevelResource, TestPluginResource> {
+ private final DynamicMap<RestView<TestPluginResource>> views;
+
+ @Inject
+ TestRootCollection(DynamicMap<RestView<TestPluginResource>> views) {
+ this.views = views;
+ }
+
+ @Override
+ public RestView<TopLevelResource> list() throws RestApiException {
+ return (RestReadView<TopLevelResource>) resource -> ImmutableList.of("one", "two");
+ }
+
+ @Override
+ public TestPluginResource parse(TopLevelResource parent, IdString id) throws Exception {
+ return new TestPluginResource();
+ }
+
+ @Override
+ public DynamicMap<RestView<TestPluginResource>> views() {
+ return views;
+ }
+ }
+
+ @Singleton
+ static class TestPostOnCollection
+ implements RestCollectionModifyView<TopLevelResource, TestPluginResource, String> {
+ @Override
+ public Object apply(TopLevelResource parentResource, String input) throws Exception {
+ return "test";
+ }
+ }
+
+ @Singleton
+ static class TestPost implements RestModifyView<TestPluginResource, String> {
+ @Override
+ public String apply(TestPluginResource resource, String input) throws Exception {
+ return "test";
+ }
+ }
+
+ @Singleton
+ static class TestGet implements RestReadView<TestPluginResource> {
+ @Override
+ public String apply(TestPluginResource resource) throws Exception {
+ return "test";
+ }
+ }
+
+ @Test
+ public void testEndpoints() throws Exception {
+ try (AutoCloseable ignored = installPlugin(PLUGIN_NAME, null, MyPluginHttpModule.class, null)) {
+ RestApiCallHelper.execute(adminRestSession, TEST_CALLS.asList());
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/PluginsRemoteAdminRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/PluginsRemoteAdminRestApiBindingsIT.java
new file mode 100644
index 0000000000..d60148eeda
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/PluginsRemoteAdminRestApiBindingsIT.java
@@ -0,0 +1,70 @@
+// 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.binding;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.rest.util.RestApiCallHelper;
+import com.google.gerrit.acceptance.rest.util.RestCall;
+import com.google.gerrit.common.RawInputUtil;
+import com.google.gerrit.extensions.api.plugins.InstallPluginInput;
+import com.google.gerrit.extensions.restapi.RawInput;
+import org.junit.Test;
+
+/**
+ * Tests for checking the remote administration bindings of the plugins REST API.
+ *
+ * <p>These tests only verify that the plugin REST endpoints are correctly bound, they do no test
+ * the functionality of the plugin REST endpoints.
+ */
+public class PluginsRemoteAdminRestApiBindingsIT extends AbstractDaemonTest {
+ /**
+ * Plugin REST endpoints to be tested, each URL contains a placeholder for the plugin identifier.
+ */
+ private static final ImmutableList<RestCall> PLUGIN_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.put("/plugins/%s"),
+
+ // For GET requests prefixing the view name with 'gerrit~' is required.
+ RestCall.get("/plugins/%s/gerrit~status"),
+
+ // POST (and PUT) requests don't require the 'gerrit~' prefix in front of the view name.
+ RestCall.post("/plugins/%s/gerrit~enable"),
+ RestCall.post("/plugins/%s/gerrit~disable"),
+ RestCall.post("/plugins/%s/gerrit~reload"),
+
+ // Plugin deletion must be tested last
+ RestCall.delete("/plugins/%s"));
+
+ private static final String JS_PLUGIN = "Gerrit.install(function(self){});\n";
+ private static final RawInput JS_PLUGIN_CONTENT = RawInputUtil.create(JS_PLUGIN.getBytes(UTF_8));
+
+ @Test
+ @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
+ public void pluginEndpoints() throws Exception {
+ String pluginName = "my-plugin";
+ installPlugin(pluginName);
+ RestApiCallHelper.execute(adminRestSession, PLUGIN_ENDPOINTS, pluginName);
+ }
+
+ private void installPlugin(String pluginName) throws Exception {
+ InstallPluginInput input = new InstallPluginInput();
+ input.raw = JS_PLUGIN_CONTENT;
+ gApi.plugins().install(pluginName + ".js", input);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
new file mode 100644
index 0000000000..ed09ddd44c
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
@@ -0,0 +1,256 @@
+// 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.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.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;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GitUtil;
+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.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;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.PushResult;
+import org.junit.Test;
+
+/**
+ * Tests for checking the bindings of the projects REST API.
+ *
+ * <p>These tests only verify that the project REST endpoints are correctly bound, they do no test
+ * the functionality of the project REST endpoints.
+ */
+public class ProjectsRestApiBindingsIT extends AbstractDaemonTest {
+ private static final ImmutableList<RestCall> PROJECT_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/projects/%s"),
+ RestCall.put("/projects/%s"),
+ RestCall.get("/projects/%s/description"),
+ RestCall.put("/projects/%s/description"),
+ RestCall.delete("/projects/%s/description"),
+ RestCall.get("/projects/%s/parent"),
+ RestCall.put("/projects/%s/parent"),
+ RestCall.get("/projects/%s/config"),
+ RestCall.put("/projects/%s/config"),
+ RestCall.get("/projects/%s/HEAD"),
+ RestCall.put("/projects/%s/HEAD"),
+ RestCall.get("/projects/%s/access"),
+ RestCall.post("/projects/%s/access"),
+ RestCall.put("/projects/%s/access:review"),
+ RestCall.get("/projects/%s/check.access"),
+ RestCall.put("/projects/%s/ban"),
+ RestCall.get("/projects/%s/statistics.git"),
+ RestCall.post("/projects/%s/index"),
+ RestCall.post("/projects/%s/gc"),
+ RestCall.get("/projects/%s/children"),
+ RestCall.get("/projects/%s/branches"),
+ RestCall.post("/projects/%s/branches:delete"),
+ RestCall.put("/projects/%s/branches/new-branch"),
+ RestCall.get("/projects/%s/tags"),
+ RestCall.post("/projects/%s/tags:delete"),
+ RestCall.put("/projects/%s/tags/new-tag"),
+ RestCall.builder(GET, "/projects/%s/commits")
+ // GET /projects/<project>/branches/<branch>/commits is not implemented
+ .expectedResponseCode(SC_NOT_FOUND)
+ .build(),
+ RestCall.get("/projects/%s/dashboards"));
+
+ /**
+ * Child project REST endpoints to be tested, each URL contains placeholders for the parent
+ * project identifier and the child project identifier.
+ */
+ private static final ImmutableList<RestCall> CHILD_PROJECT_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/projects/%s/children/%s"));
+
+ /**
+ * Branch REST endpoints to be tested, each URL contains placeholders for the project identifier
+ * and the branch identifier.
+ */
+ private static final ImmutableList<RestCall> BRANCH_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/projects/%s/branches/%s"),
+ RestCall.put("/projects/%s/branches/%s"),
+ RestCall.get("/projects/%s/branches/%s/mergeable"),
+ RestCall.builder(GET, "/projects/%s/branches/%s/reflog")
+ // The tests use DfsRepository which does not support getting the reflog.
+ .expectedResponseCode(SC_METHOD_NOT_ALLOWED)
+ .expectedMessage("reflog not supported on")
+ .build(),
+ RestCall.builder(GET, "/projects/%s/branches/%s/files")
+ // GET /projects/<project>/branches/<branch>/files is not implemented
+ .expectedResponseCode(SC_NOT_FOUND)
+ .build(),
+
+ // Branch deletion must be tested last
+ RestCall.delete("/projects/%s/branches/%s"));
+
+ /**
+ * Branch file REST endpoints to be tested, each URL contains placeholders for the project
+ * identifier, the branch identifier and the file identifier.
+ */
+ private static final ImmutableList<RestCall> BRANCH_FILE_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/projects/%s/branches/%s/files/%s/content"));
+
+ /**
+ * Dashboard REST endpoints to be tested, each URL contains placeholders for the project
+ * identifier and the dashboard identifier.
+ */
+ private static final ImmutableList<RestCall> DASHBOARD_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/projects/%s/dashboards/%s"),
+ RestCall.put("/projects/%s/dashboards/%s"),
+
+ // Dashboard deletion must be tested last
+ RestCall.delete("/projects/%s/dashboards/%s"));
+
+ /**
+ * Tag REST endpoints to be tested, each URL contains placeholders for the project identifier and
+ * the tag identifier.
+ */
+ private static final ImmutableList<RestCall> TAG_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/projects/%s/tags/%s"),
+ RestCall.put("/projects/%s/tags/%s"),
+ RestCall.delete("/projects/%s/tags/%s"));
+
+ /**
+ * Commit REST endpoints to be tested, each URL contains placeholders for the project identifier
+ * and the commit identifier.
+ */
+ private static final ImmutableList<RestCall> COMMIT_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/projects/%s/commits/%s"),
+ RestCall.get("/projects/%s/commits/%s/in"),
+ RestCall.get("/projects/%s/commits/%s/files"),
+ RestCall.post("/projects/%s/commits/%s/cherrypick"));
+
+ /**
+ * Commit file REST endpoints to be tested, each URL contains placeholders for the project
+ * identifier, the commit identifier and the file identifier.
+ */
+ private static final ImmutableList<RestCall> COMMIT_FILE_ENDPOINTS =
+ ImmutableList.of(RestCall.get("/projects/%s/commits/%s/files/%s/content"));
+
+ private static final String FILENAME = "test.txt";
+ @Inject private ProjectOperations projectOperations;
+
+ @Test
+ public void projectEndpoints() throws Exception {
+ RestApiCallHelper.execute(adminRestSession, PROJECT_ENDPOINTS, project.get());
+ }
+
+ @Test
+ public void childProjectEndpoints() throws Exception {
+ Project.NameKey childProject = projectOperations.newProject().parent(project).create();
+ RestApiCallHelper.execute(
+ adminRestSession, CHILD_PROJECT_ENDPOINTS, project.get(), childProject.get());
+ }
+
+ @Test
+ public void branchEndpoints() throws Exception {
+ RestApiCallHelper.execute(adminRestSession, BRANCH_ENDPOINTS, project.get(), "master");
+ }
+
+ @Test
+ public void branchFileEndpoints() throws Exception {
+ createAndSubmitChange(FILENAME);
+ RestApiCallHelper.execute(
+ adminRestSession, BRANCH_FILE_ENDPOINTS, project.get(), "master", FILENAME);
+ }
+
+ @Test
+ public void dashboardEndpoints() throws Exception {
+ createDefaultDashboard();
+ RestApiCallHelper.execute(
+ adminRestSession, DASHBOARD_ENDPOINTS, project.get(), DEFAULT_DASHBOARD_NAME);
+ }
+
+ @Test
+ public void tagEndpoints() throws Exception {
+ String tag = "test-tag";
+ gApi.projects().name(project.get()).tag(tag).create(new TagInput());
+ RestApiCallHelper.execute(adminRestSession, TAG_ENDPOINTS, project.get(), tag);
+ }
+
+ @Test
+ public void commitEndpoints() throws Exception {
+ String commit = createAndSubmitChange(FILENAME);
+ RestApiCallHelper.execute(adminRestSession, COMMIT_ENDPOINTS, project.get(), commit);
+ }
+
+ @Test
+ public void commitFileEndpoints() throws Exception {
+ String commit = createAndSubmitChange(FILENAME);
+ RestApiCallHelper.execute(
+ adminRestSession, COMMIT_FILE_ENDPOINTS, project.get(), commit, FILENAME);
+ }
+
+ private String createAndSubmitChange(String filename) throws Exception {
+ RevCommit c =
+ testRepo
+ .commit()
+ .message("A change")
+ .parent(getRemoteHead())
+ .add(filename, "content")
+ .insertChangeId()
+ .create();
+ String id = GitUtil.getChangeId(testRepo, c).get();
+ testRepo.reset(c);
+
+ String r = "refs/for/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ gApi.changes().id(id).current().review(ReviewInput.approve());
+ gApi.changes().id(id).current().submit();
+ return c.name();
+ }
+
+ private void createDefaultDashboard() throws Exception {
+ String dashboardRef = REFS_DASHBOARDS + "team";
+ grant(project, "refs/meta/*", Permission.CREATE);
+ gApi.projects().name(project.get()).branch(dashboardRef).create(new BranchInput());
+
+ try (Repository r = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(r)) {
+ TestRepository<Repository>.CommitBuilder cb = tr.branch(dashboardRef).commit();
+ StringBuilder content = new StringBuilder("[dashboard]\n");
+ content.append("title = ").append("Open Changes").append("\n");
+ content.append("[section \"").append("open").append("\"]\n");
+ content.append("query = ").append("is:open").append("\n");
+ cb.add("overview", content.toString());
+ cb.create();
+ }
+
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig().getProject().setLocalDefaultDashboard(dashboardRef + ":overview");
+ u.save();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/RootCollectionsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/RootCollectionsRestApiBindingsIT.java
new file mode 100644
index 0000000000..6d140c6a22
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/RootCollectionsRestApiBindingsIT.java
@@ -0,0 +1,58 @@
+// 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.binding;
+
+import static com.google.gerrit.acceptance.rest.util.RestApiCallHelper.execute;
+import static com.google.gerrit.acceptance.rest.util.RestCall.Method.GET;
+import static org.apache.http.HttpStatus.SC_NOT_FOUND;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.rest.util.RestCall;
+import org.junit.Test;
+
+/**
+ * Tests for checking the bindings of the root REST API.
+ *
+ * <p>These tests only verify that the root REST endpoints are correctly bound, they do no test the
+ * functionality of the root REST endpoints.
+ */
+public class RootCollectionsRestApiBindingsIT extends AbstractDaemonTest {
+ /** Root REST endpoints to be tested, the URLs contain no placeholders. */
+ private static final ImmutableList<RestCall> ROOT_ENDPOINTS =
+ ImmutableList.of(
+ RestCall.get("/access/"),
+ RestCall.get("/accounts/"),
+ RestCall.put("/accounts/new-account"),
+ RestCall.builder(GET, "/config/")
+ // GET /config/ is not implemented
+ .expectedResponseCode(SC_NOT_FOUND)
+ .build(),
+ RestCall.get("/changes/"),
+ RestCall.post("/changes/"),
+ RestCall.get("/groups/"),
+ RestCall.put("/groups/new-group"),
+ RestCall.get("/plugins/"),
+ RestCall.put("/plugins/new-plugin"),
+ RestCall.get("/projects/"),
+ RestCall.put("/projects/new-project"));
+
+ @Test
+ @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
+ public void rootEndpoints() throws Exception {
+ execute(adminRestSession, ROOT_ENDPOINTS);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 6622baeccf..a299b9a0c3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -37,7 +37,8 @@ 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.common.Nullable;
+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.ChangeApi;
import com.google.gerrit.extensions.api.changes.SubmitInput;
@@ -80,7 +81,6 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -115,14 +115,13 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@Inject private ApprovalsUtil approvalsUtil;
-
- @Inject private Submit submitHandler;
-
+ @Inject private DynamicSet<OnSubmitValidationListener> onSubmitValidationListeners;
@Inject private IdentifiedUser.GenericFactory userFactory;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private Submit submitHandler;
- @Inject private DynamicSet<OnSubmitValidationListener> onSubmitValidationListeners;
private RegistrationHandle onSubmitValidatorHandle;
-
private String systemTimeZone;
@Before
@@ -138,11 +137,6 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
@After
- public void cleanup() {
- db.close();
- }
-
- @After
public void removeOnSubmitValidator() {
if (onSubmitValidatorHandle != null) {
onSubmitValidatorHandle.remove();
@@ -154,12 +148,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
@TestProjectInput(createEmptyCommit = false)
public void submitToEmptyRepo() throws Exception {
- assertThat(getRemoteHead()).isNull();
+ assertThat(projectOperations.project(project).hasHead("master")).isFalse();
PushOneCommit.Result change = createChange();
assertThat(change.getCommit().getParents()).isEmpty();
Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
- RevCommit headAfterSubmitPreview = getRemoteHead();
- assertThat(headAfterSubmitPreview).isNull();
+ assertThat(projectOperations.project(project).hasHead("master")).isFalse();
assertThat(actual).hasSize(1);
submit(change.getChangeId());
@@ -314,11 +307,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
public void submitNoPermission() throws Exception {
// create project where submit is blocked
- Project.NameKey p = createProject("p");
+ Project.NameKey p = projectOperations.newProject().create();
block(p, "refs/*", Permission.SUBMIT, REGISTERED_USERS);
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
@@ -328,7 +321,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
public void noSelfSubmit() throws Exception {
// create project where submit is blocked for the change owner
- Project.NameKey p = createProject("p");
+ 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/*");
@@ -338,23 +331,23 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
- assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+ assertThat(change.owner._accountId).isEqualTo(admin.id().get());
submit(result.getChangeId(), new SubmitInput(), AuthException.class, "submit not permitted");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
submit(result.getChangeId());
}
@Test
public void onlySelfSubmit() throws Exception {
// create project where only the change owner can submit
- Project.NameKey p = createProject("p");
+ 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/*");
@@ -364,17 +357,17 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
- assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+ assertThat(change.owner._accountId).isEqualTo(admin.id().get());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
submit(result.getChangeId(), new SubmitInput(), AuthException.class, "submit not permitted");
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
submit(result.getChangeId());
}
@@ -384,8 +377,10 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
String topic = "test-topic";
// Create test projects
- TestRepository<?> repoA = createProjectWithPush("project-a", null, getSubmitType());
- TestRepository<?> repoB = createProjectWithPush("project-b", null, getSubmitType());
+ Project.NameKey keyA = createProjectForPush(getSubmitType());
+ TestRepository<?> repoA = cloneProject(keyA);
+ Project.NameKey keyB = createProjectForPush(getSubmitType());
+ TestRepository<?> repoB = cloneProject(keyB);
// Create changes on project-a
PushOneCommit.Result change1 =
@@ -418,15 +413,15 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
String topic = "test-topic";
// Create test project
- String projectName = "project-a";
- TestRepository<?> repoA = createProjectWithPush(projectName, null, getSubmitType());
+ Project.NameKey keyA = createProjectForPush(getSubmitType());
+ TestRepository<?> repoA = cloneProject(keyA);
- RevCommit initialHead = getRemoteHead(new Project.NameKey(name(projectName)), "master");
+ RevCommit initialHead = getRemoteHead(keyA, "master");
// Create the dev branch on the test project
BranchInput in = new BranchInput();
in.revision = initialHead.name();
- gApi.projects().name(name(projectName)).branch("dev").create(in);
+ gApi.projects().name(keyA.get()).branch("dev").create(in);
// Create changes on master
PushOneCommit.Result change1 =
@@ -486,7 +481,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
assertThat(log).hasSize(expectedCommitCount);
assertThat(commitsInRepo)
- .containsAllOf("Initial empty repository", "Change 1", "Change 2", "Change 3");
+ .containsAtLeast("Initial empty repository", "Change 1", "Change 2", "Change 3");
if (getSubmitType() == SubmitType.MERGE_ALWAYS) {
assertThat(commitsInRepo).contains("Merge changes from topic \"" + expectedTopic + "\"");
}
@@ -576,13 +571,12 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
enableCreateNewChangeForAllNotInTarget();
PushOneCommit push1 =
- pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+ pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
PushOneCommit.Result c1 = push1.to("refs/heads/topic");
c1.assertOkStatus();
PushOneCommit push2 =
pushFactory.create(
- db, admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+ admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
PushOneCommit.Result c2 = push2.to("refs/heads/topic");
c2.assertOkStatus();
@@ -606,11 +600,10 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
//
RevCommit master = getRemoteHead(project, "master");
PushOneCommit stableTip =
- pushFactory.create(
- db, admin.getIdent(), testRepo, "Tip of branch stable", "stable.txt", "");
+ pushFactory.create(admin.newIdent(), testRepo, "Tip of branch stable", "stable.txt", "");
PushOneCommit.Result stable = stableTip.to("refs/heads/stable");
PushOneCommit mergeCommit =
- pushFactory.create(db, admin.getIdent(), testRepo, "The merge commit", "merge.txt", "");
+ pushFactory.create(admin.newIdent(), testRepo, "The merge commit", "merge.txt", "");
mergeCommit.setParents(ImmutableList.of(master, stable.getCommit()));
PushOneCommit.Result mergeReview = mergeCommit.to("refs/for/master");
approve(mergeReview.getChangeId());
@@ -637,11 +630,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// push directly to stable to S1
PushOneCommit.Result s1 =
pushFactory
- .create(db, admin.getIdent(), testRepo, "new commit into stable", "stable1.txt", "")
+ .create(admin.newIdent(), testRepo, "new commit into stable", "stable1.txt", "")
.to("refs/heads/stable");
// move the stable tip ahead to S2
pushFactory
- .create(db, admin.getIdent(), testRepo, "Tip of branch stable", "stable2.txt", "")
+ .create(admin.newIdent(), testRepo, "Tip of branch stable", "stable2.txt", "")
.to("refs/heads/stable");
testRepo.reset(initial);
@@ -649,12 +642,12 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// move the master ahead
PushOneCommit.Result m =
pushFactory
- .create(db, admin.getIdent(), testRepo, "Move master ahead", "master.txt", "")
+ .create(admin.newIdent(), testRepo, "Move master ahead", "master.txt", "")
.to("refs/heads/master");
// create merge change
PushOneCommit mc =
- pushFactory.create(db, admin.getIdent(), testRepo, "The merge commit", "merge.txt", "");
+ pushFactory.create(admin.newIdent(), testRepo, "The merge commit", "merge.txt", "");
mc.setParents(ImmutableList.of(m.getCommit(), s1.getCommit()));
PushOneCommit.Result mergeReview = mc.to("refs/for/master");
approve(mergeReview.getChangeId());
@@ -734,18 +727,15 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
public void submitWithValidation() throws Exception {
AtomicBoolean called = new AtomicBoolean(false);
this.addOnSubmitValidationListener(
- 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);
- }
+ 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);
}
});
@@ -761,8 +751,10 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
String topic = "test-topic";
// Create test projects
- TestRepository<?> repoA = createProjectWithPush("project-a", null, getSubmitType());
- TestRepository<?> repoB = createProjectWithPush("project-b", null, getSubmitType());
+ Project.NameKey keyA = createProjectForPush(getSubmitType());
+ TestRepository<?> repoA = cloneProject(keyA);
+ Project.NameKey keyB = createProjectForPush(getSubmitType());
+ TestRepository<?> repoB = cloneProject(keyB);
// Create changes on project-a
PushOneCommit.Result change1 =
@@ -786,36 +778,31 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// succeed.
List<String> projectsCalled = new ArrayList<>(4);
this.addOnSubmitValidationListener(
- 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");
- }
+ 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);
+ }
+ projectsCalled.add(args.getProject().get());
+ if (projectsCalled.size() == 2) {
+ throw new ValidationException("time to fail");
}
});
submitWithConflict(change4.getChangeId(), "time to fail");
- assertThat(projectsCalled).containsExactly(name("project-a"), name("project-b"));
+ 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(
- name("project-a"), name("project-b"), name("project-a"), name("project-b"));
+ assertThat(projectsCalled).containsExactly(keyA.get(), keyB.get(), keyA.get(), keyB.get());
for (PushOneCommit.Result change : changes) {
change.assertChange(Change.Status.MERGED, name(topic), admin);
}
@@ -830,7 +817,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
// Create a stable branch and bootstrap it.
gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
PushOneCommit push =
- pushFactory.create(db, user.getIdent(), testRepo, "initial commit", "a.txt", "a");
+ pushFactory.create(user.newIdent(), testRepo, "initial commit", "a.txt", "a");
PushOneCommit.Result change = push.to("refs/heads/stable");
RevCommit stable = getRemoteHead(project, "stable");
@@ -894,8 +881,6 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
public void retrySubmitSingleChangeOnLockFailure() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
-
PushOneCommit.Result change = createChange();
String id = change.getChangeId();
approve(id);
@@ -921,13 +906,14 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
public void retrySubmitAfterTornTopicOnLockFailure() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
assume().that(isSubmitWholeTopicEnabled()).isTrue();
String topic = "test-topic";
- TestRepository<?> repoA = createProjectWithPush("project-a", null, getSubmitType());
- TestRepository<?> repoB = createProjectWithPush("project-b", null, getSubmitType());
+ Project.NameKey keyA = createProjectForPush(getSubmitType());
+ Project.NameKey keyB = createProjectForPush(getSubmitType());
+ TestRepository<?> repoA = cloneProject(keyA);
+ TestRepository<?> repoB = cloneProject(keyB);
PushOneCommit.Result change1 =
createChange(repoA, "master", "Change 1", "a.txt", "content", topic);
@@ -954,13 +940,13 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
repoA.git().fetch().call();
RevWalk rwA = repoA.getRevWalk();
- RevCommit masterA = rwA.parseCommit(getRemoteHead(name("project-a"), "master"));
+ RevCommit masterA = rwA.parseCommit(getRemoteHead(keyA, "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(name("project-b"), "master"));
+ RevCommit masterB = rwB.parseCommit(getRemoteHead(keyB, "master"));
RevCommit change2Ps = parseCurrentRevision(rwB, change2.getChangeId());
assertThat(rwB.isMergedInto(change2Ps, masterB)).isTrue();
@@ -1067,12 +1053,11 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
@TestProjectInput(createEmptyCommit = false, rejectEmptyCommit = InheritableBoolean.TRUE)
public void submitNonemptyCommitToEmptyRepoWithRejectEmptyCommit_allowed() throws Exception {
- assertThat(getRemoteHead()).isNull();
+ assertThat(projectOperations.project(project).hasHead("master")).isFalse();
PushOneCommit.Result change = createChange();
assertThat(change.getCommit().getParents()).isEmpty();
Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
- RevCommit headAfterSubmitPreview = getRemoteHead();
- assertThat(headAfterSubmitPreview).isNull();
+ assertThat(projectOperations.project(project).hasHead("master")).isFalse();
assertThat(actual).hasSize(1);
submit(change.getChangeId());
@@ -1083,10 +1068,10 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Test
@TestProjectInput(createEmptyCommit = false, rejectEmptyCommit = InheritableBoolean.TRUE)
public void submitEmptyCommitToEmptyRepoWithRejectEmptyCommit_allowed() throws Exception {
- assertThat(getRemoteHead()).isNull();
+ assertThat(projectOperations.project(project).hasHead("master")).isFalse();
PushOneCommit.Result change =
pushFactory
- .create(db, admin.getIdent(), testRepo, "Change 1", ImmutableMap.of())
+ .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
@@ -1094,8 +1079,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
.isEqualTo(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"));
Map<Branch.NameKey, ObjectId> actual = fetchFromSubmitPreview(change.getChangeId());
- RevCommit headAfterSubmitPreview = getRemoteHead();
- assertThat(headAfterSubmitPreview).isNull();
+ assertThat(projectOperations.project(project).hasHead("master")).isFalse();
assertThat(actual).hasSize(1);
submit(change.getChangeId());
@@ -1106,12 +1090,12 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
private void setChangeStatusToNew(PushOneCommit.Result... changes) throws Exception {
for (PushOneCommit.Result change : changes) {
try (BatchUpdate bu =
- batchUpdateFactory.create(db, project, userFactory.create(admin.id), TimeUtil.nowTs())) {
+ batchUpdateFactory.create(project, userFactory.create(admin.id()), TimeUtil.nowTs())) {
bu.addOp(
change.getChange().getId(),
new BatchUpdateOp() {
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
ctx.getChange().setStatus(Change.Status.NEW);
ctx.getUpdate(ctx.getChange().currentPatchSetId()).setStatus(Change.Status.NEW);
return true;
@@ -1240,7 +1224,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
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.getId());
+ assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(user.id());
}
protected void assertMerged(String changeId) throws RestApiException {
@@ -1266,19 +1250,19 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
protected void assertSubmitter(String changeId, int psId, TestAccount user) throws Exception {
Change c = getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change();
- ChangeNotes cn = notesFactory.createChecked(db, c);
+ ChangeNotes cn = notesFactory.createChecked(c);
PatchSetApproval submitter =
- approvalsUtil.getSubmitter(db, cn, new PatchSet.Id(cn.getChangeId(), psId));
+ approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
assertThat(submitter).isNotNull();
assertThat(submitter.isLegacySubmit()).isTrue();
- assertThat(submitter.getAccountId()).isEqualTo(user.getId());
+ assertThat(submitter.getAccountId()).isEqualTo(user.id());
}
protected void assertNoSubmitter(String changeId, int psId) throws Exception {
Change c = getOnlyElement(queryProvider.get().byKeyPrefix(changeId)).change();
- ChangeNotes cn = notesFactory.createChecked(db, c);
+ ChangeNotes cn = notesFactory.createChecked(c);
PatchSetApproval submitter =
- approvalsUtil.getSubmitter(db, cn, new PatchSet.Id(cn.getChangeId(), psId));
+ approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
assertThat(submitter).isNull();
}
@@ -1292,7 +1276,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
protected void assertRebase(TestRepository<?> testRepo, boolean contentMerge) throws Exception {
Repository repo = testRepo.getRepository();
- RevCommit localHead = getHead(repo);
+ RevCommit localHead = getHead(repo, "HEAD");
RevCommit remoteHead = getRemoteHead();
assertThat(localHead.getId()).isNotEqualTo(remoteHead.getId());
assertThat(remoteHead.getParentCount()).isEqualTo(1);
@@ -1345,18 +1329,17 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
}
}
- private TestRepository<?> createProjectWithPush(
- String name, @Nullable Project.NameKey parent, SubmitType submitType) throws Exception {
- Project.NameKey project = createProject(name, parent, true, submitType);
+ // TODO(hanwen): the submodule tests have a similar method; maybe we could share code?
+ protected Project.NameKey createProjectForPush(SubmitType submitType) throws Exception {
+ Project.NameKey project = projectOperations.newProject().submitType(submitType).create();
grant(project, "refs/heads/*", Permission.PUSH);
grant(project, "refs/for/refs/heads/*", Permission.SUBMIT);
- return cloneProject(project);
+ return project;
}
protected PushOneCommit.Result createChange(
String subject, String fileName, String content, String topic) throws Exception {
- PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, subject, fileName, content);
+ 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 29a81caa5f..36a09fd402 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -17,21 +17,10 @@ package com.google.gerrit.acceptance.rest.change;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
-import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
-import com.google.gerrit.extensions.api.changes.SubmitInput;
-import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.extensions.common.ChangeInfo;
-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.server.change.TestSubmitInput;
-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.Test;
public abstract class AbstractSubmitByMerge extends AbstractSubmit {
@@ -108,10 +97,10 @@ public abstract class AbstractSubmitByMerge extends AbstractSubmit {
assume().that(isSubmitWholeTopicEnabled()).isTrue();
PushOneCommit.Result change1 =
pushFactory
- .create(db, admin.getIdent(), testRepo, "Change 1", "a", "a")
+ .create(admin.newIdent(), testRepo, "Change 1", "a", "a")
.to("refs/for/master/" + name("topic"));
- PushOneCommit push2 = pushFactory.create(db, admin.getIdent(), testRepo, "Change 2", "b", "b");
+ PushOneCommit push2 = pushFactory.create(admin.newIdent(), testRepo, "Change 2", "b", "b");
push2.noParents();
PushOneCommit.Result change2 = push2.to("refs/for/master/" + name("topic"));
change2.assertOkStatus();
@@ -124,58 +113,4 @@ public abstract class AbstractSubmitByMerge extends AbstractSubmit {
assertThat(head.getParent(0)).isEqualTo(change1.getCommit());
assertThat(head.getParent(1)).isEqualTo(change2.getCommit());
}
-
- @Test
- public void repairChangeStateAfterFailure() throws Exception {
- // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
- assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
- RevCommit initialHead = getRemoteHead();
- PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
- submit(change.getChangeId());
- RevCommit afterChange1Head = getRemoteHead();
-
- testRepo.reset(initialHead);
- PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
- Change.Id id2 = change2.getChange().getId();
- TestSubmitInput failInput = new TestSubmitInput();
- failInput.failAfterRefUpdates = true;
- submit(
- change2.getChangeId(),
- failInput,
- ResourceConflictException.class,
- "Failing after ref updates");
-
- // Bad: ref advanced but change wasn't updated.
- PatchSet.Id psId1 = new PatchSet.Id(id2, 1);
- ChangeInfo info = gApi.changes().id(id2.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.NEW);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-
- RevCommit tip;
- try (Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
- ObjectId rev1 = repo.exactRef(psId1.toRefName()).getObjectId();
- assertThat(rev1).isNotNull();
-
- tip = rw.parseCommit(repo.exactRef("refs/heads/master").getObjectId());
- assertThat(tip.getParentCount()).isEqualTo(2);
- assertThat(tip.getParent(0)).isEqualTo(afterChange1Head);
- assertThat(tip.getParent(1)).isEqualTo(change2.getCommit());
- }
-
- submit(change2.getChangeId(), new SubmitInput(), null, null);
-
- // Change status and patch set entities were updated, and branch tip stayed
- // the same.
- info = gApi.changes().id(id2.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
- assertThat(Iterables.getLast(info.messages).message)
- .isEqualTo("Change has been successfully merged by Administrator");
-
- try (Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(tip);
- }
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index 0a92cfb0a2..c12adfaf7f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -15,28 +15,24 @@
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.GitUtil.getChangeId;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
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.extensions.restapi.ResourceConflictException;
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.change.TestSubmitInput;
import com.google.gerrit.server.project.testing.Util;
+import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -44,6 +40,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Test;
public abstract class AbstractSubmitByRebase extends AbstractSubmit {
+ @Inject private RequestScopeOperations requestScopeOperations;
@Override
protected abstract SubmitType getSubmitType();
@@ -73,8 +70,9 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
submitWithRebase(user);
}
- private void submitWithRebase(TestAccount submitter) throws Exception {
- setApiUser(submitter);
+ protected ImmutableList<PushOneCommit.Result> submitWithRebase(TestAccount submitter)
+ throws Exception {
+ requestScopeOperations.setApiUser(submitter.id());
RevCommit initialHead = getRemoteHead();
PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
submit(change.getChangeId());
@@ -90,8 +88,8 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
assertCurrentRevision(change2.getChangeId(), 2, headAfterSecondSubmit);
assertSubmitter(change2.getChangeId(), 1, submitter);
assertSubmitter(change2.getChangeId(), 2, submitter);
- assertPersonEquals(admin.getIdent(), headAfterSecondSubmit.getAuthorIdent());
- assertPersonEquals(submitter.getIdent(), headAfterSecondSubmit.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), headAfterSecondSubmit.getAuthorIdent());
+ assertPersonEquals(submitter.newIdent(), headAfterSecondSubmit.getCommitterIdent());
assertRefUpdatedEvents(
initialHead, headAfterFirstSubmit, headAfterFirstSubmit, headAfterSecondSubmit);
@@ -100,6 +98,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
headAfterFirstSubmit.name(),
change2.getChangeId(),
headAfterSecondSubmit.name());
+ return ImmutableList.of(change, change2);
}
@Test
@@ -180,7 +179,7 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
PushOneCommit.Result change1 = createChange("Added a", "a.txt", "");
PushOneCommit change2Push =
- pushFactory.create(db, admin.getIdent(), testRepo, "Merge to master", "m.txt", "");
+ pushFactory.create(admin.newIdent(), testRepo, "Merge to master", "m.txt", "");
change2Push.setParents(ImmutableList.of(initialHead, change1.getCommit()));
PushOneCommit.Result change2 = change2Push.to("refs/for/master");
@@ -242,80 +241,6 @@ public abstract class AbstractSubmitByRebase extends AbstractSubmit {
assertChangeMergedEvents(change.getChangeId(), headAfterFirstSubmit.name());
}
- @Test
- public void repairChangeStateAfterFailure() throws Exception {
- // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
- assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
- RevCommit initialHead = getRemoteHead();
- PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
- submit(change.getChangeId());
-
- RevCommit headAfterFirstSubmit = getRemoteHead();
- testRepo.reset(initialHead);
- PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
- Change.Id id2 = change2.getChange().getId();
- TestSubmitInput failInput = new TestSubmitInput();
- failInput.failAfterRefUpdates = true;
- submit(
- change2.getChangeId(),
- failInput,
- ResourceConflictException.class,
- "Failing after ref updates");
- RevCommit headAfterFailedSubmit = getRemoteHead();
-
- // Bad: ref advanced but change wasn't updated.
- PatchSet.Id psId1 = new PatchSet.Id(id2, 1);
- PatchSet.Id psId2 = new PatchSet.Id(id2, 2);
- ChangeInfo info = gApi.changes().id(id2.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.NEW);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
- assertThat(getPatchSet(psId2)).isNull();
-
- ObjectId rev2;
- try (Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
- ObjectId rev1 = repo.exactRef(psId1.toRefName()).getObjectId();
- assertThat(rev1).isNotNull();
-
- rev2 = repo.exactRef(psId2.toRefName()).getObjectId();
- assertThat(rev2).isNotNull();
- assertThat(rev2).isNotEqualTo(rev1);
- assertThat(rw.parseCommit(rev2).getParent(0)).isEqualTo(headAfterFirstSubmit);
-
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
- }
-
- submit(change2.getChangeId());
- RevCommit headAfterSecondSubmit = getRemoteHead();
- assertThat(headAfterSecondSubmit).isEqualTo(headAfterFailedSubmit);
-
- // Change status and patch set entities were updated, and branch tip stayed
- // the same.
- info = gApi.changes().id(id2.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(2);
- PatchSet ps2 = getPatchSet(psId2);
- assertThat(ps2).isNotNull();
- assertThat(ps2.getRevision().get()).isEqualTo(rev2.name());
- assertThat(Iterables.getLast(info.messages).message)
- .isEqualTo(
- "Change has been successfully rebased and submitted as "
- + rev2.name()
- + " by Administrator");
-
- try (Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
- }
-
- assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
- assertChangeMergedEvents(
- change.getChangeId(),
- headAfterFirstSubmit.name(),
- change2.getChangeId(),
- headAfterSecondSubmit.name());
- }
-
protected RevCommit parse(ObjectId id) throws Exception {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
index 0f394aa7f2..e7f3d54bb4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -24,6 +24,7 @@ import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.changes.ActionVisitor;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ListChangesOption;
@@ -54,9 +55,9 @@ public class ActionsIT extends AbstractDaemonTest {
return submitWholeTopicEnabledConfig();
}
- @Inject private RevisionJson.Factory revisionJsonFactory;
-
@Inject private DynamicSet<ActionVisitor> actionVisitors;
+ @Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private RevisionJson.Factory revisionJsonFactory;
private RegistrationHandle visitorHandle;
@@ -156,25 +157,25 @@ public class ActionsIT extends AbstractDaemonTest {
String change = createChangeWithTopic().getChangeId();
approve(change);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
String etag1 = getETag(change);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
approve(parent);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
String etag2 = getETag(change);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
String changeWithSameTopic = createChangeWithTopic().getChangeId();
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
String etag3 = getETag(change);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
approve(changeWithSameTopic);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
String etag4 = getETag(change);
if (isSubmitWholeTopicEnabled()) {
@@ -193,13 +194,13 @@ public class ActionsIT extends AbstractDaemonTest {
String change = createChange().getChangeId();
approve(change);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
String etag1 = getETag(change);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
approve(parent);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
String etag2 = getETag(change);
assertThat(etag2).isEqualTo(etag1);
}
@@ -327,7 +328,7 @@ public class ActionsIT extends AbstractDaemonTest {
}
Map<String, ActionInfo> origActions = origChange.actions;
- assertThat(origActions.keySet()).containsAllOf("followup", "abandon");
+ assertThat(origActions.keySet()).containsAtLeast("followup", "abandon");
assertThat(origActions.get("abandon").label).isEqualTo("Abandon");
Visitor v = new Visitor();
@@ -376,7 +377,7 @@ public class ActionsIT extends AbstractDaemonTest {
}
Map<String, ActionInfo> origActions = gApi.changes().id(id).current().actions();
- assertThat(origActions.keySet()).containsAllOf("cherrypick", "rebase");
+ assertThat(origActions.keySet()).containsAtLeast("cherrypick", "rebase");
assertThat(origActions.get("rebase").label).isEqualTo("Rebase");
Visitor v = new Visitor();
@@ -393,7 +394,7 @@ public class ActionsIT extends AbstractDaemonTest {
visitedCurrentRevisionActionsAssertions(origActions, revisionInfo.actions);
// ...via ChangeJson directly.
- ChangeData cd = changeDataFactory.create(db, project, changeId);
+ ChangeData cd = changeDataFactory.create(project, changeId);
revisionJsonFactory.create(opts).getRevisionInfo(cd, cd.patchSet(new PatchSet.Id(changeId, 1)));
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
index 69035f2629..2d6227bd47 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -15,7 +15,7 @@
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.common.truth.Truth.assert_;
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;
@@ -24,15 +24,17 @@ 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.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
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.extensions.restapi.UnprocessableEntityException;
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;
@@ -42,6 +44,7 @@ import org.junit.Test;
@NoHttpd
public class AssigneeIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
@BeforeClass
public static void setTimeForTesting() {
@@ -62,66 +65,58 @@ public class AssigneeIT extends AbstractDaemonTest {
@Test
public void addGetAssignee() throws Exception {
PushOneCommit.Result r = createChange();
- assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
- assertThat(getAssignee(r)._accountId).isEqualTo(user.getId().get());
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
+ assertThat(getAssignee(r)._accountId).isEqualTo(user.id().get());
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
}
@Test
public void setNewAssigneeWhenExists() throws Exception {
PushOneCommit.Result r = createChange();
- setAssignee(r, user.email);
- assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+ setAssignee(r, user.email());
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
}
@Test
public void getPastAssignees() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
- setAssignee(r, user.email);
- setAssignee(r, admin.email);
+ setAssignee(r, user.email());
+ setAssignee(r, admin.email());
List<AccountInfo> assignees = getPastAssignees(r);
assertThat(assignees).hasSize(2);
Iterator<AccountInfo> itr = assignees.iterator();
- assertThat(itr.next()._accountId).isEqualTo(user.getId().get());
- assertThat(itr.next()._accountId).isEqualTo(admin.getId().get());
+ assertThat(itr.next()._accountId).isEqualTo(user.id().get());
+ assertThat(itr.next()._accountId).isEqualTo(admin.id().get());
}
@Test
public void assigneeAddedAsReviewer() throws Exception {
- ReviewerState state;
- // Assignee is added as CC, if back-end is reviewDb (that does not support
- // CC) CC is stored as REVIEWER
- if (notesMigration.readChanges()) {
- state = ReviewerState.CC;
- } else {
- state = ReviewerState.REVIEWER;
- }
+ ReviewerState state = ReviewerState.CC;
PushOneCommit.Result r = createChange();
Iterable<AccountInfo> reviewers = getReviewers(r, state);
assertThat(reviewers).isNull();
- assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
reviewers = getReviewers(r, state);
assertThat(reviewers).hasSize(1);
AccountInfo reviewer = Iterables.getFirst(reviewers, null);
- assertThat(reviewer._accountId).isEqualTo(user.getId().get());
+ assertThat(reviewer._accountId).isEqualTo(user.id().get());
}
@Test
public void setAlreadyExistingAssignee() throws Exception {
PushOneCommit.Result r = createChange();
- setAssignee(r, user.email);
- assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+ setAssignee(r, user.email());
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
}
@Test
public void deleteAssignee() throws Exception {
PushOneCommit.Result r = createChange();
- assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
- assertThat(deleteAssignee(r)._accountId).isEqualTo(user.getId().get());
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
+ assertThat(deleteAssignee(r)._accountId).isEqualTo(user.id().get());
assertThat(getAssignee(r)).isNull();
}
@@ -134,10 +129,29 @@ public class AssigneeIT extends AbstractDaemonTest {
@Test
public void setAssigneeToInactiveUser() throws Exception {
PushOneCommit.Result r = createChange();
- gApi.accounts().id(user.getId().get()).setActive(false);
- exception.expect(UnprocessableEntityException.class);
- exception.expectMessage("is not active");
- setAssignee(r, user.email);
+ 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>");
+ }
+ }
+
+ @Test
+ public void setAssigneeToInactiveUserById() throws Exception {
+ PushOneCommit.Result r = createChange();
+ gApi.accounts().id(user.id().get()).setActive(false);
+ setAssignee(r, user.id().toString());
+ assertThat(getAssignee(r)._accountId).isEqualTo(user.id().get());
}
@Test
@@ -147,24 +161,24 @@ public class AssigneeIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange("refs/for/refs/meta/config");
exception.expect(AuthException.class);
exception.expectMessage("read not permitted");
- setAssignee(r, user.email);
+ setAssignee(r, user.email());
}
@Test
public void setAssigneeNotAllowedWithoutPermission() throws Exception {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("not permitted");
- setAssignee(r, user.email);
+ setAssignee(r, user.email());
}
@Test
public void setAssigneeAllowedWithPermission() throws Exception {
PushOneCommit.Result r = createChange();
grant(project, "refs/heads/master", Permission.EDIT_ASSIGNEE, false, REGISTERED_USERS);
- setApiUser(user);
- assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+ requestScopeOperations.setApiUser(user.id());
+ assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
}
private AccountInfo getAssignee(PushOneCommit.Result r) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java
index a2ad7fc2f8..3f1608c2a1 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeIdIT.java
@@ -18,6 +18,7 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.extensions.api.changes.ChangeApi;
+import com.google.gerrit.reviewdb.client.Change;
import org.junit.Test;
public class ChangeIdIT extends AbstractDaemonTest {
@@ -92,6 +93,43 @@ public class ChangeIdIT extends AbstractDaemonTest {
res.assertNotFound();
}
+ @Test
+ public void changeNumberRedirects() throws Exception {
+ // 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;
+ RestResponse res = anonymousRestSession.get("/" + changeId);
+ res.assertTemporaryRedirect("/c/" + project.get() + "/+/" + changeId + "/");
+ }
+
+ @Test
+ public void changeNumberRedirectsWithTrailingSlash() throws Exception {
+ int changeId = createChange().getChange().getId().id;
+ RestResponse res = anonymousRestSession.get("/" + changeId + "/");
+ res.assertTemporaryRedirect("/c/" + project.get() + "/+/" + changeId + "/");
+ }
+
+ @Test
+ public void changeNumberOverflowNotFound() throws Exception {
+ RestResponse res = anonymousRestSession.get("/9" + Long.MAX_VALUE);
+ res.assertNotFound();
+ }
+
+ @Test
+ public void unknownChangeNumberNotFound() throws Exception {
+ RestResponse res = anonymousRestSession.get("/10");
+ res.assertNotFound();
+ }
+
+ @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);
+ res.assertNotFound();
+ }
+
private static String changeDetail(String changeId) {
return "/changes/" + changeId + "/detail";
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
index 790b884d4d..51c5fc8686 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -28,6 +28,7 @@ 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.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.api.changes.DeleteChangeMessageInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -40,6 +41,7 @@ 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;
import java.util.Iterator;
@@ -54,6 +56,8 @@ import org.junit.runner.RunWith;
@RunWith(ConfigSuite.class)
public class ChangeMessagesIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
private String systemTimeZone;
@Before
@@ -127,6 +131,20 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
}
@Test
+ public void listChangeMessagesSkippedEmpty() throws Exception {
+ // Change message 1: create a change.
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+ // Will be a new commit with empty change message on the meta branch.
+ addOneReviewWithEmptyChangeMessage(changeId);
+ // Change Message 2: post a review with message "message 1".
+ addOneReview(changeId, "message");
+
+ List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
+ assertThat(messages).hasSize(2);
+ }
+
+ @Test
public void getOneChangeMessage() throws Exception {
int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
List<ChangeMessageInfo> messages = new ArrayList<>(gApi.changes().id(changeNum).get().messages);
@@ -139,7 +157,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
@Test
public void deleteCannotBeAppliedWithoutAdministrateServerCapability() throws Exception {
int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
deleteOneChangeMessage(changeNum, 0, user, "spam");
@@ -153,7 +171,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
public void deleteCanBeAppliedWithAdministrateServerCapability() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ADMINISTRATE_SERVER);
int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
deleteOneChangeMessage(changeNum, 0, user, "spam");
}
@@ -209,26 +227,32 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
private int createOneChangeWithMultipleChangeMessagesInHistory() throws Exception {
// Creates the following commit history on the meta branch of the test change.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// Commit 1: create a change.
PushOneCommit.Result result = createChange();
String changeId = result.getChangeId();
- // Commit 2: post a review with message "message 1".
- setApiUser(admin);
+ // Commit 2: post an empty change message.
+ requestScopeOperations.setApiUser(admin.id());
+ addOneReviewWithEmptyChangeMessage(changeId);
+ // Commit 3: post a review with message "message 1".
addOneReview(changeId, "message 1");
- // Commit 3: amend a new patch set.
- setApiUser(user);
+ // Commit 4: amend a new patch set.
+ requestScopeOperations.setApiUser(user.id());
amendChange(changeId);
- // Commit 4: post a review with message "message 2".
+ // Commit 5: post a review with message "message 2".
addOneReview(changeId, "message 2");
- // Commit 5: amend a new patch set.
+ // Commit 6: amend a new patch set.
amendChange(changeId);
- // Commit 6: approve the change.
- setApiUser(admin);
+ // Commit 7: approve the change.
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).current().review(ReviewInput.approve());
- // commit 7: submit the change.
+ // commit 8: submit the change.
gApi.changes().id(changeId).current().submit();
+ // Verifies there is only 7 change messages although there are 8 commits.
+ List<ChangeMessageInfo> messages = gApi.changes().id(changeId).messages();
+ assertThat(messages).hasSize(7);
+
return result.getChange().getId().get();
}
@@ -245,23 +269,24 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(reviewInput);
}
+ private void addOneReviewWithEmptyChangeMessage(String changeId) throws Exception {
+ gApi.changes().id(changeId).current().review(new ReviewInput());
+ }
+
private void deleteOneChangeMessage(
int changeNum, int deletedMessageIndex, TestAccount deletedBy, String reason)
throws Exception {
List<ChangeMessageInfo> messagesBeforeDeletion = gApi.changes().id(changeNum).messages();
List<CommentInfo> commentsBefore = getChangeSortedComments(changeNum);
- List<RevCommit> commitsBefore = new ArrayList<>();
- if (notesMigration.readChanges()) {
- commitsBefore = getChangeMetaCommitsInReverseOrder(new Change.Id(changeNum));
- }
+ List<RevCommit> commitsBefore = getChangeMetaCommitsInReverseOrder(new Change.Id(changeNum));
String id = messagesBeforeDeletion.get(deletedMessageIndex).id;
DeleteChangeMessageInput input = new DeleteChangeMessageInput(reason);
ChangeMessageInfo info = gApi.changes().id(changeNum).message(id).delete(input);
// Verify the return change message info is as expect.
- assertThat(info.message).isEqualTo(createNewChangeMessage(deletedBy.fullName, reason));
+ assertThat(info.message).isEqualTo(createNewChangeMessage(deletedBy.fullName(), reason));
List<ChangeMessageInfo> messagesAfterDeletion = gApi.changes().id(changeNum).messages();
assertMessagesAfterDeletion(
messagesBeforeDeletion, messagesAfterDeletion, deletedMessageIndex, deletedBy, reason);
@@ -271,11 +296,8 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
List<ChangeInfo> changes = gApi.changes().query("message removed").get();
assertThat(changes.stream().map(c -> c._number).collect(toSet())).contains(changeNum);
- // Verifies states of commits if NoteDb is on.
- if (notesMigration.readChanges()) {
- assertMetaCommitsAfterDeletion(
- commitsBefore, changeNum, deletedMessageIndex, deletedBy, reason);
- }
+ // Verifies states of commits.
+ assertMetaCommitsAfterDeletion(commitsBefore, changeNum, id, deletedBy, reason);
}
private void assertMessagesAfterDeletion(
@@ -304,7 +326,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
if (i == deletedMessageIndex) {
assertThat(after.message)
- .isEqualTo(createNewChangeMessage(deletedBy.fullName, deleteReason));
+ .isEqualTo(createNewChangeMessage(deletedBy.fullName(), deleteReason));
} else {
assertThat(after.message).isEqualTo(before.message);
}
@@ -314,7 +336,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
private void assertMetaCommitsAfterDeletion(
List<RevCommit> commitsBeforeDeletion,
int changeNum,
- int deletedMessageIndex,
+ String deletedMessageId,
TestAccount deletedBy,
String deleteReason)
throws Exception {
@@ -325,7 +347,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
for (int i = 0; i < commitsBeforeDeletion.size(); i++) {
RevCommit commitBefore = commitsBeforeDeletion.get(i);
RevCommit commitAfter = commitsAfterDeletion.get(i);
- if (i == deletedMessageIndex) {
+ if (commitBefore.getId().getName().equals(deletedMessageId)) {
byte[] rawBefore = commitBefore.getRawBuffer();
byte[] rawAfter = commitAfter.getRawBuffer();
Charset encodingBefore = RawParseUtils.parseEncoding(rawBefore);
@@ -368,7 +390,7 @@ public class ChangeMessagesIT extends AbstractDaemonTest {
rawAfter,
rangeAfter.get().changeMessageStart(),
rangeAfter.get().changeMessageEnd() + 1);
- assertThat(message).isEqualTo(createNewChangeMessage(deletedBy.fullName, deleteReason));
+ assertThat(message).isEqualTo(createNewChangeMessage(deletedBy.fullName(), deleteReason));
} else {
assertThat(commitAfter.getFullMessage()).isEqualTo(commitBefore.getFullMessage());
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
index ac0d0aa884..d51221e1b2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
@@ -19,23 +19,28 @@ import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
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.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;
import org.eclipse.jgit.junit.TestRepository;
import org.junit.Before;
import org.junit.Test;
public class ChangeOwnerIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
private TestAccount user2;
@Before
public void setUp() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
user2 = accountCreator.user2();
}
@@ -61,22 +66,22 @@ public class ChangeOwnerIT extends AbstractDaemonTest {
@Test
public void testChangeOwner_OwnerACLGrantedOnParentProject() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
grantApproveToChangeOwner(project);
- Project.NameKey child = createProject("child", project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
TestRepository<InMemoryRepository> childRepo = cloneProject(child, user);
approve(user, createMyChange(childRepo));
}
@Test
public void testChangeOwner_BlockedOnParentProject() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
blockApproveForChangeOwner(project);
- Project.NameKey child = createProject("child", project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
grantApproveToAll(child);
TestRepository<InMemoryRepository> childRepo = cloneProject(child, user);
String changeId = createMyChange(childRepo);
@@ -90,11 +95,11 @@ public class ChangeOwnerIT extends AbstractDaemonTest {
@Test
public void testChangeOwner_BlockedOnParentProjectAndExclusiveAllowOnChild() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
blockApproveForChangeOwner(project);
- Project.NameKey child = createProject("child", project);
+ Project.NameKey child = projectOperations.newProject().parent(project).create();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
grantExclusiveApproveToAll(child);
TestRepository<InMemoryRepository> childRepo = cloneProject(child, user);
String changeId = createMyChange(childRepo);
@@ -107,7 +112,7 @@ public class ChangeOwnerIT extends AbstractDaemonTest {
}
private void approve(TestAccount a, String changeId) throws Exception {
- Context old = setApiUser(a);
+ Context old = requestScopeOperations.setApiUser(a.id());
try {
gApi.changes().id(changeId).current().review(ReviewInput.approve());
} finally {
@@ -142,7 +147,7 @@ public class ChangeOwnerIT extends AbstractDaemonTest {
}
private String createMyChange(TestRepository<InMemoryRepository> testRepo) throws Exception {
- PushOneCommit push = pushFactory.create(db, user.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
return push.to("refs/for/master").getChangeId();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index dc71c1fffe..ac00e38fe3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -15,7 +15,6 @@
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.extensions.client.ListChangesOption.DETAILED_LABELS;
import com.google.common.collect.ImmutableList;
@@ -24,6 +23,7 @@ import com.google.common.collect.Iterables;
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.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -36,12 +36,14 @@ import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.mail.Address;
import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
import java.lang.reflect.Type;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
@Before
public void setUp() throws Exception {
@@ -52,7 +54,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void addByEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -72,9 +73,8 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void addByEmailAndById() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo byEmail = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
- AccountInfo byId = new AccountInfo(user.id.get());
+ AccountInfo byId = new AccountInfo(user.id().get());
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
PushOneCommit.Result r = createChange();
@@ -85,7 +85,7 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).addReviewer(inputByEmail);
AddReviewerInput inputById = new AddReviewerInput();
- inputById.reviewer = user.email;
+ inputById.reviewer = user.email();
inputById.state = state;
gApi.changes().id(r.getChangeId()).addReviewer(inputById);
@@ -98,7 +98,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void listReviewersByEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -126,7 +125,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void removeByEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -146,7 +144,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void convertFromCCToReviewer() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
PushOneCommit.Result r = createChange();
@@ -168,7 +165,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void addedReviewersGetNotified() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -188,7 +184,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void removingReviewerTriggersNotification() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -202,9 +197,9 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
// Review change as user
ReviewInput reviewInput = new ReviewInput();
reviewInput.message = "I have a comment";
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
revision(r).review(reviewInput);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sender.clear();
@@ -214,14 +209,13 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
assertThat(messages.get(0).rcpt())
- .containsExactly(Address.parse(addInput.reviewer), user.emailAddress);
+ .containsExactly(Address.parse(addInput.reviewer), user.getEmailAddress());
sender.clear();
}
}
@Test
public void reviewerAndCCReceiveRegularNotification() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -244,8 +238,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void reviewerAndCCReceiveSameEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
PushOneCommit.Result r = createChange();
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
for (int i = 0; i < 10; i++) {
@@ -258,7 +250,7 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
// Also add user as a regular reviewer
AddReviewerInput input = new AddReviewerInput();
- input.reviewer = user.email;
+ input.reviewer = user.email();
input.state = ReviewerState.REVIEWER;
gApi.changes().id(r.getChangeId()).addReviewer(input);
@@ -270,8 +262,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void addingMultipleReviewersAndCCsAtOnceSendsOnlyOneEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
PushOneCommit.Result r = createChange();
ReviewInput reviewInput = new ReviewInput();
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -288,7 +278,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void rejectMissingEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer("");
@@ -298,7 +287,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void rejectMalformedEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@");
@@ -308,8 +296,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void rejectWhenFeatureIsDisabled() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
ConfigInput conf = new ConfigInput();
conf.enableReviewerByEmail = InheritableBoolean.FALSE;
gApi.projects().name(project.get()).config(conf);
@@ -320,13 +306,14 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@gerritcodereview.com>");
assertThat(result.error)
.isEqualTo(
- "Foo Bar <foo.bar@gerritcodereview.com> does not identify a registered user or group");
+ "Account 'Foo Bar <foo.bar@gerritcodereview.com>' not found\n"
+ + "Foo Bar <foo.bar@gerritcodereview.com> does not identify a registered user or"
+ + " group");
assertThat(result.reviewers).isNull();
}
@Test
public void reviewersByEmailAreServedFromIndex() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
AccountInfo acc = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
@@ -337,21 +324,17 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
input.state = state;
gApi.changes().id(r.getChangeId()).addReviewer(input);
- notesMigration.setFailOnLoadForTest(true);
- try {
+ try (AutoCloseable ignored = disableNoteDb()) {
ChangeInfo info =
Iterables.getOnlyElement(
gApi.changes().query(r.getChangeId()).withOption(DETAILED_LABELS).get());
assertThat(info.reviewers).isEqualTo(ImmutableMap.of(state, ImmutableList.of(acc)));
- } finally {
- notesMigration.setFailOnLoadForTest(false);
}
}
}
@Test
public void addExistingReviewerByEmailShortCircuits() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
AddReviewerInput input = new AddReviewerInput();
@@ -369,7 +352,6 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
@Test
public void addExistingCcByEmailShortCircuits() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
AddReviewerInput input = new AddReviewerInput();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index d1f4d8420f..173b78d9c2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -15,7 +15,6 @@
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.extensions.client.ListChangesOption.DETAILED_LABELS;
import static com.google.gerrit.extensions.client.ReviewerState.CC;
import static com.google.gerrit.extensions.client.ReviewerState.REMOVED;
@@ -31,6 +30,8 @@ import com.google.gerrit.acceptance.PushOneCommit;
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.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
@@ -52,6 +53,7 @@ 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;
+import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -62,19 +64,23 @@ import java.util.Map;
import org.junit.Test;
public class ChangeReviewersIT extends AbstractDaemonTest {
+
+ @Inject private GroupOperations groupOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void addGroupAsReviewer() throws Exception {
// Set up two groups, one that is too large too add as reviewer, and one
// that is too large to add without confirmation.
- String largeGroup = createGroup("largeGroup");
- String mediumGroup = createGroup("mediumGroup");
+ String largeGroup = groupOperations.newGroup().name("largeGroup").create().get();
+ String mediumGroup = groupOperations.newGroup().name("mediumGroup").create().get();
int largeGroupSize = ReviewerAdder.DEFAULT_MAX_REVIEWERS + 1;
int mediumGroupSize = ReviewerAdder.DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK + 1;
List<TestAccount> users = createAccounts(largeGroupSize, "addGroupAsReviewer");
List<String> largeGroupUsernames = new ArrayList<>(mediumGroupSize);
for (TestAccount u : users) {
- largeGroupUsernames.add(u.username);
+ largeGroupUsernames.add(u.username());
}
List<String> mediumGroupUsernames = largeGroupUsernames.subList(0, mediumGroupSize);
gApi.groups()
@@ -121,39 +127,26 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
in.state = CC;
AddReviewerResult result = addReviewer(changeId, in);
- assertThat(result.input).isEqualTo(user.email);
+ assertThat(result.input).isEqualTo(user.email());
assertThat(result.confirm).isNull();
assertThat(result.error).isNull();
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertThat(result.reviewers).isNull();
- assertThat(result.ccs).hasSize(1);
- AccountInfo ai = result.ccs.get(0);
- assertThat(ai._accountId).isEqualTo(user.id.get());
- assertReviewers(c, CC, user);
- } else {
- assertThat(result.ccs).isNull();
- assertThat(result.reviewers).hasSize(1);
- AccountInfo ai = result.reviewers.get(0);
- assertThat(ai._accountId).isEqualTo(user.id.get());
- assertReviewers(c, REVIEWER, user);
- }
+ assertThat(result.reviewers).isNull();
+ assertThat(result.ccs).hasSize(1);
+ AccountInfo ai = result.ccs.get(0);
+ assertThat(ai._accountId).isEqualTo(user.id().get());
+ assertReviewers(c, CC, user);
// Verify email was sent to CCed account.
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
- if (notesMigration.readChanges()) {
- assertThat(m.body()).contains(admin.fullName + " has uploaded this change for review.");
- } else {
- assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
- assertThat(m.body()).contains("I'd like you to do a code review.");
- }
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.body()).contains(admin.fullName() + " has uploaded this change for review.");
}
@Test
@@ -161,7 +154,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
List<TestAccount> users = createAccounts(6, "addCcGroup");
List<String> usernames = new ArrayList<>(6);
for (TestAccount u : users) {
- usernames.add(u.username);
+ usernames.add(u.username());
}
List<TestAccount> firstUsers = users.subList(0, 3);
@@ -170,7 +163,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = createGroup("cc1");
+ in.reviewer = groupOperations.newGroup().name("cc1").create().get();
in.state = CC;
gApi.groups()
.id(in.reviewer)
@@ -180,18 +173,9 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(result.input).isEqualTo(in.reviewer);
assertThat(result.confirm).isNull();
assertThat(result.error).isNull();
- if (notesMigration.readChanges()) {
- assertThat(result.reviewers).isNull();
- } else {
- assertThat(result.ccs).isNull();
- }
+ assertThat(result.reviewers).isNull();
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertReviewers(c, CC, firstUsers);
- } else {
- assertReviewers(c, REVIEWER, firstUsers);
- assertReviewers(c, CC);
- }
+ assertReviewers(c, CC, firstUsers);
// Verify emails were sent to each of the group's accounts.
List<Message> messages = sender.getMessages();
@@ -199,51 +183,37 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
Message m = messages.get(0);
List<Address> expectedAddresses = new ArrayList<>(firstUsers.size());
for (TestAccount u : firstUsers) {
- expectedAddresses.add(u.emailAddress);
+ expectedAddresses.add(u.getEmailAddress());
}
assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
// CC a group that overlaps with some existing reviewers and CCed accounts.
TestAccount reviewer =
accountCreator.create(name("reviewer"), "addCcGroup-reviewer@example.com", "Reviewer");
- result = addReviewer(changeId, reviewer.username);
+ result = addReviewer(changeId, reviewer.username());
assertThat(result.error).isNull();
sender.clear();
- in.reviewer = createGroup("cc2");
+ in.reviewer = groupOperations.newGroup().name("cc2").create().get();
gApi.groups().id(in.reviewer).addMembers(usernames.toArray(new String[usernames.size()]));
- gApi.groups().id(in.reviewer).addMembers(reviewer.username);
+ gApi.groups().id(in.reviewer).addMembers(reviewer.username());
result = addReviewer(changeId, in);
assertThat(result.input).isEqualTo(in.reviewer);
assertThat(result.confirm).isNull();
assertThat(result.error).isNull();
c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertThat(result.ccs).hasSize(3);
- assertThat(result.reviewers).isNull();
- assertReviewers(c, REVIEWER, reviewer);
- assertReviewers(c, CC, users);
- } else {
- assertThat(result.ccs).isNull();
- assertThat(result.reviewers).hasSize(3);
- List<TestAccount> expectedUsers = new ArrayList<>(users.size() + 2);
- expectedUsers.addAll(users);
- expectedUsers.add(reviewer);
- assertReviewers(c, REVIEWER, expectedUsers);
- }
+ assertThat(result.ccs).hasSize(3);
+ assertThat(result.reviewers).isNull();
+ assertReviewers(c, REVIEWER, reviewer);
+ assertReviewers(c, CC, users);
messages = sender.getMessages();
assertThat(messages).hasSize(1);
m = messages.get(0);
expectedAddresses = new ArrayList<>(4);
for (int i = 0; i < 3; i++) {
- expectedAddresses.add(users.get(users.size() - i - 1).emailAddress);
+ expectedAddresses.add(users.get(users.size() - i - 1).getEmailAddress());
}
- if (!notesMigration.readChanges()) {
- for (int i = 0; i < 3; i++) {
- expectedAddresses.add(users.get(i).emailAddress);
- }
- }
- expectedAddresses.add(reviewer.emailAddress);
+ expectedAddresses.add(reviewer.getEmailAddress());
assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
}
@@ -252,17 +222,12 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
in.state = CC;
addReviewer(changeId, in);
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertReviewers(c, REVIEWER);
- assertReviewers(c, CC, user);
- } else {
- assertReviewers(c, REVIEWER, user);
- assertReviewers(c, CC);
- }
+ assertReviewers(c, REVIEWER);
+ assertReviewers(c, CC, user);
in.state = REVIEWER;
addReviewer(changeId, in);
@@ -288,15 +253,8 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// Verify user is added to CC list.
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertReviewers(c, REVIEWER);
- assertReviewers(c, CC, user);
- } else {
- // If we aren't reading from NoteDb, the user will appear as a
- // reviewer.
- assertReviewers(c, REVIEWER, user);
- assertReviewers(c, CC);
- }
+ assertReviewers(c, REVIEWER);
+ assertReviewers(c, CC, user);
}
@Test
@@ -305,7 +263,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
// user adds self as REVIEWER.
- ReviewInput input = new ReviewInput().reviewer(user.username);
+ ReviewInput input = new ReviewInput().reviewer(user.username());
RestResponse resp =
userRestSession.post(
"/changes/" + r.getChangeId() + "/revisions/" + r.getCommit().getName() + "/review",
@@ -324,7 +282,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(label.all).isNotNull();
assertThat(label.all).hasSize(1);
ApprovalInfo approval = label.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.getId().get());
+ assertThat(approval._accountId).isEqualTo(user.id().get());
}
@Test
@@ -333,7 +291,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
// user adds self as CC.
- ReviewInput input = new ReviewInput().reviewer(user.username, CC, false);
+ ReviewInput input = new ReviewInput().reviewer(user.username(), CC, false);
RestResponse resp =
userRestSession.post(
"/changes/" + r.getChangeId() + "/revisions/" + r.getCommit().getName() + "/review",
@@ -345,26 +303,13 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// Verify reviewer state.
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertReviewers(c, REVIEWER);
- assertReviewers(c, CC, user);
- // Verify no approvals were added.
- assertThat(c.labels).isNotNull();
- LabelInfo label = c.labels.get("Code-Review");
- assertThat(label).isNotNull();
- assertThat(label.all).isNull();
- } else {
- // When approvals are stored in ReviewDb, we still create a label for
- // the reviewing user, and force them into the REVIEWER state.
- assertReviewers(c, REVIEWER, user);
- assertReviewers(c, CC);
- LabelInfo label = c.labels.get("Code-Review");
- assertThat(label).isNotNull();
- assertThat(label.all).isNotNull();
- assertThat(label.all).hasSize(1);
- ApprovalInfo approval = label.all.get(0);
- assertThat(approval._accountId).isEqualTo(user.getId().get());
- }
+ assertReviewers(c, REVIEWER);
+ assertReviewers(c, CC, user);
+ // Verify no approvals were added.
+ assertThat(c.labels).isNotNull();
+ LabelInfo label = c.labels.get("Code-Review");
+ assertThat(label).isNotNull();
+ assertThat(label.all).isNull();
}
@Test
@@ -381,7 +326,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(label.all).isNull();
// Add user as REVIEWER.
- ReviewInput input = new ReviewInput().reviewer(user.username);
+ ReviewInput input = new ReviewInput().reviewer(user.username());
ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input);
assertThat(result.labels).isNull();
assertThat(result.reviewers).isNotNull();
@@ -400,8 +345,8 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
for (ApprovalInfo approval : label.all) {
approvals.put(approval._accountId, approval.value);
}
- assertThat(approvals).containsEntry(admin.getId().get(), 0);
- assertThat(approvals).containsEntry(user.getId().get(), 0);
+ 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
// then replace it with the default value.
@@ -425,8 +370,8 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
for (ApprovalInfo approval : label.all) {
approvals.put(approval._accountId, approval.value);
}
- assertThat(approvals).containsEntry(admin.getId().get(), 0);
- assertThat(approvals).containsEntry(user.getId().get(), 0);
+ assertThat(approvals).containsEntry(admin.id().get(), 0);
+ assertThat(approvals).containsEntry(user.id().get(), 0);
}
@Test
@@ -434,7 +379,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
TestAccount observer = accountCreator.user2();
PushOneCommit.Result r = createChange();
ReviewInput input =
- ReviewInput.approve().reviewer(user.email).reviewer(observer.email, CC, false);
+ ReviewInput.approve().reviewer(user.email()).reviewer(observer.email(), CC, false);
ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input);
assertThat(result.labels).isNotNull();
@@ -444,28 +389,22 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// Verify reviewer and CC were added. If not in NoteDb read mode, both
// parties will be returned as CCed.
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
- if (notesMigration.readChanges()) {
- assertReviewers(c, REVIEWER, admin, user);
- assertReviewers(c, CC, observer);
- } else {
- // In legacy mode, everyone should be a reviewer.
- assertReviewers(c, REVIEWER, admin, user, observer);
- assertReviewers(c, CC);
- }
+ assertReviewers(c, REVIEWER, admin, user);
+ assertReviewers(c, CC, observer);
// Verify emails were sent to added reviewers.
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(2);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress, observer.emailAddress);
- assertThat(m.body()).contains(admin.fullName + " has posted comments on this change.");
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress(), observer.getEmailAddress());
+ assertThat(m.body()).contains(admin.fullName() + " has posted comments on this change.");
assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
assertThat(m.body()).contains("Patch Set 1: Code-Review+2");
m = messages.get(1);
- assertThat(m.rcpt()).containsExactly(user.emailAddress, observer.emailAddress);
- assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress(), observer.getEmailAddress());
+ assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
assertThat(m.body()).contains("I'd like you to do a code review.");
}
@@ -476,11 +415,11 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
List<TestAccount> users = createAccounts(largeGroupSize, "reviewAndAddGroupReviewers");
List<String> usernames = new ArrayList<>(largeGroupSize);
for (TestAccount u : users) {
- usernames.add(u.username);
+ usernames.add(u.username());
}
- String largeGroup = createGroup("largeGroup");
- String mediumGroup = createGroup("mediumGroup");
+ String largeGroup = groupOperations.newGroup().name("largeGroup").create().get();
+ String mediumGroup = groupOperations.newGroup().name("mediumGroup").create().get();
gApi.groups().id(largeGroup).addMembers(usernames.toArray(new String[largeGroupSize]));
gApi.groups()
.id(mediumGroup)
@@ -492,8 +431,8 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// Attempt to add overly large group as reviewers.
ReviewInput input =
ReviewInput.approve()
- .reviewer(user.email)
- .reviewer(observer.email, CC, false)
+ .reviewer(user.email())
+ .reviewer(observer.email(), CC, false)
.reviewer(largeGroup);
ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input, SC_BAD_REQUEST);
assertThat(result.labels).isNull();
@@ -515,8 +454,8 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
// confirmation, as reviewers.
input =
ReviewInput.approve()
- .reviewer(user.email)
- .reviewer(observer.email, CC, false)
+ .reviewer(user.email())
+ .reviewer(observer.email(), CC, false)
.reviewer(mediumGroup);
result = review(r.getChangeId(), r.getCommit().name(), input, SC_BAD_REQUEST);
assertThat(result.labels).isNull();
@@ -535,7 +474,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(c.reviewers.get(CC)).isNull();
// Retrying with confirmation should successfully approve and add reviewers/CCs.
- input = ReviewInput.approve().reviewer(user.email).reviewer(mediumGroup, CC, true);
+ input = ReviewInput.approve().reviewer(user.email()).reviewer(mediumGroup, CC, true);
result = review(r.getChangeId(), r.getCommit().name(), input);
assertThat(result.labels).isNotNull();
assertThat(result.reviewers).isNotNull();
@@ -544,27 +483,16 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
c = gApi.changes().id(r.getChangeId()).get();
assertThat(c.messages).hasSize(2);
- if (notesMigration.readChanges()) {
- assertReviewers(c, REVIEWER, admin, user);
- assertReviewers(c, CC, users.subList(0, mediumGroupSize));
- } else {
- // If not in NoteDb mode, then everyone is a REVIEWER.
- List<TestAccount> expected = users.subList(0, mediumGroupSize);
- expected.add(admin);
- expected.add(user);
- assertReviewers(c, REVIEWER, expected);
- assertReviewers(c, CC);
- }
+ assertReviewers(c, REVIEWER, admin, user);
+ assertReviewers(c, CC, users.subList(0, mediumGroupSize));
}
@Test
public void noteDbAddReviewerToReviewerChangeInfo() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
in.state = CC;
addReviewer(changeId, in);
@@ -573,7 +501,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().review(ReviewInput.dislike());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// NoteDb adds reviewer to a change on every review.
gApi.changes().id(changeId).current().review(ReviewInput.dislike());
@@ -586,28 +514,28 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
Iterator<ReviewerUpdateInfo> it = c.reviewerUpdates.iterator();
ReviewerUpdateInfo reviewerChange = it.next();
assertThat(reviewerChange.state).isEqualTo(CC);
- assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.getId().get());
- assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.getId().get());
+ assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.id().get());
+ assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.id().get());
reviewerChange = it.next();
assertThat(reviewerChange.state).isEqualTo(REVIEWER);
- assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.getId().get());
- assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.getId().get());
+ assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.id().get());
+ assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.id().get());
reviewerChange = it.next();
assertThat(reviewerChange.state).isEqualTo(REMOVED);
- assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.getId().get());
- assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.getId().get());
+ assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.id().get());
+ assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.id().get());
}
@Test
public void addDuplicateReviewers() throws Exception {
PushOneCommit.Result r = createChange();
- ReviewInput input = ReviewInput.approve().reviewer(user.email).reviewer(user.email);
+ ReviewInput input = ReviewInput.approve().reviewer(user.email()).reviewer(user.email());
ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input);
assertThat(result.reviewers).isNotNull();
assertThat(result.reviewers).hasSize(1);
- AddReviewerResult reviewerResult = result.reviewers.get(user.email);
+ AddReviewerResult reviewerResult = result.reviewers.get(user.email());
assertThat(reviewerResult).isNotNull();
assertThat(reviewerResult.confirm).isNull();
assertThat(reviewerResult.error).isNull();
@@ -622,10 +550,10 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
accountCreator.create(name("user2"), emailPrefix + "user2@example.com", "User2");
TestAccount user3 =
accountCreator.create(name("user3"), emailPrefix + "user3@example.com", "User3");
- String group1 = createGroup("group1");
- String group2 = createGroup("group2");
- gApi.groups().id(group1).addMembers(user1.username, user2.username);
- gApi.groups().id(group2).addMembers(user2.username, user3.username);
+ String group1 = groupOperations.newGroup().name("group1").create().get();
+ String group2 = groupOperations.newGroup().name("group2").create().get();
+ gApi.groups().id(group1).addMembers(user1.username(), user2.username());
+ gApi.groups().id(group2).addMembers(user2.username(), user3.username());
PushOneCommit.Result r = createChange();
ReviewInput input = ReviewInput.approve().reviewer(group1).reviewer(group2);
@@ -640,9 +568,6 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(reviewerResult.reviewers).hasSize(1);
// Repeat the above for CCs
- if (!notesMigration.readChanges()) {
- return;
- }
r = createChange();
input = ReviewInput.approve().reviewer(group1, CC, false).reviewer(group2, CC, false);
result = review(r.getChangeId(), r.getCommit().name(), input);
@@ -675,7 +600,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
public void removingReviewerRemovesTheirVote() throws Exception {
String crLabel = "Code-Review";
PushOneCommit.Result r = createChange();
- ReviewInput input = ReviewInput.approve().reviewer(admin.email);
+ ReviewInput input = ReviewInput.approve().reviewer(admin.email());
ReviewResult addResult = review(r.getChangeId(), r.getCommit().name(), input);
assertThat(addResult.reviewers).isNotNull();
assertThat(addResult.reviewers).hasSize(1);
@@ -690,7 +615,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
assertThat(changeLabels.get(crLabel).all).isNull();
// Check that the vote is gone even after the reviewer is added back
- addReviewer(r.getChangeId(), admin.email);
+ addReviewer(r.getChangeId(), admin.email());
changeLabels = getChangeLabels(r.getChangeId());
assertThat(changeLabels.get(crLabel).all).isNull();
}
@@ -701,15 +626,15 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
TestAccount userToNotify = createAccounts(1, "notify-details-post-review").get(0);
ReviewInput reviewInput = new ReviewInput();
- reviewInput.reviewer(user.email, ReviewerState.REVIEWER, true);
+ reviewInput.reviewer(user.email(), ReviewerState.REVIEWER, true);
reviewInput.notify = NotifyHandling.NONE;
reviewInput.notifyDetails =
- ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email)));
+ ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email())));
sender.clear();
gApi.changes().id(r.getChangeId()).current().review(reviewInput);
assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.emailAddress);
+ assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getEmailAddress());
}
@Test
@@ -718,15 +643,15 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
TestAccount userToNotify = createAccounts(1, "notify-details-post-reviewers").get(0);
AddReviewerInput addReviewer = new AddReviewerInput();
- addReviewer.reviewer = user.email;
+ addReviewer.reviewer = user.email();
addReviewer.notify = NotifyHandling.NONE;
addReviewer.notifyDetails =
- ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email)));
+ ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email())));
sender.clear();
gApi.changes().id(r.getChangeId()).addReviewer(addReviewer);
assertThat(sender.getMessages()).hasSize(1);
- assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.emailAddress);
+ assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getEmailAddress());
}
@Test
@@ -734,12 +659,12 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
TestAccount newUser = createAccounts(1, name("foo")).get(0);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).current().review(new ReviewInput().label("Code-Review", 1));
- setApiUser(newUser);
+ requestScopeOperations.setApiUser(newUser.id());
exception.expect(AuthException.class);
exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+ gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
}
@Test
@@ -751,10 +676,10 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
TestAccount newUser = createAccounts(1, name("foo")).get(0);
grant(project, RefNames.REFS + "*", Permission.REMOVE_REVIEWER, false, REGISTERED_USERS);
- gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+ gApi.changes().id(r.getChangeId()).addReviewer(user.email());
assertThatUserIsOnlyReviewer(r.getChangeId());
- setApiUser(newUser);
- gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+ requestScopeOperations.setApiUser(newUser.id());
+ gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
assertThat(gApi.changes().id(r.getChangeId()).get().reviewers).isEmpty();
}
@@ -763,11 +688,11 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
TestAccount newUser = createAccounts(1, name("foo")).get(0);
- gApi.changes().id(r.getChangeId()).addReviewer(user.email);
- setApiUser(newUser);
+ 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();
+ gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
}
@Test
@@ -776,53 +701,51 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
TestAccount newUser = createAccounts(1, name("foo")).get(0);
AddReviewerInput input = new AddReviewerInput();
- input.reviewer = user.email;
+ input.reviewer = user.email();
input.state = ReviewerState.CC;
gApi.changes().id(r.getChangeId()).addReviewer(input);
- setApiUser(newUser);
+ requestScopeOperations.setApiUser(newUser.id());
exception.expect(AuthException.class);
exception.expectMessage("remove reviewer not permitted");
- gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+ gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
}
@Test
public void addExistingReviewerShortCircuits() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
AddReviewerInput input = new AddReviewerInput();
- input.reviewer = user.email;
+ input.reviewer = user.email();
input.state = ReviewerState.REVIEWER;
AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer(input);
assertThat(result.reviewers).hasSize(1);
ReviewerInfo info = result.reviewers.get(0);
- assertThat(info._accountId).isEqualTo(user.id.get());
+ assertThat(info._accountId).isEqualTo(user.id().get());
assertThat(gApi.changes().id(r.getChangeId()).addReviewer(input).reviewers).isEmpty();
}
@Test
public void addExistingCcShortCircuits() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
AddReviewerInput input = new AddReviewerInput();
- input.reviewer = user.email;
+ input.reviewer = user.email();
input.state = ReviewerState.CC;
AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer(input);
assertThat(result.ccs).hasSize(1);
AccountInfo info = result.ccs.get(0);
- assertThat(info._accountId).isEqualTo(user.id.get());
+ assertThat(info._accountId).isEqualTo(user.id().get());
assertThat(gApi.changes().id(r.getChangeId()).addReviewer(input).ccs).isEmpty();
}
private void assertThatUserIsOnlyReviewer(String changeId) throws Exception {
- AccountInfo userInfo = new AccountInfo(user.fullName, user.emailAddress.getEmail());
- userInfo._accountId = user.id.get();
- userInfo.username = user.username;
+ AccountInfo userInfo = new AccountInfo(user.fullName(), user.getEmailAddress().getEmail());
+ userInfo._accountId = user.id().get();
+ userInfo.username = user.username();
assertThat(gApi.changes().id(changeId).get().reviewers)
.containsExactly(ReviewerState.REVIEWER, ImmutableList.of(userInfo));
}
@@ -849,7 +772,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
}
private RestResponse deleteReviewer(String changeId, TestAccount account) throws Exception {
- return adminRestSession.delete("/changes/" + changeId + "/reviewers/" + account.getId().get());
+ return adminRestSession.delete("/changes/" + changeId + "/reviewers/" + account.id().get());
}
private ReviewResult review(String changeId, String revisionId, ReviewInput in) throws Exception {
@@ -893,7 +816,7 @@ public class ChangeReviewersIT extends AbstractDaemonTest {
}
List<Integer> expectedAccountIds = new ArrayList<>();
for (TestAccount account : accounts) {
- expectedAccountIds.add(account.getId().get());
+ expectedAccountIds.add(account.id().get());
}
assertThat(actualAccountIds).containsExactlyElementsIn(expectedAccountIds);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
index baf56de0ef..9a907aaee3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
@@ -22,6 +22,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
@@ -31,6 +32,7 @@ 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;
@@ -43,6 +45,8 @@ import org.junit.Before;
import org.junit.Test;
public class ConfigChangeIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Before
public void setUp() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
@@ -52,7 +56,7 @@ public class ConfigChangeIT extends AbstractDaemonTest {
u.save();
}
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
fetchRefsMetaConfig();
}
@@ -96,13 +100,13 @@ public class ConfigChangeIT extends AbstractDaemonTest {
@Test
@TestProjectInput(cloneAs = "user")
public void onlyAdminMayUpdateProjectParent() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ProjectInput parent = new ProjectInput();
parent.name = name("parent");
parent.permissionsOnly = true;
gApi.projects().create(parent);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
Config cfg = readProjectConfig();
assertThat(cfg.getString("access", null, "inheritFrom")).isAnyOf(null, allProjects.get());
cfg.setString("access", null, "inheritFrom", parent.name);
@@ -132,7 +136,7 @@ public class ConfigChangeIT extends AbstractDaemonTest {
assertThat(readProjectConfig().getString("access", null, "inheritFrom"))
.isAnyOf(null, allProjects.get());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(id).current().submit();
assertThat(gApi.changes().id(id).info().status).isEqualTo(ChangeStatus.MERGED);
assertThat(gApi.projects().name(project.get()).get().parent).isEqualTo(parent.name);
@@ -142,10 +146,10 @@ public class ConfigChangeIT extends AbstractDaemonTest {
@Test
public void rejectDoubleInheritance() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
// Create separate projects to test the config
- Project.NameKey parent = createProject("projectToInheritFrom");
- Project.NameKey child = createProject("projectWithMalformedConfig");
+ Project.NameKey parent = createProjectOverAPI("projectToInheritFrom", null, true, null);
+ Project.NameKey child = createProjectOverAPI("projectWithMalformedConfig", null, true, null);
String config =
gApi.projects()
@@ -164,7 +168,7 @@ public class ConfigChangeIT extends AbstractDaemonTest {
GitUtil.fetch(childRepo, RefNames.REFS_CONFIG + ":cfg");
childRepo.reset("cfg");
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), childRepo, "Subject", "project.config", config);
+ pushFactory.create(admin.newIdent(), childRepo, "Subject", "project.config", config);
PushOneCommit.Result res = push.to(RefNames.REFS_CONFIG);
res.assertErrorStatus();
res.assertMessage("cannot inherit from multiple projects");
@@ -190,12 +194,7 @@ public class ConfigChangeIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
.create(
- db,
- user.getIdent(),
- testRepo,
- "Update project config",
- "project.config",
- cfg.toText())
+ user.newIdent(), testRepo, "Update project config", "project.config", cfg.toText())
.to("refs/for/refs/meta/config");
r.assertOkStatus();
return r;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
index 0af970837e..9c42542188 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
@@ -34,6 +34,7 @@ import com.google.common.truth.StringSubject;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -44,6 +45,7 @@ import java.util.Locale;
import java.util.stream.Stream;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.cookie.Cookie;
@@ -99,6 +101,30 @@ public class CorsIT extends AbstractDaemonTest {
}
@Test
+ public void originsOnNotFoundException() throws Exception {
+ String url = "/changes/999/detail";
+ check(url, true, "http://example.com", adminRestSession, 404);
+ check(url, false, "http://friendsly", adminRestSession, 404);
+ }
+
+ @Test
+ public void originsOnBadRequestException() throws Exception {
+ String url = "/config/server/caches/?format=NONSENSE";
+ check(url, true, "http://example.com", adminRestSession, 400);
+ check(url, false, "http://friendsly", adminRestSession, 400);
+ }
+
+ @Test
+ public void originsOnForbidden() throws Exception {
+ Result change = createChange();
+ // Make change private to hide it
+ gApi.changes().id(change.getChangeId()).setPrivate(true, "now private");
+ String url = "/changes/" + change.getChangeId() + "/detail";
+ check(url, true, "http://example.com", anonymousRestSession, 404);
+ check(url, false, "http://friendsly", anonymousRestSession, 404);
+ }
+
+ @Test
public void putWithServerOriginAcceptedWithNoCorsResponseHeaders() throws Exception {
Result change = createChange();
String origin = adminRestSession.url();
@@ -179,7 +205,7 @@ public class CorsIT extends AbstractDaemonTest {
BasicCookieStore cookies = new BasicCookieStore();
Executor http = Executor.newInstance().cookieStore(cookies);
- Request req = Request.Get(canonicalWebUrl.get() + "/login/?account_id=" + admin.id.get());
+ Request req = Request.Get(canonicalWebUrl.get() + "/login/?account_id=" + admin.id().get());
http.execute(req);
String auth = null;
for (Cookie c : cookies.getCookies()) {
@@ -247,9 +273,15 @@ public class CorsIT extends AbstractDaemonTest {
}
private void check(String url, boolean accept, String origin) throws Exception {
+ check(url, accept, origin, adminRestSession, HttpStatus.SC_OK);
+ }
+
+ private void check(
+ String url, boolean accept, String origin, RestSession restSession, int httpStatusCode)
+ throws Exception {
Header hdr = new BasicHeader(ORIGIN, origin);
- RestResponse r = adminRestSession.getWithHeader(url, hdr);
- r.assertOK();
+ RestResponse r = restSession.getWithHeader(url, hdr);
+ r.assertStatus(httpStatusCode);
checkCors(r, accept, origin);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index a83bf7b175..bc675e539c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -15,7 +15,6 @@
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.common.data.Permission.READ;
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
@@ -28,6 +27,7 @@ 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.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -44,9 +44,12 @@ 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;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -66,6 +69,8 @@ import org.junit.BeforeClass;
import org.junit.Test;
public class CreateChangeIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@BeforeClass
public static void setTimeForTesting() {
TestTimeUtil.resetWithClockStep(1, SECONDS);
@@ -84,6 +89,13 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
+ public void createEmptyChange_NonExistingBranch() throws Exception {
+ ChangeInput ci = newChangeInput(ChangeStatus.NEW);
+ ci.branch = "non-existing";
+ assertCreateFails(ci, BadRequestException.class, "Destination branch does not exist");
+ }
+
+ @Test
public void createEmptyChange_MissingMessage() throws Exception {
ChangeInput ci = new ChangeInput();
ci.project = project.get();
@@ -151,18 +163,18 @@ public class CreateChangeIT extends AbstractDaemonTest {
@Test
public void notificationsOnChangeCreation() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
watch(project.get());
// check that watcher is notified
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
- assertThat(m.body()).contains(admin.fullName + " has uploaded this change for review.");
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(m.body()).contains(admin.fullName() + " has uploaded this change for review.");
// check that watcher is not notified if notify=NONE
sender.clear();
@@ -181,7 +193,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
assertThat(message)
.contains(
String.format(
- "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.getIdent().getEmailAddress()));
+ "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.newIdent().getEmailAddress()));
} finally {
setSignedOffByFooter(false);
}
@@ -202,7 +214,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
assertThat(message)
.contains(
String.format(
- "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.getIdent().getEmailAddress()));
+ "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.newIdent().getEmailAddress()));
} finally {
setSignedOffByFooter(false);
}
@@ -240,6 +252,16 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
+ public void createChangeWithNonExistingParentCommitFails() throws Exception {
+ ChangeInput input = newChangeInput(ChangeStatus.NEW);
+ input.baseCommit = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ assertCreateFails(
+ input,
+ UnprocessableEntityException.class,
+ String.format("Base %s doesn't exist", input.baseCommit));
+ }
+
+ @Test
public void createChangeWithParentCommitOnWrongBranchFails() throws Exception {
Map<String, PushOneCommit.Result> setup =
changeInTwoBranches("foo", "foo.txt", "bar", "bar.txt");
@@ -253,6 +275,20 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
+ public void createChangeWithParentCommitWithNonExistingTargetBranch() throws Exception {
+ Result initialCommit =
+ pushFactory
+ .create(user.newIdent(), testRepo, "initial commit", "readme.txt", "initial commit")
+ .to("refs/heads/master");
+ initialCommit.assertOkStatus();
+
+ ChangeInput input = newChangeInput(ChangeStatus.NEW);
+ input.branch = "non-existing";
+ input.baseCommit = initialCommit.getCommit().getName();
+ assertCreateFails(input, BadRequestException.class, "Destination branch does not exist");
+ }
+
+ @Test
public void createChangeOnNonExistingBaseChangeFails() throws Exception {
ChangeInput input = newChangeInput(ChangeStatus.NEW);
input.baseChange = "999999";
@@ -274,19 +310,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
- public void createChangeOnInvisibleBranchFails() throws Exception {
- changeInTwoBranches("invisible-branch", "a.txt", "branchB", "b.txt");
- block(project, "refs/heads/invisible-branch", READ, REGISTERED_USERS);
-
- ChangeInput in = newChangeInput(ChangeStatus.NEW);
- in.branch = "invisible-branch";
- assertCreateFails(in, ResourceNotFoundException.class, "");
- }
-
- @Test
public void noteDbCommit() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
ChangeInfo c = assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = new RevWalk(repo)) {
@@ -296,7 +320,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
assertThat(commit.getShortMessage()).isEqualTo("Create change");
PersonIdent expectedAuthor =
- changeNoteUtil.newIdent(getAccount(admin.id), c.created, serverIdent.get());
+ changeNoteUtil.newIdent(getAccount(admin.id()), c.created, serverIdent.get());
assertThat(commit.getAuthorIdent()).isEqualTo(expectedAuthor);
assertThat(commit.getCommitterIdent())
@@ -327,6 +351,62 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
@Test
+ public void createMergeChangeFailsWithConflictIfThereAreTooManyCommonPredecessors()
+ throws Exception {
+ // Create an initial commit in master.
+ Result initialCommit =
+ pushFactory
+ .create(user.newIdent(), testRepo, "initial commit", "readme.txt", "initial commit")
+ .to("refs/heads/master");
+ initialCommit.assertOkStatus();
+
+ String file = "shared.txt";
+ List<RevCommit> parents = new ArrayList<>();
+ // RecursiveMerger#MAX_BASES = 200, cannot use RecursiveMerger#MAX_BASES as it is not static.
+ int maxBases = 200;
+
+ // Create more than RecursiveMerger#MAX_BASES base commits.
+ for (int i = 1; i <= maxBases + 1; i++) {
+ parents.add(
+ testRepo
+ .commit()
+ .message("Base " + i)
+ .add(file, "content " + i)
+ .parent(initialCommit.getCommit())
+ .create());
+ }
+
+ // Create 2 branches.
+ String branchA = "branchA";
+ String branchB = "branchB";
+ createBranch(new Branch.NameKey(project, branchA));
+ createBranch(new Branch.NameKey(project, branchB));
+
+ // Push an octopus merge to both of the branches.
+ Result octopusA =
+ pushFactory
+ .create(user.newIdent(), testRepo)
+ .setParents(parents)
+ .to("refs/heads/" + branchA);
+ octopusA.assertOkStatus();
+
+ Result octopusB =
+ pushFactory
+ .create(user.newIdent(), testRepo)
+ .setParents(parents)
+ .to("refs/heads/" + branchB);
+ octopusB.assertOkStatus();
+
+ // Creating a merge commit for the 2 octopus commits fails, because they have more than
+ // RecursiveMerger#MAX_BASES common predecessors.
+ assertCreateFails(
+ newMergeChangeInput("branchA", "branchB", ""),
+ ResourceConflictException.class,
+ "Cannot create merge commit: No merge base could be determined."
+ + " Reason=TOO_MANY_MERGE_BASES.");
+ }
+
+ @Test
public void invalidSource() throws Exception {
changeInTwoBranches("branchA", "a.txt", "branchB", "b.txt");
ChangeInput in = newMergeChangeInput("branchA", "invalid", "");
@@ -429,7 +509,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
CyclicBarrier sync = new CyclicBarrier(2);
Callable<ChangeInfo> createChange =
() -> {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
sync.await();
return assertCreateSucceeds(changeInput);
};
@@ -446,6 +526,48 @@ public class CreateChangeIT extends AbstractDaemonTest {
}
}
+ @Test
+ public void createChangeOnExistingBranchNotPermitted() throws Exception {
+ createBranch(new Branch.NameKey(project, "foo"));
+ blockRead("refs/heads/*");
+ requestScopeOperations.setApiUser(user.id());
+ ChangeInput input = newChangeInput(ChangeStatus.NEW);
+ input.branch = "foo";
+
+ assertCreateFails(input, ResourceNotFoundException.class, "ref refs/heads/foo not found");
+ }
+
+ @Test
+ public void createChangeOnNonExistingBranch() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ ChangeInput input = newChangeInput(ChangeStatus.NEW);
+ input.branch = "foo";
+ input.newBranch = true;
+ assertCreateSucceeds(input);
+ }
+
+ @Test
+ public void createChangeOnNonExistingBranchNotPermitted() throws Exception {
+ blockRead("refs/heads/*");
+ requestScopeOperations.setApiUser(user.id());
+ ChangeInput input = newChangeInput(ChangeStatus.NEW);
+ input.branch = "foo";
+ // sets this option to be true to make sure permission check happened before this option could
+ // be considered.
+ input.newBranch = true;
+
+ assertCreateFails(input, ResourceNotFoundException.class, "ref refs/heads/foo not found");
+ }
+
+ @Test
+ public void createMergeChangeOnNonExistingBranchNotPossible() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ ChangeInput input = newMergeChangeInput("foo", "master", "");
+ input.newBranch = true;
+ assertCreateFails(
+ input, BadRequestException.class, "Cannot create merge: destination branch does not exist");
+ }
+
private ChangeInput newChangeInput(ChangeStatus status) {
ChangeInput in = new ChangeInput();
in.project = project.get();
@@ -459,12 +581,20 @@ public class CreateChangeIT extends AbstractDaemonTest {
private ChangeInfo assertCreateSucceeds(ChangeInput in) throws Exception {
ChangeInfo out = gApi.changes().create(in).get();
assertThat(out.project).isEqualTo(in.project);
- assertThat(out.branch).isEqualTo(in.branch);
+ assertThat(RefNames.fullName(out.branch)).isEqualTo(RefNames.fullName(in.branch));
assertThat(out.subject).isEqualTo(in.subject.split("\n")[0]);
assertThat(out.topic).isEqualTo(in.topic);
assertThat(out.status).isEqualTo(in.status);
- assertThat(out.isPrivate).isEqualTo(in.isPrivate);
- assertThat(out.workInProgress).isEqualTo(in.workInProgress);
+ if (in.isPrivate) {
+ assertThat(out.isPrivate).isTrue();
+ } else {
+ assertThat(out.isPrivate).isNull();
+ }
+ if (in.workInProgress) {
+ assertThat(out.workInProgress).isTrue();
+ } else {
+ assertThat(out.workInProgress).isNull();
+ }
assertThat(out.revisions).hasSize(1);
assertThat(out.submitted).isNull();
assertThat(in.status).isEqualTo(ChangeStatus.NEW);
@@ -481,12 +611,12 @@ public class CreateChangeIT extends AbstractDaemonTest {
// TODO(davido): Expose setting of account preferences in the API
private void setSignedOffByFooter(boolean value) throws Exception {
- RestResponse r = adminRestSession.get("/accounts/" + admin.email + "/preferences");
+ RestResponse r = adminRestSession.get("/accounts/" + admin.email() + "/preferences");
r.assertOK();
GeneralPreferencesInfo i = newGson().fromJson(r.getReader(), GeneralPreferencesInfo.class);
i.signedOffBy = value;
- r = adminRestSession.put("/accounts/" + admin.email + "/preferences", i);
+ r = adminRestSession.put("/accounts/" + admin.email() + "/preferences", i);
r.assertOK();
GeneralPreferencesInfo o = newGson().fromJson(r.getReader(), GeneralPreferencesInfo.class);
@@ -496,7 +626,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
assertThat(o.signedOffBy).isNull();
}
- resetCurrentApiUser();
+ requestScopeOperations.resetCurrentApiUser();
}
private ChangeInput newMergeChangeInput(String targetBranch, String sourceRef, String strategy) {
@@ -530,7 +660,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
// create a initial commit in master
Result initialCommit =
pushFactory
- .create(db, user.getIdent(), testRepo, "initial commit", "readme.txt", "initial commit")
+ .create(user.newIdent(), testRepo, "initial commit", "readme.txt", "initial commit")
.to("refs/heads/master");
initialCommit.assertOkStatus();
@@ -541,13 +671,13 @@ public class CreateChangeIT extends AbstractDaemonTest {
// create a commit in branchA
Result changeA =
pushFactory
- .create(db, user.getIdent(), testRepo, "change A", fileA, "A content")
+ .create(user.newIdent(), testRepo, "change A", fileA, "A content")
.to("refs/heads/" + branchA);
changeA.assertOkStatus();
// create a commit in branchB
PushOneCommit commitB =
- pushFactory.create(db, user.getIdent(), testRepo, "change B", fileB, "B content");
+ pushFactory.create(user.newIdent(), testRepo, "change B", fileB, "B content");
commitB.setParent(initialCommit.getCommit());
Result changeB = commitB.to("refs/heads/" + branchB);
changeB.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
index 940f2cfa6d..286980f392 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
@@ -22,6 +22,7 @@ import com.google.common.collect.Iterables;
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.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -29,12 +30,15 @@ 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;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.junit.Test;
public class DeleteVoteIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void deleteVoteOnChange() throws Exception {
deleteVote(false);
@@ -51,7 +55,7 @@ public class DeleteVoteIT extends AbstractDaemonTest {
PushOneCommit.Result r2 = amendChange(r.getChangeId());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
recommend(r.getChangeId());
sender.clear();
@@ -60,7 +64,7 @@ public class DeleteVoteIT extends AbstractDaemonTest {
+ r.getChangeId()
+ (onRevisionLevel ? ("/revisions/" + r2.getCommit().getName()) : "")
+ "/reviewers/"
- + user.getId().toString()
+ + user.id().toString()
+ "/votes/Code-Review";
RestResponse response = adminRestSession.delete(endPoint);
@@ -69,17 +73,17 @@ public class DeleteVoteIT extends AbstractDaemonTest {
List<FakeEmailSender.Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
FakeEmailSender.Message msg = messages.get(0);
- assertThat(msg.rcpt()).containsExactly(user.emailAddress);
- assertThat(msg.body()).contains(admin.fullName + " has removed a vote from this change.");
+ assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
+ assertThat(msg.body()).contains(admin.fullName() + " has removed a vote from this change.");
assertThat(msg.body())
- .contains("Removed Code-Review+1 by " + user.fullName + " <" + user.email + ">\n");
+ .contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");
endPoint =
"/changes/"
+ r.getChangeId()
+ (onRevisionLevel ? ("/revisions/" + r2.getCommit().getName()) : "")
+ "/reviewers/"
- + user.getId().toString()
+ + user.id().toString()
+ "/votes";
response = adminRestSession.get(endPoint);
@@ -93,10 +97,10 @@ public class DeleteVoteIT extends AbstractDaemonTest {
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
ChangeMessageInfo message = Iterables.getLast(c.messages);
- assertThat(message.author._accountId).isEqualTo(admin.getId().get());
+ assertThat(message.author._accountId).isEqualTo(admin.id().get());
assertThat(message.message).isEqualTo("Removed Code-Review+1 by User <user@example.com>\n");
assertThat(getReviewers(c.reviewers.get(REVIEWER)))
- .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+ .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
}
private Iterable<Account.Id> getReviewers(Collection<AccountInfo> r) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
index 864f08ddd4..c13df497d7 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
@@ -15,7 +15,6 @@
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.server.group.SystemGroupBackend.REGISTERED_USERS;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -26,24 +25,20 @@ 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.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.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@NoHttpd
public class HashtagsIT extends AbstractDaemonTest {
- @Before
- public void before() {
- assume().that(notesMigration.readChanges()).isTrue();
- }
-
@BeforeClass
public static void setTimeForTesting() {
TestTimeUtil.resetWithClockStep(1, SECONDS);
@@ -54,6 +49,8 @@ public class HashtagsIT extends AbstractDaemonTest {
TestTimeUtil.useSystemTime();
}
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void getNoHashtags() throws Exception {
// Get on a change with no hashtags returns an empty list.
@@ -260,7 +257,7 @@ public class HashtagsIT extends AbstractDaemonTest {
@Test
public void addHashtagWithoutPermissionNotAllowed() throws Exception {
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("edit hashtags not permitted");
addHashtags(r, "MyHashtag");
@@ -270,7 +267,7 @@ public class HashtagsIT extends AbstractDaemonTest {
public void addHashtagWithPermissionAllowed() throws Exception {
PushOneCommit.Result r = createChange();
grant(project, "refs/heads/master", Permission.EDIT_HASHTAGS, false, REGISTERED_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
addHashtags(r, "MyHashtag");
assertThatGet(r).containsExactly("MyHashtag");
assertMessage(r, "Hashtag added: MyHashtag");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
index 6555fe8307..f49d1fb156 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
@@ -20,17 +20,25 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
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.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;
import org.eclipse.jgit.junit.TestRepository;
import org.junit.Test;
public class IndexChangeIT extends AbstractDaemonTest {
+ @Inject private GroupOperations groupOperations;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void indexChange() throws Exception {
String changeId = createChange().getChangeId();
@@ -48,11 +56,12 @@ public class IndexChangeIT extends AbstractDaemonTest {
public void indexChangeAfterOwnerLosesVisibility() throws Exception {
// Create a test group with 2 users as members
TestAccount user2 = accountCreator.user2();
- String group = createGroup("test");
- gApi.groups().id(group).addMembers("admin", "user", user2.username);
+ AccountGroup.UUID groupId = groupOperations.newGroup().name("test").create();
+ String group = groupOperations.group(groupId).get().name();
+ gApi.groups().id(group).addMembers("admin", "user", user2.username());
// Create a project and restrict its visibility to the group
- Project.NameKey p = createProject("p");
+ Project.NameKey p = projectOperations.newProject().create();
try (ProjectConfigUpdate u = updateProject(p)) {
Util.allow(
u.getConfig(),
@@ -65,39 +74,39 @@ public class IndexChangeIT extends AbstractDaemonTest {
// Clone it and push a change as a regular user
TestRepository<InMemoryRepository> repo = cloneProject(p, user);
- PushOneCommit push = pushFactory.create(db, user.getIdent(), repo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), repo);
PushOneCommit.Result result = push.to("refs/for/master");
result.assertOkStatus();
- assertThat(result.getChange().change().getOwner()).isEqualTo(user.id);
+ assertThat(result.getChange().change().getOwner()).isEqualTo(user.id());
String changeId = result.getChangeId();
// User can see the change and it is mergeable
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
List<ChangeInfo> changes = gApi.changes().query(changeId).get();
assertThat(changes).hasSize(1);
assertThat(changes.get(0).mergeable).isNotNull();
// Other user can see the change and it is mergeable
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
changes = gApi.changes().query(changeId).get();
assertThat(changes).hasSize(1);
assertThat(changes.get(0).mergeable).isTrue();
// Remove the user from the group so they can no longer see the project
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.groups().id(group).removeMembers("user");
// User can no longer see the change
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
changes = gApi.changes().query(changeId).get();
assertThat(changes).isEmpty();
// Reindex the change
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(changeId).index();
// Other user can still see the change and it is still mergeable
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
changes = gApi.changes().query(changeId).get();
assertThat(changes).hasSize(1);
assertThat(changes.get(0).mergeable).isTrue();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
index 174280d3e3..0db3508382 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
@@ -48,8 +48,7 @@ public class ListChangesOptionsIT extends AbstractDaemonTest {
String subject = "Change subject";
String fileName = "a.txt";
PushOneCommit push =
- pushFactory.create(
- db, admin.getIdent(), testRepo, subject, fileName, content, baseChangeId);
+ pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content, baseChangeId);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
return r;
@@ -66,7 +65,7 @@ public class ListChangesOptionsIT extends AbstractDaemonTest {
public void currentRevision() throws Exception {
ChangeInfo c = get(changeId, CURRENT_REVISION);
assertThat(c.currentRevision).isEqualTo(commitId(2));
- assertThat(c.revisions.keySet()).containsAllIn(ImmutableSet.of(commitId(2)));
+ assertThat(c.revisions.keySet()).containsAtLeastElementsIn(ImmutableSet.of(commitId(2)));
assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3);
}
@@ -75,7 +74,7 @@ public class ListChangesOptionsIT extends AbstractDaemonTest {
ChangeInfo c = get(changeId, CURRENT_REVISION, MESSAGES);
assertThat(c.revisions).hasSize(1);
assertThat(c.currentRevision).isEqualTo(commitId(2));
- assertThat(c.revisions.keySet()).containsAllIn(ImmutableSet.of(commitId(2)));
+ assertThat(c.revisions.keySet()).containsAtLeastElementsIn(ImmutableSet.of(commitId(2)));
assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3);
}
@@ -84,7 +83,7 @@ public class ListChangesOptionsIT extends AbstractDaemonTest {
ChangeInfo c = get(changeId, ALL_REVISIONS);
assertThat(c.currentRevision).isEqualTo(commitId(2));
assertThat(c.revisions.keySet())
- .containsAllIn(ImmutableSet.of(commitId(0), commitId(1), commitId(2)));
+ .containsAtLeastElementsIn(ImmutableSet.of(commitId(0), commitId(1), commitId(2)));
assertThat(c.revisions.get(commitId(0))._number).isEqualTo(1);
assertThat(c.revisions.get(commitId(1))._number).isEqualTo(2);
assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
index 206cc685ef..2448ff881c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
@@ -19,9 +19,11 @@ import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
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.request.RequestScopeOperations;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
@@ -30,12 +32,14 @@ import com.google.gerrit.extensions.api.changes.ReviewInput;
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.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.inject.Inject;
import java.util.Arrays;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.PersonIdent;
@@ -44,6 +48,8 @@ import org.junit.Test;
@NoHttpd
public class MoveChangeIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void moveChangeWithShortRef() throws Exception {
// Move change to a different branch using short ref name
@@ -141,8 +147,8 @@ public class MoveChangeIT extends AbstractDaemonTest {
.parent(r1.getCommit())
.parent(r2.getCommit())
.message("Move change Merge Commit")
- .author(admin.getIdent())
- .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()));
+ .author(admin.newIdent())
+ .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()));
RevCommit c = commitBuilder.create();
pushHead(testRepo, "refs/for/master", false, false);
@@ -180,7 +186,7 @@ public class MoveChangeIT extends AbstractDaemonTest {
r.getChange().change().getDest().get(),
Permission.ABANDON,
systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("move not permitted");
move(r.getChangeId(), newBranch.get());
@@ -273,9 +279,9 @@ public class MoveChangeIT extends AbstractDaemonTest {
input.label(testLabelC, -1);
gApi.changes().id(changeId).current().review(input);
- assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().keySet())
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().keySet())
.containsExactly(codeReviewLabel, testLabelA, testLabelB, testLabelC);
- assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
.containsExactly((short) -2, (short) -1, (short) -1, (short) -1);
// Move the change to the 'foo' branch.
@@ -284,13 +290,13 @@ public class MoveChangeIT extends AbstractDaemonTest {
assertThat(gApi.changes().id(changeId).get().branch).isEqualTo("foo");
// 'Code-Review -2' and 'Label-A -1' will be kept.
- assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
.containsExactly((short) -2, (short) -1, (short) 0, (short) 0);
// Move the change back to 'master'.
move(changeId, "master");
assertThat(gApi.changes().id(changeId).get().branch).isEqualTo("master");
- assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
.containsExactly((short) -2, (short) -1, (short) 0, (short) 0);
}
@@ -313,9 +319,9 @@ public class MoveChangeIT extends AbstractDaemonTest {
input.label(testLabelA, -1);
gApi.changes().id(changeId).current().review(input);
- assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().keySet())
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().keySet())
.containsExactly(testLabelA);
- assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+ assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
.containsExactly((short) -1);
move(changeId, "foo");
@@ -332,6 +338,16 @@ public class MoveChangeIT extends AbstractDaemonTest {
move(r.getChangeId(), null);
}
+ @Test
+ @GerritConfig(name = "change.move", value = "false")
+ public void moveCanBeDisabledByConfig() throws Exception {
+ PushOneCommit.Result r = createChange();
+
+ exception.expect(MethodNotAllowedException.class);
+ exception.expectMessage("move changes endpoint is disabled");
+ move(r.getChangeId(), null);
+ }
+
private void move(int changeNum, String destination) throws RestApiException {
gApi.changes().id(changeNum).move(destination);
}
@@ -348,7 +364,7 @@ public class MoveChangeIT extends AbstractDaemonTest {
}
private PushOneCommit.Result createChange(String branch, String changeId) throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo, changeId);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, changeId);
PushOneCommit.Result result = push.to("refs/for/" + branch);
result.assertOkStatus();
return result;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java
new file mode 100644
index 0000000000..649c7ae107
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/change/PluginFieldsIT.java
@@ -0,0 +1,151 @@
+// 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.rest.change;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+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.json.OutputFormat;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+
+public class PluginFieldsIT extends AbstractPluginFieldsTest {
+ private static final Gson GSON = OutputFormat.JSON.newGson();
+
+ @Test
+ public void queryChangeWithNullAttribute() throws Exception {
+ getChangeWithNullAttribute(
+ id -> pluginInfoFromSingletonList(adminRestSession.get(changeQueryUrl(id))));
+ }
+
+ @Test
+ public void getChangeWithNullAttribute() throws Exception {
+ getChangeWithNullAttribute(id -> pluginInfoFromChangeInfo(adminRestSession.get(changeUrl(id))));
+ }
+
+ @Test
+ public void getChangeDetailWithNullAttribute() throws Exception {
+ getChangeWithNullAttribute(
+ id -> pluginInfoFromChangeInfo(adminRestSession.get(changeDetailUrl(id))));
+ }
+
+ @Test
+ public void queryChangeWithSimpleAttribute() throws Exception {
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromSingletonList(adminRestSession.get(changeQueryUrl(id))));
+ }
+
+ @Test
+ public void getChangeWithSimpleAttribute() throws Exception {
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromChangeInfo(adminRestSession.get(changeUrl(id))));
+ }
+
+ @Test
+ public void getChangeDetailWithSimpleAttribute() throws Exception {
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromChangeInfo(adminRestSession.get(changeDetailUrl(id))));
+ }
+
+ @Test
+ public void queryChangeWithOption() throws Exception {
+ getChangeWithOption(
+ id -> pluginInfoFromSingletonList(adminRestSession.get(changeQueryUrl(id))),
+ (id, opts) -> pluginInfoFromSingletonList(adminRestSession.get(changeQueryUrl(id, opts))));
+ }
+
+ @Test
+ public void getChangeWithOption() throws Exception {
+ getChangeWithOption(
+ id -> pluginInfoFromChangeInfo(adminRestSession.get(changeUrl(id))),
+ (id, opts) -> pluginInfoFromChangeInfo(adminRestSession.get(changeUrl(id, opts))));
+ }
+
+ @Test
+ public void getChangeDetailWithOption() throws Exception {
+ getChangeWithOption(
+ id -> pluginInfoFromChangeInfo(adminRestSession.get(changeDetailUrl(id))),
+ (id, opts) -> pluginInfoFromChangeInfo(adminRestSession.get(changeDetailUrl(id, opts))));
+ }
+
+ private String changeQueryUrl(Change.Id id) {
+ return changeQueryUrl(id, ImmutableListMultimap.of());
+ }
+
+ private String changeQueryUrl(Change.Id id, ImmutableListMultimap<String, String> opts) {
+ String url = "/changes/?q=" + id;
+ String queryString = buildQueryString(opts);
+ if (!queryString.isEmpty()) {
+ url += "&" + queryString;
+ }
+ return url;
+ }
+
+ private String changeUrl(Change.Id id) {
+ return changeUrl(id, ImmutableListMultimap.of());
+ }
+
+ private String changeUrl(Change.Id id, ImmutableListMultimap<String, String> pluginOptions) {
+ return changeUrl(id, "", pluginOptions);
+ }
+
+ private String changeDetailUrl(Change.Id id) {
+ return changeDetailUrl(id, ImmutableListMultimap.of());
+ }
+
+ private String changeDetailUrl(
+ Change.Id id, ImmutableListMultimap<String, String> pluginOptions) {
+ return changeUrl(id, "/detail", pluginOptions);
+ }
+
+ private String changeUrl(
+ Change.Id id, String suffix, ImmutableListMultimap<String, String> pluginOptions) {
+ String url = "/changes/" + project + "~" + id + suffix;
+ String queryString = buildQueryString(pluginOptions);
+ if (!queryString.isEmpty()) {
+ url += "?" + queryString;
+ }
+ return url;
+ }
+
+ private static String buildQueryString(ImmutableListMultimap<String, String> opts) {
+ return Joiner.on('&').withKeyValueSeparator('=').join(opts.entries());
+ }
+
+ @Nullable
+ private static List<MyInfo> pluginInfoFromSingletonList(RestResponse res) throws Exception {
+ res.assertOK();
+ List<Map<String, Object>> changeInfos =
+ GSON.fromJson(res.getReader(), new TypeToken<List<Map<String, Object>>>() {}.getType());
+ assertThat(changeInfos).hasSize(1);
+ return decodeRawPluginsList(GSON, changeInfos.get(0).get("plugins"));
+ }
+
+ @Nullable
+ private List<MyInfo> pluginInfoFromChangeInfo(RestResponse res) throws Exception {
+ res.assertOK();
+ Map<String, Object> changeInfo =
+ GSON.fromJson(res.getReader(), new TypeToken<Map<String, Object>>() {}.getType());
+ return decodeRawPluginsList(GSON, changeInfo.get("plugins"));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
index 0ece00a4c6..ca4288f636 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
@@ -19,12 +19,14 @@ import static com.google.common.truth.Truth.assertThat;
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.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;
@@ -34,11 +36,12 @@ import org.junit.Test;
public class PrivateByDefaultIT extends AbstractDaemonTest {
private Project.NameKey project1;
private Project.NameKey project2;
+ @Inject private ProjectOperations projectOperations;
@Before
public void setUp() throws Exception {
- project1 = createProject("project-1");
- project2 = createProject("project-2", project1);
+ project1 = projectOperations.newProject().create();
+ project2 = projectOperations.newProject().parent(project1).create();
setPrivateByDefault(project1, InheritableBoolean.FALSE);
}
@@ -118,7 +121,7 @@ public class PrivateByDefaultIT extends AbstractDaemonTest {
TestRepository<InMemoryRepository> testRepo = cloneProject(project2);
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%private");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
result.assertErrorStatus();
}
@@ -127,14 +130,14 @@ public class PrivateByDefaultIT extends AbstractDaemonTest {
public void pushDraftsWithPrivateByDefaultAndDisablePrivateChangesTrue() throws Exception {
setPrivateByDefault(project2, InheritableBoolean.TRUE);
- RevCommit initialHead = getRemoteHead();
+ RevCommit initialHead = getRemoteHead(project2, "master");
TestRepository<InMemoryRepository> testRepo = cloneProject(project2);
PushOneCommit.Result result =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%draft");
+ pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
result.assertErrorStatus();
testRepo.reset(initialHead);
- result = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/drafts/master");
+ result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
result.assertErrorStatus();
}
@@ -151,7 +154,7 @@ public class PrivateByDefaultIT extends AbstractDaemonTest {
private PushOneCommit.Result createChange(Project.NameKey proj, String ref) throws Exception {
TestRepository<InMemoryRepository> testRepo = cloneProject(proj);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result result = push.to(ref);
result.assertOkStatus();
return result;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index 8160d9a2a6..2ad4acabc6 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -15,7 +15,6 @@
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.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
@@ -31,19 +30,12 @@ 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.extensions.restapi.ResourceConflictException;
-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.change.TestSubmitInput;
import com.google.gerrit.server.git.ChangeMessageModifier;
import com.google.gerrit.server.submit.CommitMergeStatus;
import com.google.inject.Inject;
import java.util.List;
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.Test;
public class SubmitByCherryPickIT extends AbstractSubmit {
@@ -84,8 +76,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
assertCurrentRevision(change2.getChangeId(), 2, newHead);
assertSubmitter(change2.getChangeId(), 1);
assertSubmitter(change2.getChangeId(), 2);
- assertPersonEquals(admin.getIdent(), newHead.getAuthorIdent());
- assertPersonEquals(admin.getIdent(), newHead.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), newHead.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), newHead.getCommitterIdent());
assertRefUpdatedEvents(initialHead, headAfterFirstSubmit, headAfterFirstSubmit, newHead);
assertChangeMergedEvents(
@@ -98,16 +90,8 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
RegistrationHandle handle =
changeMessageModifiers.add(
"gerrit",
- new ChangeMessageModifier() {
- @Override
- public String onSubmit(
- String newCommitMessage,
- RevCommit original,
- RevCommit mergeTip,
- Branch.NameKey destination) {
- return newCommitMessage + "Custom: " + destination.get();
- }
- });
+ (newCommitMessage, original, mergeTip, destination) ->
+ newCommitMessage + "Custom: " + destination.get());
try {
submit(change.getChangeId());
} finally {
@@ -389,76 +373,4 @@ public class SubmitByCherryPickIT extends AbstractSubmit {
change2.getChangeId(),
headAfterFirstSubmit.name());
}
-
- @Test
- public void repairChangeStateAfterFailure() throws Exception {
- // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
- assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
- RevCommit initialHead = getRemoteHead();
- PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
- submit(change.getChangeId());
-
- RevCommit headAfterFirstSubmit = getRemoteHead();
- testRepo.reset(initialHead);
- PushOneCommit.Result change2 = createChange("Change 2", "b.txt", "other content");
- Change.Id id2 = change2.getChange().getId();
- TestSubmitInput failInput = new TestSubmitInput();
- failInput.failAfterRefUpdates = true;
- submit(
- change2.getChangeId(),
- failInput,
- ResourceConflictException.class,
- "Failing after ref updates");
- RevCommit headAfterFailedSubmit = getRemoteHead();
-
- // Bad: ref advanced but change wasn't updated.
- PatchSet.Id psId1 = new PatchSet.Id(id2, 1);
- PatchSet.Id psId2 = new PatchSet.Id(id2, 2);
- ChangeInfo info = gApi.changes().id(id2.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.NEW);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
- assertThat(getPatchSet(psId2)).isNull();
-
- ObjectId rev2;
- try (Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
- ObjectId rev1 = repo.exactRef(psId1.toRefName()).getObjectId();
- assertThat(rev1).isNotNull();
-
- rev2 = repo.exactRef(psId2.toRefName()).getObjectId();
- assertThat(rev2).isNotNull();
- assertThat(rev2).isNotEqualTo(rev1);
- assertThat(rw.parseCommit(rev2).getParent(0)).isEqualTo(headAfterFirstSubmit);
-
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
- }
-
- submit(change2.getChangeId());
-
- // Change status and patch set entities were updated, and branch tip stayed
- // the same.
- RevCommit headAfterSecondSubmit = getRemoteHead();
- assertThat(headAfterSecondSubmit).isEqualTo(headAfterFailedSubmit);
- info = gApi.changes().id(id2.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(2);
- PatchSet ps2 = getPatchSet(psId2);
- assertThat(ps2).isNotNull();
- assertThat(ps2.getRevision().get()).isEqualTo(rev2.name());
- assertThat(Iterables.getLast(info.messages).message)
- .isEqualTo(
- "Change has been successfully cherry-picked as " + rev2.name() + " by Administrator");
-
- try (Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev2);
- }
-
- assertRefUpdatedEvents(initialHead, headAfterFirstSubmit);
- assertChangeMergedEvents(
- change.getChangeId(),
- headAfterFirstSubmit.name(),
- change2.getChangeId(),
- headAfterSecondSubmit.name());
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
index ea8b98a839..974180c83f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -15,26 +15,16 @@
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.GitUtil.pushHead;
-import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.GitUtil;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.extensions.client.ChangeStatus;
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.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.change.TestSubmitInput;
import java.util.Map;
-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.eclipse.jgit.transport.PushResult;
import org.junit.Test;
@@ -80,8 +70,8 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
assertSubmitter(change.getChangeId(), 1);
assertSubmitter(change2.getChangeId(), 1);
assertSubmitter(change3.getChangeId(), 1);
- assertPersonEquals(admin.getIdent(), updatedHead.getAuthorIdent());
- assertPersonEquals(admin.getIdent(), updatedHead.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), updatedHead.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), updatedHead.getCommitterIdent());
assertSubmittedTogether(id1, id3, id2, id1);
assertSubmittedTogether(id2, id3, id2, id1);
assertSubmittedTogether(id3, id3, id2, id1);
@@ -146,52 +136,6 @@ public class SubmitByFastForwardIT extends AbstractSubmit {
}
@Test
- public void repairChangeStateAfterFailure() throws Exception {
- // In NoteDb-only mode, repo and meta updates are atomic (at least in InMemoryRepository).
- assume().that(notesMigration.disableChangeReviewDb()).isFalse();
-
- PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
- Change.Id id = change.getChange().getId();
- TestSubmitInput failInput = new TestSubmitInput();
- failInput.failAfterRefUpdates = true;
- submit(
- change.getChangeId(),
- failInput,
- ResourceConflictException.class,
- "Failing after ref updates");
-
- // Bad: ref advanced but change wasn't updated.
- PatchSet.Id psId = new PatchSet.Id(id, 1);
- ChangeInfo info = gApi.changes().id(id.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.NEW);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
-
- ObjectId rev;
- try (Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
- rev = repo.exactRef(psId.toRefName()).getObjectId();
- assertThat(rev).isNotNull();
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev);
- }
-
- submit(change.getChangeId());
-
- // Change status was updated, and branch tip stayed the same.
- info = gApi.changes().id(id.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
- assertThat(info.revisions.get(info.currentRevision)._number).isEqualTo(1);
- assertThat(Iterables.getLast(info.messages).message)
- .isEqualTo("Change has been successfully merged by Administrator");
-
- try (Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef("refs/heads/master").getObjectId()).isEqualTo(rev);
- }
-
- assertRefUpdatedEvents();
- assertChangeMergedEvents(change.getChangeId(), getRemoteHead().name());
- }
-
- @Test
public void submitSameCommitsAsInExperimentalBranch() throws Exception {
RevCommit initialHead = getRemoteHead();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
index 4af27ab6e1..9bc5a2f592 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
@@ -38,7 +38,7 @@ public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
assertThat(headAfterSubmit.getParent(0)).isEqualTo(initialHead);
assertThat(headAfterSubmit.getParent(1)).isEqualTo(change.getCommit());
assertSubmitter(change.getChangeId(), 1);
- assertPersonEquals(admin.getIdent(), headAfterSubmit.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), headAfterSubmit.getAuthorIdent());
assertPersonEquals(serverIdent.get(), headAfterSubmit.getCommitterIdent());
assertRefUpdatedEvents(initialHead, headAfterSubmit);
@@ -82,7 +82,7 @@ public class SubmitByMergeAlwaysIT extends AbstractSubmitByMerge {
assertThat(headAfterSecondSubmit.getParent(0).getShortMessage())
.isEqualTo(headAfterFirstSubmit.getShortMessage());
assertThat(headAfterSecondSubmit.getParent(0).getId()).isEqualTo(headAfterFirstSubmit.getId());
- assertPersonEquals(admin.getIdent(), headAfterSecondSubmit.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), headAfterSecondSubmit.getAuthorIdent());
assertPersonEquals(serverIdent.get(), headAfterSecondSubmit.getCommitterIdent());
assertRefUpdatedEvents(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
index c1dda00aaf..5ebcd85ae3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
@@ -15,25 +15,28 @@
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.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
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.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.changes.SubmitInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
-import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.SubmitType;
+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;
import java.nio.file.Files;
@@ -51,6 +54,8 @@ import org.eclipse.jgit.transport.RefSpec;
import org.junit.Test;
public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Override
protected SubmitType getSubmitType() {
@@ -66,8 +71,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
assertThat(updatedHead.getId()).isEqualTo(change.getCommit());
assertThat(updatedHead.getParent(0)).isEqualTo(initialHead);
assertSubmitter(change.getChangeId(), 1);
- assertPersonEquals(admin.getIdent(), updatedHead.getAuthorIdent());
- assertPersonEquals(admin.getIdent(), updatedHead.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), updatedHead.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), updatedHead.getCommitterIdent());
assertRefUpdatedEvents(initialHead, updatedHead);
assertChangeMergedEvents(change.getChangeId(), updatedHead.name());
@@ -95,8 +100,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
assertThat(headAfterFirstSubmit.getShortMessage())
.isEqualTo(change2.getCommit().getShortMessage());
assertThat(headAfterFirstSubmit.getParent(0).getId()).isEqualTo(initialHead.getId());
- assertPersonEquals(admin.getIdent(), headAfterFirstSubmit.getAuthorIdent());
- assertPersonEquals(admin.getIdent(), headAfterFirstSubmit.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), headAfterFirstSubmit.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), headAfterFirstSubmit.getCommitterIdent());
// We need to merge changes 3, 4 and 5.
approve(change3.getChangeId());
@@ -109,7 +114,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
assertThat(headAfterSecondSubmit.getParent(0).getShortMessage())
.isEqualTo(change2.getCommit().getShortMessage());
- assertPersonEquals(admin.getIdent(), headAfterSecondSubmit.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), headAfterSecondSubmit.getAuthorIdent());
assertPersonEquals(serverIdent.get(), headAfterSecondSubmit.getCommitterIdent());
// First change stays untouched.
@@ -132,9 +137,9 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
@Test
public void submitChangesAcrossRepos() throws Exception {
- Project.NameKey p1 = createProject("project-where-we-submit");
- Project.NameKey p2 = createProject("project-impacted-via-topic");
- Project.NameKey p3 = createProject("project-impacted-indirectly-via-topic");
+ 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");
@@ -209,9 +214,9 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
@Test
public void submitChangesAcrossReposBlocked() throws Exception {
- Project.NameKey p1 = createProject("project-where-we-submit");
- Project.NameKey p2 = createProject("project-impacted-via-topic");
- Project.NameKey p3 = createProject("project-impacted-indirectly-via-topic");
+ Project.NameKey p1 = projectOperations.newProject().create();
+ Project.NameKey p2 = projectOperations.newProject().create();
+ Project.NameKey p3 = projectOperations.newProject().create();
TestRepository<?> repo1 = cloneProject(p1);
TestRepository<?> repo2 = cloneProject(p2);
@@ -388,7 +393,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
"3",
"a-topic-here");
- Project.NameKey p3 = createProject("project-related-to-change3");
+ Project.NameKey p3 = projectOperations.newProject().create();
TestRepository<?> repo3 = cloneProject(p3);
RevCommit repo3Head = getRemoteHead(p3, "master");
PushOneCommit.Result change3b =
@@ -440,8 +445,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
// Push a change to master
- PushOneCommit push =
- pushFactory.create(db, user.getIdent(), testRepo, "small fix", "a.txt", "2");
+ PushOneCommit push = pushFactory.create(user.newIdent(), testRepo, "small fix", "a.txt", "2");
PushOneCommit.Result change = push.to("refs/for/master");
submit(change.getChangeId());
RevCommit headAfterFirstSubmit = getRemoteLog(project, "master").get(0);
@@ -493,8 +497,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
// Propose a change for master, but leave it open for master!
- PushOneCommit change =
- pushFactory.create(db, user.getIdent(), testRepo, "small fix", "a.txt", "2");
+ PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "small fix", "a.txt", "2");
PushOneCommit.Result change2result = change.to("refs/for/master");
// Now cherry pick to stable
@@ -531,13 +534,13 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
@Test
public void dependencyOnOutdatedPatchSetPreventsMerge() throws Exception {
// Create a change
- PushOneCommit change = pushFactory.create(db, user.getIdent(), testRepo, "fix", "a.txt", "foo");
+ PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
PushOneCommit.Result changeResult = change.to("refs/for/master");
PatchSet.Id patchSetId = changeResult.getPatchSetId();
// Create a successor change.
PushOneCommit change2 =
- pushFactory.create(db, user.getIdent(), testRepo, "feature", "b.txt", "bar");
+ pushFactory.create(user.newIdent(), testRepo, "feature", "b.txt", "bar");
PushOneCommit.Result change2Result = change2.to("refs/for/master");
// Create new patch set for first change.
@@ -573,12 +576,12 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
@Test
public void dependencyOnDeletedChangePreventsMerge() throws Exception {
// Create a change
- PushOneCommit change = pushFactory.create(db, user.getIdent(), testRepo, "fix", "a.txt", "foo");
+ PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
PushOneCommit.Result changeResult = change.to("refs/for/master");
// Create a successor change.
PushOneCommit change2 =
- pushFactory.create(db, user.getIdent(), testRepo, "feature", "b.txt", "bar");
+ pushFactory.create(user.newIdent(), testRepo, "feature", "b.txt", "bar");
PushOneCommit.Result change2Result = change2.to("refs/for/master");
// Delete first change.
@@ -596,7 +599,9 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
+ " depends on commit "
+ changeResult.getCommit().name()
+ " which cannot be merged."
- + " Is the change of this commit not visible or was it deleted?");
+ + " Is the change of this commit not visible to '"
+ + admin.username()
+ + "' or was it deleted?");
assertRefUpdatedEvents();
assertChangeMergedEvents();
@@ -608,14 +613,13 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
grant(project, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
// Create a change
- PushOneCommit change =
- pushFactory.create(db, admin.getIdent(), testRepo, "fix", "a.txt", "foo");
+ PushOneCommit change = pushFactory.create(admin.newIdent(), testRepo, "fix", "a.txt", "foo");
PushOneCommit.Result changeResult = change.to("refs/for/master");
approve(changeResult.getChangeId());
// Create a successor change.
PushOneCommit change2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "feature", "b.txt", "bar");
+ pushFactory.create(admin.newIdent(), testRepo, "feature", "b.txt", "bar");
PushOneCommit.Result change2Result = change2.to("refs/for/master");
// Move the first change to a destination branch that is non-visible to user so that user cannot
@@ -628,7 +632,7 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
gApi.changes().id(changeResult.getChangeId()).move(secretBranch.get());
block(secretBranch.get(), "read", ANONYMOUS_USERS);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// Verify that user cannot see the first change.
try {
@@ -650,33 +654,34 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
+ " depends on commit "
+ changeResult.getCommit().name()
+ " which cannot be merged."
- + " Is the change of this commit not visible or was it deleted?");
+ + " Is the change of this commit not visible to '"
+ + user.username()
+ + "' or was it deleted?");
assertRefUpdatedEvents();
assertChangeMergedEvents();
}
@Test
- public void dependencyOnHiddenChangeShouldPreventMergeButDoesnt() throws Exception {
+ 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);
// Create a change
- PushOneCommit change =
- pushFactory.create(db, admin.getIdent(), testRepo, "fix", "a.txt", "foo");
+ PushOneCommit change = pushFactory.create(admin.newIdent(), testRepo, "fix", "a.txt", "foo");
PushOneCommit.Result changeResult = change.to("refs/for/master");
approve(changeResult.getChangeId());
// Create a successor change.
PushOneCommit change2 =
- pushFactory.create(db, admin.getIdent(), testRepo, "feature", "b.txt", "bar");
+ pushFactory.create(admin.newIdent(), testRepo, "feature", "b.txt", "bar");
PushOneCommit.Result change2Result = change2.to("refs/for/master");
approve(change2Result.getChangeId());
// Mark the first change private so that it's not visible to user.
gApi.changes().id(changeResult.getChangeId()).setPrivate(true, "nobody should see this");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// Verify that user cannot see the first change.
try {
@@ -686,23 +691,84 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
assertThat(e.getMessage()).isEqualTo("Not found: " + changeResult.getChangeId());
}
- // Submit the second change which has a dependency on the first change which is not visible to
- // the user. We would expect the submit to fail, but instead the submit succeeds and the hidden
- // change gets submitted too.
- // TODO(ekempin): Make this submit fail.
- gApi.changes().id(change2Result.getChangeId()).current().submit(new SubmitInput());
-
- // Verify that both changes have been submitted.
- setApiUser(admin);
- assertThat(gApi.changes().id(changeResult.getChangeId()).get().status)
- .isEqualTo(ChangeStatus.MERGED);
- assertThat(gApi.changes().id(change2Result.getChangeId()).get().status)
- .isEqualTo(ChangeStatus.MERGED);
+ // 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");
+ }
+ assertRefUpdatedEvents();
+ assertChangeMergedEvents();
+ }
+
+ @Test
+ public void dependencyOnHiddenChangeUsingTopicPreventsMerge() throws Exception {
+ // 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)
+ // |
+ // (c2a) <= private
+ assume().that(isSubmitWholeTopicEnabled()).isTrue();
+
+ 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);
+
+ TestRepository<?> repo1 = cloneProject(p1);
+ TestRepository<?> repo2 = cloneProject(p2);
+
+ PushOneCommit.Result change1 =
+ createChange(repo1, "master", "A fresh change in repo1", "a.txt", "1", "topic-to-submit");
+ approve(change1.getChangeId());
+ PushOneCommit push =
+ pushFactory.create(admin.newIdent(), repo2, "An ancestor change in repo2", "a.txt", "2");
+ PushOneCommit.Result change2a = push.to("refs/for/master");
+ approve(change2a.getChangeId());
+ PushOneCommit.Result change2b =
+ createChange(
+ repo2, "master", "A topic-linked change in repo2", "a.txt", "2", "topic-to-submit");
+ approve(change2b.getChangeId());
+
+ // Mark change2a private so that it's not visible to user.
+ gApi.changes().id(change2a.getChangeId()).setPrivate(true, "nobody should see this");
+
+ 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());
+ }
+
+ // 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");
+ }
+ assertRefUpdatedEvents();
+ assertChangeMergedEvents();
}
@Test
public void testPreviewSubmitTgz() throws Exception {
- Project.NameKey p1 = createProject("project-name");
+ Project.NameKey p1 = projectOperations.newProject().create();
TestRepository<?> repo1 = cloneProject(p1);
PushOneCommit.Result change1 = createChange(repo1, "master", "test", "a.txt", "1", "topic");
@@ -727,6 +793,6 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge {
untarredFiles.add(entry.getName());
}
}
- assertThat(untarredFiles).containsExactly(name("project-name") + ".git");
+ assertThat(untarredFiles).containsExactly(p1.get() + ".git");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index 3d8d06ef75..eb8dea572d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -15,26 +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 com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.common.FooterConstants;
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.reviewdb.client.Branch;
+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.List;
+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;
@Override
protected SubmitType getSubmitType() {
@@ -55,8 +64,8 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
assertCurrentRevision(change.getChangeId(), 2, head);
assertSubmitter(change.getChangeId(), 1);
assertSubmitter(change.getChangeId(), 2);
- assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
- assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), head.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), head.getCommitterIdent());
assertRefUpdatedEvents(oldHead, head);
assertChangeMergedEvents(change.getChangeId(), head.name());
}
@@ -80,43 +89,86 @@ public class SubmitByRebaseAlwaysIT extends AbstractSubmitByRebase {
}
@Test
- @TestProjectInput(useContentMerge = InheritableBoolean.TRUE)
- public void changeMessageOnSubmit() throws Exception {
- PushOneCommit.Result change1 = createChange();
- PushOneCommit.Result change2 = createChange();
+ public void rebaseInvokesChangeMessageModifiers() throws Exception {
+ 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";
+
+ try (AutoCloseable ignored = installChangeMessageModifiers(modifier1, modifier2, 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();
+
+ assertThat(gApi.changes().id(cd2.getId().get()).revision(2).commit(false).message)
+ .isEqualTo(
+ "Change 2\n\n"
+ + ("Change-Id: " + cd2.change().getKey() + "\n")
+ + ("Reviewed-on: "
+ + urlFormatter.get().getChangeViewUrl(project, cd2.getId()).get()
+ + "\n")
+ + "Reviewed-by: Administrator <admin@example.com>\n"
+ + ("This-change-before-rebase: " + change2Ps1Commit + "\n")
+ + ("Previous-step-tip: " + change1CurrentCommit + "\n")
+ + "Dest: master\n");
+ }
+ }
+
+ @Test
+ public void failingChangeMessageModifierShortCircuits() throws Exception {
+ 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");
+ }
+ }
+ }
+
+ @Test
+ public void changeMessageModifierReturningNullShortCircuits() throws Exception {
+ 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");
+ }
+ }
+ }
- RegistrationHandle handle =
- changeMessageModifiers.add(
- "gerrit",
- new ChangeMessageModifier() {
- @Override
- public String onSubmit(
- String newCommitMessage,
- RevCommit original,
- RevCommit mergeTip,
- Branch.NameKey destination) {
- List<String> custom = mergeTip.getFooterLines("Custom");
- if (!custom.isEmpty()) {
- newCommitMessage += "Custom-Parent: " + custom.get(0) + "\n";
- }
- return newCommitMessage + "Custom: " + destination.get();
- }
- });
- try {
- // change1 is a fast-forward, but should be rebased in cherry pick style
- // anyway, making change2 not a fast-forward, requiring a rebase.
- approve(change1.getChangeId());
- submit(change2.getChangeId());
- } finally {
- handle.remove();
+ 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]));
}
- // ... but both changes should get custom footers.
- assertThat(getCurrentCommit(change1).getFooterLines("Custom"))
- .containsExactly("refs/heads/master");
- assertThat(getCurrentCommit(change2).getFooterLines("Custom"))
- .containsExactly("refs/heads/master");
- assertThat(getCurrentCommit(change2).getFooterLines("Custom-Parent"))
- .containsExactly("refs/heads/master");
+ return () -> {
+ while (!handles.isEmpty()) {
+ handles.pop().remove();
+ }
+ };
}
private void assertLatestRevisionHasFooters(PushOneCommit.Result change) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
index 19f170643d..7bb31a0197 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
@@ -42,8 +42,8 @@ public class SubmitByRebaseIfNecessaryIT extends AbstractSubmitByRebase {
assertApproved(change.getChangeId());
assertCurrentRevision(change.getChangeId(), 1, head);
assertSubmitter(change.getChangeId(), 1);
- assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
- assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
+ assertPersonEquals(admin.newIdent(), head.getAuthorIdent());
+ assertPersonEquals(admin.newIdent(), head.getCommitterIdent());
assertRefUpdatedEvents(oldHead, head);
assertChangeMergedEvents(change.getChangeId(), head.name());
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
index e9ac07a481..87fc9f6b72 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
@@ -29,7 +29,6 @@ import com.google.gerrit.server.restapi.change.Submit;
import com.google.gerrit.server.submit.ChangeSet;
import com.google.gerrit.server.submit.MergeSuperSet;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -305,9 +304,9 @@ public class SubmitResolvingMergeCommitIT extends AbstractDaemonTest {
}
private void assertChangeSetMergeable(ChangeData change, boolean expected)
- throws MissingObjectException, IncorrectObjectTypeException, IOException, OrmException,
+ throws MissingObjectException, IncorrectObjectTypeException, IOException,
PermissionBackendException {
- ChangeSet cs = mergeSuperSet.get().completeChangeSet(db, change.change(), user(admin));
+ ChangeSet cs = mergeSuperSet.get().completeChangeSet(change.change(), user(admin));
assertThat(submit.unmergeableChanges(cs).isEmpty()).isEqualTo(expected);
}
@@ -333,7 +332,7 @@ public class SubmitResolvingMergeCommitIT extends AbstractDaemonTest {
List<RevCommit> parents,
String ref)
throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), repo, subject, fileName, content);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), repo, subject, fileName, content);
if (!parents.isEmpty()) {
push.setParents(parents);
@@ -351,7 +350,7 @@ public class SubmitResolvingMergeCommitIT extends AbstractDaemonTest {
private PushOneCommit.Result createChange(TestRepository<?> repo, String subject)
throws Exception {
- return createChange(repo, subject, "x", "x", new ArrayList<RevCommit>(), "refs/for/master");
+ return createChange(repo, subject, "x", "x", new ArrayList<>(), "refs/for/master");
}
private PushOneCommit.Result createChange(
@@ -366,8 +365,7 @@ public class SubmitResolvingMergeCommitIT extends AbstractDaemonTest {
@Override
protected PushOneCommit.Result createChange(String subject) throws Exception {
- return createChange(
- testRepo, subject, "", "", Collections.<RevCommit>emptyList(), "refs/for/master");
+ return createChange(testRepo, subject, "", "", Collections.emptyList(), "refs/for/master");
}
private PushOneCommit.Result createChange(String subject, List<RevCommit> parents)
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index b0a89c35a0..9bbe1dd9c4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -14,6 +14,7 @@
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.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static java.util.stream.Collectors.toList;
@@ -23,31 +24,35 @@ import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.TestAccount;
+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.data.GlobalCapability;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
-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.reviewdb.client.Project;
-import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.restapi.group.CreateGroup;
import com.google.inject.Inject;
-import java.util.Arrays;
import java.util.List;
+import java.util.Set;
+import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Test;
public class SuggestReviewersIT extends AbstractDaemonTest {
- @Inject private CreateGroup createGroup;
+ @Inject private AccountOperations accountOperations;
+ @Inject private GroupOperations groupOperations;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
- private InternalGroup group1;
- private InternalGroup group2;
- private InternalGroup group3;
+ private AccountGroup.UUID group1;
+ private AccountGroup.UUID group2;
+ private AccountGroup.UUID group3;
private TestAccount user1;
private TestAccount user2;
@@ -56,14 +61,16 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
@Before
public void setUp() throws Exception {
- group1 = newGroup("users1");
- group2 = newGroup("users2");
- group3 = newGroup("users3");
-
- user1 = user("user1", "First1 Last1", group1);
- user2 = user("user2", "First2 Last2", group2);
- user3 = user("user3", "First3 Last3", group1, group2);
+ user1 = user("user1", "First1 Last1");
+ user2 = user("user2", "First2 Last2");
+ user3 = user("user3", "First3 Last3");
user4 = user("jdoe", "John Doe", "JDOE");
+
+ group1 =
+ groupOperations.newGroup().name(name("users1")).members(user1.id(), user3.id()).create();
+ group2 =
+ groupOperations.newGroup().name(name("users2")).members(user2.id(), user3.id()).create();
+ group3 = groupOperations.newGroup().name(name("users3")).members(user1.id()).create();
}
@Test
@@ -105,7 +112,8 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
assertReviewers(
reviewers, ImmutableList.of(user1, user2, user3), ImmutableList.of(group1, group2));
- reviewers = suggestReviewers(changeId, group3.getName(), 10);
+ String group3Name = groupOperations.group(group3).get().name();
+ reviewers = suggestReviewers(changeId, group3Name, 10);
assertReviewers(reviewers, ImmutableList.of(), ImmutableList.of(group3));
// Suggested accounts are ordered by activity. All users have no activity,
@@ -116,7 +124,9 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
assertThat(reviewers.get(0).account).isNotNull();
assertThat(ImmutableList.of(reviewers.get(0).account._accountId))
.containsAnyIn(
- ImmutableList.of(user1, user2, user3).stream().map(u -> u.id.get()).collect(toList()));
+ ImmutableList.of(user1, user2, user3).stream()
+ .map(u -> u.id().get())
+ .collect(toList()));
}
@Test
@@ -125,23 +135,23 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
List<SuggestedReviewerInfo> reviewers;
- reviewers = suggestReviewers(changeId, user2.username, 2);
+ reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).hasSize(1);
- assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+ assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
- setApiUser(user1);
- reviewers = suggestReviewers(changeId, user2.fullName, 2);
+ requestScopeOperations.setApiUser(user1.id());
+ reviewers = suggestReviewers(changeId, user2.fullName(), 2);
assertThat(reviewers).isEmpty();
- setApiUser(user2);
- reviewers = suggestReviewers(changeId, user2.username, 2);
+ requestScopeOperations.setApiUser(user2.id());
+ reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).hasSize(1);
- assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+ assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
- setApiUser(user3);
- reviewers = suggestReviewers(changeId, user2.username, 2);
+ requestScopeOperations.setApiUser(user3.id());
+ reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).hasSize(1);
- assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+ assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
}
@Test
@@ -149,10 +159,10 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
List<SuggestedReviewerInfo> reviewers;
- setApiUser(user3);
+ requestScopeOperations.setApiUser(user3.id());
block("refs/*", "read", ANONYMOUS_USERS);
- allow("refs/*", "read", group1.getGroupUUID());
- reviewers = suggestReviewers(changeId, user2.username, 2);
+ allow("refs/*", "read", group1);
+ reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).isEmpty();
}
@@ -162,15 +172,16 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
String changeId = createChange().getChangeId();
List<SuggestedReviewerInfo> reviewers;
- setApiUser(user1);
- reviewers = suggestReviewers(changeId, user2.username, 2);
+ requestScopeOperations.setApiUser(user1.id());
+ reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).isEmpty();
- setApiUser(user1); // Clear cached group info.
- allowGlobalCapabilities(group1.getGroupUUID(), GlobalCapability.VIEW_ALL_ACCOUNTS);
- reviewers = suggestReviewers(changeId, user2.username, 2);
+ // Clear cached group info.
+ requestScopeOperations.setApiUser(user1.id());
+ allowGlobalCapabilities(group1, GlobalCapability.VIEW_ALL_ACCOUNTS);
+ reviewers = suggestReviewers(changeId, user2.username(), 2);
assertThat(reviewers).hasSize(1);
- assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+ assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
}
@Test
@@ -216,44 +227,38 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
reviewers = suggestReviewers(changeId, name("user"));
assertThat(reviewers).hasSize(6);
- reviewers = suggestReviewers(changeId, user1.username);
+ reviewers = suggestReviewers(changeId, user1.username());
assertThat(reviewers).hasSize(1);
reviewers = suggestReviewers(changeId, "example.com");
assertThat(reviewers).hasSize(5);
- reviewers = suggestReviewers(changeId, user1.email);
+ reviewers = suggestReviewers(changeId, user1.email());
assertThat(reviewers).hasSize(1);
- reviewers = suggestReviewers(changeId, user1.username + " example");
+ reviewers = suggestReviewers(changeId, user1.username() + " example");
assertThat(reviewers).hasSize(1);
- reviewers = suggestReviewers(changeId, user4.email.toLowerCase());
+ reviewers = suggestReviewers(changeId, user4.email().toLowerCase());
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.get(0).account.email).isEqualTo(user4.email);
+ assertThat(reviewers.get(0).account.email).isEqualTo(user4.email());
}
@Test
public void suggestReviewersWithoutLimitOptionSpecified() throws Exception {
String changeId = createChange().getChangeId();
- String query = user3.username;
+ String query = user3.username();
List<SuggestedReviewerInfo> suggestedReviewerInfos =
gApi.changes().id(changeId).suggestReviewers(query).get();
assertThat(suggestedReviewerInfos).hasSize(1);
}
@Test
- @GerritConfig(name = "addreviewer.maxAllowed", value = "2")
+ @GerritConfig(name = "addreviewer.maxAllowed", value = "1")
@GerritConfig(name = "addreviewer.maxWithoutConfirmation", value = "1")
- public void suggestReviewersGroupSizeConsiderations() throws Exception {
- InternalGroup largeGroup = newGroup("large");
- InternalGroup mediumGroup = newGroup("medium");
-
- // Both groups have Administrator as a member. Add two users to large
- // group to push it past maxAllowed, and one to medium group to push it
- // past maxWithoutConfirmation.
- user("individual 0", "Test0 Last0", largeGroup, mediumGroup);
- user("individual 1", "Test1 Last1", largeGroup);
+ public void confirmationIsNeverRequestedForAccounts() throws Exception {
+ user("individual 0", "Test0 Last0");
+ user("individual 1", "Test1 Last1");
String changeId = createChange().getChangeId();
List<SuggestedReviewerInfo> reviewers;
@@ -265,60 +270,107 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
reviewer = reviewers.get(0);
assertThat(reviewer.count).isEqualTo(1);
assertThat(reviewer.confirm).isNull();
+ }
+
+ @Test
+ @GerritConfig(name = "addreviewer.maxAllowed", value = "2")
+ @GerritConfig(name = "addreviewer.maxWithoutConfirmation", value = "1")
+ public void suggestReviewersGroupSizeConsiderations() throws Exception {
+ AccountGroup.UUID largeGroup = createGroupWithArbitraryMembers(3);
+ String largeGroupName = groupOperations.group(largeGroup).get().name();
+ AccountGroup.UUID mediumGroup = createGroupWithArbitraryMembers(2);
+ String mediumGroupName = groupOperations.group(mediumGroup).get().name();
+
+ String changeId = createChange().getChangeId();
+ List<SuggestedReviewerInfo> reviewers;
+ SuggestedReviewerInfo reviewer;
// Large group should never be suggested.
- reviewers = suggestReviewers(changeId, largeGroup.getName(), 10);
+ reviewers = suggestReviewers(changeId, largeGroupName, 10);
assertThat(reviewers).isEmpty();
// Medium group should be suggested with appropriate count and confirm.
- reviewers = suggestReviewers(changeId, mediumGroup.getName(), 10);
+ reviewers = suggestReviewers(changeId, mediumGroupName, 10);
assertThat(reviewers).hasSize(1);
reviewer = reviewers.get(0);
- assertThat(reviewer.group.name).isEqualTo(mediumGroup.getName());
+ assertThat(reviewer.group.id).isEqualTo(mediumGroup.get());
assertThat(reviewer.count).isEqualTo(2);
assertThat(reviewer.confirm).isTrue();
}
@Test
+ @GerritConfig(name = "addreviewer.maxAllowed", value = "20")
+ @GerritConfig(name = "addreviewer.maxWithoutConfirmation", value = "0")
+ public void confirmationIsNotNecessaryForLargeGroupWhenLimitIsRemoved() throws Exception {
+ String changeId = createChange().getChangeId();
+ int numMembers = 15;
+ AccountGroup.UUID largeGroup = createGroupWithArbitraryMembers(numMembers);
+ String groupName = groupOperations.group(largeGroup).get().name();
+
+ List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, groupName, 10);
+
+ assertThat(reviewers).hasSize(1);
+ SuggestedReviewerInfo reviewer = Iterables.getOnlyElement(reviewers);
+ assertThat(reviewer.group.id).isEqualTo(largeGroup.get());
+ // Confirmation should not be necessary.
+ assertThat(reviewer.confirm).isNull();
+ }
+
+ @Test
+ @GerritConfig(name = "addreviewer.maxAllowed", value = "0")
+ public void largeGroupIsSuggestedWhenLimitIsRemoved() throws Exception {
+ String changeId = createChange().getChangeId();
+ int numMembers = 30;
+ AccountGroup.UUID largeGroup = createGroupWithArbitraryMembers(numMembers);
+ String groupName = groupOperations.group(largeGroup).get().name();
+
+ List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId, groupName, 10);
+
+ assertThat(reviewers).hasSize(1);
+ SuggestedReviewerInfo reviewer = Iterables.getOnlyElement(reviewers);
+ assertThat(reviewer.group.id).isEqualTo(largeGroup.get());
+ }
+
+ @Test
public void defaultReviewerSuggestion() throws Exception {
TestAccount user1 = user("customuser1", "User1");
TestAccount reviewer1 = user("customuser2", "User2");
TestAccount reviewer2 = user("customuser3", "User3");
- setApiUser(user1);
+ requestScopeOperations.setApiUser(user1.id());
String changeId1 = createChangeFromApi();
- setApiUser(reviewer1);
+ requestScopeOperations.setApiUser(reviewer1.id());
reviewChange(changeId1);
- setApiUser(user1);
+ requestScopeOperations.setApiUser(user1.id());
String changeId2 = createChangeFromApi();
- setApiUser(reviewer1);
+ requestScopeOperations.setApiUser(reviewer1.id());
reviewChange(changeId2);
- setApiUser(reviewer2);
+ requestScopeOperations.setApiUser(reviewer2.id());
reviewChange(changeId2);
- setApiUser(user1);
+ requestScopeOperations.setApiUser(user1.id());
String changeId3 = createChangeFromApi();
List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId3, null, 4);
assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
- .containsExactly(reviewer1.id.get(), reviewer2.id.get())
+ .containsExactly(reviewer1.id().get(), reviewer2.id().get())
.inOrder();
// check that existing reviewers are filtered out
- gApi.changes().id(changeId3).addReviewer(reviewer1.email);
+ gApi.changes().id(changeId3).addReviewer(reviewer1.email());
reviewers = suggestReviewers(changeId3, null, 4);
assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
- .containsExactly(reviewer2.id.get())
+ .containsExactly(reviewer2.id().get())
.inOrder();
}
@Test
public void defaultReviewerSuggestionOnFirstChange() throws Exception {
TestAccount user1 = user("customuser1", "User1");
- setApiUser(user1);
+ requestScopeOperations.setApiUser(user1.id());
List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChange().getChangeId(), "", 4);
assertThat(reviewers).isEmpty();
}
@@ -337,23 +389,23 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
TestAccount userWhoLooksForSuggestions = user("customuser5", fullName);
// Create a change as userWhoOwns and add some reviews
- setApiUser(userWhoOwns);
+ requestScopeOperations.setApiUser(userWhoOwns.id());
String changeId1 = createChangeFromApi();
- setApiUser(reviewer1);
+ requestScopeOperations.setApiUser(reviewer1.id());
reviewChange(changeId1);
- setApiUser(user1);
+ requestScopeOperations.setApiUser(user1.id());
String changeId2 = createChangeFromApi();
- setApiUser(reviewer1);
+ requestScopeOperations.setApiUser(reviewer1.id());
reviewChange(changeId2);
- setApiUser(reviewer2);
+ requestScopeOperations.setApiUser(reviewer2.id());
reviewChange(changeId2);
// Create a comment as a different user
- setApiUser(userWhoComments);
+ requestScopeOperations.setApiUser(userWhoComments.id());
ReviewInput ri = new ReviewInput();
ri.message = "Test";
gApi.changes().id(changeId1).revision(1).review(ri);
@@ -361,18 +413,21 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
// Create a change as a new user to assert that we receive the correct
// ranking
- setApiUser(userWhoLooksForSuggestions);
+ 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())
+ reviewer1.id().get(),
+ reviewer2.id().get(),
+ userWhoOwns.id().get(),
+ userWhoComments.id().get())
.inOrder();
}
@Test
public void reviewerRankingProjectIsolation() throws Exception {
// Create new project
- Project.NameKey newProject = createProject("test");
+ Project.NameKey newProject = projectOperations.newProject().create();
// Create users who review changes in both the default and the new project
String fullName = "Primum Finalis";
@@ -380,31 +435,31 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
TestAccount reviewer1 = user("customuser2", fullName);
TestAccount reviewer2 = user("customuser3", fullName);
- setApiUser(userWhoOwns);
+ requestScopeOperations.setApiUser(userWhoOwns.id());
String changeId1 = createChangeFromApi();
- setApiUser(reviewer1);
+ requestScopeOperations.setApiUser(reviewer1.id());
reviewChange(changeId1);
- setApiUser(userWhoOwns);
+ requestScopeOperations.setApiUser(userWhoOwns.id());
String changeId2 = createChangeFromApi(newProject);
- setApiUser(reviewer2);
+ requestScopeOperations.setApiUser(reviewer2.id());
reviewChange(changeId2);
- setApiUser(userWhoOwns);
+ requestScopeOperations.setApiUser(userWhoOwns.id());
String changeId3 = createChangeFromApi(newProject);
- setApiUser(reviewer2);
+ requestScopeOperations.setApiUser(reviewer2.id());
reviewChange(changeId3);
- setApiUser(userWhoOwns);
+ requestScopeOperations.setApiUser(userWhoOwns.id());
List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Prim", 4);
// 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())
+ .containsExactly(reviewer1.id().get(), reviewer2.id().get())
.inOrder();
}
@@ -412,17 +467,17 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
public void suggestNoInactiveAccounts() throws Exception {
String name = name("foo");
TestAccount foo1 = accountCreator.create(name + "-1");
- assertThat(gApi.accounts().id(foo1.username).getActive()).isTrue();
+ assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
TestAccount foo2 = accountCreator.create(name + "-2");
- assertThat(gApi.accounts().id(foo2.username).getActive()).isTrue();
+ assertThat(gApi.accounts().id(foo2.username()).getActive()).isTrue();
String changeId = createChange().getChangeId();
assertReviewers(
suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
- gApi.accounts().id(foo2.username).setActive(false);
- assertThat(gApi.accounts().id(foo2.username).getActive()).isFalse();
+ 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());
}
@@ -444,7 +499,7 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
String secondaryEmail = "foo.secondary@example.com";
createAccountWithSecondaryEmail("foo", secondaryEmail);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
List<SuggestedReviewerInfo> reviewers =
suggestReviewers(createChange().getChangeId(), secondaryEmail, 4);
assertThat(reviewers).isEmpty();
@@ -464,7 +519,7 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
assertThat(Iterables.getOnlyElement(reviewers).account.secondaryEmails)
.containsExactly(secondaryEmail);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
reviewers = suggestReviewers(createChange().getChangeId(), "foo", 4);
assertReviewers(reviewers, ImmutableList.of(foo), ImmutableList.of());
assertThat(Iterables.getOnlyElement(reviewers).account.secondaryEmails).isNull();
@@ -476,7 +531,7 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
EmailInput input = new EmailInput();
input.email = secondaryEmail;
input.noConfirmation = true;
- gApi.accounts().id(foo.id.get()).addEmail(input);
+ gApi.accounts().id(foo.id().get()).addEmail(input);
return foo;
}
@@ -490,21 +545,20 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
return gApi.changes().id(changeId).suggestReviewers(query).withLimit(n).get();
}
- private InternalGroup newGroup(String name) throws Exception {
- GroupInfo group =
- createGroup.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(name(name)), null);
- return group(new AccountGroup.UUID(group.id));
+ private AccountGroup.UUID createGroupWithArbitraryMembers(int numMembers) {
+ Set<Account.Id> members =
+ IntStream.rangeClosed(1, numMembers)
+ .mapToObj(i -> accountOperations.newAccount().create())
+ .collect(toImmutableSet());
+ return groupOperations.newGroup().members(members).create();
}
- private TestAccount user(String name, String fullName, String emailName, InternalGroup... groups)
- throws Exception {
- String[] groupNames = Arrays.stream(groups).map(InternalGroup::getName).toArray(String[]::new);
- return accountCreator.create(
- name(name), name(emailName) + "@example.com", fullName, groupNames);
+ private TestAccount user(String name, String fullName, String emailName) throws Exception {
+ return accountCreator.create(name(name), name(emailName) + "@example.com", fullName);
}
- private TestAccount user(String name, String fullName, InternalGroup... groups) throws Exception {
- return user(name, fullName, name, groups);
+ private TestAccount user(String name, String fullName) throws Exception {
+ return user(name, fullName, name);
}
private void reviewChange(String changeId) throws RestApiException {
@@ -525,23 +579,23 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
return gApi.changes().create(ci).get().changeId;
}
- private void assertReviewers(
+ private static void assertReviewers(
List<SuggestedReviewerInfo> actual,
List<TestAccount> expectedUsers,
- List<InternalGroup> expectedGroups) {
+ List<AccountGroup.UUID> expectedGroups) {
List<Integer> actualAccountIds =
actual.stream()
.filter(i -> i.account != null)
.map(i -> i.account._accountId)
.collect(toList());
assertThat(actualAccountIds)
- .containsExactlyElementsIn(expectedUsers.stream().map(u -> u.id.get()).collect(toList()));
+ .containsExactlyElementsIn(expectedUsers.stream().map(u -> u.id().get()).collect(toList()));
List<String> actualGroupIds =
actual.stream().filter(i -> i.group != null).map(i -> i.group.id).collect(toList());
assertThat(actualGroupIds)
.containsExactlyElementsIn(
- expectedGroups.stream().map(g -> g.getGroupUUID().get()).collect(toList()))
+ expectedGroups.stream().map(AccountGroup.UUID::get).collect(toList()))
.inOrder();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
index 34d87d0030..49692dde42 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
@@ -18,12 +18,15 @@ import static com.google.common.truth.Truth.assertThat;
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.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;
@@ -31,21 +34,24 @@ import org.junit.Before;
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 = createProject("project-1");
- project2 = createProject("project-2", project1);
+ project1 = projectOperations.newProject().create();
+ project2 = projectOperations.newProject().parent(project1).create();
}
@After
public void tearDown() throws Exception {
- setApiUser(admin);
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+ requestScopeOperations.setApiUser(admin.id());
+ GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
prefs.workInProgressByDefault = false;
- gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+ gApi.accounts().id(admin.id().get()).setPreferences(prefs);
}
@Test
@@ -139,9 +145,9 @@ public class WorkInProgressByDefaultIT extends AbstractDaemonTest {
}
private void setWorkInProgressByDefaultForUser() throws Exception {
- GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+ GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
prefs.workInProgressByDefault = true;
- gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+ gApi.accounts().id(admin.id().get()).setPreferences(prefs);
}
private PushOneCommit.Result createChange(Project.NameKey p) throws Exception {
@@ -150,7 +156,7 @@ public class WorkInProgressByDefaultIT extends AbstractDaemonTest {
private PushOneCommit.Result createChange(Project.NameKey p, String r) throws Exception {
TestRepository<InMemoryRepository> testRepo = cloneProject(p);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result result = push.to(r);
result.assertOkStatus();
return result;
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
index 7133580280..99fdbc82ab 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
@@ -16,9 +16,9 @@ package com.google.gerrit.acceptance.rest.config;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.server.mail.EmailTokenVerifier;
+import com.google.gerrit.server.mail.SignedToken;
import com.google.gerrit.server.restapi.config.ConfirmEmail;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gwtjsonrpc.server.SignedToken;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
@@ -36,14 +36,14 @@ public class ConfirmEmailIT extends AbstractDaemonTest {
@Test
public void confirm() throws Exception {
ConfirmEmail.Input in = new ConfirmEmail.Input();
- in.token = emailTokenVerifier.encode(admin.getId(), "new.mail@example.com");
+ in.token = emailTokenVerifier.encode(admin.id(), "new.mail@example.com");
adminRestSession.put("/config/server/email.confirm", in).assertNoContent();
}
@Test
public void confirmForOtherUser_UnprocessableEntity() throws Exception {
ConfirmEmail.Input in = new ConfirmEmail.Input();
- in.token = emailTokenVerifier.encode(user.getId(), "new.mail@example.com");
+ in.token = emailTokenVerifier.encode(user.id(), "new.mail@example.com");
adminRestSession.put("/config/server/email.confirm", in).assertUnprocessableEntity();
}
@@ -57,7 +57,7 @@ public class ConfirmEmailIT extends AbstractDaemonTest {
@Test
public void confirmAlreadyInUse_UnprocessableEntity() throws Exception {
ConfirmEmail.Input in = new ConfirmEmail.Input();
- in.token = emailTokenVerifier.encode(admin.getId(), user.email);
+ in.token = emailTokenVerifier.encode(admin.id(), user.email());
adminRestSession.put("/config/server/email.confirm", in).assertUnprocessableEntity();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java b/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
index 874e04ec84..4a740182e2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
@@ -70,8 +70,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
// gerrit
@GerritConfig(name = "gerrit.allProjects", value = "Root")
@GerritConfig(name = "gerrit.allUsers", value = "Users")
- @GerritConfig(name = "gerrit.enableGwtUi", value = "true")
- @GerritConfig(name = "gerrit.reportBugText", value = "REPORT BUG")
@GerritConfig(name = "gerrit.reportBugUrl", value = "https://example.com/report")
// suggest
@@ -113,7 +111,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
assertThat(i.gerrit.allProjects).isEqualTo("Root");
assertThat(i.gerrit.allUsers).isEqualTo("Users");
assertThat(i.gerrit.reportBugUrl).isEqualTo("https://example.com/report");
- assertThat(i.gerrit.reportBugText).isEqualTo("REPORT BUG");
// plugin
assertThat(i.plugin.jsResourcePaths).isEmpty();
@@ -128,10 +125,7 @@ public class ServerInfoIT extends AbstractDaemonTest {
assertThat(i.user.anonymousCowardName).isEqualTo("Unnamed User");
// notedb
- notesMigration.setReadChanges(true);
assertThat(gApi.config().server().getInfo().noteDbEnabled).isTrue();
- notesMigration.setReadChanges(false);
- assertThat(gApi.config().server().getInfo().noteDbEnabled).isNull();
}
@Test
@@ -183,7 +177,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
assertThat(i.gerrit.allProjects).isEqualTo(AllProjectsNameProvider.DEFAULT);
assertThat(i.gerrit.allUsers).isEqualTo(AllUsersNameProvider.DEFAULT);
assertThat(i.gerrit.reportBugUrl).isNull();
- assertThat(i.gerrit.reportBugText).isNull();
// plugin
assertThat(i.plugin.jsResourcePaths).isEmpty();
@@ -197,13 +190,4 @@ public class ServerInfoIT extends AbstractDaemonTest {
// user
assertThat(i.user.anonymousCowardName).isEqualTo(AnonymousCowardNameProvider.DEFAULT);
}
-
- @Test
- @GerritConfig(name = "auth.contributorAgreements", value = "true")
- public void anonymousAccess() throws Exception {
- configureContributorAgreement(true);
-
- setApiUserAnonymous();
- gApi.config().server().getInfo();
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AbstractHttpPushTag.java b/javatests/com/google/gerrit/acceptance/rest/project/AbstractHttpPushTag.java
index 58ae63ff42..5e2af147d8 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AbstractHttpPushTag.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AbstractHttpPushTag.java
@@ -25,7 +25,7 @@ public abstract class AbstractHttpPushTag extends AbstractPushTag {
public void cloneProjectOverHttp() throws Exception {
// clone with user to avoid inherited tag permissions of admin user
CredentialsProvider.setDefault(
- new UsernamePasswordCredentialsProvider(user.username, user.httpPassword));
+ new UsernamePasswordCredentialsProvider(user.username(), user.httpPassword()));
testRepo = GitUtil.cloneProject(project, user.getHttpUrl(server) + "/" + project.get());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java b/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
index 12468c3734..9420304681 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
@@ -21,16 +21,13 @@ 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.server.group.SystemGroupBackend.ANONYMOUS_USERS;
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.common.data.Permission;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.project.testing.Util;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.PushResult;
@@ -58,7 +55,7 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
public void setUpTestEnvironment() throws Exception {
initialHead = getRemoteHead();
tagType = getTagType();
- removeAnonymousRead();
+ blockAnonymousRead();
}
protected abstract TagType getTagType();
@@ -187,7 +184,7 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
if (force) {
testRepo.reset(initialHead);
}
- commit(user.getIdent(), "subject");
+ commit(user.newIdent(), "subject");
boolean createTag = tagName == null;
tagName = MoreObjects.firstNonNull(tagName, "v1_" + System.nanoTime());
@@ -196,9 +193,9 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
break;
case ANNOTATED:
if (createTag) {
- createAnnotatedTag(testRepo, tagName, user.getIdent());
+ createAnnotatedTag(testRepo, tagName, user.newIdent());
} else {
- updateAnnotatedTag(testRepo, tagName, user.getIdent());
+ updateAnnotatedTag(testRepo, tagName, user.newIdent());
}
break;
default:
@@ -250,17 +247,6 @@ public abstract class AbstractPushTag extends AbstractDaemonTest {
removePermission(project, "refs/tags/*", Permission.PUSH);
}
- private void removeAnonymousRead() 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();
- }
- }
-
private void commit(PersonIdent ident, String subject) throws Exception {
commitBuilder().ident(ident).message(subject + " (" + System.nanoTime() + ")").create();
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 27226489ca..72af07583e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -21,6 +21,8 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS
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.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
@@ -63,20 +65,20 @@ import org.junit.Test;
public class AccessIT extends AbstractDaemonTest {
- private static final String PROJECT_NAME = "newProject";
-
private static final String REFS_ALL = Constants.R_REFS + "*";
private static final String REFS_HEADS = Constants.R_HEADS + "*";
private static final String LABEL_CODE_REVIEW = "Code-Review";
- private Project.NameKey newProjectName;
-
@Inject private DynamicSet<FileHistoryWebLink> fileHistoryWebLinkDynamicSet;
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
+ private Project.NameKey newProjectName;
@Before
public void setUp() throws Exception {
- newProjectName = createProject(PROJECT_NAME);
+ newProjectName = projectOperations.newProject().create();
}
@Test
@@ -90,14 +92,8 @@ public class AccessIT extends AbstractDaemonTest {
RegistrationHandle handle =
fileHistoryWebLinkDynamicSet.add(
"gerrit",
- new FileHistoryWebLink() {
- @Override
- public WebLinkInfo getFileHistoryWebLink(
- String projectName, String revision, String fileName) {
- return new WebLinkInfo(
- "name", "imageURL", "http://view/" + projectName + "/" + fileName);
- }
- });
+ (projectName, revision, fileName) ->
+ new WebLinkInfo("name", "imageURL", "http://view/" + projectName + "/" + fileName));
try {
ProjectAccessInfo info = pApi().access();
assertThat(info.configWebLinks).hasSize(1);
@@ -113,14 +109,8 @@ public class AccessIT extends AbstractDaemonTest {
RegistrationHandle handle =
fileHistoryWebLinkDynamicSet.add(
"gerrit",
- new FileHistoryWebLink() {
- @Override
- public WebLinkInfo getFileHistoryWebLink(
- String projectName, String revision, String fileName) {
- return new WebLinkInfo(
- "name", "imageURL", "http://view/" + projectName + "/" + fileName);
- }
- });
+ (projectName, revision, fileName) ->
+ new WebLinkInfo("name", "imageURL", "http://view/" + projectName + "/" + fileName));
try (Repository repo = repoManager.openRepository(newProjectName)) {
RefUpdate u = repo.updateRef(RefNames.REFS_CONFIG);
u.setForceUpdate(true);
@@ -181,7 +171,7 @@ public class AccessIT extends AbstractDaemonTest {
public void createAccessChange() throws Exception {
allow(newProjectName, RefNames.REFS_CONFIG, Permission.READ, REGISTERED_USERS);
// User can see the branch
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
pApi().branch("refs/heads/master").get();
ProjectAccessInput accessInput = newProjectAccessInput();
@@ -196,7 +186,7 @@ public class AccessIT extends AbstractDaemonTest {
accessSection.permissions.put(Permission.READ, read);
accessInput.add.put(REFS_HEADS, accessSection);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ChangeInfo out = pApi().accessChange(accessInput);
assertThat(out.project).isEqualTo(newProjectName.get());
@@ -204,7 +194,7 @@ public class AccessIT extends AbstractDaemonTest {
assertThat(out.status).isEqualTo(ChangeStatus.NEW);
assertThat(out.submitted).isNull();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ChangeInfo c = gApi.changes().id(out._number).get(MESSAGES);
assertThat(c.messages.stream().map(m -> m.message)).containsExactly("Uploaded patch set 1");
@@ -215,7 +205,7 @@ public class AccessIT extends AbstractDaemonTest {
gApi.changes().id(out._number).current().submit();
// check that the change took effect.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
BranchInfo info = pApi().branch("refs/heads/master").get();
fail("wanted failure, got " + newGson().toJson(info));
@@ -226,16 +216,16 @@ public class AccessIT extends AbstractDaemonTest {
// Restore.
accessInput.add.clear();
accessInput.remove.put(REFS_HEADS, accessSection);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
out = pApi().accessChange(accessInput);
gApi.changes().id(out._number).current().review(reviewIn);
gApi.changes().id(out._number).current().submit();
// Now it works again.
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
pApi().branch("refs/heads/master").get();
}
@@ -335,7 +325,7 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.add.put(REFS_ALL, accessSectionInfo);
pApi().access(accessInput);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceNotFoundException.class);
pApi().access();
}
@@ -355,7 +345,7 @@ public class AccessIT extends AbstractDaemonTest {
AccessSectionInfo accessSectionInfoToApply = createDefaultAccessSectionInfo();
accessInfoToApply.add.put(REFS_HEADS, accessSectionInfoToApply);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceNotFoundException.class);
pApi().access();
}
@@ -402,7 +392,7 @@ public class AccessIT extends AbstractDaemonTest {
assertThat(owners.includes).isNull();
// PROJECT_OWNERS is invisible to anonymous user, but GetAccess disregards visibility.
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
ProjectAccessInfo anonResult = pApi().access();
assertThat(anonResult.groups.keySet())
.containsExactly(
@@ -412,13 +402,13 @@ public class AccessIT extends AbstractDaemonTest {
@Test
public void updateParentAsUser() throws Exception {
// Create child
- String newParentProjectName = createProject(PROJECT_NAME + "PA").get();
+ String newParentProjectName = projectOperations.newProject().create().get();
// Set new parent
ProjectAccessInput accessInput = newProjectAccessInput();
accessInput.parent = newParentProjectName;
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
exception.expectMessage("administrate server not permitted");
pApi().access(accessInput);
@@ -427,7 +417,7 @@ public class AccessIT extends AbstractDaemonTest {
@Test
public void updateParentAsAdministrator() throws Exception {
// Create parent
- String newParentProjectName = createProject(PROJECT_NAME + "PA").get();
+ String newParentProjectName = projectOperations.newProject().create().get();
// Set new parent
ProjectAccessInput accessInput = newProjectAccessInput();
@@ -445,7 +435,7 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.projects().name(allProjects.get()).access(accessInput);
}
@@ -465,7 +455,7 @@ public class AccessIT extends AbstractDaemonTest {
.get(AccessSection.GLOBAL_CAPABILITIES)
.permissions
.keySet())
- .containsAllIn(accessSectionInfo.permissions.keySet());
+ .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet());
}
@Test
@@ -501,7 +491,7 @@ public class AccessIT extends AbstractDaemonTest {
accessInput.remove.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.projects().name(allProjects.get()).access(accessInput);
}
@@ -527,7 +517,7 @@ public class AccessIT extends AbstractDaemonTest {
.get(AccessSection.GLOBAL_CAPABILITIES)
.permissions
.keySet())
- .containsAllIn(accessSectionInfo.permissions.keySet());
+ .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet());
// Remove
accessInput.add.clear();
@@ -571,7 +561,7 @@ public class AccessIT extends AbstractDaemonTest {
config = cfg.toText();
PushOneCommit push =
pushFactory.create(
- db, admin.getIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config);
+ admin.newIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config);
push.to(RefNames.REFS_CONFIG).assertOkStatus();
// Verify that unknownPermission is present
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/BUILD b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
index 975a711cba..6e66aab4d4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
@@ -9,6 +9,7 @@ acceptance_tests(
":project",
":push_tag_util",
":refassert",
+ "//lib/commons:lang",
],
)
@@ -34,7 +35,6 @@ java_library(
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//lib:guava",
- "//lib:gwtorm",
"//lib/truth",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
index c49a62b29b..698f7e03c0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
@@ -21,8 +21,8 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInfo;
@@ -35,11 +35,14 @@ 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 RequestScopeOperations requestScopeOperations;
+
private Branch.NameKey testBranch;
@Before
@@ -62,7 +65,7 @@ public class CreateBranchIT extends AbstractDaemonTest {
@Test
public void createBranch_Forbidden() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test");
}
@@ -80,7 +83,7 @@ public class CreateBranchIT extends AbstractDaemonTest {
@Test
public void createBranchByProjectOwner() throws Exception {
grantOwner();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertCreateSucceeds(testBranch);
}
@@ -94,7 +97,7 @@ public class CreateBranchIT extends AbstractDaemonTest {
public void createBranchByProjectOwnerCreateReferenceBlocked_Forbidden() throws Exception {
grantOwner();
blockCreateReference();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test");
}
@@ -112,13 +115,12 @@ public class CreateBranchIT extends AbstractDaemonTest {
allow(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH, REGISTERED_USERS);
assertCreateFails(
new Branch.NameKey(allUsers, RefNames.refsUsers(new Account.Id(1))),
- RefNames.refsUsers(admin.getId()),
+ RefNames.refsUsers(admin.id()),
ResourceConflictException.class,
"Not allowed to create user branch.");
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void createGroupBranch_Conflict() throws Exception {
allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.CREATE, REGISTERED_USERS);
allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.PUSH, REGISTERED_USERS);
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index 97558c4a3a..463532f71e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -30,6 +30,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
@@ -48,6 +49,7 @@ 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;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
@@ -70,6 +72,8 @@ import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Test;
public class CreateProjectIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void createProjectHttp() throws Exception {
String newProjectName = name("newProject");
@@ -117,7 +121,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
Future<RestResponse> r1 = executor.submit(createProjectFoo);
Future<RestResponse> r2 = executor.submit(createProjectFoo);
assertThat(ImmutableList.of(r1.get().getStatusCode(), r2.get().getStatusCode()))
- .containsAllOf(HttpStatus.SC_CREATED, HttpStatus.SC_CONFLICT);
+ .containsAtLeast(HttpStatus.SC_CREATED, HttpStatus.SC_CONFLICT);
}
} finally {
executor.shutdown();
@@ -321,7 +325,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
public void createProjectWithCapability() throws Exception {
allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
try {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ProjectInput in = new ProjectInput();
in.name = name("newProject");
ProjectInfo p = gApi.projects().create(in).get();
@@ -334,7 +338,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
@Test
public void createProjectWithoutCapability_Forbidden() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ProjectInput in = new ProjectInput();
in.name = name("newProject");
assertCreateFails(in, AuthException.class);
@@ -353,7 +357,7 @@ public class CreateProjectIT extends AbstractDaemonTest {
parent.setState(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
try {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ProjectInput in = new ProjectInput();
in.name = name("newProject");
ProjectInfo p = gApi.projects().create(in).get();
@@ -470,8 +474,8 @@ public class CreateProjectIT extends AbstractDaemonTest {
}
private Optional<String> readProjectConfig(String projectName) throws Exception {
- try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName))) {
- TestRepository<?> tr = new TestRepository<>(repo);
+ try (Repository repo = repoManager.openRepository(new Project.NameKey(projectName));
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
RevWalk rw = tr.getRevWalk();
Ref ref = repo.exactRef(RefNames.REFS_CONFIG);
if (ref == null) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
index 18e6dc89e4..ccdc4973dd 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
@@ -21,8 +21,9 @@ import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.GerritConfig;
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.BranchApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
@@ -33,23 +34,26 @@ 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;
public class DeleteBranchIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
private Branch.NameKey testBranch;
@Before
public void setUp() throws Exception {
- project = createProject(name("p"));
+ project = projectOperations.newProject().create();
testBranch = new Branch.NameKey(project, "test");
branch(testBranch).create(new BranchInput());
}
@Test
public void deleteBranch_Forbidden() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteForbidden(testBranch);
}
@@ -61,7 +65,7 @@ public class DeleteBranchIT extends AbstractDaemonTest {
@Test
public void deleteBranchByProjectOwner() throws Exception {
grantOwner();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteSucceeds(testBranch);
}
@@ -75,21 +79,21 @@ public class DeleteBranchIT extends AbstractDaemonTest {
public void deleteBranchByProjectOwnerForcePushBlocked_Forbidden() throws Exception {
grantOwner();
blockForcePush();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteForbidden(testBranch);
}
@Test
public void deleteBranchByUserWithForcePushPermission() throws Exception {
grantForcePush();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteSucceeds(testBranch);
}
@Test
public void deleteBranchByUserWithDeletePermission() throws Exception {
grantDelete();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteSucceeds(testBranch);
}
@@ -136,11 +140,10 @@ public class DeleteBranchIT extends AbstractDaemonTest {
exception.expect(ResourceConflictException.class);
exception.expectMessage("Not allowed to delete user branch.");
- branch(new Branch.NameKey(allUsers, RefNames.refsUsers(admin.id))).delete();
+ branch(new Branch.NameKey(allUsers, RefNames.refsUsers(admin.id()))).delete();
}
@Test
- @GerritConfig(name = "noteDb.groups.write", value = "true")
public void deleteGroupBranch_Conflict() throws Exception {
allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.CREATE, REGISTERED_USERS);
allow(allUsers, RefNames.REFS_GROUPS + "*", Permission.PUSH, REGISTERED_USERS);
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
index ca7517cc36..4d7212312d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
@@ -25,6 +25,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
@@ -34,6 +35,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.reviewdb.client.RefNames;
+import com.google.inject.Inject;
import java.util.HashMap;
import java.util.List;
import org.eclipse.jgit.lib.Repository;
@@ -46,6 +48,8 @@ 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 RequestScopeOperations requestScopeOperations;
+
@Before
public void setUp() throws Exception {
allow("refs/*", Permission.CREATE, REGISTERED_USERS);
@@ -72,14 +76,14 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
DeleteBranchesInput input = new DeleteBranchesInput();
input.branches = branchToDelete;
- setApiUser(user);
+ 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");
}
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
assertBranches(BRANCHES);
}
@@ -87,14 +91,14 @@ public class DeleteBranchesIT extends AbstractDaemonTest {
public void deleteMultiBranchesWithoutPermissionForbidden() throws Exception {
DeleteBranchesInput input = new DeleteBranchesInput();
input.branches = BRANCHES;
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
project().deleteBranches(input);
fail("Expected ResourceConflictException");
} catch (ResourceConflictException e) {
assertThat(e).hasMessageThat().isEqualTo(errorMessageForBranches(BRANCHES));
}
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
assertBranches(BRANCHES);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
index 3ae0b44001..07bb2b1d30 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
@@ -21,18 +21,22 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.TagApi;
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.api.projects.TagInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.inject.Inject;
import org.junit.Before;
import org.junit.Test;
public class DeleteTagIT extends AbstractDaemonTest {
private static final String TAG = "refs/tags/test";
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Before
public void setUp() throws Exception {
tag().create(new TagInput());
@@ -40,7 +44,7 @@ public class DeleteTagIT extends AbstractDaemonTest {
@Test
public void deleteTag_Forbidden() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteForbidden();
}
@@ -52,7 +56,7 @@ public class DeleteTagIT extends AbstractDaemonTest {
@Test
public void deleteTagByProjectOwner() throws Exception {
grantOwner();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteSucceeds();
}
@@ -66,21 +70,21 @@ public class DeleteTagIT extends AbstractDaemonTest {
public void deleteTagByProjectOwnerForcePushBlocked_Forbidden() throws Exception {
grantOwner();
blockForcePush();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteForbidden();
}
@Test
public void deleteTagByUserWithForcePushPermission() throws Exception {
grantForcePush();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteSucceeds();
}
@Test
public void deleteTagByUserWithDeletePermission() throws Exception {
grantDelete();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertDeleteSucceeds();
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
index 2ada724173..fae9d00d4e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
@@ -23,11 +23,13 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.projects.DeleteTagsInput;
import com.google.gerrit.extensions.api.projects.ProjectApi;
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.inject.Inject;
import java.util.HashMap;
import java.util.List;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -39,6 +41,8 @@ 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 RequestScopeOperations requestScopeOperations;
+
@Before
public void setUp() throws Exception {
for (String name : TAGS) {
@@ -61,14 +65,14 @@ public class DeleteTagsIT extends AbstractDaemonTest {
public void deleteTagsForbidden() throws Exception {
DeleteTagsInput input = new DeleteTagsInput();
input.tags = TAGS;
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
try {
project().deleteTags(input);
fail("Expected ResourceConflictException");
} catch (ResourceConflictException e) {
assertThat(e).hasMessageThat().isEqualTo(errorMessageForTags(TAGS));
}
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
assertTags(TAGS);
}
@@ -154,6 +158,6 @@ public class DeleteTagsIT extends AbstractDaemonTest {
}
private void assertTagsDeleted() throws Exception {
- assertTags(ImmutableList.<String>of());
+ assertTags(ImmutableList.of());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
index 78d027010e..48527af552 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GarbageCollectionIT.java
@@ -18,12 +18,14 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
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.inject.Inject;
import org.junit.Before;
import org.junit.Test;
public class GarbageCollectionIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Inject private GcAssert gcAssert;
@@ -31,7 +33,7 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
@Before
public void setUp() throws Exception {
- project2 = createProject("p2");
+ project2 = projectOperations.newProject().create();
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
index d5e811dd9f..d7365781b0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetChildProjectIT.java
@@ -18,13 +18,16 @@ import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertProj
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
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;
@NoHttpd
public class GetChildProjectIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Test
public void getNonExistingChildProject_NotFound() throws Exception {
@@ -33,15 +36,15 @@ public class GetChildProjectIT extends AbstractDaemonTest {
@Test
public void getNonChildProject_NotFound() throws Exception {
- Project.NameKey p1 = createProject("p1");
- Project.NameKey p2 = createProject("p2");
+ Project.NameKey p1 = projectOperations.newProject().create();
+ Project.NameKey p2 = projectOperations.newProject().create();
assertChildNotFound(p1, p2.get());
}
@Test
public void getChildProject() throws Exception {
- Project.NameKey child = createProject("p1");
+ Project.NameKey child = projectOperations.newProject().create();
ProjectInfo childInfo = gApi.projects().name(allProjects.get()).child(child.get()).get();
assertProjectInfo(projectCache.get(child).getProject(), childInfo);
@@ -49,16 +52,16 @@ public class GetChildProjectIT extends AbstractDaemonTest {
@Test
public void getGrandChildProject_NotFound() throws Exception {
- Project.NameKey child = createProject("p1");
- Project.NameKey grandChild = createProject("p1.1", child);
+ Project.NameKey child = projectOperations.newProject().create();
+ Project.NameKey grandChild = projectOperations.newProject().parent(child).create();
assertChildNotFound(allProjects, grandChild.get());
}
@Test
public void getGrandChildProjectWithRecursiveFlag() throws Exception {
- Project.NameKey child = createProject("p1");
- Project.NameKey grandChild = createProject("p1.1", child);
+ Project.NameKey child = projectOperations.newProject().create();
+ Project.NameKey grandChild = projectOperations.newProject().parent(child).create();
ProjectInfo grandChildInfo =
gApi.projects().name(allProjects.get()).child(grandChild.get()).get(true);
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
index 0632221431..18c706b9f5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
@@ -85,8 +85,7 @@ public class GetCommitIT extends AbstractDaemonTest {
@Test
public void getOpenChange_Found() throws Exception {
unblockRead();
- PushOneCommit.Result r =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master");
+ PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
r.assertOkStatus();
CommitInfo info = getCommit(r.getCommit());
@@ -108,8 +107,7 @@ public class GetCommitIT extends AbstractDaemonTest {
@Test
public void getOpenChange_NotFound() throws Exception {
- PushOneCommit.Result r =
- pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master");
+ PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
r.assertOkStatus();
assertNotFound(r.getCommit());
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
index b46b087506..ec1c708c20 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -20,15 +20,19 @@ 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.request.RequestScopeOperations;
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 RequestScopeOperations requestScopeOperations;
+
@Test
public void listBranchesOfNonExistingProject_NotFound() throws Exception {
exception.expect(ResourceNotFoundException.class);
@@ -38,7 +42,7 @@ public class ListBranchesIT extends AbstractDaemonTest {
@Test
public void listBranchesOfNonVisibleProject_NotFound() throws Exception {
blockRead("refs/*");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceNotFoundException.class);
gApi.projects().name(project.get()).branches().get();
}
@@ -70,7 +74,7 @@ public class ListBranchesIT extends AbstractDaemonTest {
blockRead("refs/heads/dev");
String master = pushTo("refs/heads/master").getCommit().name();
pushTo("refs/heads/dev");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// refs/meta/config is hidden since user is no project owner
assertRefs(
ImmutableList.of(
@@ -83,7 +87,7 @@ public class ListBranchesIT extends AbstractDaemonTest {
blockRead("refs/heads/master");
pushTo("refs/heads/master");
String dev = pushTo("refs/heads/dev").getCommit().name();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
// refs/meta/config is hidden since user is no project owner
assertRefs(ImmutableList.of(branch("refs/heads/dev", dev, false)), list().get());
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
index 90906ba299..7746820203 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListChildProjectsIT.java
@@ -18,12 +18,16 @@ import static com.google.gerrit.acceptance.rest.project.ProjectAssert.assertThat
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
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;
@NoHttpd
public class ListChildProjectsIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
@Test
public void listChildrenOfNonExistingProject_NotFound() throws Exception {
@@ -39,32 +43,38 @@ public class ListChildProjectsIT extends AbstractDaemonTest {
@Test
public void listChildren() throws Exception {
- Project.NameKey child1 = createProject("p1");
- Project.NameKey child1_1 = createProject("p1.1", child1);
- Project.NameKey child1_2 = createProject("p1.2", child1);
+ Project.NameKey child1 = projectOperations.newProject().create();
+ 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())
- .containsExactly(child1_1, child1_2)
- .inOrder();
+ .containsExactly(child1_1, child1_2);
}
@Test
public void listChildrenWithLimit() throws Exception {
- Project.NameKey child1 = createProject("p1");
- Project.NameKey child1_1 = createProject("p1.1", child1);
- createProject("p1.2", child1);
+ String prefix = RandomStringUtils.randomAlphabetic(8);
+ Project.NameKey child1 = projectOperations.newProject().name(prefix + "p1").create();
+ Project.NameKey child1_1 =
+ projectOperations.newProject().parent(child1).name(prefix + "p1.1").create();
+ projectOperations.newProject().parent(child1).name(prefix + "p1.2").create();
assertThatNameList(gApi.projects().name(child1.get()).children(1)).containsExactly(child1_1);
}
@Test
public void listChildrenRecursively() throws Exception {
- Project.NameKey child1 = createProject("p1");
- createProject("p2");
- Project.NameKey child1_1 = createProject("p1.1", child1);
- Project.NameKey child1_2 = createProject("p1.2", child1);
- Project.NameKey child1_1_1 = createProject("p1.1.1", child1_1);
- Project.NameKey child1_1_1_1 = createProject("p1.1.1.1", child1_1_1);
+ String prefix = RandomStringUtils.randomAlphabetic(8);
+ Project.NameKey child1 = projectOperations.newProject().name(prefix + "p1").create();
+ Project.NameKey child1_1 =
+ projectOperations.newProject().parent(child1).name(prefix + "p1.1").create();
+ Project.NameKey child1_2 =
+ projectOperations.newProject().parent(child1).name(prefix + "p1.2").create();
+ Project.NameKey child1_1_1 =
+ projectOperations.newProject().parent(child1_1).name(prefix + "p1.1.1").create();
+ Project.NameKey child1_1_1_1 =
+ projectOperations.newProject().parent(child1_1_1).name(prefix + "p1.1.1.1").create();
assertThatNameList(gApi.projects().name(child1.get()).children(true))
.containsExactly(child1_1, child1_1_1, child1_1_1_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 dfce8830fa..9dba8cfe81 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -28,6 +28,8 @@ import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.Sandboxed;
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.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
@@ -36,9 +38,8 @@ import com.google.gerrit.extensions.api.projects.Projects.ListRequest.FilterType
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.extensions.restapi.RestApiException;
+import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.project.ProjectCacheImpl;
import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.restapi.project.ListProjects;
@@ -55,19 +56,21 @@ import org.junit.Test;
@NoHttpd
@Sandboxed
public class ListProjectsIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
@Inject private ListProjects listProjects;
@Test
public void listProjects() throws Exception {
- Project.NameKey someProject = createProject("some-project");
- assertThatNameList(filter(gApi.projects().list().get()))
- .containsExactly(allProjects, allUsers, project, someProject)
- .inOrder();
+ Project.NameKey someProject = projectOperations.newProject().create();
+ assertThatNameList(gApi.projects().list().get())
+ .containsExactly(allProjects, allUsers, project, someProject);
+ assertThatNameList(gApi.projects().list().get()).isOrdered();
}
@Test
public void listProjectsFiltersInvisibleProjects() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
assertThatNameList(gApi.projects().list().get()).contains(project);
try (ProjectConfigUpdate u = updateProject(project)) {
@@ -75,7 +78,7 @@ public class ListProjectsIT extends AbstractDaemonTest {
u.save();
}
- assertThatNameList(filter(gApi.projects().list().get())).doesNotContain(project);
+ assertThatNameList(gApi.projects().list().get()).doesNotContain(project);
}
@Test
@@ -104,16 +107,15 @@ public class ListProjectsIT extends AbstractDaemonTest {
@Test
public void listProjectsWithLimit() throws Exception {
ProjectCacheImpl projectCacheImpl = (ProjectCacheImpl) projectCache;
- for (int i = 0; i < 5; i++) {
- createProject("someProject" + i);
+ String pre = "lpwl-someProject";
+ int n = 6;
+ for (int i = 0; i < n; i++) {
+ projectOperations.newProject().name(pre + i).create();
}
- String p = name("");
- // 5, plus p which was automatically created.
- int n = 6;
projectCacheImpl.evictAllByName();
for (int i = 1; i <= n + 2; i++) {
- assertThatNameList(gApi.projects().list().withPrefix(p).withLimit(i).get())
+ assertThatNameList(gApi.projects().list().withPrefix(pre).withLimit(i).get())
.hasSize(Math.min(i, n));
assertThat(projectCacheImpl.sizeAllByName())
.isAtMost((long) (i + 2)); // 2 = AllProjects + AllUsers
@@ -189,91 +191,87 @@ public class ListProjectsIT extends AbstractDaemonTest {
private List<String> createProjects(String prefix, int numProjects) {
return IntStream.range(0, numProjects)
- .mapToObj(
- i -> {
- String projectName = prefix + i;
- try {
- return createProject(projectName);
- } catch (RestApiException e) {
- throw new IllegalStateException("Unable to create project " + projectName, e);
- }
- })
+ .mapToObj(i -> projectOperations.newProject().name(prefix + i).create())
.map(Project.NameKey::get)
.collect(toList());
}
@Test
public void listProjectsWithPrefix() throws Exception {
- Project.NameKey someProject = createProject("some-project");
- Project.NameKey someOtherProject = createProject("some-other-project");
- createProject("project-awesome");
+ Project.NameKey someProject = projectOperations.newProject().name("listtest-p1").create();
+ Project.NameKey someOtherProject = projectOperations.newProject().name("listtest-p2").create();
+ projectOperations.newProject().name("other-prefix-project").create();
- String p = name("some");
+ String p = "listtest";
assertBadRequest(gApi.projects().list().withPrefix(p).withRegex(".*"));
assertBadRequest(gApi.projects().list().withPrefix(p).withSubstring(p));
- assertThatNameList(filter(gApi.projects().list().withPrefix(p).get()))
- .containsExactly(someOtherProject, someProject)
- .inOrder();
- p = name("SOME");
- assertThatNameList(filter(gApi.projects().list().withPrefix(p).get())).isEmpty();
+ assertThatNameList(gApi.projects().list().withPrefix(p).get())
+ .containsExactly(someOtherProject, someProject);
+ p = "notlisttest";
+ assertThatNameList(gApi.projects().list().withPrefix(p).get()).isEmpty();
}
@Test
public void listProjectsWithRegex() throws Exception {
- Project.NameKey someProject = createProject("some-project");
- Project.NameKey someOtherProject = createProject("some-other-project");
- Project.NameKey projectAwesome = createProject("project-awesome");
+ Project.NameKey someProject = projectOperations.newProject().name("lpwr-some-project").create();
+ Project.NameKey someOtherProject =
+ projectOperations.newProject().name("lpwr-some-other-project").create();
+ Project.NameKey projectAwesome =
+ projectOperations.newProject().name("lpwr-project-awesome").create();
assertBadRequest(gApi.projects().list().withRegex("[.*"));
assertBadRequest(gApi.projects().list().withRegex(".*").withPrefix("p"));
assertBadRequest(gApi.projects().list().withRegex(".*").withSubstring("p"));
- assertThatNameList(filter(gApi.projects().list().withRegex(".*some").get()))
+ assertThatNameList(gApi.projects().list().withRegex(".*some").get())
.containsExactly(projectAwesome);
- String r = name("some-project$").replace(".", "\\.");
- assertThatNameList(filter(gApi.projects().list().withRegex(r).get()))
- .containsExactly(someProject);
- assertThatNameList(filter(gApi.projects().list().withRegex(".*").get()))
+ String r = ("lpwr-some-project$").replace(".", "\\.");
+ assertThatNameList(gApi.projects().list().withRegex(r).get()).containsExactly(someProject);
+ assertThatNameList(gApi.projects().list().withRegex(".*").get())
.containsExactly(
- allProjects, allUsers, project, projectAwesome, someOtherProject, someProject)
- .inOrder();
+ allProjects, allUsers, project, projectAwesome, someOtherProject, someProject);
}
@Test
public void listProjectsWithStart() throws Exception {
+ String pre = "lpws-";
for (int i = 0; i < 5; i++) {
- createProject(new Project.NameKey("someProject" + i).get());
+ projectOperations.newProject().name(pre + i).create();
}
- String p = name("");
- List<ProjectInfo> all = gApi.projects().list().withPrefix(p).get();
- // 5, plus p which was automatically created.
- int n = 6;
+ List<ProjectInfo> all = gApi.projects().list().withPrefix(pre).get();
+ int n = 5;
assertThat(all).hasSize(n);
- assertThatNameList(gApi.projects().list().withPrefix(p).withStart(n - 1).get())
+ assertThatNameList(gApi.projects().list().withPrefix(pre).withStart(n - 1).get())
.containsExactly(new Project.NameKey(Iterables.getLast(all).name));
}
@Test
public void listProjectsWithSubstring() throws Exception {
- Project.NameKey someProject = createProject("some-project");
- Project.NameKey someOtherProject = createProject("some-other-project");
- Project.NameKey projectAwesome = createProject("project-awesome");
+ Project.NameKey someProject = projectOperations.newProject().name("some-project").create();
+ Project.NameKey someOtherProject =
+ projectOperations.newProject().name("some-other-project").create();
+ Project.NameKey projectAwesome =
+ projectOperations.newProject().name("project-awesome").create();
assertBadRequest(gApi.projects().list().withSubstring("some").withRegex(".*"));
assertBadRequest(gApi.projects().list().withSubstring("some").withPrefix("some"));
- assertThatNameList(filter(gApi.projects().list().withSubstring("some").get()))
- .containsExactly(projectAwesome, someOtherProject, someProject)
- .inOrder();
- assertThatNameList(filter(gApi.projects().list().withSubstring("SOME").get()))
- .containsExactly(projectAwesome, someOtherProject, someProject)
- .inOrder();
+ assertThatNameList(gApi.projects().list().withSubstring("some").get())
+ .containsExactly(projectAwesome, someOtherProject, someProject);
+ assertThatNameList(gApi.projects().list().withSubstring("SOME").get())
+ .containsExactly(projectAwesome, someOtherProject, someProject);
}
@Test
public void listProjectsWithTree() throws Exception {
- Project.NameKey someParentProject = createProject("some-parent-project");
- Project.NameKey someChildProject = createProject("some-child-project", someParentProject);
+ Project.NameKey someParentProject =
+ projectOperations.newProject().name("some-parent-project").create();
+ Project.NameKey someChildProject =
+ projectOperations
+ .newProject()
+ .name("some-child-project")
+ .parent(someParentProject)
+ .create();
Map<String, ProjectInfo> result = gApi.projects().list().withTree(true).getAsMap();
assertThat(result).containsKey(someChildProject.get());
@@ -286,36 +284,14 @@ public class ListProjectsIT extends AbstractDaemonTest {
gApi.projects().list().withType(FilterType.PERMISSIONS).getAsMap();
assertThat(result.keySet()).containsExactly(allProjects.get(), allUsers.get());
- assertThatNameList(filter(gApi.projects().list().withType(FilterType.ALL).get()))
- .containsExactly(allProjects, allUsers, project)
- .inOrder();
- }
-
- @Test
- public void listParentCandidates() throws Exception {
- Map<String, ProjectInfo> result =
- gApi.projects().list().withType(FilterType.PARENT_CANDIDATES).getAsMap();
- assertThat(result).hasSize(1);
- assertThat(result).containsKey(allProjects.get());
-
- // Create a new project with 'project' as parent
- Project.NameKey testProject = createProject(name("test"), project);
-
- // Parent candidates are All-Projects and 'project'
- assertThatNameList(filter(gApi.projects().list().withType(FilterType.PARENT_CANDIDATES).get()))
- .containsExactly(allProjects, project)
- .inOrder();
-
- // All projects are listed
- assertThatNameList(filter(gApi.projects().list().get()))
- .containsExactly(allProjects, allUsers, testProject, project)
- .inOrder();
+ assertThatNameList(gApi.projects().list().withType(FilterType.ALL).get())
+ .containsExactly(allProjects, allUsers, project);
}
@Test
public void listWithHiddenAndReadonlyProjects() throws Exception {
- Project.NameKey hidden = createProject("project-to-hide");
- Project.NameKey readonly = createProject("project-to-read");
+ Project.NameKey hidden = projectOperations.newProject().create();
+ Project.NameKey readonly = projectOperations.newProject().create();
// Set project read-only
ConfigInput input = new ConfigInput();
@@ -326,8 +302,7 @@ public class ListProjectsIT extends AbstractDaemonTest {
// The hidden project is included because it was not hidden yet.
// The read-only project is included.
assertThatNameList(gApi.projects().list().get())
- .containsExactly(allProjects, allUsers, project, hidden, readonly)
- .inOrder();
+ .containsExactly(allProjects, allUsers, project, hidden, readonly);
// Hide the project
input.state = ProjectState.HIDDEN;
@@ -339,18 +314,15 @@ public class ListProjectsIT extends AbstractDaemonTest {
// Hidden project is not included in the list
assertThatNameList(gApi.projects().list().get())
- .containsExactly(allProjects, allUsers, project, readonly)
- .inOrder();
+ .containsExactly(allProjects, allUsers, project, readonly);
// ALL filter applies to type, and doesn't include hidden state
assertThatNameList(gApi.projects().list().withType(FilterType.ALL).get())
- .containsExactly(allProjects, allUsers, project, readonly)
- .inOrder();
+ .containsExactly(allProjects, allUsers, project, readonly);
// "All" boolean option causes hidden projects to be included
assertThatNameList(gApi.projects().list().withAll(true).get())
- .containsExactly(allProjects, allUsers, project, hidden, readonly)
- .inOrder();
+ .containsExactly(allProjects, allUsers, project, hidden, readonly);
// "State" option causes only the projects in that state to be included
assertThatNameList(gApi.projects().list().withState(ProjectState.HIDDEN).get())
@@ -358,8 +330,7 @@ public class ListProjectsIT extends AbstractDaemonTest {
assertThatNameList(gApi.projects().list().withState(ProjectState.READ_ONLY).get())
.containsExactly(readonly);
assertThatNameList(gApi.projects().list().withState(ProjectState.ACTIVE).get())
- .containsExactly(allProjects, allUsers, project)
- .inOrder();
+ .containsExactly(allProjects, allUsers, project);
// Cannot use "all" and "state" together
assertBadRequest(gApi.projects().list().withAll(true).withState(ProjectState.ACTIVE));
@@ -373,16 +344,4 @@ public class ListProjectsIT extends AbstractDaemonTest {
// Expected.
}
}
-
- private Iterable<ProjectInfo> filter(Iterable<ProjectInfo> infos) {
- String prefix = name("");
- return Iterables.filter(
- infos,
- p -> {
- return p.name != null
- && (p.name.equals(allProjects.get())
- || p.name.equals(allUsers.get())
- || p.name.startsWith(prefix));
- });
- }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/PluginAccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/PluginAccessIT.java
index 1e6afa83ba..e7663f7fd5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/PluginAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/PluginAccessIT.java
@@ -26,15 +26,21 @@ 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.config.CapabilityDefinition;
+import com.google.gerrit.extensions.config.PluginProjectPermissionDefinition;
import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.permissions.PluginPermissionsUtil;
import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
import com.google.inject.Module;
+import java.util.Set;
import org.junit.Test;
-public class PluginAccessIT extends AbstractDaemonTest {
+public final class PluginAccessIT extends AbstractDaemonTest {
+ private static final String TEST_PLUGIN_NAME = "gerrit";
+ private static final String TEST_PLUGIN_CAPABILITY = "aPluginCapability";
+ private static final String TEST_PLUGIN_PROJECT_PERMISSION = "aPluginProjectPermission";
- private static final String CORE_PLUGIN_PREFIX = "gerrit-";
- private static final String PLUGIN_CAPABILITY = "printHello";
+ @Inject PluginPermissionsUtil pluginPermissionsUtil;
@Override
public Module createModule() {
@@ -42,12 +48,21 @@ public class PluginAccessIT extends AbstractDaemonTest {
@Override
protected void configure() {
bind(CapabilityDefinition.class)
- .annotatedWith(Exports.named(PLUGIN_CAPABILITY))
+ .annotatedWith(Exports.named(TEST_PLUGIN_CAPABILITY))
.toInstance(
new CapabilityDefinition() {
@Override
public String getDescription() {
- return "Print Hello";
+ return "A Plugin Capability";
+ }
+ });
+ bind(PluginProjectPermissionDefinition.class)
+ .annotatedWith(Exports.named(TEST_PLUGIN_PROJECT_PERMISSION))
+ .toInstance(
+ new PluginProjectPermissionDefinition() {
+ @Override
+ public String getDescription() {
+ return "A Plugin Project Permission";
}
});
}
@@ -55,24 +70,47 @@ public class PluginAccessIT extends AbstractDaemonTest {
}
@Test
- public void addPluginCapability() throws Exception {
- ProjectAccessInput accessInput = new ProjectAccessInput();
- AccessSectionInfo accessSectionInfo = new AccessSectionInfo();
- PermissionInfo email = new PermissionInfo(null, null);
- PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false);
+ public void setAccessAddPluginCapabilitySucceed() throws Exception {
+ String pluginCapability = TEST_PLUGIN_NAME + "-" + TEST_PLUGIN_CAPABILITY;
+ ProjectAccessInput accessInput =
+ createAccessInput(AccessSection.GLOBAL_CAPABILITIES, pluginCapability);
+
+ ProjectAccessInfo projectAccessInfo =
+ gApi.projects().name(allProjects.get()).access(accessInput);
- email.rules = ImmutableMap.of(SystemGroupBackend.REGISTERED_USERS.get(), pri);
- accessSectionInfo.permissions = ImmutableMap.of(CORE_PLUGIN_PREFIX + PLUGIN_CAPABILITY, email);
- accessInput.add = ImmutableMap.of(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
+ Set<String> capabilities =
+ projectAccessInfo.local.get(AccessSection.GLOBAL_CAPABILITIES).permissions.keySet();
+ assertThat(capabilities).contains(pluginCapability);
+ // Verifies the plugin defined capability could be listed.
+ assertThat(pluginPermissionsUtil.collectPluginCapabilities()).containsKey(pluginCapability);
+ }
+
+ @Test
+ public void setAccessAddPluginProjectPermissionSucceed() throws Exception {
+ String pluginProjectPermission =
+ "plugin-" + TEST_PLUGIN_NAME + "-" + TEST_PLUGIN_PROJECT_PERMISSION;
+ String accessSection = "refs/heads/plugin-permission";
+ ProjectAccessInput accessInput = createAccessInput(accessSection, pluginProjectPermission);
- ProjectAccessInfo updatedAccessSectionInfo =
+ ProjectAccessInfo projectAccessInfo =
gApi.projects().name(allProjects.get()).access(accessInput);
- assertThat(
- updatedAccessSectionInfo
- .local
- .get(AccessSection.GLOBAL_CAPABILITIES)
- .permissions
- .keySet())
- .containsAllIn(accessSectionInfo.permissions.keySet());
+
+ Set<String> permissions = projectAccessInfo.local.get(accessSection).permissions.keySet();
+ assertThat(permissions).contains(pluginProjectPermission);
+ // Verifies the plugin defined capability could be listed.
+ assertThat(pluginPermissionsUtil.collectPluginProjectPermissions())
+ .containsKey(pluginProjectPermission);
+ }
+
+ private static ProjectAccessInput createAccessInput(String accessSection, String permissionName) {
+ ProjectAccessInput accessInput = new ProjectAccessInput();
+ PermissionRuleInfo ruleInfo = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false);
+ PermissionInfo email = new PermissionInfo(null, null);
+ email.rules = ImmutableMap.of(SystemGroupBackend.REGISTERED_USERS.get(), ruleInfo);
+ AccessSectionInfo accessSectionInfo = new AccessSectionInfo();
+ accessSectionInfo.permissions = ImmutableMap.of(permissionName, email);
+ accessInput.add = ImmutableMap.of(accessSection, accessSectionInfo);
+
+ return accessInput;
}
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
index c78b47bbfe..bf2a5342b2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
@@ -19,9 +19,11 @@ 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.server.project.ProjectState;
+import com.google.inject.Inject;
import java.util.Arrays;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -29,6 +31,8 @@ import org.junit.Before;
import org.junit.Test;
public class ProjectLevelConfigIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+
@Before
public void setUp() throws Exception {
fetch(testRepo, RefNames.REFS_CONFIG + ":refs/heads/config");
@@ -43,12 +47,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
cfg.setString("s2", "ss", "k2", "v2");
PushOneCommit push =
pushFactory.create(
- db,
- admin.getIdent(),
- testRepo,
- "Create Project Level Config",
- configName,
- cfg.toText());
+ admin.newIdent(), testRepo, "Create Project Level Config", configName, cfg.toText());
push.to(RefNames.REFS_CONFIG);
ProjectState state = projectCache.get(project);
@@ -73,8 +72,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"Create Project Level Config",
configName,
@@ -82,7 +80,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
.to(RefNames.REFS_CONFIG)
.assertOkStatus();
- Project.NameKey childProject = createProject("child", project);
+ Project.NameKey childProject = projectOperations.newProject().parent(project).create();
TestRepository<?> childTestRepo = cloneProject(childProject);
fetch(childTestRepo, RefNames.REFS_CONFIG + ":refs/heads/config");
childTestRepo.reset("refs/heads/config");
@@ -93,8 +91,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
childTestRepo,
"Create Project Level Config",
configName,
@@ -128,8 +125,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
"Create Project Level Config",
configName,
@@ -137,7 +133,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
.to(RefNames.REFS_CONFIG)
.assertOkStatus();
- Project.NameKey childProject = createProject("child", project);
+ Project.NameKey childProject = projectOperations.newProject().parent(project).create();
TestRepository<?> childTestRepo = cloneProject(childProject);
fetch(childTestRepo, RefNames.REFS_CONFIG + ":refs/heads/config");
childTestRepo.reset("refs/heads/config");
@@ -154,8 +150,7 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
childTestRepo,
"Create Project Level Config",
configName,
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
index f2def7ad8b..c945a2b359 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
@@ -23,6 +23,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.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
import com.google.gerrit.extensions.api.projects.TagApi;
@@ -34,6 +35,7 @@ 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.server.project.ProjectConfig;
+import com.google.inject.Inject;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.List;
@@ -60,6 +62,8 @@ public class TagsIT extends AbstractDaemonTest {
+ "=XFeC\n"
+ "-----END PGP SIGNATURE-----";
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Before
public void setupPermissions() throws Exception {
try (ProjectConfigUpdate u = updateProject(allProjects)) {
@@ -85,7 +89,7 @@ public class TagsIT extends AbstractDaemonTest {
@Test
public void listTagsOfNonVisibleProject() throws Exception {
blockRead("refs/*");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(ResourceNotFoundException.class);
gApi.projects().name(project.get()).tags().get();
}
@@ -140,7 +144,7 @@ public class TagsIT extends AbstractDaemonTest {
public void listTagsOfNonVisibleBranch() throws Exception {
grantTagPermissions();
- PushOneCommit push1 = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push1 = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r1 = push1.to("refs/heads/master");
r1.assertOkStatus();
TagInput tag1 = new TagInput();
@@ -151,7 +155,7 @@ public class TagsIT extends AbstractDaemonTest {
assertThat(result.revision).isEqualTo(tag1.revision);
pushTo("refs/heads/hidden");
- PushOneCommit push2 = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push2 = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r2 = push2.to("refs/heads/hidden");
r2.assertOkStatus();
@@ -180,7 +184,7 @@ public class TagsIT extends AbstractDaemonTest {
public void lightweightTag() throws Exception {
grant(project, R_TAGS + "*", Permission.CREATE);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
@@ -201,7 +205,7 @@ public class TagsIT extends AbstractDaemonTest {
assertThat(result.canDelete).isTrue();
assertThat(result.created).isEqualTo(timestamp(r));
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
result = tag(input.ref).get();
assertThat(result.canDelete).isNull();
@@ -212,7 +216,7 @@ public class TagsIT extends AbstractDaemonTest {
public void annotatedTag() throws Exception {
grant(project, R_TAGS + "*", Permission.CREATE_TAG);
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
@@ -225,8 +229,8 @@ public class TagsIT extends AbstractDaemonTest {
assertThat(result.ref).isEqualTo(R_TAGS + input.ref);
assertThat(result.object).isEqualTo(input.revision);
assertThat(result.message).isEqualTo(input.message);
- assertThat(result.tagger.name).isEqualTo(admin.fullName);
- assertThat(result.tagger.email).isEqualTo(admin.email);
+ assertThat(result.tagger.name).isEqualTo(admin.fullName());
+ assertThat(result.tagger.email).isEqualTo(admin.email());
assertThat(result.created).isEqualTo(result.tagger.date);
eventRecorder.assertRefUpdatedEvents(project.get(), result.ref, null, result.revision);
@@ -240,8 +244,8 @@ public class TagsIT extends AbstractDaemonTest {
assertThat(result2.ref).isEqualTo(input2.ref);
assertThat(result2.object).isEqualTo(input2.revision);
assertThat(result2.message).isEqualTo(input2.message);
- assertThat(result2.tagger.name).isEqualTo(admin.fullName);
- assertThat(result2.tagger.email).isEqualTo(admin.email);
+ assertThat(result2.tagger.name).isEqualTo(admin.fullName());
+ assertThat(result2.tagger.email).isEqualTo(admin.email());
assertThat(result2.created).isEqualTo(result2.tagger.date);
eventRecorder.assertRefUpdatedEvents(project.get(), result2.ref, null, result2.revision);
diff --git a/javatests/com/google/gerrit/acceptance/rest/util/BUILD b/javatests/com/google/gerrit/acceptance/rest/util/BUILD
new file mode 100644
index 0000000000..1d3fe65015
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/util/BUILD
@@ -0,0 +1,12 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "util",
+ testonly = True,
+ srcs = glob(["*.java"]),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/acceptance:lib",
+ "//lib/commons:lang",
+ ],
+)
diff --git a/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java b/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java
new file mode 100644
index 0000000000..52e72febf4
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/util/RestApiCallHelper.java
@@ -0,0 +1,104 @@
+// 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.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;
+import static org.apache.http.HttpStatus.SC_NOT_FOUND;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import java.util.List;
+import org.junit.Ignore;
+
+/** Helper to execute REST API calls using the HTTP client. */
+@Ignore
+public class RestApiCallHelper {
+ /** @see #execute(RestSession, List, BeforeRestCall, String...) */
+ public static void execute(RestSession restSession, List<RestCall> restCalls, String... args)
+ throws Exception {
+ execute(restSession, restCalls, () -> {}, args);
+ }
+
+ /** @see #execute(RestSession, List, BeforeRestCall, String...) */
+ public static void execute(
+ RestSession restSession,
+ List<RestCall> restCalls,
+ BeforeRestCall beforeRestCall,
+ String... args)
+ throws Exception {
+ for (RestCall restCall : restCalls) {
+ beforeRestCall.run();
+ execute(restSession, restCall, args);
+ }
+ }
+
+ /**
+ * This method sends a request to a given REST endpoint and verifies that an implementation is
+ * found (no '404 Not Found' response) and that the request doesn't fail (no '500 Internal Server
+ * Error' response). It doesn't verify that the REST endpoint works correctly. This is okay since
+ * the purpose of the test is only to verify that the REST endpoint implementations are correctly
+ * bound.
+ */
+ public static void execute(RestSession restSession, RestCall restCall, String... args)
+ throws Exception {
+ String method = restCall.httpMethod().name();
+ String uri = restCall.uri(args);
+
+ RestResponse response;
+ switch (restCall.httpMethod()) {
+ case GET:
+ response = restSession.get(uri);
+ break;
+ case PUT:
+ response = restSession.put(uri);
+ break;
+ case POST:
+ response = restSession.post(uri);
+ break;
+ case DELETE:
+ response = restSession.delete(uri);
+ break;
+ default:
+ assert_().fail(String.format("unsupported method: %s", restCall.httpMethod().name()));
+ throw new IllegalStateException();
+ }
+
+ int status = response.getStatusCode();
+ String body = response.hasContent() ? response.getEntityContent() : "";
+
+ String msg = String.format("%s %s returned %d: %s", method, uri, status, body);
+ if (restCall.expectedResponseCode().isPresent()) {
+ assertWithMessage(msg).that(status).isEqualTo(restCall.expectedResponseCode().get());
+ if (restCall.expectedMessage().isPresent()) {
+ assertWithMessage(msg).that(body).contains(restCall.expectedMessage().get());
+ }
+ } else {
+ assertWithMessage(msg)
+ .that(status)
+ .isNotIn(ImmutableList.of(SC_FORBIDDEN, SC_NOT_FOUND, SC_METHOD_NOT_ALLOWED));
+ assertWithMessage(msg).that(status).isLessThan(SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @FunctionalInterface
+ public interface BeforeRestCall {
+ void run() throws Exception;
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/util/RestCall.java b/javatests/com/google/gerrit/acceptance/rest/util/RestCall.java
new file mode 100644
index 0000000000..7b0002c961
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/util/RestCall.java
@@ -0,0 +1,88 @@
+// 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.util;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.value.AutoValue;
+import java.util.Optional;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Ignore;
+
+/** Data container for test REST requests. */
+@Ignore
+@AutoValue
+public abstract class RestCall {
+ public enum Method {
+ GET,
+ PUT,
+ POST,
+ DELETE
+ }
+
+ public static RestCall get(String uriFormat) {
+ return builder(Method.GET, uriFormat).build();
+ }
+
+ public static RestCall put(String uriFormat) {
+ return builder(Method.PUT, uriFormat).build();
+ }
+
+ public static RestCall post(String uriFormat) {
+ return builder(Method.POST, uriFormat).build();
+ }
+
+ public static RestCall delete(String uriFormat) {
+ return builder(Method.DELETE, uriFormat).build();
+ }
+
+ public static Builder builder(Method httpMethod, String uriFormat) {
+ return new AutoValue_RestCall.Builder().httpMethod(httpMethod).uriFormat(uriFormat);
+ }
+
+ public abstract Method httpMethod();
+
+ public abstract String uriFormat();
+
+ public abstract Optional<Integer> expectedResponseCode();
+
+ public abstract Optional<String> expectedMessage();
+
+ public String uri(String... args) {
+ String uriFormat = uriFormat();
+ int expectedArgNum = StringUtils.countMatches(uriFormat, "%s");
+ checkState(
+ args.length == expectedArgNum,
+ "uriFormat %s needs %s arguments, got only %s: %s",
+ uriFormat,
+ expectedArgNum,
+ args.length,
+ args);
+ return String.format(uriFormat, (Object[]) args);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder httpMethod(Method httpMethod);
+
+ public abstract Builder uriFormat(String uriFormat);
+
+ public abstract Builder expectedResponseCode(int expectedResponseCode);
+
+ public abstract Builder expectedMessage(String expectedMessage);
+
+ public abstract RestCall build();
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
new file mode 100644
index 0000000000..fa1b4678c0
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -0,0 +1,357 @@
+// 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.account;
+
+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 com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+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.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;
+import com.google.gerrit.server.account.AccountResolver.Result;
+import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.util.Optional;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class AccountResolverIT extends AbstractDaemonTest {
+ @ConfigSuite.Default
+ public static Config defaultConfig() {
+ Config cfg = new Config();
+ cfg.setEnum("accounts", null, "visibility", AccountVisibility.SAME_GROUP);
+ return cfg;
+ }
+
+ @Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdateProvider;
+ @Inject private AccountOperations accountOperations;
+ @Inject private AccountResolver accountResolver;
+ @Inject private Provider<CurrentUser> self;
+ @Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private Sequences sequences;
+
+ @Test
+ public void bySelf() throws Exception {
+ assertThat(resolve("Self")).isEmpty();
+ accountOperations.newAccount().fullname("self").create();
+
+ Result result = resolveAsResult("self");
+ assertThat(result.asIdSet()).containsExactly(admin.id());
+ assertThat(result.isSelf()).isTrue();
+ assertThat(result.asUniqueUser()).isSameInstanceAs(self.get());
+
+ result = resolveAsResult("me");
+ assertThat(result.asIdSet()).containsExactly(admin.id());
+ assertThat(result.isSelf()).isTrue();
+ assertThat(result.asUniqueUser()).isSameInstanceAs(self.get());
+
+ requestScopeOperations.setApiUserAnonymous();
+ checkBySelfFails();
+
+ requestScopeOperations.setApiUserInternal();
+ checkBySelfFails();
+ }
+
+ 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();
+ }
+ }
+
+ @Test
+ public void bySelfInactive() throws Exception {
+ gApi.accounts().id(user.id().get()).setActive(false);
+
+ requestScopeOperations.setApiUser(user.id());
+ assertThat(gApi.accounts().id("self").getActive()).isFalse();
+
+ Result result = resolveAsResult("self");
+ assertThat(result.asIdSet()).containsExactly(user.id());
+ assertThat(result.isSelf()).isTrue();
+ assertThat(result.asUniqueUser()).isSameInstanceAs(self.get());
+ }
+
+ @Test
+ public void byExactAccountId() throws Exception {
+ Account.Id existingId = accountOperations.newAccount().create();
+ Account.Id idWithExistingIdAsFullname =
+ accountOperations.newAccount().fullname(existingId.toString()).create();
+
+ Account.Id nonexistentId = new Account.Id(sequences.nextAccountId());
+ accountOperations.newAccount().fullname(nonexistentId.toString()).create();
+
+ assertThat(resolve(existingId)).containsExactly(existingId);
+ assertThat(resolve(nonexistentId)).isEmpty();
+
+ assertThat(resolveByNameOrEmail(existingId)).containsExactly(idWithExistingIdAsFullname);
+ }
+
+ @Test
+ public void byParenthesizedAccountId() throws Exception {
+ Account.Id existingId = accountOperations.newAccount().fullname("Test User").create();
+ accountOperations.newAccount().fullname(existingId.toString()).create();
+
+ Account.Id nonexistentId = new Account.Id(sequences.nextAccountId());
+ accountOperations.newAccount().fullname("Any Name (" + nonexistentId + ")").create();
+ accountOperations.newAccount().fullname(nonexistentId.toString()).create();
+
+ String existingInput = "Any Name (" + existingId + ")";
+ assertThat(resolve(existingInput)).containsExactly(existingId);
+ assertThat(resolve("Any Name (" + nonexistentId + ")")).isEmpty();
+
+ assertThat(resolveByNameOrEmail(existingInput)).isEmpty();
+ }
+
+ @Test
+ public void byUsername() throws Exception {
+ String existingUsername = "myusername";
+ Account.Id idWithUsername = accountOperations.newAccount().username(existingUsername).create();
+ Account.Id idWithExistingUsernameAsFullname =
+ accountOperations.newAccount().fullname(existingUsername).create();
+
+ String nonexistentUsername = "anotherusername";
+ Account.Id idWithFullname = accountOperations.newAccount().fullname("anotherusername").create();
+
+ assertThat(resolve(existingUsername)).containsExactly(idWithUsername);
+
+ // Doesn't short-circuit just because the input looks like a valid username.
+ assertThat(ExternalId.isValidUsername(nonexistentUsername)).isTrue();
+ assertThat(resolve(nonexistentUsername)).containsExactly(idWithFullname);
+
+ assertThat(resolveByNameOrEmail(existingUsername))
+ .containsExactly(idWithExistingUsernameAsFullname);
+ }
+
+ @Test
+ public void byNameAndEmail() throws Exception {
+ String email = name("user@example.com");
+ Account.Id idWithEmail = accountOperations.newAccount().preferredEmail(email).create();
+ accountOperations.newAccount().fullname(email).create();
+
+ String input = "First Last <" + email + ">";
+ assertThat(resolve(input)).containsExactly(idWithEmail);
+ assertThat(resolveByNameOrEmail(input)).containsExactly(idWithEmail);
+ }
+
+ @Test
+ public void byNameAndEmailPrefersAccountsWithMatchingFullName() throws Exception {
+ String email = name("user@example.com");
+ Account.Id id1 = accountOperations.newAccount().fullname("Aaa Bbb").create();
+ setPreferredEmailBypassingUniquenessCheck(id1, email);
+ Account.Id id2 = accountOperations.newAccount().fullname("Ccc Ddd").create();
+ setPreferredEmailBypassingUniquenessCheck(id2, email);
+
+ String input = "First Last <" + email + ">";
+ assertThat(resolve(input)).containsExactly(id1, id2);
+ assertThat(resolveByNameOrEmail(input)).containsExactly(id1, id2);
+
+ Account.Id id3 = accountOperations.newAccount().fullname("First Last").create();
+ setPreferredEmailBypassingUniquenessCheck(id3, email);
+ assertThat(resolve(input)).containsExactly(id3);
+ assertThat(resolveByNameOrEmail(input)).containsExactly(id3);
+
+ Account.Id id4 = accountOperations.newAccount().fullname("First Last").create();
+ setPreferredEmailBypassingUniquenessCheck(id4, email);
+ assertThat(resolve(input)).containsExactly(id3, id4);
+ assertThat(resolveByNameOrEmail(input)).containsExactly(id3, id4);
+ }
+
+ @Test
+ public void byEmail() throws Exception {
+ String email = name("user@example.com");
+ Account.Id idWithEmail = accountOperations.newAccount().preferredEmail(email).create();
+ accountOperations.newAccount().fullname(email).create();
+
+ assertThat(resolve(email)).containsExactly(idWithEmail);
+ assertThat(resolveByNameOrEmail(email)).containsExactly(idWithEmail);
+ }
+
+ // Can't test for ByRealm because DefaultRealm with the default (OPENID) auth type doesn't support
+ // email expansion, so anything that would return a non-null value from DefaultRealm#lookup would
+ // just be an email address, handled by other tests. This could be avoided if we inject some sort
+ // of custom test realm instance, but the ugliness is not worth it for this small bit of test
+ // coverage.
+
+ @Test
+ public void byFullName() throws Exception {
+ Account.Id id1 = accountOperations.newAccount().fullname("Somebodys Name").create();
+ accountOperations.newAccount().fullname("A totally different name").create();
+ String input = "Somebodys name";
+ assertThat(resolve(input)).containsExactly(id1);
+ assertThat(resolveByNameOrEmail(input)).containsExactly(id1);
+ }
+
+ @Test
+ public void byDefaultSearch() throws Exception {
+ Account.Id id1 = accountOperations.newAccount().fullname("John Doe").create();
+ Account.Id id2 = accountOperations.newAccount().fullname("Jane Doe").create();
+ assertThat(resolve("doe")).containsExactly(id1, id2);
+ assertThat(resolveByNameOrEmail("doe")).containsExactly(id1, id2);
+ }
+
+ @Test
+ public void onlyExactIdReturnsInactiveAccounts() throws Exception {
+ TestAccount account =
+ accountOperations
+ .account(
+ accountOperations
+ .newAccount()
+ .fullname("Inactiveuser Name")
+ .preferredEmail("inactiveuser@example.com")
+ .username("inactiveusername")
+ .create())
+ .get();
+ Account.Id id = account.accountId();
+ String nameEmail = account.fullname().get() + " <" + account.preferredEmail().get() + ">";
+ ImmutableList<String> inputs =
+ ImmutableList.of(
+ account.fullname().get() + " (" + account.accountId() + ")",
+ account.fullname().get(),
+ account.preferredEmail().get(),
+ nameEmail,
+ Splitter.on(' ').splitToList(account.fullname().get()).get(0));
+
+ assertThat(resolve(account.accountId())).containsExactly(id);
+ for (String input : inputs) {
+ assertThat(resolve(input)).named("results for %s (active)", 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)
+ .isEmpty();
+ }
+ }
+
+ @Test
+ public void filterVisibility() throws Exception {
+ Account.Id id1 =
+ accountOperations
+ .newAccount()
+ .fullname("John Doe")
+ .preferredEmail("johndoe@example.com")
+ .create();
+ Account.Id id2 =
+ accountOperations
+ .newAccount()
+ .fullname("Jane Doe")
+ .preferredEmail("janedoe@example.com")
+ .create();
+
+ // Admin can see all accounts. Use a variety of searches, including with/without
+ // callerMayAssumeCandidatesAreVisible.
+ assertThat(resolve(id1)).containsExactly(id1);
+ assertThat(resolve("John Doe")).containsExactly(id1);
+ assertThat(resolve("johndoe@example.com")).containsExactly(id1);
+ assertThat(resolve(id2)).containsExactly(id2);
+ assertThat(resolve("Jane Doe")).containsExactly(id2);
+ assertThat(resolve("janedoe@example.com")).containsExactly(id2);
+ assertThat(resolve("doe")).containsExactly(id1, id2);
+
+ // id2 can't see id1, and vice versa.
+ requestScopeOperations.setApiUser(id1);
+ assertThat(resolve(id1)).containsExactly(id1);
+ assertThat(resolve("John Doe")).containsExactly(id1);
+ assertThat(resolve("johndoe@example.com")).containsExactly(id1);
+ assertThat(resolve(id2)).isEmpty();
+ assertThat(resolve("Jane Doe")).isEmpty();
+ assertThat(resolve("janedoe@example.com")).isEmpty();
+ assertThat(resolve("doe")).containsExactly(id1);
+
+ requestScopeOperations.setApiUser(id2);
+ assertThat(resolve(id1)).isEmpty();
+ assertThat(resolve("John Doe")).isEmpty();
+ assertThat(resolve("johndoe@example.com")).isEmpty();
+ assertThat(resolve(id2)).containsExactly(id2);
+ assertThat(resolve("Jane Doe")).containsExactly(id2);
+ assertThat(resolve("janedoe@example.com")).containsExactly(id2);
+ assertThat(resolve("doe")).containsExactly(id2);
+ }
+
+ private ImmutableSet<Account.Id> resolve(Object input) throws Exception {
+ return resolveAsResult(input).asIdSet();
+ }
+
+ private Result resolveAsResult(Object input) throws Exception {
+ return accountResolver.resolve(input.toString());
+ }
+
+ @SuppressWarnings("deprecation")
+ private ImmutableSet<Account.Id> resolveByNameOrEmail(Object input) throws Exception {
+ return accountResolver.resolveByNameOrEmail(input.toString()).asIdSet();
+ }
+
+ private void setPreferredEmailBypassingUniquenessCheck(Account.Id id, String email)
+ throws Exception {
+ Optional<AccountState> result =
+ accountsUpdateProvider
+ .get()
+ .update("Force set preferred email", id, (s, u) -> u.setPreferredEmail(email));
+ assertThat(result.map(a -> a.getAccount().getPreferredEmail())).hasValue(email);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/account/BUILD b/javatests/com/google/gerrit/acceptance/server/account/BUILD
new file mode 100644
index 0000000000..48fac9941e
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/account/BUILD
@@ -0,0 +1,7 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "server_account",
+ labels = ["server"],
+)
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index d5cbc23666..35fe48f7d3 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -16,7 +16,6 @@ package com.google.gerrit.acceptance.server.change;
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.PushOneCommit.FILE_NAME;
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
import static java.util.stream.Collectors.groupingBy;
@@ -28,9 +27,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
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.extensions.api.changes.DeleteCommentInput;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -79,20 +79,18 @@ import org.junit.Test;
@NoHttpd
public class CommentsIT extends AbstractDaemonTest {
-
+ @Inject private ChangeNoteUtil noteUtil;
+ @Inject private FakeEmailSender email;
+ @Inject private ProjectOperations projectOperations;
@Inject private Provider<ChangesCollection> changes;
-
@Inject private Provider<PostReview> postReview;
-
- @Inject private FakeEmailSender email;
-
- @Inject private ChangeNoteUtil noteUtil;
+ @Inject private RequestScopeOperations requestScopeOperations;
private final Integer[] lines = {0, 1};
@Before
public void setUp() {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
}
@Test
@@ -157,7 +155,7 @@ public class CommentsIT extends AbstractDaemonTest {
String file = "file";
String contents = "contents " + line;
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents);
+ 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();
@@ -181,7 +179,7 @@ public class CommentsIT extends AbstractDaemonTest {
String file = "file";
String contents = "contents " + line;
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents);
+ 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();
@@ -213,7 +211,7 @@ public class CommentsIT extends AbstractDaemonTest {
String file = "file";
String contents = "contents " + line;
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents);
+ 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();
@@ -293,7 +291,7 @@ public class CommentsIT extends AbstractDaemonTest {
public void postCommentsUnreachableData() throws Exception {
String file = "file";
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, "l1\nl2\n");
+ pushFactory.create(admin.newIdent(), testRepo, "first subject", file, "l1\nl2\n");
String dest = "refs/for/master";
PushOneCommit.Result r1 = push.to(dest);
@@ -304,7 +302,7 @@ public class CommentsIT extends AbstractDaemonTest {
PushOneCommit.Result r2 = amendChange(r1.getChangeId());
r2.assertOkStatus();
- String draftRefName = RefNames.refsDraftComments(r1.getChange().getId(), admin.getId());
+ String draftRefName = RefNames.refsDraftComments(r1.getChange().getId(), admin.id());
DraftInput draft = newDraft(file, Side.REVISION, 1, "comment");
addDraft(changeId, "1", draft);
@@ -332,7 +330,7 @@ public class CommentsIT extends AbstractDaemonTest {
public void listComments() throws Exception {
String file = "file";
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, "contents");
+ 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();
@@ -363,15 +361,14 @@ public class CommentsIT extends AbstractDaemonTest {
/**
* This test makes sure that the commits in the refs/draft-comments ref in NoteDb have no parent
* commits. This is important so that each new draft update (add, modify, delete) does not keep
- * track of previous history. Run the test with this flag: --test_env=GERRIT_NOTEDB=ON
+ * track of previous history.
*/
@Test
public void commitsInDraftCommentsRefHaveNoParent() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result r = createChange();
String changeId = r.getChangeId();
String revId = r.getCommit().getName();
- String draftRefName = RefNames.refsDraftComments(r.getChange().getId(), user.getId());
+ String draftRefName = RefNames.refsDraftComments(r.getChange().getId(), user.id());
DraftInput comment1 = newDraft("file_1", Side.REVISION, 1, "comment 1");
CommentInfo commentInfo1 = addDraft(changeId, revId, comment1);
@@ -390,7 +387,7 @@ public class CommentsIT extends AbstractDaemonTest {
.containsExactly("comment 2");
deleteDraft(changeId, revId, commentInfo2.id);
- assertThat(getHeadOfDraftCommentsRef(draftRefName)).isNull();
+ assertThat(projectOperations.project(allUsers).hasHead(draftRefName)).isFalse();
assertThat(getDraftComments(changeId, revId).values().stream().flatMap(List::stream)).isEmpty();
}
@@ -480,7 +477,7 @@ public class CommentsIT extends AbstractDaemonTest {
String file = "file";
String contents = "contents " + line;
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents);
+ 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();
@@ -523,7 +520,7 @@ public class CommentsIT extends AbstractDaemonTest {
PushOneCommit.Result r2 =
pushFactory
- .create(db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "content")
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "content")
.to("refs/for/master");
changeId = r2.getChangeId();
revId = r2.getCommit().getName();
@@ -538,11 +535,10 @@ public class CommentsIT extends AbstractDaemonTest {
PushOneCommit.Result r2 =
pushFactory
- .create(
- db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
.to("refs/for/master");
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
addDraft(
r1.getChangeId(),
r1.getCommit().getName(),
@@ -552,13 +548,13 @@ public class CommentsIT extends AbstractDaemonTest {
r2.getCommit().getName(),
newDraft(FILE_NAME, Side.REVISION, 1, "typo: content"));
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
addDraft(
r2.getChangeId(),
r2.getCommit().getName(),
newDraft(FILE_NAME, Side.REVISION, 1, "+1, please fix"));
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
Map<String, List<CommentInfo>> actual = gApi.changes().id(r1.getChangeId()).drafts();
assertThat(actual.keySet()).containsExactly(FILE_NAME);
List<CommentInfo> comments = actual.get(FILE_NAME);
@@ -585,8 +581,7 @@ public class CommentsIT extends AbstractDaemonTest {
PushOneCommit.Result r2 =
pushFactory
- .create(
- db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", r1.getChangeId())
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", r1.getChangeId())
.to("refs/for/master");
addComment(r1, "nit: trailing whitespace");
@@ -599,14 +594,14 @@ public class CommentsIT extends AbstractDaemonTest {
assertThat(comments).hasSize(2);
CommentInfo c1 = comments.get(0);
- assertThat(c1.author._accountId).isEqualTo(user.getId().get());
+ assertThat(c1.author._accountId).isEqualTo(user.id().get());
assertThat(c1.patchSet).isEqualTo(1);
assertThat(c1.message).isEqualTo("nit: trailing whitespace");
assertThat(c1.side).isNull();
assertThat(c1.line).isEqualTo(1);
CommentInfo c2 = comments.get(1);
- assertThat(c2.author._accountId).isEqualTo(user.getId().get());
+ assertThat(c2.author._accountId).isEqualTo(user.id().get());
assertThat(c2.patchSet).isEqualTo(2);
assertThat(c2.message).isEqualTo("typo: content");
assertThat(c2.side).isNull();
@@ -631,19 +626,18 @@ public class CommentsIT extends AbstractDaemonTest {
String changeId = result.getChangeId();
pushFactory
- .create(db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "initial content\n", changeId)
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "initial content\n", changeId)
.to("refs/heads/master");
PushOneCommit.Result r1 =
pushFactory
- .create(db, admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "old boring content\n")
+ .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "old boring content\n")
.to("refs/for/master");
PushOneCommit.Result r2 =
pushFactory
.create(
- db,
- admin.getIdent(),
+ admin.newIdent(),
testRepo,
SUBJECT,
FILE_NAME,
@@ -683,11 +677,11 @@ public class CommentsIT extends AbstractDaemonTest {
other.getCommit().getName(),
newDraft(FILE_NAME, Side.REVISION, 1, "unrelated comment"));
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
// Drafts by other users aren't returned.
addDraft(
r2.getChangeId(), r2.getCommit().getName(), newDraft(FILE_NAME, Side.REVISION, 2, "oops"));
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
ReviewInput reviewInput = new ReviewInput();
reviewInput.drafts = DraftHandling.PUBLISH_ALL_REVISIONS;
@@ -837,7 +831,7 @@ public class CommentsIT extends AbstractDaemonTest {
}
@Test
- public void queryChangesWithUnresolvedCommentCount() throws Exception {
+ public void queryChangesWithCommentCount() throws Exception {
// PS1 has three comments in three different threads, PS2 has one comment in one thread.
PushOneCommit.Result result = createChange("change 1", FILE_NAME, "content 1");
String changeId1 = result.getChangeId();
@@ -866,16 +860,16 @@ public class CommentsIT extends AbstractDaemonTest {
assertThat(comments.get(FILE_NAME)).hasSize(1);
addComment(result, "comment 2", false, true, comments.get(FILE_NAME).get(0).id);
- AcceptanceTestRequestScope.Context ctx = disableDb();
- try {
+ try (AutoCloseable ignored = disableNoteDb()) {
ChangeInfo changeInfo1 = Iterables.getOnlyElement(query(changeId1));
ChangeInfo changeInfo2 = Iterables.getOnlyElement(query(changeId2));
ChangeInfo changeInfo3 = Iterables.getOnlyElement(query(changeId3));
assertThat(changeInfo1.unresolvedCommentCount).isEqualTo(2);
+ assertThat(changeInfo1.totalCommentCount).isEqualTo(4);
assertThat(changeInfo2.unresolvedCommentCount).isEqualTo(0);
+ assertThat(changeInfo2.totalCommentCount).isEqualTo(2);
assertThat(changeInfo3.unresolvedCommentCount).isEqualTo(1);
- } finally {
- enableDb(ctx);
+ assertThat(changeInfo3.totalCommentCount).isEqualTo(2);
}
}
@@ -893,7 +887,7 @@ public class CommentsIT extends AbstractDaemonTest {
String uuid = commentsMap.get(targetComment.path).get(0).id;
DeleteCommentInput input = new DeleteCommentInput("contains confidential information");
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.changes().id(result.getChangeId()).current().comment(uuid).delete(input);
}
@@ -964,12 +958,9 @@ public class CommentsIT extends AbstractDaemonTest {
// PS4 has comments [c7, c8].
assertThat(getRevisionComments(changeId, ps4)).hasSize(2);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
for (int i = 0; i < commentsBeforeDelete.size(); i++) {
- List<RevCommit> commitsBeforeDelete = new ArrayList<>();
- if (notesMigration.commitChangeWrites()) {
- commitsBeforeDelete = getChangeMetaCommitsInReverseOrder(id);
- }
+ List<RevCommit> commitsBeforeDelete = getChangeMetaCommitsInReverseOrder(id);
CommentInfo comment = commentsBeforeDelete.get(i);
String uuid = comment.id;
@@ -982,15 +973,13 @@ public class CommentsIT extends AbstractDaemonTest {
gApi.changes().id(changeId).revision(patchSet).comment(uuid).delete(input);
String expectedMsg =
- String.format("Comment removed by: %s; Reason: %s", admin.fullName, input.reason);
+ String.format("Comment removed by: %s; Reason: %s", admin.fullName(), input.reason);
assertThat(updatedComment.message).isEqualTo(expectedMsg);
oldComment.message = expectedMsg;
assertThat(updatedComment).isEqualTo(oldComment);
// Check the NoteDb state after the deletion.
- if (notesMigration.commitChangeWrites()) {
- assertMetaBranchCommitsAfterRewriting(commitsBeforeDelete, id, uuid, expectedMsg);
- }
+ assertMetaBranchCommitsAfterRewriting(commitsBeforeDelete, id, uuid, expectedMsg);
comment.message = expectedMsg;
commentsBeforeDelete.set(i, comment);
@@ -1037,12 +1026,9 @@ public class CommentsIT extends AbstractDaemonTest {
String uuid = targetComment.get().id;
CommentInfo oldComment = gApi.changes().id(changeId).revision(ps1).comment(uuid).get();
- List<RevCommit> commitsBeforeDelete = new ArrayList<>();
- if (notesMigration.commitChangeWrites()) {
- commitsBeforeDelete = getChangeMetaCommitsInReverseOrder(id);
- }
+ List<RevCommit> commitsBeforeDelete = getChangeMetaCommitsInReverseOrder(id);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
for (int i = 0; i < 3; i++) {
DeleteCommentInput input = new DeleteCommentInput("delete comment 2, iteration: " + i);
gApi.changes().id(changeId).revision(ps1).comment(uuid).delete(input);
@@ -1051,26 +1037,24 @@ public class CommentsIT extends AbstractDaemonTest {
CommentInfo updatedComment = gApi.changes().id(changeId).revision(ps1).comment(uuid).get();
String expectedMsg =
String.format(
- "Comment removed by: %s; Reason: %s", admin.fullName, "delete comment 2, iteration: 2");
+ "Comment removed by: %s; Reason: %s",
+ admin.fullName(), "delete comment 2, iteration: 2");
assertThat(updatedComment.message).isEqualTo(expectedMsg);
oldComment.message = expectedMsg;
assertThat(updatedComment).isEqualTo(oldComment);
- if (notesMigration.commitChangeWrites()) {
- assertMetaBranchCommitsAfterRewriting(commitsBeforeDelete, id, uuid, expectedMsg);
- }
+ assertMetaBranchCommitsAfterRewriting(commitsBeforeDelete, id, uuid, expectedMsg);
assertThat(getChangeSortedComments(id.get())).hasSize(3);
}
@Test
public void jsonCommentHasLegacyFormatFalse() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result result = createChange();
Change.Id changeId = result.getChange().getId();
addComment(result.getChangeId(), "comment");
Collection<com.google.gerrit.reviewdb.client.Comment> comments =
- notesFactory.createChecked(db, project, changeId).getComments().values();
+ 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");
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index 2af90a8cfc..7c375bd0d5 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -15,11 +15,9 @@
package com.google.gerrit.acceptance.server.change;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.extensions.common.ProblemInfo.Status.FIXED;
import static com.google.gerrit.extensions.common.ProblemInfo.Status.FIX_FAILED;
import static com.google.gerrit.testing.TestChanges.newPatchSet;
-import static java.util.Collections.singleton;
import static java.util.Objects.requireNonNull;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -28,34 +26,28 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.FixInput;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
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.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.Sequences;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ConsistencyChecker;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
-import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
+import com.google.gerrit.server.notedb.Sequences;
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.RepoContext;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.NoteDbMode;
import com.google.gerrit.testing.TestChanges;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
@@ -88,8 +80,6 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Inject private ChangeNoteUtil noteUtil;
- @Inject @AnonymousCowardName private String anonymousCowardName;
-
@Inject private Sequences sequences;
private RevCommit tip;
@@ -97,11 +87,6 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
private ConsistencyChecker checker;
private TestRepository<InMemoryRepository> serverSideTestRepo;
- private void assumeNoteDbDisabled() {
- assume().that(notesMigration.readChanges()).isFalse();
- assume().that(NoteDbMode.get()).isNotEqualTo(NoteDbMode.CHECK);
- }
-
@Before
public void setUp() throws Exception {
serverSideTestRepo =
@@ -110,7 +95,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
serverSideTestRepo
.getRevWalk()
.parseCommit(serverSideTestRepo.getRepository().exactRef("HEAD").getObjectId());
- adminId = admin.getId();
+ adminId = admin.id();
checker = checkerProvider.get();
}
@@ -129,41 +114,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
public void missingOwner() throws Exception {
TestAccount owner = accountCreator.create("missing");
ChangeNotes notes = insertChange(owner);
- deleteUserBranch(owner.getId());
-
- assertProblems(notes, null, problem("Missing change owner: " + owner.getId()));
- }
-
- @Test
- public void missingRepo() throws Exception {
- // NoteDb can't have a change without a repo.
- assumeNoteDbDisabled();
-
- ChangeNotes notes = insertChange();
- Project.NameKey name = notes.getProjectName();
- ((InMemoryRepositoryManager) repoManager).deleteRepository(name);
- assertThat(checker.check(notes, null).problems())
- .containsExactly(problem("Destination repository not found: " + name));
- }
-
- @Test
- public void invalidRevision() throws Exception {
- // NoteDb always parses the revision when inserting a patch set, so we can't
- // create an invalid patch set.
- assumeNoteDbDisabled();
-
- ChangeNotes notes = insertChange();
- PatchSet ps =
- newPatchSet(
- notes.getChange().currentPatchSetId(),
- "fooooooooooooooooooooooooooooooooooooooo",
- adminId);
- db.patchSets().update(singleton(ps));
+ deleteUserBranch(owner.id());
- assertProblems(
- notes,
- null,
- problem("Invalid revision on patch set 1: fooooooooooooooooooooooooooooooooooooooo"));
+ assertProblems(notes, null, problem("Missing change owner: " + owner.id()));
}
// No test for ref existing but object missing; InMemoryRepository won't let
@@ -201,7 +154,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
public void patchSetRefMissing() throws Exception {
ChangeNotes notes = insertChange();
serverSideTestRepo.update(
- "refs/other/foo", ObjectId.fromString(psUtil.current(db, notes).getRevision().get()));
+ "refs/other/foo", ObjectId.fromString(psUtil.current(notes).getRevision().get()));
String refName = notes.getChange().currentPatchSetId().toRefName();
deleteRef(refName);
@@ -211,7 +164,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void patchSetRefMissingWithFix() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
serverSideTestRepo.update("refs/other/foo", ObjectId.fromString(rev));
String refName = notes.getChange().currentPatchSetId().toRefName();
deleteRef(refName);
@@ -225,7 +178,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void patchSetObjectAndRefMissingWithDeletingPatchSet() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(db, notes);
+ PatchSet ps1 = psUtil.current(notes);
String rev2 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
PatchSet ps2 = insertMissingPatchSet(notes, rev2);
@@ -241,20 +194,20 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(1);
- assertThat(psUtil.get(db, notes, ps1.getId())).isNotNull();
- assertThat(psUtil.get(db, notes, ps2.getId())).isNull();
+ assertThat(psUtil.get(notes, ps1.getId())).isNotNull();
+ assertThat(psUtil.get(notes, ps2.getId())).isNull();
}
@Test
public void patchSetMultipleObjectsMissingWithDeletingPatchSets() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(db, notes);
+ PatchSet ps1 = psUtil.current(notes);
String rev2 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
PatchSet ps2 = insertMissingPatchSet(notes, rev2);
notes = incrementPatchSet(reload(notes));
- PatchSet ps3 = psUtil.current(db, notes);
+ PatchSet ps3 = psUtil.current(notes);
String rev4 = "c0ffeeeec0ffeeeec0ffeeeec0ffeeeec0ffeeee";
PatchSet ps4 = insertMissingPatchSet(notes, rev4);
@@ -272,27 +225,20 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(3);
- assertThat(psUtil.get(db, notes, ps1.getId())).isNotNull();
- assertThat(psUtil.get(db, notes, ps2.getId())).isNull();
- assertThat(psUtil.get(db, notes, ps3.getId())).isNotNull();
- assertThat(psUtil.get(db, notes, ps4.getId())).isNull();
+ 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();
}
@Test
public void onlyPatchSetObjectMissingWithFix() throws Exception {
- Change c = TestChanges.newChange(project, admin.getId(), sequences.nextChangeId());
-
- // Set review started, mimicking Schema_153, so tests pass with NoteDbMode.CHECK.
- c.setReviewStarted(true);
+ Change c = TestChanges.newChange(project, admin.id(), sequences.nextChangeId());
PatchSet.Id psId = c.currentPatchSetId();
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
PatchSet ps = newPatchSet(psId, rev, adminId);
- if (notesMigration.changePrimaryStorage() == PrimaryStorage.REVIEW_DB) {
- db.changes().insert(singleton(c));
- db.patchSets().insert(singleton(ps));
- }
addNoteDbCommit(
c.getId(),
"Create change\n"
@@ -311,8 +257,8 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
+ "Groups: "
+ rev
+ "\n");
- indexer.index(db, c.getProject(), c.getId());
- ChangeNotes notes = changeNotesFactory.create(db, c.getProject(), c.getId());
+ indexer.index(c.getProject(), c.getId());
+ ChangeNotes notes = changeNotesFactory.create(c.getProject(), c.getId());
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
@@ -327,23 +273,13 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId().get()).isEqualTo(1);
- assertThat(psUtil.current(db, notes)).isNotNull();
- }
-
- @Test
- public void currentPatchSetMissing() throws Exception {
- // NoteDb can't create a change without a patch set.
- assumeNoteDbDisabled();
-
- ChangeNotes notes = insertChange();
- db.patchSets().deleteKeys(singleton(notes.getChange().currentPatchSetId()));
- assertProblems(notes, null, problem("Current patch set 1 not found"));
+ assertThat(psUtil.current(notes)).isNotNull();
}
@Test
public void duplicatePatchSetRevisions() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(db, notes);
+ PatchSet ps1 = psUtil.current(notes);
String rev = ps1.getRevision().get();
notes =
@@ -376,7 +312,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes.getChangeId(),
new BatchUpdateOp() {
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
ctx.getChange().setStatus(Change.Status.MERGED);
ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
return true;
@@ -386,7 +322,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
}
notes = reload(notes);
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
ObjectId tip = getDestRef(notes);
assertProblems(
notes,
@@ -403,7 +339,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void newChangeIsMerged() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
serverSideTestRepo
.branch(notes.getChange().getDest().get())
.update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
@@ -423,7 +359,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void newChangeIsMergedWithFix() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
serverSideTestRepo
.branch(notes.getChange().getDest().get())
.update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
@@ -442,14 +378,14 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
"Marked change as merged"));
notes = reload(notes);
- assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(notes.getChange().isMerged()).isTrue();
assertNoProblems(notes, null);
}
@Test
public void extensionApiReturnsUpdatedValueAfterFix() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
serverSideTestRepo
.branch(notes.getChange().getDest().get())
.update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
@@ -464,7 +400,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitIsLatestPatchSet() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
serverSideTestRepo
.branch(notes.getChange().getDest().get())
.update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev)));
@@ -485,14 +421,14 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
"Marked change as merged"));
notes = reload(notes);
- assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
+ assertThat(notes.getChange().isMerged()).isTrue();
assertNoProblems(notes, null);
}
@Test
public void expectedMergedCommitNotMergedIntoDestination() throws Exception {
ChangeNotes notes = insertChange();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
serverSideTestRepo.branch(notes.getChange().getDest().get()).update(commit);
@@ -515,7 +451,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
public void createNewPatchSetForExpectedMergeCommitWithNoChangeId() throws Exception {
ChangeNotes notes = insertChange();
String dest = notes.getChange().getDest().get();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
RevCommit mergedAs =
@@ -547,7 +483,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(psUtil.get(db, notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
+ assertThat(psUtil.get(notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
assertNoProblems(notes, null);
}
@@ -556,7 +492,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
public void createNewPatchSetForExpectedMergeCommitWithChangeId() throws Exception {
ChangeNotes notes = insertChange();
String dest = notes.getChange().getDest().get();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
RevCommit mergedAs =
@@ -595,7 +531,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(psUtil.get(db, notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
+ assertThat(psUtil.get(notes, psId2).getRevision().get()).isEqualTo(mergedAs.name());
assertNoProblems(notes, null);
}
@@ -603,10 +539,10 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitIsOldPatchSetOfSameChange() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(db, notes);
+ PatchSet ps1 = psUtil.current(notes);
String rev1 = ps1.getRevision().get();
notes = incrementPatchSet(notes);
- PatchSet ps2 = psUtil.current(db, notes);
+ PatchSet ps2 = psUtil.current(notes);
serverSideTestRepo
.branch(notes.getChange().getDest().get())
.update(serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev1)));
@@ -635,15 +571,15 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
PatchSet.Id psId3 = new PatchSet.Id(notes.getChangeId(), 3);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId3);
- assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(ps2.getId(), psId3);
- assertThat(psUtil.get(db, notes, psId3).getRevision().get()).isEqualTo(rev1);
+ assertThat(notes.getChange().isMerged()).isTrue();
+ assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps2.getId(), psId3);
+ assertThat(psUtil.get(notes, psId3).getRevision().get()).isEqualTo(rev1);
}
@Test
public void expectedMergedCommitIsDanglingPatchSetOlderThanCurrent() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(db, notes);
+ 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);
@@ -652,7 +588,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
serverSideTestRepo.branch(psId2.toRefName()).update(commit2);
notes = incrementPatchSet(notes);
- PatchSet ps3 = psUtil.current(db, notes);
+ PatchSet ps3 = psUtil.current(notes);
assertThat(ps3.getId().get()).isEqualTo(3);
serverSideTestRepo
@@ -683,16 +619,16 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
PatchSet.Id psId4 = new PatchSet.Id(notes.getChangeId(), 4);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId4);
- assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(psUtil.byChangeAsMap(db, notes).keySet())
+ assertThat(notes.getChange().isMerged()).isTrue();
+ assertThat(psUtil.byChangeAsMap(notes).keySet())
.containsExactly(ps1.getId(), ps3.getId(), psId4);
- assertThat(psUtil.get(db, notes, psId4).getRevision().get()).isEqualTo(rev2);
+ assertThat(psUtil.get(notes, psId4).getRevision().get()).isEqualTo(rev2);
}
@Test
public void expectedMergedCommitIsDanglingPatchSetNewerThanCurrent() throws Exception {
ChangeNotes notes = insertChange();
- PatchSet ps1 = psUtil.current(db, notes);
+ PatchSet ps1 = psUtil.current(notes);
// Create dangling ref with no patch set.
PatchSet.Id psId2 = new PatchSet.Id(notes.getChangeId(), 2);
@@ -720,9 +656,9 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
notes = reload(notes);
assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId2);
- assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(ps1.getId(), psId2);
- assertThat(psUtil.get(db, notes, psId2).getRevision().get()).isEqualTo(rev2);
+ assertThat(notes.getChange().isMerged()).isTrue();
+ assertThat(psUtil.byChangeAsMap(notes).keySet()).containsExactly(ps1.getId(), psId2);
+ assertThat(psUtil.get(notes, psId2).getRevision().get()).isEqualTo(rev2);
}
@Test
@@ -730,7 +666,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
ChangeNotes notes = insertChange();
String dest = notes.getChange().getDest().get();
RevCommit parent = serverSideTestRepo.branch(dest).commit().message("parent").create();
- String rev = psUtil.current(db, notes).getRevision().get();
+ String rev = psUtil.current(notes).getRevision().get();
RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
serverSideTestRepo.branch(dest).update(commit);
@@ -764,19 +700,19 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
@Test
public void expectedMergedCommitMatchesMultiplePatchSets() throws Exception {
ChangeNotes notes1 = insertChange();
- PatchSet.Id psId1 = psUtil.current(db, notes1).getId();
+ PatchSet.Id psId1 = psUtil.current(notes1).getId();
String dest = notes1.getChange().getDest().get();
- String rev = psUtil.current(db, notes1).getRevision().get();
+ String rev = psUtil.current(notes1).getRevision().get();
RevCommit commit = serverSideTestRepo.getRevWalk().parseCommit(ObjectId.fromString(rev));
serverSideTestRepo.branch(dest).update(commit);
ChangeNotes notes2 = insertChange();
notes2 = incrementPatchSet(notes2, commit);
- PatchSet.Id psId2 = psUtil.current(db, notes2).getId();
+ PatchSet.Id psId2 = psUtil.current(notes2).getId();
ChangeNotes notes3 = insertChange();
notes3 = incrementPatchSet(notes3, commit);
- PatchSet.Id psId3 = psUtil.current(db, notes3).getId();
+ PatchSet.Id psId3 = psUtil.current(notes3).getId();
FixInput fix = new FixInput();
fix.expectMergedAs = commit.name();
@@ -796,7 +732,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
}
private BatchUpdate newUpdate(Account.Id owner) {
- return batchUpdateFactory.create(db, project, userFactory.create(owner), TimeUtil.nowTs());
+ return batchUpdateFactory.create(project, userFactory.create(owner), TimeUtil.nowTs());
}
private ChangeNotes insertChange() throws Exception {
@@ -810,18 +746,18 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
private ChangeNotes insertChange(TestAccount owner, String dest) throws Exception {
Change.Id id = new Change.Id(sequences.nextChangeId());
ChangeInserter ins;
- try (BatchUpdate bu = newUpdate(owner.getId())) {
+ try (BatchUpdate bu = newUpdate(owner.id())) {
RevCommit commit = patchSetCommit(new PatchSet.Id(id, 1));
+ bu.setNotify(NotifyResolver.Result.none());
ins =
changeInserterFactory
.create(id, commit, dest)
.setValidate(false)
- .setNotify(NotifyHandling.NONE)
.setFireRevisionCreated(false)
.setSendMail(false);
bu.insertChange(ins).execute();
}
- return changeNotesFactory.create(db, project, ins.getChange().getId());
+ return changeNotesFactory.create(project, ins.getChange().getId());
}
private PatchSet.Id nextPatchSetId(ChangeNotes notes) throws Exception {
@@ -836,19 +772,19 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
private ChangeNotes incrementPatchSet(ChangeNotes notes, RevCommit commit) throws Exception {
PatchSetInserter ins;
try (BatchUpdate bu = newUpdate(notes.getChange().getOwner())) {
+ bu.setNotify(NotifyResolver.Result.none());
ins =
patchSetInserterFactory
.create(notes, nextPatchSetId(notes), commit)
.setValidate(false)
- .setFireRevisionCreated(false)
- .setNotify(NotifyHandling.NONE);
+ .setFireRevisionCreated(false);
bu.addOp(notes.getChangeId(), ins).execute();
}
return reload(notes);
}
private ChangeNotes reload(ChangeNotes notes) throws Exception {
- return changeNotesFactory.create(db, notes.getChange().getProject(), notes.getChangeId());
+ return changeNotesFactory.create(notes.getChange().getProject(), notes.getChangeId());
}
private RevCommit patchSetCommit(PatchSet.Id psId) throws Exception {
@@ -865,11 +801,6 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
PatchSet ps = newPatchSet(psId, rev, adminId);
- if (PrimaryStorage.of(c) == PrimaryStorage.REVIEW_DB) {
- db.patchSets().insert(singleton(ps));
- db.changes().update(singleton(c));
- }
-
addNoteDbCommit(
c.getId(),
"Update patch set "
@@ -885,7 +816,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
+ "Subject: "
+ subject
+ "\n");
- indexer.index(db, c.getProject(), c.getId());
+ indexer.index(c.getProject(), c.getId());
return ps;
}
@@ -897,12 +828,8 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
}
private void addNoteDbCommit(Change.Id id, String commitMessage) throws Exception {
- if (!notesMigration.commitChangeWrites()) {
- return;
- }
PersonIdent committer = serverIdent.get();
- PersonIdent author =
- noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
+ PersonIdent author = noteUtil.newIdent(getAccount(admin.id()), committer.getWhen(), committer);
serverSideTestRepo
.branch(RefNames.changeMetaRef(id))
.commit()
@@ -921,7 +848,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
private ChangeNotes mergeChange(ChangeNotes notes) throws Exception {
final ObjectId oldId = getDestRef(notes);
- final ObjectId newId = ObjectId.fromString(psUtil.current(db, notes).getRevision().get());
+ final ObjectId newId = ObjectId.fromString(psUtil.current(notes).getRevision().get());
final String dest = notes.getChange().getDest().get();
try (BatchUpdate bu = newUpdate(adminId)) {
@@ -934,7 +861,7 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
}
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
+ public boolean updateChange(ChangeContext ctx) {
ctx.getChange().setStatus(Change.Status.MERGED);
ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
return true;
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index dd47c011c3..b9b7ab39c8 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -22,17 +22,28 @@ 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.testsuite.account.AccountOperations;
+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.GlobalCapability;
+import com.google.gerrit.common.data.PermissionRule;
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;
@@ -42,10 +53,12 @@ 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.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -66,6 +79,10 @@ public class GetRelatedIT extends AbstractDaemonTest {
return cfg;
}
+ @Inject private AccountOperations accountOperations;
+ @Inject private GroupOperations groupOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
private String systemTimeZone;
@Before
@@ -85,7 +102,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
@Test
public void getRelatedNoResult() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
+ PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
assertRelated(push.to("refs/for/master").getPatchSetId());
}
@@ -516,7 +533,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
// Pretend PS1,1 was pushed before the groups field was added.
clearGroups(psId1_1);
- indexer.index(changeDataFactory.create(db, project, psId1_1.getParentKey()));
+ indexer.index(changeDataFactory.create(project, psId1_1.getParentKey()));
// PS1,1 has no groups, so disappeared from related changes.
assertRelated(psId2_1);
@@ -558,7 +575,6 @@ public class GetRelatedIT extends AbstractDaemonTest {
@Test
public void getRelatedManyGroups() throws Exception {
- List<RevCommit> commits = new ArrayList<>();
RevCommit last = null;
int n = 2 * MAX_TERMS;
assertThat(n).isGreaterThan(indexConfig.maxTerms());
@@ -567,22 +583,82 @@ public class GetRelatedIT extends AbstractDaemonTest {
last = cb.add("a.txt", Integer.toString(i)).message("subject: " + i).create();
testRepo.reset(last);
assertPushOk(pushHead(testRepo, "refs/for/master", false), "refs/for/master");
- commits.add(last);
}
ChangeData cd = getChange(last);
assertThat(cd.patchSets()).hasSize(n);
- assertThat(GetRelated.getAllGroups(cd.notes(), db, psUtil)).hasSize(n);
+ assertThat(GetRelated.getAllGroups(cd.notes(), psUtil)).hasSize(n);
assertRelated(cd.change().currentPatchSetId());
}
- private List<RelatedChangeAndCommitInfo> getRelated(PatchSet.Id ps) throws Exception {
- return getRelated(ps.getParentKey(), ps.get());
+ @Test
+ public void getRelatedManyChanges() throws Exception {
+ List<ObjectId> commitIds = new ArrayList<>();
+ for (int i = 1; i <= 5; i++) {
+ commitIds.add(commitBuilder().add(i + ".txt", "i").message("subject: " + i).create().copy());
+ }
+ pushHead(testRepo, "refs/for/master", false);
+
+ List<RelatedChangeAndCommitInfo> expected = new ArrayList<>(commitIds.size());
+ for (ObjectId commitId : commitIds) {
+ expected.add(changeAndCommit(getPatchSetId(commitId), commitId, 1));
+ }
+ Collections.reverse(expected);
+
+ PatchSet.Id lastPsId = getPatchSetId(Iterables.getLast(commitIds));
+ assertRelated(lastPsId, expected);
+
+ 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();
+ }
+ requestScopeOperations.setApiUser(accountId);
+
+ assertRelated(lastPsId, expected);
+ }
+
+ @Test
+ public void stateOfRelatedChangesMatchesDocumentedValues() throws Exception {
+ // Set up three related changes, one new, the other abandoned, and the third merged.
+ RevCommit commit1 =
+ commitBuilder().add("a.txt", "File content 1").message("Subject 1").create();
+ RevCommit commit2 =
+ commitBuilder().add("b.txt", "File content 2").message("Subject 2").create();
+ RevCommit commit3 =
+ commitBuilder().add("c.txt", "File content 3").message("Subject 3").create();
+ pushHead(testRepo, "refs/for/master", false);
+ Change change1 = getChange(commit1).change();
+ Change change2 = getChange(commit2).change();
+ Change change3 = getChange(commit3).change();
+ gApi.changes().id(change1.getChangeId()).current().review(ReviewInput.approve());
+ gApi.changes().id(change1.getChangeId()).current().submit();
+ gApi.changes().id(change2.getChangeId()).abandon();
+
+ List<RelatedChangeAndCommitInfo> relatedChanges =
+ gApi.changes().id(change3.getChangeId()).current().related().changes;
+
+ // Ensure that our REST API returns the states exactly as documented (and required by the
+ // frontend).
+ assertThat(relatedChanges)
+ .comparingElementsUsing(getRelatedChangeToStatusCorrespondence())
+ .containsExactly("NEW", "ABANDONED", "MERGED");
}
- private List<RelatedChangeAndCommitInfo> getRelated(Change.Id changeId, int ps) throws Exception {
- return gApi.changes().id(changeId.get()).revision(ps).related().changes;
+ 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);
+ }
+ },
+ "has status");
}
private RevCommit parseBody(RevCommit c) throws Exception {
@@ -612,15 +688,14 @@ public class GetRelatedIT extends AbstractDaemonTest {
}
private void clearGroups(PatchSet.Id psId) throws Exception {
- try (BatchUpdate bu = batchUpdateFactory.create(db, project, user(user), TimeUtil.nowTs())) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user(user), TimeUtil.nowTs())) {
bu.addOp(
psId.getParentKey(),
new BatchUpdateOp() {
@Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
- PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
- psUtil.setGroups(ctx.getDb(), ctx.getUpdate(psId), ps, ImmutableList.<String>of());
- ctx.dontBumpLastUpdatedOn();
+ public boolean updateChange(ChangeContext ctx) {
+ PatchSet ps = psUtil.get(ctx.getNotes(), psId);
+ psUtil.setGroups(ctx.getUpdate(psId), ps, ImmutableList.of());
return true;
}
});
@@ -630,12 +705,18 @@ public class GetRelatedIT extends AbstractDaemonTest {
private void assertRelated(PatchSet.Id psId, RelatedChangeAndCommitInfo... expected)
throws Exception {
- List<RelatedChangeAndCommitInfo> actual = getRelated(psId);
- assertThat(actual).named("related to " + psId).hasSize(expected.length);
+ assertRelated(psId, Arrays.asList(expected));
+ }
+
+ 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());
for (int i = 0; i < actual.size(); i++) {
String name = "index " + i + " related to " + psId;
RelatedChangeAndCommitInfo a = actual.get(i);
- RelatedChangeAndCommitInfo e = expected[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);
// Don't bother checking changeId; assume _changeNumber is sufficient.
@@ -644,6 +725,7 @@ public class GetRelatedIT extends AbstractDaemonTest {
assertThat(a._currentRevisionNumber)
.named("current revision of " + name)
.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 424dbac422..42fdae42b7 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
@@ -275,7 +275,7 @@ public class PatchListCacheIT extends AbstractDaemonTest {
PatchListCacheImpl.LargeObjectTombstone tombstone =
new PatchListCacheImpl.LargeObjectTombstone();
abstractPatchListCache.put(key, tombstone);
- assertThat(abstractPatchListCache.getIfPresent(key)).isSameAs(tombstone);
+ assertThat(abstractPatchListCache.getIfPresent(key)).isSameInstanceAs(tombstone);
}
private static void assertAdded(String expectedNewName, PatchListEntry e) {
diff --git a/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java b/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java
index 304a1e48a4..389859cde2 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/SubmittedTogetherIT.java
@@ -21,6 +21,8 @@ import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.N
import com.google.gerrit.acceptance.AbstractDaemonTest;
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.extensions.api.changes.SubmittedTogetherInfo;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.ListChangesOption;
@@ -29,6 +31,7 @@ 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;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -41,6 +44,9 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
return submitWholeTopicEnabledConfig();
}
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void doesNotIncludeCurrentFiles() throws Exception {
RevCommit c1_1 = commitBuilder().add("a.txt", "1").message("subject: 1").create();
@@ -100,7 +106,7 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
RevCommit b = commitBuilder().add("b", "1").message("change 2").create();
pushHead(testRepo, "refs/for/master", false);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
assertSubmittedTogether(getChangeId(a));
assertSubmittedTogether(getChangeId(b), getChangeId(b), getChangeId(a));
}
@@ -139,7 +145,7 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
pushHead(testRepo, "refs/for/master/" + name("topic"), false);
String id2 = getChangeId(b);
- setApiUserAnonymous();
+ requestScopeOperations.setApiUserAnonymous();
if (isSubmitWholeTopicEnabled()) {
assertSubmittedTogether(id1, id2, id1);
assertSubmittedTogether(id2, id2, id1);
@@ -226,7 +232,8 @@ public class SubmittedTogetherIT extends AbstractDaemonTest {
@Test
public void newBranchTwoChangesTogether() throws Exception {
- Project.NameKey p1 = createProject("a-new-project", null, false);
+ Project.NameKey p1 = projectOperations.newProject().noEmptyCommit().create();
+
TestRepository<?> repo1 = cloneProject(p1);
RevCommit c1 =
diff --git a/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java b/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
index f4a833ff80..4f98dd0683 100644
--- a/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
@@ -74,15 +74,7 @@ public class CommentAddedEventIT extends AbstractDaemonTest {
u.save();
}
- eventListenerRegistration =
- source.add(
- "gerrit",
- new CommentAddedListener() {
- @Override
- public void onCommentAdded(Event event) {
- lastCommentAddedEvent = event;
- }
- });
+ eventListenerRegistration = source.add("gerrit", event -> lastCommentAddedEvent = event);
}
@After
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
index c1b5cf448c..768c269d68 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
@@ -18,23 +18,26 @@ import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.CommentInput;
import com.google.gerrit.extensions.client.Comment;
import com.google.gerrit.extensions.client.Side;
import com.google.gerrit.mail.MailMessage;
+import com.google.inject.Inject;
import java.time.Instant;
import java.util.HashMap;
import org.junit.Ignore;
@Ignore
public class AbstractMailIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
protected MailMessage.Builder messageBuilderWithDefaultFields() {
MailMessage.Builder b = MailMessage.builder();
b.id("some id");
- b.from(user.emailAddress);
- b.addTo(user.emailAddress); // Not evaluated
+ b.from(user.getEmailAddress());
+ b.addTo(user.getEmailAddress()); // Not evaluated
b.subject("");
b.dateReceived(Instant.now());
return b;
@@ -49,12 +52,12 @@ public class AbstractMailIT extends AbstractDaemonTest {
String file = "gerrit-server/test.txt";
String contents = "contents \nlorem \nipsum \nlorem";
PushOneCommit push =
- pushFactory.create(db, admin.getIdent(), testRepo, "first subject", file, contents);
+ pushFactory.create(admin.newIdent(), testRepo, "first subject", file, contents);
PushOneCommit.Result r = push.to("refs/for/master");
String changeId = r.getChangeId();
// Review it
- setApiUser(reviewer);
+ requestScopeOperations.setApiUser(reviewer.id());
ReviewInput input = new ReviewInput();
input.message = "I have two comments";
input.comments = new HashMap<>();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index c84f66fc02..25e07085f9 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -14,7 +14,6 @@
package com.google.gerrit.acceptance.server.mail;
-import static com.google.common.truth.TruthJUnit.assume;
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;
@@ -32,6 +31,7 @@ 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.request.RequestScopeOperations;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.AbandonInput;
@@ -47,16 +47,22 @@ import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
import com.google.gerrit.extensions.client.InheritableBoolean;
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;
+import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
public class ChangeNotificationsIT extends AbstractNotificationTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
/*
* Set up for extra standard test accounts and permissions.
*/
@@ -74,11 +80,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
@Before
public void grantPermissions() throws Exception {
- grant(project, "refs/*", Permission.FORGE_COMMITTER, false, REGISTERED_USERS);
- grant(project, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
- grant(project, "refs/heads/master", Permission.ABANDON, false, REGISTERED_USERS);
- ProjectConfig cfg = projectCache.get(project).getConfig();
- Util.allow(cfg, Permission.forLabel("Code-Review"), -2, +2, REGISTERED_USERS, "refs/*");
+ 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();
+ }
}
/*
@@ -96,6 +105,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ABANDONED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -110,6 +120,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ABANDONED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -125,6 +136,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ABANDONED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -140,6 +152,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ABANDONED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -151,13 +164,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer, sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void abandonReviewableChangeNotifyOwner() throws Exception {
StagedChange sc = stageReviewableChange();
abandon(sc.changeId, sc.owner, OWNER);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -167,7 +181,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
// Self-CC applies *after* need for sending notification is determined.
// Since there are no recipients before including the user taking action,
// there should no notification sent.
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -176,20 +190,21 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
TestAccount other = accountCreator.create("other", "other@example.com", "other");
abandon(sc.changeId, other, CC_ON_OWN_COMMENTS, OWNER);
assertThat(sender).sent("abandon", sc).to(sc.owner).cc(other).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void abandonReviewableChangeNotifyNone() throws Exception {
StagedChange sc = stageReviewableChange();
abandon(sc.changeId, sc.owner, NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void abandonReviewableChangeNotifyNoneCcingSelf() throws Exception {
StagedChange sc = stageReviewableChange();
abandon(sc.changeId, sc.owner, CC_ON_OWN_COMMENTS, NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -203,13 +218,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ABANDONED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void abandonWipChange() throws Exception {
StagedChange sc = stageWipChange();
abandon(sc.changeId, sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -223,6 +239,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ABANDONED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
private void abandon(String changeId, TestAccount by) throws Exception {
@@ -243,7 +260,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
String changeId, TestAccount by, EmailStrategy emailStrategy, @Nullable NotifyHandling notify)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
AbandonInput in = new AbandonInput();
if (notify != null) {
in.notify = notify;
@@ -255,34 +272,10 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
* AddReviewerSender tests.
*/
- private void addReviewerToReviewableChangeInReviewDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
- assertThat(sender)
- .sent("newchange", sc)
- .to(reviewer)
- .cc(sc.reviewer, sc.ccer)
- .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
- .noOneElse();
- }
-
- @Test
- public void addReviewerToReviewableChangeInReviewDbSingly() throws Exception {
- addReviewerToReviewableChangeInReviewDb(singly());
- }
-
- @Test
- public void addReviewerToReviewableChangeInReviewDbBatch() throws Exception {
- addReviewerToReviewableChangeInReviewDb(batch());
- }
-
- private void addReviewerToReviewableChangeInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addReviewerToReviewableChange(Adder adder) throws Exception {
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email());
// TODO(logan): Should CCs be included?
assertThat(sender)
.sent("newchange", sc)
@@ -290,23 +283,23 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeInNoteDbSingly() throws Exception {
- addReviewerToReviewableChangeInNoteDb(singly());
+ public void addReviewerToReviewableChangeSingly() throws Exception {
+ addReviewerToReviewableChange(singly());
}
@Test
- public void addReviewerToReviewableChangeInNoteDbBatch() throws Exception {
- addReviewerToReviewableChangeInNoteDb(batch());
+ public void addReviewerToReviewableChangeBatch() throws Exception {
+ addReviewerToReviewableChange(batch());
}
- private void addReviewerToReviewableChangeByOwnerCcingSelfInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addReviewerToReviewableChangeByOwnerCcingSelf(Adder adder) throws Exception {
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, null);
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, null);
// TODO(logan): Should CCs be included?
assertThat(sender)
.sent("newchange", sc)
@@ -314,24 +307,24 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.owner, sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeByOwnerCcingSelfInNoteDbSingly() throws Exception {
- addReviewerToReviewableChangeByOwnerCcingSelfInNoteDb(singly());
+ public void addReviewerToReviewableChangeByOwnerCcingSelfSingly() throws Exception {
+ addReviewerToReviewableChangeByOwnerCcingSelf(singly());
}
@Test
- public void addReviewerToReviewableChangeByOwnerCcingSelfInNoteDbBatch() throws Exception {
- addReviewerToReviewableChangeByOwnerCcingSelfInNoteDb(batch());
+ public void addReviewerToReviewableChangeByOwnerCcingSelfBatch() throws Exception {
+ addReviewerToReviewableChangeByOwnerCcingSelf(batch());
}
- private void addReviewerToReviewableChangeByOtherInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addReviewerToReviewableChangeByOther(Adder adder) throws Exception {
TestAccount other = accountCreator.create("other", "other@example.com", "other");
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, other, reviewer.email);
+ addReviewer(adder, sc.changeId, other, reviewer.email());
// TODO(logan): Should CCs be included?
assertThat(sender)
.sent("newchange", sc)
@@ -339,24 +332,24 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.owner, sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeByOtherInNoteDbSingly() throws Exception {
- addReviewerToReviewableChangeByOtherInNoteDb(singly());
+ public void addReviewerToReviewableChangeByOtherSingly() throws Exception {
+ addReviewerToReviewableChangeByOther(singly());
}
@Test
- public void addReviewerToReviewableChangeByOtherInNoteDbBatch() throws Exception {
- addReviewerToReviewableChangeByOtherInNoteDb(batch());
+ public void addReviewerToReviewableChangeByOtherBatch() throws Exception {
+ addReviewerToReviewableChangeByOther(batch());
}
- private void addReviewerToReviewableChangeByOtherCcingSelfInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addReviewerToReviewableChangeByOtherCcingSelf(Adder adder) throws Exception {
TestAccount other = accountCreator.create("other", "other@example.com", "other");
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, other, reviewer.email, CC_ON_OWN_COMMENTS, null);
+ addReviewer(adder, sc.changeId, other, reviewer.email(), CC_ON_OWN_COMMENTS, null);
// TODO(logan): Should CCs be included?
assertThat(sender)
.sent("newchange", sc)
@@ -364,38 +357,20 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.owner, sc.reviewer, other)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeByOtherCcingSelfInNoteDbSingly() throws Exception {
- addReviewerToReviewableChangeByOtherCcingSelfInNoteDb(singly());
- }
-
- @Test
- public void addReviewerToReviewableChangeByOtherCcingSelfInNoteDbBatch() throws Exception {
- addReviewerToReviewableChangeByOtherCcingSelfInNoteDb(batch());
- }
-
- private void addReviewerByEmailToReviewableChangeInReviewDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- String email = "addedbyemail@example.com";
- StagedChange sc = stageReviewableChange();
- addReviewer(adder, sc.changeId, sc.owner, email);
- assertThat(sender).notSent();
- }
-
- @Test
- public void addReviewerByEmailToReviewableChangeInReviewDbSingly() throws Exception {
- addReviewerByEmailToReviewableChangeInReviewDb(singly());
+ public void addReviewerToReviewableChangeByOtherCcingSelfSingly() throws Exception {
+ addReviewerToReviewableChangeByOtherCcingSelf(singly());
}
@Test
- public void addReviewerByEmailToReviewableChangeInReviewDbBatch() throws Exception {
- addReviewerByEmailToReviewableChangeInReviewDb(batch());
+ public void addReviewerToReviewableChangeByOtherCcingSelfBatch() throws Exception {
+ addReviewerToReviewableChangeByOtherCcingSelf(batch());
}
- private void addReviewerByEmailToReviewableChangeInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addReviewerByEmailToReviewableChange(Adder adder) throws Exception {
String email = "addedbyemail@example.com";
StagedChange sc = stageReviewableChange();
addReviewer(adder, sc.changeId, sc.owner, email);
@@ -406,23 +381,24 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerByEmailToReviewableChangeInNoteDbSingly() throws Exception {
- addReviewerByEmailToReviewableChangeInNoteDb(singly());
+ public void addReviewerByEmailToReviewableChangeSingly() throws Exception {
+ addReviewerByEmailToReviewableChange(singly());
}
@Test
- public void addReviewerByEmailToReviewableChangeInNoteDbBatch() throws Exception {
- addReviewerByEmailToReviewableChangeInNoteDb(batch());
+ public void addReviewerByEmailToReviewableChangeBatch() throws Exception {
+ addReviewerByEmailToReviewableChange(batch());
}
private void addReviewerToWipChange(Adder adder) throws Exception {
StagedChange sc = stageWipChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
- assertThat(sender).notSent();
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email());
+ assertThat(sender).didNotSend();
}
@Test
@@ -435,29 +411,26 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
addReviewerToWipChange(batch());
}
- private void addReviewerToReviewableWipChange(Adder adder) throws Exception {
- StagedChange sc = stageReviewableWipChange();
- TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
- assertThat(sender).notSent();
- }
-
@Test
public void addReviewerToReviewableWipChangeSingly() throws Exception {
- addReviewerToReviewableWipChange(singly());
+ StagedChange sc = stageReviewableWipChange();
+ TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
+ addReviewer(singly(), sc.changeId, sc.owner, reviewer.email());
+ // TODO(dborowitz): In theory this should match the batch case, but we don't currently pass
+ // enough info into AddReviewersEmail#emailReviewers to distinguish the reviewStarted case.
+ // Complicating the emailReviewers arguments is not the answer; this needs to be rewritten.
+ // Tolerate the difference for now.
+ assertThat(sender).didNotSend();
}
@Test
public void addReviewerToReviewableWipChangeBatch() throws Exception {
- addReviewerToReviewableWipChange(batch());
- }
-
- private void addReviewerToWipChangeInNoteDbNotifyAll(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
- StagedChange sc = stageWipChange();
+ StagedChange sc = stageReviewableWipChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email, NotifyHandling.ALL);
- // TODO(logan): Should CCs be included?
+ addReviewer(batch(), sc.changeId, sc.owner, reviewer.email());
+ // For a review-started WIP change, same as in the notify=ALL case. It's not especially
+ // important to notify just because a reviewer is added, but we do want to notify in the other
+ // case that hits this codepath: posting an actual review.
assertThat(sender)
.sent("newchange", sc)
.to(reviewer)
@@ -466,45 +439,34 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.noOneElse();
}
- @Test
- public void addReviewerToWipChangeInNoteDbNotifyAllSingly() throws Exception {
- addReviewerToWipChangeInNoteDbNotifyAll(singly());
- }
-
- @Test
- public void addReviewerToWipChangeInNoteDbNotifyAllBatch() throws Exception {
- addReviewerToWipChangeInNoteDbNotifyAll(batch());
- }
-
- private void addReviewerToWipChangeInReviewDbNotifyAll(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
+ private void addReviewerToWipChangeNotifyAll(Adder adder) throws Exception {
StagedChange sc = stageWipChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email, NotifyHandling.ALL);
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), NotifyHandling.ALL);
+ // TODO(logan): Should CCs be included?
assertThat(sender)
.sent("newchange", sc)
.to(reviewer)
- .cc(sc.reviewer, sc.ccer)
+ .cc(sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToWipChangeInReviewDbNotifyAllSingly() throws Exception {
- addReviewerToWipChangeInReviewDbNotifyAll(singly());
+ public void addReviewerToWipChangeNotifyAllSingly() throws Exception {
+ addReviewerToWipChangeNotifyAll(singly());
}
@Test
- public void addReviewerToWipChangeInReviewDbNotifyAllBatch() throws Exception {
- addReviewerToWipChangeInReviewDbNotifyAll(batch());
+ public void addReviewerToWipChangeNotifyAllBatch() throws Exception {
+ addReviewerToWipChangeNotifyAll(batch());
}
- private void addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewers(Adder adder)
- throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addReviewerToReviewableChangeNotifyOwnerReviewers(Adder adder) throws Exception {
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email, OWNER_REVIEWERS);
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), OWNER_REVIEWERS);
// TODO(logan): Should CCs be included?
assertThat(sender)
.sent("newchange", sc)
@@ -512,62 +474,56 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewersSingly() throws Exception {
- addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewers(singly());
+ public void addReviewerToReviewableChangeNotifyOwnerReviewersSingly() throws Exception {
+ addReviewerToReviewableChangeNotifyOwnerReviewers(singly());
}
@Test
- public void addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewersBatch() throws Exception {
- addReviewerToReviewableChangeInNoteDbNotifyOwnerReviewers(batch());
+ public void addReviewerToReviewableChangeNotifyOwnerReviewersBatch() throws Exception {
+ addReviewerToReviewableChangeNotifyOwnerReviewers(batch());
}
- private void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwner(Adder adder)
+ private void addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwner(Adder adder)
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, OWNER);
- assertThat(sender).notSent();
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, OWNER);
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwnerSingly()
- throws Exception {
- addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwner(singly());
+ public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwnerSingly() throws Exception {
+ addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwner(singly());
}
@Test
- public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwnerBatch()
- throws Exception {
- addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyOwner(batch());
+ public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwnerBatch() throws Exception {
+ addReviewerToReviewableChangeByOwnerCcingSelfNotifyOwner(batch());
}
- private void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNone(Adder adder)
+ private void addReviewerToReviewableChangeByOwnerCcingSelfNotifyNone(Adder adder)
throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
StagedChange sc = stageReviewableChange();
TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
- addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, NONE);
- assertThat(sender).notSent();
+ addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, NONE);
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNoneSingly()
- throws Exception {
- addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNone(singly());
+ public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyNoneSingly() throws Exception {
+ addReviewerToReviewableChangeByOwnerCcingSelfNotifyNone(singly());
}
@Test
- public void addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNoneBatch()
- throws Exception {
- addReviewerToReviewableChangeInNoteDbByOwnerCcingSelfNotifyNone(batch());
+ public void addReviewerToReviewableChangeByOwnerCcingSelfNotifyNoneBatch() throws Exception {
+ addReviewerToReviewableChangeByOwnerCcingSelfNotifyNone(batch());
}
- private void addNonUserReviewerByEmailInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addNonUserReviewerByEmail(Adder adder) throws Exception {
StagedChange sc = stageReviewableChange();
addReviewer(adder, sc.changeId, sc.owner, "nonexistent@example.com");
assertThat(sender)
@@ -576,20 +532,20 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer)
.cc(StagedUsers.CC_BY_EMAIL, StagedUsers.REVIEWER_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addNonUserReviewerByEmailInNoteDbSingly() throws Exception {
- addNonUserReviewerByEmailInNoteDb(singly(ReviewerState.REVIEWER));
+ public void addNonUserReviewerByEmailSingly() throws Exception {
+ addNonUserReviewerByEmail(singly(ReviewerState.REVIEWER));
}
@Test
- public void addNonUserReviewerByEmailInNoteDbBatch() throws Exception {
- addNonUserReviewerByEmailInNoteDb(batch(ReviewerState.REVIEWER));
+ public void addNonUserReviewerByEmailBatch() throws Exception {
+ addNonUserReviewerByEmail(batch(ReviewerState.REVIEWER));
}
- private void addNonUserCcByEmailInNoteDb(Adder adder) throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ private void addNonUserCcByEmail(Adder adder) throws Exception {
StagedChange sc = stageReviewableChange();
addReviewer(adder, sc.changeId, sc.owner, "nonexistent@example.com");
assertThat(sender)
@@ -598,16 +554,17 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer)
.cc(StagedUsers.CC_BY_EMAIL, StagedUsers.REVIEWER_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void addNonUserCcByEmailInNoteDbSingly() throws Exception {
- addNonUserCcByEmailInNoteDb(singly(ReviewerState.CC));
+ public void addNonUserCcByEmailSingly() throws Exception {
+ addNonUserCcByEmail(singly(ReviewerState.CC));
}
@Test
- public void addNonUserCcByEmailInNoteDbBatch() throws Exception {
- addNonUserCcByEmailInNoteDb(batch(ReviewerState.CC));
+ public void addNonUserCcByEmailBatch() throws Exception {
+ addNonUserCcByEmail(batch(ReviewerState.CC));
}
private interface Adder {
@@ -666,7 +623,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
@Nullable NotifyHandling notify)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
adder.addReviewer(changeId, reviewer, notify);
}
@@ -685,6 +642,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -699,6 +657,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -713,6 +672,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -727,6 +687,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -742,6 +703,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -757,6 +719,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -768,13 +731,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer, sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void commentOnReviewableChangeByOwnerNotifyOwner() throws Exception {
StagedChange sc = stageReviewableChange();
review(sc.owner, sc.changeId, ENABLED, OWNER);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -782,14 +746,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChange();
setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
review(sc.owner, sc.changeId, ENABLED, OWNER);
- assertThat(sender).notSent(); // TODO(logan): Why not send to owner?
+ assertThat(sender).didNotSend(); // TODO(logan): Why not send to owner?
}
@Test
public void commentOnReviewableChangeByOwnerNotifyNone() throws Exception {
StagedChange sc = stageReviewableChange();
review(sc.owner, sc.changeId, ENABLED, NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -797,7 +761,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChange();
setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
review(sc.owner, sc.changeId, ENABLED, NONE);
- assertThat(sender).notSent(); // TODO(logan): Why not send to owner?
+ assertThat(sender).didNotSend(); // TODO(logan): Why not send to owner?
}
@Test
@@ -811,20 +775,21 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer, sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void commentOnWipChangeByOwner() throws Exception {
StagedChange sc = stageWipChange();
review(sc.owner, sc.changeId, ENABLED);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void commentOnWipChangeByOwnerCcingSelf() throws Exception {
StagedChange sc = stageWipChange();
review(sc.owner, sc.changeId, CC_ON_OWN_COMMENTS);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -838,6 +803,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -846,6 +812,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
TestAccount bot = sc.testAccount("bot");
review(bot, sc.changeId, ENABLED, null, "autogenerated:tag");
assertThat(sender).sent("comment", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -854,6 +821,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
TestAccount bot = sc.testAccount("bot");
review(bot, sc.changeId, ENABLED, null, "autogenerated:tag");
assertThat(sender).sent("comment", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -869,6 +837,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -882,6 +851,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -889,7 +859,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChange();
ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
gApi.changes().id(sc.changeId).revision("current").review(in);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -904,7 +874,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -919,14 +889,13 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
- public void addReviewerOnWipChangeAndStartReviewInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void addReviewerOnWipChangeAndStartReview() throws Exception {
StagedChange sc = stageWipChange();
- ReviewInput in = ReviewInput.noScore().reviewer(other.email).setWorkInProgress(false);
+ ReviewInput in = ReviewInput.noScore().reviewer(other.email()).setWorkInProgress(false);
gApi.changes().id(sc.changeId).revision("current").review(in);
assertThat(sender)
.sent("comment", sc)
@@ -942,29 +911,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
- assertThat(sender).notSent();
- }
-
- @Test
- public void addReviewerOnWipChangeAndStartReviewInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageWipChange();
- ReviewInput in = ReviewInput.noScore().reviewer(other.email).setWorkInProgress(false);
- gApi.changes().id(sc.changeId).revision("current").review(in);
- assertThat(sender)
- .sent("comment", sc)
- .cc(sc.reviewer, sc.ccer, other)
- .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(ALL_COMMENTS)
- .noOneElse();
- assertThat(sender)
- .sent("newchange", sc)
- .to(other)
- .cc(sc.reviewer, sc.ccer)
- .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
- .noOneElse();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1018,12 +965,13 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.to(spc.watchingProjectOwner)
.bcc(NEW_CHANGES, NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void createWipChange() throws Exception {
stagePreChange("refs/for/master%wip");
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1031,7 +979,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
setWorkInProgressByDefault(project, InheritableBoolean.TRUE);
StagedPreChange spc = stagePreChange("refs/for/master");
Truth.assertThat(gApi.changes().id(spc.changeId).get().workInProgress).isTrue();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1039,41 +987,41 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
// Make sure owner user is created
StagedChange sc = stageReviewableChange();
// All was cleaned already
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
// Toggle workInProgress flag for owner
- GeneralPreferencesInfo prefs = gApi.accounts().id(sc.owner.id.get()).getPreferences();
+ GeneralPreferencesInfo prefs = gApi.accounts().id(sc.owner.id().get()).getPreferences();
prefs.workInProgressByDefault = true;
- gApi.accounts().id(sc.owner.id.get()).setPreferences(prefs);
+ gApi.accounts().id(sc.owner.id().get()).setPreferences(prefs);
// Create another change without notification that should be wip
StagedPreChange spc = stagePreChange("refs/for/master");
Truth.assertThat(gApi.changes().id(spc.changeId).get().workInProgress).isTrue();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
// Clean up workInProgressByDefault by owner
- prefs = gApi.accounts().id(sc.owner.id.get()).getPreferences();
+ prefs = gApi.accounts().id(sc.owner.id().get()).getPreferences();
Truth.assertThat(prefs.workInProgressByDefault).isTrue();
prefs.workInProgressByDefault = false;
- gApi.accounts().id(sc.owner.id.get()).setPreferences(prefs);
+ gApi.accounts().id(sc.owner.id().get()).setPreferences(prefs);
}
@Test
public void createReviewableChangeWithNotifyOwnerReviewers() throws Exception {
stagePreChange("refs/for/master%notify=OWNER_REVIEWERS");
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void createReviewableChangeWithNotifyOwner() throws Exception {
stagePreChange("refs/for/master%notify=OWNER");
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void createReviewableChangeWithNotifyNone() throws Exception {
stagePreChange("refs/for/master%notify=OWNER");
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1084,6 +1032,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.to(spc.watchingProjectOwner)
.bcc(NEW_CHANGES, NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1091,21 +1040,17 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedPreChange spc =
stagePreChange(
"refs/for/master",
- users -> ImmutableList.of("r=" + users.reviewer.username, "cc=" + users.ccer.username));
+ users ->
+ ImmutableList.of("r=" + users.reviewer.username(), "cc=" + users.ccer.username()));
FakeEmailSenderSubject subject =
assertThat(sender).sent("newchange", spc).to(spc.reviewer, spc.watchingProjectOwner);
- if (notesMigration.readChanges()) {
- subject.cc(spc.ccer);
- } else {
- // CCs are considered reviewers in the storage layer.
- subject.to(spc.ccer);
- }
+ subject.cc(spc.ccer);
subject.bcc(NEW_CHANGES, NEW_PATCHSETS).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void createReviewableChangeWithReviewersAndCcsByEmailInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void createReviewableChangeWithReviewersAndCcsByEmail() throws Exception {
StagedPreChange spc =
stagePreChange(
"refs/for/master",
@@ -1118,6 +1063,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc("nobody2@example.com")
.bcc(NEW_CHANGES, NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
/*
@@ -1127,7 +1073,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
@Test
public void deleteReviewerFromReviewableChange() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
removeReviewer(sc, extraReviewer);
assertThat(sender)
.sent("deleteReviewer", sc)
@@ -1137,6 +1083,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1152,12 +1099,13 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromReviewableChangeByAdmin() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
removeReviewer(sc, extraReviewer);
assertThat(sender)
.sent("deleteReviewer", sc)
@@ -1167,13 +1115,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromReviewableChangeByAdminCcingSelf() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
setEmailStrategy(admin, EmailStrategy.CC_ON_OWN_COMMENTS);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
removeReviewer(sc, extraReviewer);
assertThat(sender)
.sent("deleteReviewer", sc)
@@ -1183,12 +1132,13 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteCcerFromReviewableChange() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
removeReviewer(sc, extraCcer);
assertThat(sender)
.sent("deleteReviewer", sc)
@@ -1198,12 +1148,13 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromReviewableChangeNotifyOwnerReviewers() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
removeReviewer(sc, extraReviewer, NotifyHandling.OWNER_REVIEWERS);
assertThat(sender)
.sent("deleteReviewer", sc)
@@ -1211,13 +1162,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(extraCcer, sc.reviewer, sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromReviewableChangeNotifyOwner() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
removeReviewer(sc, extraReviewer, NotifyHandling.OWNER);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1226,13 +1178,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
setEmailStrategy(sc.owner, EmailStrategy.CC_ON_OWN_COMMENTS);
removeReviewer(sc, extraReviewer, NotifyHandling.OWNER);
assertThat(sender).sent("deleteReviewer", sc).to(sc.owner, extraReviewer).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromReviewableChangeNotifyNone() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
removeReviewer(sc, extraReviewer, NotifyHandling.NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1240,27 +1193,27 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
setEmailStrategy(sc.owner, EmailStrategy.CC_ON_OWN_COMMENTS);
removeReviewer(sc, extraReviewer, NotifyHandling.NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromReviewableWipChange() throws Exception {
StagedChange sc = stageReviewableWipChangeWithExtraReviewer();
removeReviewer(sc, extraReviewer);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromWipChange() throws Exception {
StagedChange sc = stageWipChangeWithExtraReviewer();
removeReviewer(sc, extraReviewer);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerFromWipChangeNotifyAll() throws Exception {
StagedChange sc = stageWipChangeWithExtraReviewer();
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
removeReviewer(sc, extraReviewer, NotifyHandling.ALL);
assertThat(sender)
.sent("deleteReviewer", sc)
@@ -1270,15 +1223,17 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteReviewerWithApprovalFromWipChange() throws Exception {
StagedChange sc = stageWipChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
removeReviewer(sc, extraReviewer);
assertThat(sender).sent("deleteReviewer", sc).to(extraReviewer).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1286,19 +1241,18 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageWipChangeWithExtraReviewer();
recommend(sc, extraReviewer);
removeReviewer(sc, extraReviewer, NotifyHandling.OWNER);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
- public void deleteReviewerByEmailFromWipChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void deleteReviewerByEmailFromWipChange() throws Exception {
StagedChange sc = stageWipChangeWithExtraReviewer();
gApi.changes().id(sc.changeId).reviewer(StagedUsers.REVIEWER_BY_EMAIL).remove();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
private void recommend(StagedChange sc, TestAccount by) throws Exception {
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.recommend());
}
@@ -1310,15 +1264,18 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stager.stage();
ReviewInput in =
ReviewInput.noScore()
- .reviewer(extraReviewer.email)
- .reviewer(extraCcer.email, ReviewerState.CC, false);
- setApiUser(extraReviewer);
+ .reviewer(extraReviewer.email())
+ .reviewer(extraCcer.email(), ReviewerState.CC, false);
+ requestScopeOperations.setApiUser(extraReviewer.id());
gApi.changes().id(sc.changeId).revision("current").review(in);
+ sender.clear();
return sc;
}
private StagedChange stageReviewableChangeWithExtraReviewer() throws Exception {
- return stageChangeWithExtraReviewer(this::stageReviewableChange);
+ StagedChange sc = stageChangeWithExtraReviewer(this::stageReviewableChange);
+ sender.clear();
+ return sc;
}
private StagedChange stageReviewableWipChangeWithExtraReviewer() throws Exception {
@@ -1326,12 +1283,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
}
private StagedChange stageWipChangeWithExtraReviewer() throws Exception {
- return stageChangeWithExtraReviewer(this::stageWipChange);
+ StagedChange sc = stageChangeWithExtraReviewer(this::stageWipChange);
+ assertThat(sender).didNotSend();
+ return sc;
}
private void removeReviewer(StagedChange sc, TestAccount account) throws Exception {
sender.clear();
- gApi.changes().id(sc.changeId).reviewer(account.email).remove();
+ gApi.changes().id(sc.changeId).reviewer(account.email()).remove();
}
private void removeReviewer(StagedChange sc, TestAccount account, NotifyHandling notify)
@@ -1339,7 +1298,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
sender.clear();
DeleteReviewerInput in = new DeleteReviewerInput();
in.notify = notify;
- gApi.changes().id(sc.changeId).reviewer(account.email).remove(in);
+ gApi.changes().id(sc.changeId).reviewer(account.email()).remove(in);
}
/*
@@ -1350,7 +1309,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
public void deleteVoteFromReviewableChange() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1359,6 +1318,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1366,7 +1326,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1376,13 +1336,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteVoteFromReviewableChangeByAdmin() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
deleteVote(sc, extraReviewer);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1392,6 +1353,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1399,7 +1361,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
setEmailStrategy(admin, EmailStrategy.CC_ON_OWN_COMMENTS);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
deleteVote(sc, extraReviewer);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1409,19 +1371,21 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteVoteFromReviewableChangeNotifyOwnerReviewers() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer, NotifyHandling.OWNER_REVIEWERS);
assertThat(sender)
.sent("deleteVote", sc)
.cc(sc.reviewer, sc.ccer, extraReviewer, extraCcer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1429,7 +1393,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer, NotifyHandling.OWNER_REVIEWERS);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1437,24 +1401,26 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer, sc.ccer, extraReviewer, extraCcer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteVoteFromReviewableChangeNotifyOwner() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
deleteVote(sc, extraReviewer, NotifyHandling.OWNER);
assertThat(sender).sent("deleteVote", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteVoteFromReviewableChangeNotifyNone() throws Exception {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer, NotifyHandling.NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1462,16 +1428,16 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChangeWithExtraReviewer();
recommend(sc, extraReviewer);
setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer, NotifyHandling.NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteVoteFromReviewableWipChange() throws Exception {
StagedChange sc = stageReviewableWipChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1480,13 +1446,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void deleteVoteFromWipChange() throws Exception {
StagedChange sc = stageWipChangeWithExtraReviewer();
recommend(sc, extraReviewer);
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
deleteVote(sc, extraReviewer);
assertThat(sender)
.sent("deleteVote", sc)
@@ -1495,11 +1462,12 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
private void deleteVote(StagedChange sc, TestAccount account) throws Exception {
sender.clear();
- gApi.changes().id(sc.changeId).reviewer(account.email).deleteVote("Code-Review");
+ gApi.changes().id(sc.changeId).reviewer(account.email()).deleteVote("Code-Review");
}
private void deleteVote(StagedChange sc, TestAccount account, NotifyHandling notify)
@@ -1508,7 +1476,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
DeleteVoteInput in = new DeleteVoteInput();
in.label = "Code-Review";
in.notify = notify;
- gApi.changes().id(sc.changeId).reviewer(account.email).deleteVote(in);
+ gApi.changes().id(sc.changeId).reviewer(account.email()).deleteVote(in);
}
/*
@@ -1516,16 +1484,48 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
*/
@Test
- public void mergeByOwner() throws Exception {
- StagedChange sc = stageChangeReadyForMerge();
- merge(sc.changeId, sc.owner);
- assertThat(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();
+ public void mergeByOwnerAllSubmitStrategies() throws Exception {
+ mergeByOwnerAllSubmitStrategies(false);
+ }
+
+ @Test
+ public void mergeByOwnerAllSubmitStrategiesWithAdvancingBranch() throws Exception {
+ mergeByOwnerAllSubmitStrategies(true);
+ }
+
+ private void mergeByOwnerAllSubmitStrategies(boolean advanceBranchBeforeSubmitting)
+ throws Exception {
+ for (SubmitType submitType : SubmitType.values()) {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig().getProject().setSubmitType(submitType);
+ u.save();
+ }
+
+ StagedChange sc = stageChangeReadyForMerge();
+
+ String name = submitType + " sender";
+ if (advanceBranchBeforeSubmitting) {
+ if (submitType == SubmitType.FAST_FORWARD_ONLY) {
+ continue;
+ }
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch("master").commit().create();
+ }
+ name += " after branch has advanced";
+ }
+
+ merge(sc.changeId, sc.owner);
+ assertThat(sender)
+ .named(name)
+ .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();
+ }
}
@Test
@@ -1540,6 +1540,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS, SUBMITTED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1554,6 +1555,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS, SUBMITTED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1568,6 +1570,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS, SUBMITTED_CHANGES)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1580,6 +1583,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.reviewer, sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1587,6 +1591,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageChangeReadyForMerge();
merge(sc.changeId, other, OWNER);
assertThat(sender).sent("merged", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1595,13 +1600,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
setEmailStrategy(other, EmailStrategy.CC_ON_OWN_COMMENTS);
merge(sc.changeId, other, OWNER);
assertThat(sender).sent("merged", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void mergeByOtherNotifyNone() throws Exception {
StagedChange sc = stageChangeReadyForMerge();
merge(sc.changeId, other, NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1609,7 +1615,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageChangeReadyForMerge();
setEmailStrategy(other, EmailStrategy.CC_ON_OWN_COMMENTS);
merge(sc.changeId, other, NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
private void merge(String changeId, TestAccount by) throws Exception {
@@ -1619,7 +1625,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private void merge(String changeId, TestAccount by, EmailStrategy emailStrategy)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
gApi.changes().id(changeId).revision("current").submit();
}
@@ -1631,7 +1637,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
String changeId, TestAccount by, EmailStrategy emailStrategy, NotifyHandling notify)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
SubmitInput in = new SubmitInput();
in.notify = notify;
gApi.changes().id(changeId).revision("current").submit(in);
@@ -1639,7 +1645,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private StagedChange stageChangeReadyForMerge() throws Exception {
StagedChange sc = stageReviewableChange();
- setApiUser(sc.reviewer);
+ requestScopeOperations.setApiUser(sc.reviewer.id());
gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.approve());
sender.clear();
return sc;
@@ -1650,8 +1656,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
*/
@Test
- public void newPatchSetByOwnerOnReviewableChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetByOwnerOnReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master", sc.owner);
assertThat(sender)
@@ -1662,24 +1667,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetByOwnerOnReviewableChangeInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- pushTo(sc, "refs/for/master", sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void newPatchSetByOtherOnReviewableChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetByOtherOnReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master", other);
assertThat(sender)
@@ -1691,25 +1683,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetByOtherOnReviewableChangeInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- pushTo(sc, "refs/for/master", other);
- assertThat(sender)
- .sent("newpatchset", sc)
- .notTo(sc.owner) // TODO(logan): This email shouldn't come from the owner.
- .to(sc.reviewer, sc.ccer, other)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetByOtherOnReviewableChangeOwnerSelfCc() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master", other, EmailStrategy.CC_ON_OWN_COMMENTS);
assertThat(sender)
@@ -1721,25 +1699,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- pushTo(sc, "refs/for/master", other, EmailStrategy.CC_ON_OWN_COMMENTS);
- assertThat(sender)
- .sent("newpatchset", sc)
- .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
- .to(sc.reviewer, sc.ccer, other)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void newPatchSetByOtherOnReviewableChangeNotifyOwnerReviewersInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetByOtherOnReviewableChangeNotifyOwnerReviewers() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other);
assertThat(sender)
@@ -1750,26 +1714,12 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetByOtherOnReviewableChangeNotifyOwnerReviewersInReviewDb()
+ public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewers()
throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other);
- assertThat(sender)
- .sent("newpatchset", sc)
- .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
- .to(sc.reviewer, sc.ccer)
- .to(other)
- .noOneElse();
- }
-
- @Test
- public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInNoteDb()
- throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other, EmailStrategy.CC_ON_OWN_COMMENTS);
assertThat(sender)
@@ -1780,27 +1730,14 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
- }
-
- @Test
- public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInReviewDb()
- throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- pushTo(sc, "refs/for/master%notify=OWNER_REVIEWERS", other, EmailStrategy.CC_ON_OWN_COMMENTS);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .to(other)
- .notTo(sc.owner) // TODO(logan): This shouldn't be sent *from* the owner.
- .noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void newPatchSetByOtherOnReviewableChangeNotifyOwner() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master%notify=OWNER", other);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1809,7 +1746,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
pushTo(sc, "refs/for/master%notify=OWNER", other, EmailStrategy.CC_ON_OWN_COMMENTS);
// TODO(logan): This email shouldn't come from the owner, and that's why
// no email is currently sent (owner isn't CCing self).
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -1818,33 +1755,32 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
pushTo(sc, "refs/for/master%notify=NONE", other);
// TODO(logan): This email shouldn't come from the owner, and that's why
// no email is currently sent (owner isn't CCing self).
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void newPatchSetByOtherOnReviewableChangeOwnerSelfCcNotifyNone() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master%notify=NONE", other, EmailStrategy.CC_ON_OWN_COMMENTS);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void newPatchSetByOwnerOnReviewableChangeToWip() throws Exception {
StagedChange sc = stageReviewableChange();
pushTo(sc, "refs/for/master%wip", sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void newPatchSetOnWipChange() throws Exception {
StagedChange sc = stageWipChange();
pushTo(sc, "refs/for/master%wip", sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetOnWipChangeNotifyAllInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetOnWipChangeNotifyAll() throws Exception {
StagedChange sc = stageWipChange();
pushTo(sc, "refs/for/master%wip,notify=ALL", sc.owner);
assertThat(sender)
@@ -1855,24 +1791,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetOnWipChangeNotifyAllInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageWipChange();
- pushTo(sc, "refs/for/master%wip,notify=ALL", sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void newPatchSetOnWipChangeToReadyInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetOnWipChangeToReady() throws Exception {
StagedChange sc = stageWipChange();
pushTo(sc, "refs/for/master%ready", sc.owner);
assertThat(sender)
@@ -1883,34 +1806,21 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
- }
-
- @Test
- public void newPatchSetOnWipChangeToReadyInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageWipChange();
- pushTo(sc, "refs/for/master%ready", sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void newPatchSetOnReviewableWipChange() throws Exception {
StagedChange sc = stageReviewableWipChange();
pushTo(sc, "refs/for/master%wip", sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetOnReviewableChangeAddingReviewerInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetOnReviewableChangeAddingReviewer() throws Exception {
StagedChange sc = stageReviewableChange();
TestAccount newReviewer = sc.testAccount("newReviewer");
- pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
+ pushTo(sc, "refs/for/master%r=" + newReviewer.username(), sc.owner);
assertThat(sender)
.sent("newpatchset", sc)
.to(sc.reviewer, newReviewer)
@@ -1919,39 +1829,22 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
- assertThat(sender).notSent();
- }
-
- @Test
- public void newPatchSetOnReviewableChangeAddingReviewerInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- TestAccount newReviewer = sc.testAccount("newReviewer");
- pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer, newReviewer)
- .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void newPatchSetOnWipChangeAddingReviewer() throws Exception {
StagedChange sc = stageWipChange();
TestAccount newReviewer = sc.testAccount("newReviewer");
- pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
- assertThat(sender).notSent();
+ pushTo(sc, "refs/for/master%r=" + newReviewer.username(), sc.owner);
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetOnWipChangeAddingReviewerNotifyAllInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetOnWipChangeAddingReviewerNotifyAll() throws Exception {
StagedChange sc = stageWipChange();
TestAccount newReviewer = sc.testAccount("newReviewer");
- pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username, sc.owner);
+ pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username(), sc.owner);
assertThat(sender)
.sent("newpatchset", sc)
.to(sc.reviewer, newReviewer)
@@ -1960,28 +1853,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
- assertThat(sender).notSent();
- }
-
- @Test
- public void newPatchSetOnWipChangeAddingReviewerNotifyAllInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageWipChange();
- TestAccount newReviewer = sc.testAccount("newReviewer");
- pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username, sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer, newReviewer)
- .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
- public void newPatchSetOnWipChangeSettingReadyInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void newPatchSetOnWipChangeSettingReady() throws Exception {
StagedChange sc = stageWipChange();
pushTo(sc, "refs/for/master%ready", sc.owner);
assertThat(sender)
@@ -1992,22 +1868,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
- assertThat(sender).notSent();
- }
-
- @Test
- public void newPatchSetOnWipChangeSettingReadyInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageWipChange();
- pushTo(sc, "refs/for/master%ready", sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
private void pushTo(StagedChange sc, String ref, TestAccount by) throws Exception {
@@ -2017,12 +1878,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private void pushTo(StagedChange sc, String ref, TestAccount by, EmailStrategy emailStrategy)
throws Exception {
setEmailStrategy(by, emailStrategy);
- pushFactory.create(db, by.getIdent(), sc.repo, sc.changeId).to(ref).assertOkStatus();
+ pushFactory.create(by.newIdent(), sc.repo, sc.changeId).to(ref).assertOkStatus();
}
@Test
- public void editCommitMessageEditByOwnerOnReviewableChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void editCommitMessageEditByOwnerOnReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, sc.owner);
assertThat(sender)
@@ -2033,24 +1893,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void editCommitMessageEditByOwnerOnReviewableChangeInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- editCommitMessage(sc, sc.owner);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void editCommitMessageEditByOtherOnReviewableChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void editCommitMessageEditByOtherOnReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other);
assertThat(sender)
@@ -2061,24 +1908,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void editCommitMessageEditByOtherOnReviewableChangeInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- editCommitMessage(sc, other);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.owner, sc.reviewer, sc.ccer)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCc() throws Exception {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, CC_ON_OWN_COMMENTS);
assertThat(sender)
@@ -2089,25 +1923,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- editCommitMessage(sc, other, CC_ON_OWN_COMMENTS);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.owner, sc.reviewer, sc.ccer, other)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
- }
-
- @Test
- public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewersInNoteDb()
- throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewers() throws Exception {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, OWNER_REVIEWERS);
assertThat(sender)
@@ -2116,21 +1936,12 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.ccer)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void editCommitMessageByOtherOnReviewableChangeNotifyOwnerReviewersInReviewDb()
+ public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewers()
throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- editCommitMessage(sc, other, OWNER_REVIEWERS);
- assertThat(sender).sent("newpatchset", sc).to(sc.owner, sc.reviewer, sc.ccer).noOneElse();
- }
-
- @Test
- public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInNoteDb()
- throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, OWNER_REVIEWERS, CC_ON_OWN_COMMENTS);
assertThat(sender)
@@ -2139,19 +1950,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.cc(sc.ccer, other)
.cc(StagedUsers.REVIEWER_BY_EMAIL, StagedUsers.CC_BY_EMAIL)
.noOneElse();
- }
-
- @Test
- public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyOwnerReviewersInReviewDb()
- throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- editCommitMessage(sc, other, OWNER_REVIEWERS, CC_ON_OWN_COMMENTS);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.owner, sc.reviewer, sc.ccer)
- .cc(other)
- .noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2159,6 +1958,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, OWNER);
assertThat(sender).sent("newpatchset", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2166,27 +1966,28 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, OWNER, CC_ON_OWN_COMMENTS);
assertThat(sender).sent("newpatchset", sc).to(sc.owner).cc(other).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void editCommitMessageByOtherOnReviewableChangeNotifyNone() throws Exception {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, NONE);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void editCommitMessageByOtherOnReviewableChangeOwnerSelfCcNotifyNone() throws Exception {
StagedChange sc = stageReviewableChange();
editCommitMessage(sc, other, NONE, CC_ON_OWN_COMMENTS);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
public void editCommitMessageOnWipChange() throws Exception {
StagedChange sc = stageWipChange();
editCommitMessage(sc, sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2194,6 +1995,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageWipChange();
editCommitMessage(sc, other);
assertThat(sender).sent("newpatchset", sc).to(sc.owner).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2201,11 +2003,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedChange sc = stageWipChange();
editCommitMessage(sc, other, CC_ON_OWN_COMMENTS);
assertThat(sender).sent("newpatchset", sc).to(sc.owner).cc(other).noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void editCommitMessageOnWipChangeNotifyAllInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void editCommitMessageOnWipChangeNotifyAll() throws Exception {
StagedChange sc = stageWipChange();
editCommitMessage(sc, sc.owner, ALL);
assertThat(sender)
@@ -2216,19 +2018,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(NEW_PATCHSETS)
.noOneElse();
- }
-
- @Test
- public void editCommitMessageOnWipChangeNotifyAllInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageWipChange();
- editCommitMessage(sc, sc.owner, ALL);
- assertThat(sender)
- .sent("newpatchset", sc)
- .to(sc.reviewer, sc.ccer)
- .bcc(sc.starrer)
- .bcc(NEW_PATCHSETS)
- .noOneElse();
+ assertThat(sender).didNotSend();
}
private void editCommitMessage(StagedChange sc, TestAccount by) throws Exception {
@@ -2271,6 +2061,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2284,6 +2075,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2297,6 +2089,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2311,6 +2104,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2325,6 +2119,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2339,6 +2134,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
private void restore(String changeId, TestAccount by) throws Exception {
@@ -2348,7 +2144,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private void restore(String changeId, TestAccount by, EmailStrategy emailStrategy)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
gApi.changes().id(changeId).restore();
}
@@ -2357,31 +2153,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
*/
@Test
- public void revertChangeByOwnerInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageChange();
- revert(sc, sc.owner);
-
- // email for the newly created revert change
- assertThat(sender)
- .sent("newchange", sc)
- .to(sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
- .bcc(NEW_CHANGES, NEW_PATCHSETS)
- .noOneElse();
-
- // email for the change that is reverted
- assertThat(sender)
- .sent("revert", sc)
- .cc(sc.reviewer, sc.ccer, admin)
- .cc(StagedUsers.REVIEWER_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(ALL_COMMENTS)
- .noOneElse();
- }
-
- @Test
- public void revertChangeByOwnerInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void revertChangeByOwner() throws Exception {
StagedChange sc = stageChange();
revert(sc, sc.owner);
@@ -2401,36 +2173,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void revertChangeByOwnerCcingSelfInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageChange();
- revert(sc, sc.owner, CC_ON_OWN_COMMENTS);
-
- // email for the newly created revert change
- assertThat(sender)
- .sent("newchange", sc)
- .to(sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
- .cc(sc.owner)
- .bcc(NEW_CHANGES, NEW_PATCHSETS)
- .noOneElse();
-
- // email for the change that is reverted
- assertThat(sender)
- .sent("revert", sc)
- .to(sc.owner)
- .cc(sc.reviewer, sc.ccer, admin)
- .cc(StagedUsers.REVIEWER_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(ALL_COMMENTS)
- .noOneElse();
- }
-
- @Test
- public void revertChangeByOwnerCcingSelfInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void revertChangeByOwnerCcingSelf() throws Exception {
StagedChange sc = stageChange();
revert(sc, sc.owner, CC_ON_OWN_COMMENTS);
@@ -2451,35 +2198,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void revertChangeByOtherInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageChange();
- revert(sc, other);
-
- // email for the newly created revert change
- assertThat(sender)
- .sent("newchange", sc)
- .to(sc.owner, sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
- .bcc(NEW_CHANGES, NEW_PATCHSETS)
- .noOneElse();
-
- // email for the change that is reverted
- assertThat(sender)
- .sent("revert", sc)
- .to(sc.owner)
- .cc(sc.reviewer, sc.ccer, admin)
- .cc(StagedUsers.REVIEWER_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(ALL_COMMENTS)
- .noOneElse();
- }
-
- @Test
- public void revertChangeByOtherInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void revertChangeByOther() throws Exception {
StagedChange sc = stageChange();
revert(sc, other);
@@ -2500,36 +2223,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void revertChangeByOtherCcingSelfInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageChange();
- revert(sc, other, CC_ON_OWN_COMMENTS);
-
- // email for the newly created revert change
- assertThat(sender)
- .sent("newchange", sc)
- .to(sc.owner, sc.reviewer, sc.ccer, sc.watchingProjectOwner, admin)
- .cc(other)
- .bcc(NEW_CHANGES, NEW_PATCHSETS)
- .noOneElse();
-
- // email for the change that is reverted
- assertThat(sender)
- .sent("revert", sc)
- .to(sc.owner)
- .cc(other, sc.reviewer, sc.ccer, admin)
- .cc(StagedUsers.REVIEWER_BY_EMAIL)
- .bcc(sc.starrer)
- .bcc(ALL_COMMENTS)
- .noOneElse();
- }
-
- @Test
- public void revertChangeByOtherCcingSelfInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void revertChangeByOtherCcingSelf() throws Exception {
StagedChange sc = stageChange();
revert(sc, other, CC_ON_OWN_COMMENTS);
@@ -2550,11 +2248,12 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
private StagedChange stageChange() throws Exception {
StagedChange sc = stageReviewableChange();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.approve());
gApi.changes().id(sc.changeId).revision("current").submit();
sender.clear();
@@ -2568,7 +2267,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private void revert(StagedChange sc, TestAccount by, EmailStrategy emailStrategy)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
gApi.changes().id(sc.changeId).revert();
}
@@ -2587,6 +2286,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2601,6 +2301,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2614,6 +2315,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2628,11 +2330,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void setAssigneeToSelfOnReviewableChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void setAssigneeToSelfOnReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
assign(sc, sc.owner, sc.owner);
assertThat(sender)
@@ -2641,14 +2343,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.REVIEWER_BY_EMAIL,
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.noOneElse();
- }
-
- @Test
- public void setAssigneeToSelfOnReviewableChangeInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- assign(sc, sc.owner, sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2665,11 +2360,11 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
- public void changeAssigneeToSelfOnReviewableChangeInNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void changeAssigneeToSelfOnReviewableChange() throws Exception {
StagedChange sc = stageReviewableChange();
assign(sc, sc.owner, sc.assignee);
sender.clear();
@@ -2680,16 +2375,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.REVIEWER_BY_EMAIL,
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.noOneElse();
- }
-
- @Test
- public void changeAssigneeToSelfOnReviewableChangeInReviewDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- StagedChange sc = stageReviewableChange();
- assign(sc, sc.owner, sc.assignee);
- sender.clear();
- assign(sc, sc.owner, sc.owner);
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2703,6 +2389,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2716,6 +2403,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
StagedUsers.CC_BY_EMAIL) // TODO(logan): This is probably not intended!
.to(sc.assignee)
.noOneElse();
+ assertThat(sender).didNotSend();
}
private void assign(StagedChange sc, TestAccount by, TestAccount to) throws Exception {
@@ -2725,9 +2413,9 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
private void assign(StagedChange sc, TestAccount by, TestAccount to, EmailStrategy emailStrategy)
throws Exception {
setEmailStrategy(by, emailStrategy);
- setApiUser(by);
+ requestScopeOperations.setApiUser(by.id());
AssigneeInput in = new AssigneeInput();
- in.assignee = to.email;
+ in.assignee = to.email();
gApi.changes().id(sc.changeId).setAssignee(in);
}
@@ -2746,6 +2434,7 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
@@ -2761,20 +2450,19 @@ public class ChangeNotificationsIT extends AbstractNotificationTest {
.bcc(sc.starrer)
.bcc(ALL_COMMENTS)
.noOneElse();
+ assertThat(sender).didNotSend();
}
@Test
public void setWorkInProgress() throws Exception {
StagedChange sc = stageReviewableChange();
gApi.changes().id(sc.changeId).setWorkInProgress();
- assertThat(sender).notSent();
+ assertThat(sender).didNotSend();
}
private void startReview(StagedChange sc) throws Exception {
- setApiUser(sc.owner);
+ requestScopeOperations.setApiUser(sc.owner.id());
gApi.changes().id(sc.changeId).setReadyForReview();
- // PolyGerrit current immediately follows up with a review.
- gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.noScore());
}
private void setWorkInProgressByDefault(Project.NameKey p, InheritableBoolean v)
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index a0170d262c..1386aec56f 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -20,6 +20,7 @@ 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.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.mail.EmailHeader;
@@ -27,6 +28,7 @@ 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;
import java.time.ZonedDateTime;
@@ -41,6 +43,8 @@ import org.junit.Test;
/** Tests the presence of required metadata in email headers, text and html. */
public class MailMetadataIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
private String systemTimeZone;
@Before
@@ -58,7 +62,7 @@ public class MailMetadataIT extends AbstractDaemonTest {
@Test
public void metadataOnNewChange() throws Exception {
PushOneCommit.Result newChange = createChange();
- gApi.changes().id(newChange.getChangeId()).addReviewer(user.getId().toString());
+ gApi.changes().id(newChange.getChangeId()).addReviewer(user.id().toString());
List<FakeEmailSender.Message> emails = sender.getMessages();
assertThat(emails).hasSize(1);
@@ -85,14 +89,14 @@ public class MailMetadataIT extends AbstractDaemonTest {
@Test
public void metadataOnNewComment() throws Exception {
PushOneCommit.Result newChange = createChange();
- gApi.changes().id(newChange.getChangeId()).addReviewer(user.getId().toString());
+ gApi.changes().id(newChange.getChangeId()).addReviewer(user.id().toString());
sender.clear();
// Review change
ReviewInput input = new ReviewInput();
input.message = "Test";
revision(newChange).review(input);
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
Collection<ChangeMessageInfo> result =
gApi.changes().id(newChange.getChangeId()).get().messages;
assertThat(result).isNotEmpty();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index 9ff2c05b4b..f917fd894b 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -17,6 +17,7 @@ package com.google.gerrit.acceptance.server.mail;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommentInfo;
@@ -33,6 +34,7 @@ import org.junit.Test;
public class MailProcessorIT extends AbstractMailIT {
@Inject private MailProcessor mailProcessor;
+ @Inject private AccountOperations accountOperations;
@Test
public void parseAndPersistChangeMessage() throws Exception {
@@ -163,16 +165,13 @@ public class MailProcessorIT extends AbstractMailIT {
b.textContent(txt + textFooterForChange(changeInfo._number, ts));
// Set account state to inactive
- gApi.accounts().id("user").setActive(false);
+ accountOperations.account(user.id()).forUpdate().inactive().update();
mailProcessor.process(b.build());
comments = gApi.changes().id(changeId).current().commentsAsList();
// Check that comment size has not changed
assertThat(comments).hasSize(2);
-
- // Reset
- gApi.accounts().id("user").setActive(true);
}
@Test
@@ -190,7 +189,7 @@ public class MailProcessorIT extends AbstractMailIT {
newPlaintextBody(getChangeUrl(changeInfo) + "/1", "Test Message", null, null, null);
MailMessage.Builder b =
messageBuilderWithDefaultFields()
- .from(user.emailAddress)
+ .from(user.getEmailAddress())
.textContent(txt + textFooterForChange(changeInfo._number, ts));
sender.clear();
@@ -212,7 +211,7 @@ public class MailProcessorIT extends AbstractMailIT {
newPlaintextBody(getChangeUrl(changeInfo) + "/1", "Test Message", null, null, null);
MailMessage.Builder b =
messageBuilderWithDefaultFields()
- .from(user.emailAddress)
+ .from(user.getEmailAddress())
.textContent(txt + textFooterForChange(changeInfo._number, ts));
sender.clear();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 8d21b5b477..c395c817d5 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -41,7 +41,7 @@ public class MailSenderIT extends AbstractMailIT {
// Check that the user's email was added as Reply-To
assertThat(sender.getMessages()).hasSize(1);
Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
- assertThat(headerString(headers, "Reply-To")).contains(user.email);
+ assertThat(headerString(headers, "Reply-To")).contains(user.email());
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java b/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java
index c8292ba5e4..628b90c06b 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java
@@ -18,24 +18,27 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
import com.google.gerrit.testing.FakeEmailSender;
+import com.google.inject.Inject;
import org.junit.Test;
public class NotificationMailFormatIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
@Test
public void userReceivesPlaintextEmail() throws Exception {
// Set user preference to receive only plaintext content
GeneralPreferencesInfo i = new GeneralPreferencesInfo();
i.emailFormat = EmailFormat.PLAINTEXT;
- gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+ gApi.accounts().id(admin.id().toString()).setPreferences(i);
// Create change as admin and review as user
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.recommend());
// Check that admin has received only plaintext content
@@ -43,20 +46,20 @@ public class NotificationMailFormatIT extends AbstractDaemonTest {
FakeEmailSender.Message m = sender.getMessages().get(0);
assertThat(m.body()).isNotNull();
assertThat(m.htmlBody()).isNull();
- assertMailReplyTo(m, admin.email);
- assertMailReplyTo(m, user.email);
+ assertMailReplyTo(m, admin.email());
+ assertMailReplyTo(m, user.email());
// Reset user preference
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
i.emailFormat = EmailFormat.HTML_PLAINTEXT;
- gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+ gApi.accounts().id(admin.id().toString()).setPreferences(i);
}
@Test
public void userReceivesHtmlAndPlaintextEmail() throws Exception {
// Create change as admin and review as user
PushOneCommit.Result r = createChange();
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.recommend());
// Check that admin has received both HTML and plaintext content
@@ -64,7 +67,7 @@ public class NotificationMailFormatIT extends AbstractDaemonTest {
FakeEmailSender.Message m = sender.getMessages().get(0);
assertThat(m.body()).isNotNull();
assertThat(m.htmlBody()).isNotNull();
- assertMailReplyTo(m, admin.email);
- assertMailReplyTo(m, user.email);
+ assertMailReplyTo(m, admin.email());
+ assertMailReplyTo(m, user.email());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/SignedTokenEmailTokenVerifierIT.java b/javatests/com/google/gerrit/acceptance/server/mail/SignedTokenEmailTokenVerifierIT.java
index 733511228f..c24b1a08f8 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/SignedTokenEmailTokenVerifierIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/SignedTokenEmailTokenVerifierIT.java
@@ -11,6 +11,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.
+
package com.google.gerrit.acceptance.server.mail;
import static com.google.common.truth.Truth.assertThat;
@@ -20,9 +21,9 @@ import com.google.common.io.BaseEncoding;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.server.mail.EmailTokenVerifier;
import com.google.gerrit.server.mail.EmailTokenVerifier.InvalidTokenException;
+import com.google.gerrit.server.mail.SignedToken;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
import com.google.gerrit.testing.ConfigSuite;
-import com.google.gwtjsonrpc.server.SignedToken;
import java.nio.charset.StandardCharsets;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
@@ -51,27 +52,27 @@ public class SignedTokenEmailTokenVerifierIT extends AbstractDaemonTest {
/** Test encode */
@Test
public void encode() throws Exception {
- String tokenString = signedTokenEmailTokenVerifier.encode(user.id, user.email);
+ String tokenString = signedTokenEmailTokenVerifier.encode(user.id(), user.email());
int index = tokenString.indexOf("$");
String text = tokenString.substring(index + 1);
String textDecoded = new String(BaseEncoding.base64Url().decode(text), StandardCharsets.UTF_8);
int pos = textDecoded.indexOf(":");
- assertThat(textDecoded.substring(0, pos)).isEqualTo(user.id.toString());
- assertThat(textDecoded.substring(pos + 1)).isEqualTo(user.email);
+ assertThat(textDecoded.substring(0, pos)).isEqualTo(user.id().toString());
+ assertThat(textDecoded.substring(pos + 1)).isEqualTo(user.email());
}
/** Test decode */
@Test
public void decode() throws Exception {
- String tokenString = signedTokenEmailTokenVerifier.encode(user.id, user.email);
+ String tokenString = signedTokenEmailTokenVerifier.encode(user.id(), user.email());
String tokenKey = tokenString.substring(0, tokenString.indexOf("$"));
- String text = user.id + ":" + user.email;
+ String text = user.id() + ":" + user.email();
String invalidTokenString =
tokenKey + "$" + BaseEncoding.base64Url().encode(text.getBytes(StandardCharsets.UTF_8));
EmailTokenVerifier.ParsedToken parsedToken =
signedTokenEmailTokenVerifier.decode(invalidTokenString);
- assertThat(parsedToken.getAccountId()).isEqualTo(user.id);
- assertThat(parsedToken.getEmailAddress()).isEqualTo(user.email);
+ assertThat(parsedToken.getAccountId()).isEqualTo(user.id());
+ assertThat(parsedToken.getEmailAddress()).isEqualTo(user.email());
}
/** Test token format is wrong(without '$' to split key and text) */
@@ -105,15 +106,15 @@ public class SignedTokenEmailTokenVerifierIT extends AbstractDaemonTest {
assertThrows(
InvalidTokenException.class,
() -> signedTokenEmailTokenVerifier.decode("Illegal token key$...."));
- assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("Token length mismatch");
+ assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("Base64 decoding failed");
}
/** Test token text not match the required pattern */
@Test
public void tokenTextPatternMismatch() throws Exception {
- String tokenString = signedTokenEmailTokenVerifier.encode(user.id, user.email);
+ String tokenString = signedTokenEmailTokenVerifier.encode(user.id(), user.email());
String tokenKey = tokenString.substring(0, tokenString.indexOf("$"));
- String pattern = user.id + ":" + user.email;
+ String pattern = user.id() + ":" + user.email();
String invalidTokenTextPattern = tokenKey + "$" + pattern.replace(":", "");
InvalidTokenException thrown =
assertThrows(
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
deleted file mode 100644
index 5ebfa9e926..0000000000
--- a/javatests/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ /dev/null
@@ -1,1594 +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.acceptance.server.notedb;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
-import static com.google.gerrit.reviewdb.client.RefNames.refsDraftComments;
-import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.concurrent.TimeUnit.DAYS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Ordering;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.acceptance.TestAccount;
-import com.google.gerrit.common.data.GlobalCapability;
-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.CommentInfo;
-import com.google.gerrit.extensions.common.Input;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-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.Patch;
-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.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.change.RevisionResource;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbUpdateManager;
-import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.testing.Util;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.restapi.change.PostReview;
-import com.google.gerrit.server.restapi.change.Rebuild;
-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.UpdateException;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.NoteDbChecker;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gerrit.testing.TestChanges;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Timestamp;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
-import org.apache.http.Header;
-import org.apache.http.message.BasicHeader;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ChangeRebuilderIT extends AbstractDaemonTest {
- @ConfigSuite.Default
- public static Config defaultConfig() {
- Config cfg = new Config();
- cfg.setBoolean("noteDb", null, "testRebuilderWrapper", true);
-
- // Disable async reindex-if-stale check after index update. This avoids
- // unintentional auto-rebuilding of the change in NoteDb during the read
- // path of the reindex-if-stale check. For the purposes of this test, we
- // want precise control over when auto-rebuilding happens.
- cfg.setBoolean("index", null, "autoReindexIfStale", false);
-
- // setNotesMigration tries to keep IDs in sync between ReviewDb and NoteDb, which is behavior
- // unique to this test. This gets prohibitively slow if we use the default sequence gap.
- cfg.setInt("noteDb", "changes", "initialSequenceGap", 0);
-
- return cfg;
- }
-
- @Inject private NoteDbChecker checker;
-
- @Inject private Rebuild rebuildHandler;
-
- @Inject private Provider<ReviewDb> dbProvider;
-
- @Inject private CommentsUtil commentsUtil;
-
- @Inject private Provider<PostReview> postReview;
-
- @Inject private TestChangeRebuilderWrapper rebuilderWrapper;
-
- @Inject private Sequences seq;
-
- @Inject private ChangeBundleReader bundleReader;
-
- @Inject private PatchSetInfoFactory patchSetInfoFactory;
-
- @Inject private PatchListCache patchListCache;
-
- @Before
- public void setUp() throws Exception {
- assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- setNotesMigration(false, false);
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- }
-
- @SuppressWarnings("deprecation")
- private void setNotesMigration(boolean writeChanges, boolean readChanges) throws Exception {
- notesMigration.setWriteChanges(writeChanges);
- notesMigration.setReadChanges(readChanges);
- db = atrScope.reopenDb().getReviewDbProvider().get();
-
- if (notesMigration.readChangeSequence()) {
- // Copy next ReviewDb ID to NoteDb.
- seq.getChangeIdRepoSequence().set(db.nextChangeId());
- } else {
- // Copy next NoteDb ID to ReviewDb.
- while (db.nextChangeId() < seq.getChangeIdRepoSequence().next()) {}
- }
- }
-
- @Test
- public void changeFields() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- gApi.changes().id(id.get()).topic(name("a-topic"));
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void patchSets() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- amendChange(r.getChangeId());
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void publishedComment() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putComment(user, id, 1, "comment", null);
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void publishedCommentAndReply() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putComment(user, id, 1, "comment", null);
- Map<String, List<CommentInfo>> comments = getPublishedComments(id);
- String parentUuid = comments.get("a.txt").get(0).id;
- putComment(user, id, 1, "comment", parentUuid);
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void patchSetWithNullGroups() throws Exception {
- Timestamp ts = TimeUtil.nowTs();
- Change c = TestChanges.newChange(project, user.getId(), seq.nextChangeId());
- c.setCreatedOn(ts);
- c.setLastUpdatedOn(ts);
- c.setReviewStarted(true);
- PatchSet ps =
- TestChanges.newPatchSet(
- c.currentPatchSetId(), "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", user.getId());
- ps.setCreatedOn(ts);
- db.changes().insert(Collections.singleton(c));
- db.patchSets().insert(Collections.singleton(ps));
-
- assertThat(ps.getGroups()).isEmpty();
- checker.rebuildAndCheckChanges(c.getId());
- }
-
- @Test
- public void draftComment() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "comment", null);
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void draftAndPublishedComment() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "draft comment", null);
- putComment(user, id, 1, "published comment", null);
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void publishDraftComment() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "draft comment", null);
- publishDrafts(user, id);
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void nullAccountId() throws Exception {
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId = r.getPatchSetId();
- Change.Id id = psId.getParentKey();
-
- // Events need to be otherwise identical for the account ID to be compared.
- ChangeMessage msg1 = insertMessage(id, psId, user.getId(), TimeUtil.nowTs(), "message 1");
- insertMessage(id, psId, null, msg1.getWrittenOn(), "message 2");
-
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void nullPatchSetId() throws Exception {
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId1 = r.getPatchSetId();
- Change.Id id = psId1.getParentKey();
-
- // Events need to be otherwise identical for the PatchSet.ID to be compared.
- ChangeMessage msg1 = insertMessage(id, null, user.getId(), TimeUtil.nowTs(), "message 1");
- insertMessage(id, null, user.getId(), msg1.getWrittenOn(), "message 2");
-
- PatchSet.Id psId2 = amendChange(r.getChangeId()).getPatchSetId();
-
- ChangeMessage msg3 = insertMessage(id, null, user.getId(), TimeUtil.nowTs(), "message 3");
- insertMessage(id, null, user.getId(), msg3.getWrittenOn(), "message 4");
-
- checker.rebuildAndCheckChanges(id);
-
- setNotesMigration(true, true);
-
- ChangeNotes notes = notesFactory.create(db, project, id);
- Map<String, PatchSet.Id> psIds = new HashMap<>();
- for (ChangeMessage msg : notes.getChangeMessages()) {
- PatchSet.Id psId = msg.getPatchSetId();
- assertThat(psId).named("patchset for " + msg).isNotNull();
- psIds.put(msg.getMessage(), psId);
- }
- // Patch set IDs were replaced during conversion process.
- assertThat(psIds).containsEntry("message 1", psId1);
- assertThat(psIds).containsEntry("message 2", psId1);
- assertThat(psIds).containsEntry("message 3", psId2);
- assertThat(psIds).containsEntry("message 4", psId2);
- }
-
- @Test
- public void noWriteToNewRef() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- checker.assertNoChangeRef(project, id);
-
- setNotesMigration(true, false);
- gApi.changes().id(id.get()).topic(name("a-topic"));
-
- // First write doesn't create the ref, but rebuilding works.
- checker.assertNoChangeRef(project, id);
- assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isNull();
- checker.rebuildAndCheckChanges(id);
-
- // Now that there is a ref, writes are "turned on" for this change, and
- // NoteDb stays up to date without explicit rebuilding.
- gApi.changes().id(id.get()).topic(name("new-topic"));
- assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isNotNull();
- checker.checkChanges(id);
- }
-
- @Test
- public void restApiNotFoundWhenNoteDbDisabled() throws Exception {
- PushOneCommit.Result r = createChange();
- exception.expect(ResourceNotFoundException.class);
- rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Input());
- }
-
- @Test
- public void rebuildViaRestApi() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- setNotesMigration(true, false);
-
- checker.assertNoChangeRef(project, id);
- rebuildHandler.apply(parseChangeResource(r.getChangeId()), new Input());
- checker.checkChanges(id);
- }
-
- @Test
- public void writeToNewRefForNewChange() throws Exception {
- PushOneCommit.Result r1 = createChange();
- Change.Id id1 = r1.getPatchSetId().getParentKey();
-
- setNotesMigration(true, false);
- gApi.changes().id(id1.get()).topic(name("a-topic"));
- PushOneCommit.Result r2 = createChange();
- Change.Id id2 = r2.getPatchSetId().getParentKey();
-
- // Second change was created after NoteDb writes were turned on, so it was
- // allowed to write to a new ref.
- checker.checkChanges(id2);
-
- // First change was created before NoteDb writes were turned on, so its meta
- // ref doesn't exist until a manual rebuild.
- checker.assertNoChangeRef(project, id1);
- checker.rebuildAndCheckChanges(id1);
- }
-
- @Test
- public void noteDbChangeState() throws Exception {
- setNotesMigration(true, true);
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
-
- ObjectId changeMetaId = getMetaRef(project, changeMetaRef(id));
- assertThat(getUnwrappedDb().changes().get(id).getNoteDbState()).isEqualTo(changeMetaId.name());
-
- putDraft(user, id, 1, "comment by user", null);
- ObjectId userDraftsId = getMetaRef(allUsers, refsDraftComments(id, user.getId()));
- assertThat(getUnwrappedDb().changes().get(id).getNoteDbState())
- .isEqualTo(changeMetaId.name() + "," + user.getId() + "=" + userDraftsId.name());
-
- putDraft(admin, id, 2, "comment by admin", null);
- ObjectId adminDraftsId = getMetaRef(allUsers, refsDraftComments(id, admin.getId()));
- assertThat(admin.getId().get()).isLessThan(user.getId().get());
- assertThat(getUnwrappedDb().changes().get(id).getNoteDbState())
- .isEqualTo(
- changeMetaId.name()
- + ","
- + admin.getId()
- + "="
- + adminDraftsId.name()
- + ","
- + user.getId()
- + "="
- + userDraftsId.name());
-
- putDraft(admin, id, 2, "revised comment by admin", null);
- adminDraftsId = getMetaRef(allUsers, refsDraftComments(id, admin.getId()));
- assertThat(getUnwrappedDb().changes().get(id).getNoteDbState())
- .isEqualTo(
- changeMetaId.name()
- + ","
- + admin.getId()
- + "="
- + adminDraftsId.name()
- + ","
- + user.getId()
- + "="
- + userDraftsId.name());
- }
-
- @Test
- public void rebuildAutomaticallyWhenChangeOutOfDate() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- assertChangeUpToDate(true, id);
-
- // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
- setNotesMigration(false, false);
- gApi.changes().id(id.get()).topic(name("a-topic"));
- setInvalidNoteDbState(id);
- assertChangeUpToDate(false, id);
-
- // On next NoteDb read, the change is transparently rebuilt.
- setNotesMigration(true, true);
- assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
- assertChangeUpToDate(true, id);
-
- // Check that the bundles are equal.
- ChangeBundle actual =
- ChangeBundle.fromNotes(commentsUtil, notesFactory.create(dbProvider.get(), project, id));
- ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
- assertThat(actual.differencesFrom(expected)).isEmpty();
- }
-
- @Test
- public void rebuildAutomaticallyWithinBatchUpdate() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- final Change.Id id = r.getPatchSetId().getParentKey();
- assertChangeUpToDate(true, id);
-
- // Update ReviewDb and NoteDb, then revert the corresponding NoteDb change
- // to simulate it failing.
- NoteDbChangeState oldState = NoteDbChangeState.parse(getUnwrappedDb().changes().get(id));
- String topic = name("a-topic");
- gApi.changes().id(id.get()).topic(topic);
- try (Repository repo = repoManager.openRepository(project)) {
- new TestRepository<>(repo).update(RefNames.changeMetaRef(id), oldState.getChangeMetaId());
- }
- assertChangeUpToDate(false, id);
-
- // Next NoteDb read comes inside the transaction started by BatchUpdate. In
- // reality this could be caused by a failed update happening between when
- // the change is parsed by ChangesCollection and when the BatchUpdate
- // executes. We simulate it here by using BatchUpdate directly and not going
- // through an API handler.
- final String msg = "message from BatchUpdate";
- try (BatchUpdate bu =
- batchUpdateFactory.create(
- db, project, identifiedUserFactory.create(user.getId()), TimeUtil.nowTs())) {
- bu.addOp(
- id,
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx) throws OrmException {
- PatchSet.Id psId = ctx.getChange().currentPatchSetId();
- ChangeMessage cm =
- new ChangeMessage(
- new ChangeMessage.Key(id, ChangeUtil.messageUuid()),
- ctx.getAccountId(),
- ctx.getWhen(),
- psId);
- cm.setMessage(msg);
- ctx.getDb().changeMessages().insert(Collections.singleton(cm));
- ctx.getUpdate(psId).setChangeMessage(msg);
- return true;
- }
- });
- try {
- bu.execute();
- fail("expected update to fail");
- } catch (UpdateException e) {
- assertThat(e.getMessage()).contains("cannot copy ChangeNotesState");
- }
- }
-
- // TODO(dborowitz): Re-enable these assertions once we fix auto-rebuilding
- // in the BatchUpdate path.
- // As an implementation detail, change wasn't actually rebuilt inside the
- // BatchUpdate transaction, but it was rebuilt during read for the
- // subsequent reindex. Thus it's impossible to actually observe an
- // out-of-date state in the caller.
- // assertChangeUpToDate(true, id);
-
- // Check that the bundles are equal.
- // ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
- // ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
- // ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
- // assertThat(actual.differencesFrom(expected)).isEmpty();
- // assertThat(
- // Iterables.transform(
- // notes.getChangeMessages(),
- // ChangeMessage::getMessage))
- // .contains(msg);
- // assertThat(actual.getChange().getTopic()).isEqualTo(topic);
- }
-
- @Test
- public void rebuildIgnoresErrorIfChangeIsUpToDateAfter() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- assertChangeUpToDate(true, id);
-
- // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
- setNotesMigration(false, false);
- gApi.changes().id(id.get()).topic(name("a-topic"));
- setInvalidNoteDbState(id);
- assertChangeUpToDate(false, id);
-
- // Force the next rebuild attempt to fail but also rebuild the change in the
- // background.
- rebuilderWrapper.stealNextUpdate();
- setNotesMigration(true, true);
- assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
- assertChangeUpToDate(true, id);
-
- // Check that the bundles are equal.
- ChangeBundle actual =
- ChangeBundle.fromNotes(commentsUtil, notesFactory.create(dbProvider.get(), project, id));
- ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
- assertThat(actual.differencesFrom(expected)).isEmpty();
- }
-
- @Test
- public void rebuildReturnsCorrectResultEvenIfSavingToNoteDbFailed() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- assertChangeUpToDate(true, id);
- ObjectId oldMetaId = getMetaRef(project, changeMetaRef(id));
-
- // Make a ReviewDb change behind NoteDb's back.
- setNotesMigration(false, false);
- gApi.changes().id(id.get()).topic(name("a-topic"));
- setInvalidNoteDbState(id);
- assertChangeUpToDate(false, id);
- assertThat(getMetaRef(project, changeMetaRef(id))).isEqualTo(oldMetaId);
-
- // Force the next rebuild attempt to fail.
- rebuilderWrapper.failNextUpdate();
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-
- // Not up to date, but the actual returned state matches anyway.
- assertChangeUpToDate(false, id);
- assertThat(getMetaRef(project, changeMetaRef(id))).isEqualTo(oldMetaId);
- ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
- ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
- assertThat(actual.differencesFrom(expected)).isEmpty();
- assertChangeUpToDate(false, id);
-
- // Another rebuild attempt succeeds
- notesFactory.create(dbProvider.get(), project, id);
- assertThat(getMetaRef(project, changeMetaRef(id))).isNotEqualTo(oldMetaId);
- assertChangeUpToDate(true, id);
- }
-
- @Test
- public void rebuildReturnsDraftResultWhenRebuildingInChangeNotesFails() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "comment by user", null);
- assertChangeUpToDate(true, id);
-
- ObjectId oldMetaId = getMetaRef(allUsers, refsDraftComments(id, user.getId()));
-
- // Add a draft behind NoteDb's back.
- setNotesMigration(false, false);
- putDraft(user, id, 1, "second comment by user", null);
- setInvalidNoteDbState(id);
- assertDraftsUpToDate(false, id, user);
- assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
- // Force the next rebuild attempt to fail (in ChangeNotes).
- rebuilderWrapper.failNextUpdate();
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
- notes.getDraftComments(user.getId());
- assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
- // Not up to date, but the actual returned state matches anyway.
- assertDraftsUpToDate(false, id, user);
- ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
- ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
- assertThat(actual.differencesFrom(expected)).isEmpty();
-
- // Another rebuild attempt succeeds
- notesFactory.create(dbProvider.get(), project, id);
- assertChangeUpToDate(true, id);
- assertDraftsUpToDate(true, id, user);
- assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isNotEqualTo(oldMetaId);
- }
-
- @Test
- public void rebuildReturnsDraftResultWhenRebuildingInDraftCommentNotesFails() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "comment by user", null);
- assertChangeUpToDate(true, id);
-
- ObjectId oldMetaId = getMetaRef(allUsers, refsDraftComments(id, user.getId()));
-
- // Add a draft behind NoteDb's back.
- setNotesMigration(false, false);
- putDraft(user, id, 1, "second comment by user", null);
-
- ReviewDb db = getUnwrappedDb();
- Change c = db.changes().get(id);
- // Leave change meta ID alone so DraftCommentNotes does the rebuild.
- ObjectId badSha = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- NoteDbChangeState bogusState =
- new NoteDbChangeState(
- id,
- PrimaryStorage.REVIEW_DB,
- Optional.of(
- NoteDbChangeState.RefState.create(
- NoteDbChangeState.parse(c).getChangeMetaId(),
- ImmutableMap.of(user.getId(), badSha))),
- Optional.empty());
- c.setNoteDbState(bogusState.toString());
- db.changes().update(Collections.singleton(c));
-
- assertDraftsUpToDate(false, id, user);
- assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
- // Force the next rebuild attempt to fail (in DraftCommentNotes).
- rebuilderWrapper.failNextUpdate();
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
- notes.getDraftComments(user.getId());
- assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isEqualTo(oldMetaId);
-
- // Not up to date, but the actual returned state matches anyway.
- assertChangeUpToDate(true, id);
- assertDraftsUpToDate(false, id, user);
- ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
- ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
- assertThat(actual.differencesFrom(expected)).isEmpty();
-
- // Another rebuild attempt succeeds
- notesFactory.create(dbProvider.get(), project, id).getDraftComments(user.getId());
- assertChangeUpToDate(true, id);
- assertDraftsUpToDate(true, id, user);
- assertThat(getMetaRef(allUsers, refsDraftComments(id, user.getId()))).isNotEqualTo(oldMetaId);
- }
-
- @Test
- public void rebuildAutomaticallyWhenDraftsOutOfDate() throws Exception {
- setNotesMigration(true, true);
- setApiUser(user);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "comment", null);
- assertDraftsUpToDate(true, id, user);
-
- // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
- setNotesMigration(false, false);
- putDraft(user, id, 1, "comment", null);
- setInvalidNoteDbState(id);
- assertDraftsUpToDate(false, id, user);
-
- // On next NoteDb read, the drafts are transparently rebuilt.
- setNotesMigration(true, true);
- assertThat(gApi.changes().id(id.get()).current().drafts()).containsKey(PushOneCommit.FILE_NAME);
- assertDraftsUpToDate(true, id, user);
- }
-
- @Test
- public void pushCert() throws Exception {
- // We don't have the code in our test harness to do signed pushes, so just
- // use a hard-coded cert. This cert was actually generated by C git 2.2.0
- // (albeit not for sending to Gerrit).
- String cert =
- "certificate version 0.1\n"
- + "pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n"
- + "pushee git://localhost/repo.git\n"
- + "nonce 1433954361-bde756572d665bba81d8\n"
- + "\n"
- + "0000000000000000000000000000000000000000"
- + "b981a177396fb47345b7df3e4d3f854c6bea7"
- + "s/heads/master\n"
- + "-----BEGIN PGP SIGNATURE-----\n"
- + "Version: GnuPG v1\n"
- + "\n"
- + "iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
- + "9tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
- + "htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
- + "4ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
- + "IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
- + "+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
- + "=XFeC\n"
- + "-----END PGP SIGNATURE-----\n";
-
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId = r.getPatchSetId();
- Change.Id id = psId.getParentKey();
-
- PatchSet ps = db.patchSets().get(psId);
- ps.setPushCertificate(cert);
- db.patchSets().update(Collections.singleton(ps));
- indexer.index(db, project, id);
-
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void emptyTopic() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- Change c = db.changes().get(id);
- assertThat(c.getTopic()).isNull();
- c.setTopic("");
- db.changes().update(Collections.singleton(c));
-
- checker.rebuildAndCheckChanges(id);
-
- setNotesMigration(true, true);
-
- // Rebuild and check was successful, but NoteDb doesn't support storing an
- // empty topic, so it comes out as null.
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(notes.getChange().getTopic()).isNull();
- }
-
- @Test
- public void commentBeforeFirstPatchSet() throws Exception {
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId = r.getPatchSetId();
- Change.Id id = psId.getParentKey();
-
- Change c = db.changes().get(id);
- c.setCreatedOn(new Timestamp(c.getCreatedOn().getTime() - 5000));
- db.changes().update(Collections.singleton(c));
- indexer.index(db, project, id);
-
- ReviewInput rin = new ReviewInput();
- rin.message = "comment";
-
- Timestamp ts = new Timestamp(c.getCreatedOn().getTime() + 2000);
- assertThat(ts).isGreaterThan(c.getCreatedOn());
- assertThat(ts).isLessThan(db.patchSets().get(psId).getCreatedOn());
- RevisionResource revRsrc = parseCurrentRevisionResource(r.getChangeId());
- postReview.get().apply(batchUpdateFactory, revRsrc, rin, ts);
-
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void commentPredatingChangeBySomeoneOtherThanOwner() throws Exception {
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId = r.getPatchSetId();
- Change.Id id = psId.getParentKey();
- Change c = db.changes().get(id);
-
- ReviewInput rin = new ReviewInput();
- rin.message = "comment";
-
- Timestamp ts = new Timestamp(c.getCreatedOn().getTime() - 10000);
- RevisionResource revRsrc = parseCurrentRevisionResource(r.getChangeId());
- setApiUser(user);
- postReview.get().apply(batchUpdateFactory, revRsrc, rin, ts);
-
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void noteDbUsesOriginalSubjectFromPatchSetAndIgnoresChangeField() throws Exception {
- PushOneCommit.Result r = createChange();
- String orig = r.getChange().change().getSubject();
- r =
- pushFactory
- .create(
- db,
- admin.getIdent(),
- testRepo,
- orig + " v2",
- PushOneCommit.FILE_NAME,
- "new contents",
- r.getChangeId())
- .to("refs/for/master");
- r.assertOkStatus();
-
- PatchSet.Id psId = r.getPatchSetId();
- Change.Id id = psId.getParentKey();
- Change c = db.changes().get(id);
-
- c.setCurrentPatchSet(psId, c.getSubject(), "Bogus original subject");
- db.changes().update(Collections.singleton(c));
-
- checker.rebuildAndCheckChanges(id);
-
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(db, project, id);
- Change nc = notes.getChange();
- assertThat(nc.getSubject()).isEqualTo(c.getSubject());
- assertThat(nc.getSubject()).isEqualTo(orig + " v2");
- assertThat(nc.getOriginalSubject()).isNotEqualTo(c.getOriginalSubject());
- assertThat(nc.getOriginalSubject()).isEqualTo(orig);
- }
-
- @Test
- public void ignorePatchLineCommentsOnPatchSet0() throws Exception {
- PushOneCommit.Result r = createChange();
- Change change = r.getChange().change();
- Change.Id id = change.getId();
-
- PatchLineComment comment =
- new PatchLineComment(
- new PatchLineComment.Key(
- new Patch.Key(new PatchSet.Id(id, 0), PushOneCommit.FILE_NAME), "uuid"),
- 0,
- user.getId(),
- null,
- TimeUtil.nowTs());
- comment.setSide((short) 1);
- comment.setMessage("message");
- comment.setStatus(PatchLineComment.Status.PUBLISHED);
- db.patchComments().insert(Collections.singleton(comment));
- indexer.index(db, change.getProject(), id);
-
- checker.rebuildAndCheckChanges(id);
-
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(notes.getComments()).isEmpty();
- }
-
- @Test
- public void leadingSpacesInSubject() throws Exception {
- String subj = " " + PushOneCommit.SUBJECT;
- PushOneCommit push =
- pushFactory.create(
- db,
- admin.getIdent(),
- testRepo,
- subj,
- PushOneCommit.FILE_NAME,
- PushOneCommit.FILE_CONTENT);
- PushOneCommit.Result r = push.to("refs/for/master");
- r.assertOkStatus();
- Change change = r.getChange().change();
- assertThat(change.getSubject()).isEqualTo(subj);
- Change.Id id = r.getPatchSetId().getParentKey();
-
- checker.rebuildAndCheckChanges(id);
-
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(notes.getChange().getSubject()).isNotEqualTo(subj);
- assertThat(notes.getChange().getSubject()).isEqualTo(PushOneCommit.SUBJECT);
- }
-
- @Test
- public void allTimestampsExceptUpdatedAreEqualDueToBadMigration() throws Exception {
- // https://bugs.chromium.org/p/gerrit/issues/detail?id=7397
- PushOneCommit.Result r = createChange();
- Change c = r.getChange().change();
- Change.Id id = c.getId();
- Timestamp ts = TimeUtil.nowTs();
- Timestamp origUpdated = c.getLastUpdatedOn();
-
- c.setCreatedOn(ts);
- assertThat(c.getCreatedOn()).isGreaterThan(c.getLastUpdatedOn());
- db.changes().update(Collections.singleton(c));
-
- List<ChangeMessage> cm = db.changeMessages().byChange(id).toList();
- cm.forEach(m -> m.setWrittenOn(ts));
- db.changeMessages().update(cm);
-
- List<PatchSet> ps = db.patchSets().byChange(id).toList();
- ps.forEach(p -> p.setCreatedOn(ts));
- db.patchSets().update(ps);
-
- List<PatchSetApproval> psa = db.patchSetApprovals().byChange(id).toList();
- psa.forEach(p -> p.setGranted(ts));
- db.patchSetApprovals().update(psa);
-
- List<PatchLineComment> plc = db.patchComments().byChange(id).toList();
- plc.forEach(p -> p.setWrittenOn(ts));
- db.patchComments().update(plc);
-
- checker.rebuildAndCheckChanges(id);
-
- setNotesMigration(true, true);
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(notes.getChange().getCreatedOn()).isEqualTo(origUpdated);
- assertThat(notes.getChange().getLastUpdatedOn()).isAtLeast(origUpdated);
- assertThat(notes.getPatchSets().get(new PatchSet.Id(id, 1)).getCreatedOn())
- .isEqualTo(origUpdated);
- }
-
- @Test
- public void createWithAutoRebuildingDisabled() throws Exception {
- ReviewDb oldDb = db;
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- ChangeNotes oldNotes = notesFactory.create(db, project, id);
-
- // Make a ReviewDb change behind NoteDb's back.
- Change c = oldDb.changes().get(id);
- assertThat(c.getTopic()).isNull();
- String topic = name("a-topic");
- c.setTopic(topic);
- oldDb.changes().update(Collections.singleton(c));
-
- c = oldDb.changes().get(c.getId());
- ChangeNotes newNotes = notesFactory.createWithAutoRebuildingDisabled(c, null);
- assertThat(newNotes.getChange().getTopic()).isNotEqualTo(topic);
- assertThat(newNotes.getChange().getTopic()).isEqualTo(oldNotes.getChange().getTopic());
- }
-
- @Test
- public void rebuildDeletesOldDraftRefs() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "comment", null);
-
- Account.Id otherAccountId = new Account.Id(user.getId().get() + 1234);
- String otherDraftRef = refsDraftComments(id, otherAccountId);
-
- try (Repository repo = repoManager.openRepository(allUsers);
- ObjectInserter ins = repo.newObjectInserter()) {
- ObjectId sha = ins.insert(OBJ_BLOB, "garbage data".getBytes(UTF_8));
- ins.flush();
- RefUpdate ru = repo.updateRef(otherDraftRef);
- ru.setExpectedOldObjectId(ObjectId.zeroId());
- ru.setNewObjectId(sha);
- assertThat(ru.update()).isEqualTo(RefUpdate.Result.NEW);
- }
-
- checker.rebuildAndCheckChanges(id);
-
- try (Repository repo = repoManager.openRepository(allUsers)) {
- assertThat(repo.exactRef(otherDraftRef)).isNull();
- }
- }
-
- @Test
- public void failWhenWritesDisabled() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- assertChangeUpToDate(true, id);
- assertThat(gApi.changes().id(id.get()).info().topic).isNull();
-
- // Turning off writes causes failure.
- setNotesMigration(false, true);
- try {
- gApi.changes().id(id.get()).topic(name("a-topic"));
- fail("Expected write to fail");
- } catch (RestApiException e) {
- assertChangesReadOnly(e);
- }
-
- // Update was not written.
- assertThat(gApi.changes().id(id.get()).info().topic).isNull();
- assertChangeUpToDate(true, id);
- }
-
- @Test
- public void rebuildWhenWritesDisabledWorksButDoesNotWrite() throws Exception {
- setNotesMigration(true, true);
-
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- assertChangeUpToDate(true, id);
-
- // Make a ReviewDb change behind NoteDb's back and ensure it's detected.
- setNotesMigration(false, false);
- gApi.changes().id(id.get()).topic(name("a-topic"));
- setInvalidNoteDbState(id);
- assertChangeUpToDate(false, id);
-
- // On next NoteDb read, change is rebuilt in-memory but not stored.
- setNotesMigration(false, true);
- assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
- assertChangeUpToDate(false, id);
-
- // Attempting to write directly causes failure.
- try {
- gApi.changes().id(id.get()).topic(name("other-topic"));
- fail("Expected write to fail");
- } catch (RestApiException e) {
- assertChangesReadOnly(e);
- }
-
- // Update was not written.
- assertThat(gApi.changes().id(id.get()).info().topic).isEqualTo(name("a-topic"));
- assertChangeUpToDate(false, id);
- }
-
- @Test
- public void rebuildChangeWithNoPatchSets() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- db.changes().beginTransaction(id);
- try {
- db.patchSets().delete(db.patchSets().byChange(id));
- db.commit();
- } finally {
- db.rollback();
- }
-
- try {
- checker.rebuildAndCheckChanges(id);
- assert_().fail("expected NoPatchSetsException");
- } catch (NoPatchSetsException e) {
- // Expected.
- }
-
- Change c = db.changes().get(id);
- assertThat(c.getNoteDbState()).isNull();
- checker.assertNoChangeRef(project, id);
- }
-
- @Test
- public void rebuildChangeWithNoEntitiesOtherThanChange() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- db.changes().beginTransaction(id);
- try {
- db.changeMessages().delete(db.changeMessages().byChange(id));
- db.patchSets().delete(db.patchSets().byChange(id));
- db.patchSetApprovals().delete(db.patchSetApprovals().byChange(id));
- db.patchComments().delete(db.patchComments().byChange(id));
- db.commit();
- } finally {
- db.rollback();
- }
-
- try {
- checker.rebuildAndCheckChanges(id);
- assert_().fail("expected NoPatchSetsException");
- } catch (NoPatchSetsException e) {
- // Expected.
- }
-
- Change c = db.changes().get(id);
- assertThat(c.getNoteDbState()).isNull();
- checker.assertNoChangeRef(project, id);
- }
-
- @Test
- public void rebuildEntitiesCreatedByImpersonation() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- PatchSet.Id psId = new PatchSet.Id(id, 1);
- String prefix = "/changes/" + id + "/revisions/current/";
-
- // For each of the entities that have a real user field, create one entity
- // without impersonation and one with.
- CommentInput ci = new CommentInput();
- ci.path = Patch.COMMIT_MSG;
- ci.side = Side.REVISION;
- ci.line = 1;
- ci.message = "comment without impersonation";
- ReviewInput ri = new ReviewInput();
- ri.label("Code-Review", -1);
- ri.message = "message without impersonation";
- ri.drafts = DraftHandling.KEEP;
- ri.comments = ImmutableMap.of(ci.path, ImmutableList.of(ci));
- userRestSession.post(prefix + "review", ri).assertOK();
-
- DraftInput di = new DraftInput();
- di.path = Patch.COMMIT_MSG;
- di.side = Side.REVISION;
- di.line = 1;
- di.message = "draft without impersonation";
- userRestSession.put(prefix + "drafts", di).assertCreated();
-
- allowRunAs();
- try {
- Header runAs = new BasicHeader("X-Gerrit-RunAs", user.id.toString());
- ci.message = "comment with impersonation";
- ri.message = "message with impersonation";
- ri.label("Code-Review", 1);
- adminRestSession.postWithHeader(prefix + "review", runAs, ri).assertOK();
-
- di.message = "draft with impersonation";
- adminRestSession.putWithHeader(prefix + "drafts", runAs, di).assertCreated();
- } finally {
- removeRunAs();
- }
-
- List<ChangeMessage> msgs =
- Ordering.natural()
- .onResultOf(ChangeMessage::getWrittenOn)
- .sortedCopy(db.changeMessages().byChange(id));
- assertThat(msgs).hasSize(3);
- assertThat(msgs.get(1).getMessage()).endsWith("message without impersonation");
- assertThat(msgs.get(1).getAuthor()).isEqualTo(user.id);
- assertThat(msgs.get(1).getRealAuthor()).isEqualTo(user.id);
- assertThat(msgs.get(2).getMessage()).endsWith("message with impersonation");
- assertThat(msgs.get(2).getAuthor()).isEqualTo(user.id);
- assertThat(msgs.get(2).getRealAuthor()).isEqualTo(admin.id);
-
- List<PatchSetApproval> psas = db.patchSetApprovals().byChange(id).toList();
- assertThat(psas).hasSize(1);
- assertThat(psas.get(0).getLabel()).isEqualTo("Code-Review");
- assertThat(psas.get(0).getValue()).isEqualTo(1);
- assertThat(psas.get(0).getAccountId()).isEqualTo(user.id);
- assertThat(psas.get(0).getRealAccountId()).isEqualTo(admin.id);
-
- Ordering<PatchLineComment> commentOrder =
- Ordering.natural().onResultOf(PatchLineComment::getWrittenOn);
- List<PatchLineComment> drafts =
- commentOrder.sortedCopy(db.patchComments().draftByPatchSetAuthor(psId, user.id));
- assertThat(drafts).hasSize(2);
- assertThat(drafts.get(0).getMessage()).isEqualTo("draft without impersonation");
- assertThat(drafts.get(0).getAuthor()).isEqualTo(user.id);
- assertThat(drafts.get(0).getRealAuthor()).isEqualTo(user.id);
- assertThat(drafts.get(1).getMessage()).isEqualTo("draft with impersonation");
- assertThat(drafts.get(1).getAuthor()).isEqualTo(user.id);
- assertThat(drafts.get(1).getRealAuthor()).isEqualTo(admin.id);
-
- List<PatchLineComment> pub =
- commentOrder.sortedCopy(db.patchComments().publishedByPatchSet(psId));
- assertThat(pub).hasSize(2);
- assertThat(pub.get(0).getMessage()).isEqualTo("comment without impersonation");
- assertThat(pub.get(0).getAuthor()).isEqualTo(user.id);
- assertThat(pub.get(0).getRealAuthor()).isEqualTo(user.id);
- assertThat(pub.get(1).getMessage()).isEqualTo("comment with impersonation");
- assertThat(pub.get(1).getAuthor()).isEqualTo(user.id);
- assertThat(pub.get(1).getRealAuthor()).isEqualTo(admin.id);
- }
-
- @Test
- public void laterEventsDependingOnEarlierPatchSetDontIntefereWithOtherPatchSets()
- throws Exception {
- PushOneCommit.Result r1 = createChange();
- ChangeData cd = r1.getChange();
- Change.Id id = cd.getId();
- amendChange(cd.change().getKey().get());
- TestTimeUtil.incrementClock(90, TimeUnit.DAYS);
-
- ReviewInput rin = ReviewInput.approve();
- rin.message = "Some very late message on PS1";
- gApi.changes().id(id.get()).revision(1).review(rin);
-
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void ignoreChangeMessageBeyondCurrentPatchSet() throws Exception {
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId1 = r.getPatchSetId();
- Change.Id id = psId1.getParentKey();
- gApi.changes().id(id.get()).current().review(ReviewInput.recommend());
-
- r = amendChange(r.getChangeId());
- PatchSet.Id psId2 = r.getPatchSetId();
-
- assertThat(db.patchSets().byChange(id)).hasSize(2);
- assertThat(db.changeMessages().byPatchSet(psId2)).hasSize(1);
- db.patchSets().deleteKeys(Collections.singleton(psId2));
-
- checker.rebuildAndCheckChanges(psId2.getParentKey());
- setNotesMigration(true, true);
-
- ChangeData cd = changeDataFactory.create(db, project, id);
- assertThat(cd.change().currentPatchSetId()).isEqualTo(psId1);
- assertThat(cd.patchSets().stream().map(PatchSet::getId).collect(toList()))
- .containsExactly(psId1);
- PatchSet ps = cd.currentPatchSet();
- assertThat(ps).isNotNull();
- assertThat(ps.getId()).isEqualTo(psId1);
- }
-
- @Test
- public void highestNumberedPatchSetIsNotCurrent() throws Exception {
- PushOneCommit.Result r1 = createChange();
- PatchSet.Id psId1 = r1.getPatchSetId();
- Change.Id id = psId1.getParentKey();
- PushOneCommit.Result r2 = amendChange(r1.getChangeId());
- PatchSet.Id psId2 = r2.getPatchSetId();
-
- try (BatchUpdate bu =
- batchUpdateFactory.create(
- db, project, identifiedUserFactory.create(user.getId()), TimeUtil.nowTs())) {
- bu.addOp(
- id,
- new BatchUpdateOp() {
- @Override
- public boolean updateChange(ChangeContext ctx)
- throws PatchSetInfoNotAvailableException {
- ctx.getChange()
- .setCurrentPatchSet(patchSetInfoFactory.get(ctx.getDb(), ctx.getNotes(), psId1));
- return true;
- }
- });
- bu.execute();
- }
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(psId1, psId2);
- assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId1);
-
- assertThat(db.changes().get(id).currentPatchSetId()).isEqualTo(psId1);
-
- checker.rebuildAndCheckChanges(id);
- setNotesMigration(true, true);
-
- notes = notesFactory.create(db, project, id);
- assertThat(psUtil.byChangeAsMap(db, notes).keySet()).containsExactly(psId1, psId2);
- assertThat(notes.getChange().currentPatchSetId()).isEqualTo(psId1);
- }
-
- @Test
- public void resolveCommentsInheritsValueFromParentWhenUnspecified() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getPatchSetId().getParentKey();
- putDraft(user, id, 1, "comment", true);
- putDraft(user, id, 1, "newComment", null);
-
- Map<String, List<CommentInfo>> comments = gApi.changes().id(id.get()).current().drafts();
- for (List<CommentInfo> cList : comments.values()) {
- for (CommentInfo ci : cList) {
- assertThat(ci.unresolved).isTrue();
- }
- }
- }
-
- @Test
- public void rebuilderRespectsReadOnlyInNoteDbChangeState() throws Exception {
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- PushOneCommit.Result r = createChange();
- PatchSet.Id psId1 = r.getPatchSetId();
- Change.Id id = psId1.getParentKey();
-
- checker.rebuildAndCheckChanges(id);
- setNotesMigration(true, true);
-
- ReviewDb db = getUnwrappedDb();
- Change c = db.changes().get(id);
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- Timestamp until = new Timestamp(TimeUtil.nowMs() + MILLISECONDS.convert(1, DAYS));
- state = state.withReadOnlyUntil(until);
- c.setNoteDbState(state.toString());
- db.changes().update(Collections.singleton(c));
-
- try {
- rebuilderWrapper.rebuild(db, id);
- fail("expected rebuild to fail");
- } catch (OrmRuntimeException e) {
- assertThat(e.getMessage()).contains("read-only until");
- }
-
- TestTimeUtil.setClock(new Timestamp(until.getTime() + MILLISECONDS.convert(1, SECONDS)));
- rebuilderWrapper.rebuild(db, id);
- }
-
- @Test
- public void commitWithCrLineEndings() throws Exception {
- PushOneCommit.Result r =
- createChange("Subject\r\rBody\r", PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT);
- Change c = r.getChange().change();
-
- // This assertion demonstrates an arguable bug in JGit's commit subject
- // parsing, and shows how this kind of data might have gotten into
- // ReviewDb. If that bug ever gets fixed upstream, this assert may start
- // failing. If that happens, this test can be rewritten to directly set the
- // subject field in ReviewDb.
- assertThat(c.getSubject()).isEqualTo("Subject\r\rBody");
-
- checker.rebuildAndCheckChanges(c.getId());
- }
-
- @Test
- public void patchSetsOutOfOrder() throws Exception {
- String id = createChange().getChangeId();
- amendChange(id);
- PushOneCommit.Result r = amendChange(id);
-
- ChangeData cd = r.getChange();
- PatchSet.Id psId3 = cd.change().currentPatchSetId();
- assertThat(psId3.get()).isEqualTo(3);
-
- PatchSet ps1 = db.patchSets().get(new PatchSet.Id(cd.getId(), 1));
- PatchSet ps3 = db.patchSets().get(psId3);
- assertThat(ps1.getCreatedOn()).isLessThan(ps3.getCreatedOn());
-
- // Simulate an old Gerrit bug by setting the created timestamp of the latest
- // patch set ID to the timestamp of PS1.
- ps3.setCreatedOn(ps1.getCreatedOn());
- db.patchSets().update(Collections.singleton(ps3));
-
- checker.rebuildAndCheckChanges(cd.getId());
-
- setNotesMigration(true, true);
- cd = changeDataFactory.create(db, project, cd.getId());
- assertThat(cd.change().currentPatchSetId()).isEqualTo(psId3);
-
- List<PatchSet> patchSets = ImmutableList.copyOf(cd.patchSets());
- assertThat(patchSets).hasSize(3);
-
- PatchSet newPs1 = patchSets.get(0);
- assertThat(newPs1.getId()).isEqualTo(ps1.getId());
- assertThat(newPs1.getCreatedOn()).isEqualTo(ps1.getCreatedOn());
-
- PatchSet newPs2 = patchSets.get(1);
- assertThat(newPs2.getCreatedOn()).isGreaterThan(newPs1.getCreatedOn());
-
- PatchSet newPs3 = patchSets.get(2);
- assertThat(newPs3.getId()).isEqualTo(ps3.getId());
- // Migrated with a newer timestamp than the original, to preserve ordering.
- assertThat(newPs3.getCreatedOn()).isAtLeast(newPs2.getCreatedOn());
- assertThat(newPs3.getCreatedOn()).isGreaterThan(ps1.getCreatedOn());
- }
-
- @Test
- public void ignoreNoteDbStateWithNoCorrespondingRefWhenWritesAndReadsDisabled() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- ReviewDb db = getUnwrappedDb();
- Change c = db.changes().get(id);
- c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- db.changes().update(Collections.singleton(c));
- c = db.changes().get(id);
-
- String refName = RefNames.changeMetaRef(id);
- assertThat(getMetaRef(project, refName)).isNull();
-
- ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
- assertThat(notes.getChange().getRowVersion()).isEqualTo(c.getRowVersion());
-
- notes = notesFactory.createChecked(dbProvider.get(), project, id);
- assertThat(notes.getChange().getRowVersion()).isEqualTo(c.getRowVersion());
-
- assertThat(getMetaRef(project, refName)).isNull();
- }
-
- @Test
- public void autoRebuildMissingRefWriteOnly() throws Exception {
- setNotesMigration(true, false);
- testAutoRebuildMissingRef();
- }
-
- @Test
- public void autoRebuildMissingRefReadWrite() throws Exception {
- setNotesMigration(true, true);
- testAutoRebuildMissingRef();
- }
-
- private void testAutoRebuildMissingRef() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- assertChangeUpToDate(true, id);
- notesFactory.createChecked(db, project, id);
-
- try (Repository repo = repoManager.openRepository(project)) {
- RefUpdate ru = repo.updateRef(RefNames.changeMetaRef(id));
- ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
- }
- assertChangeUpToDate(false, id);
-
- notesFactory.createChecked(db, project, id);
- assertChangeUpToDate(true, id);
- }
-
- @Test
- public void missingPatchSetCommitOkForCommentsNotOnParentSide() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- putDraft(user, id, 1, "draft comment", null, Side.REVISION);
- putComment(user, id, 1, "published comment", null, Side.REVISION);
-
- ReviewDb db = getUnwrappedDb();
- PatchSet ps = db.patchSets().get(new PatchSet.Id(id, 1));
- ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- db.patchSets().update(Collections.singleton(ps));
-
- try {
- patchListCache.getOldId(db.changes().get(id), ps, null);
- assert_().fail("Expected PatchListNotAvailableException");
- } catch (PatchListNotAvailableException e) {
- // Expected.
- }
-
- checker.rebuildAndCheckChanges(id);
- }
-
- @Test
- public void missingPatchSetCommitOmitsCommentsOnParentSide() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- CommentInfo draftInfo = putDraft(user, id, 1, "draft comment", null, Side.PARENT);
- putComment(user, id, 1, "published comment", null, Side.PARENT);
- CommentInfo commentInfo =
- gApi.changes().id(id.get()).comments().values().stream()
- .flatMap(List::stream)
- .findFirst()
- .get();
-
- ReviewDb db = getUnwrappedDb();
- PatchSet ps = db.patchSets().get(new PatchSet.Id(id, 1));
- ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- db.patchSets().update(Collections.singleton(ps));
-
- try {
- patchListCache.getOldId(db.changes().get(id), ps, null);
- assert_().fail("Expected PatchListNotAvailableException");
- } catch (PatchListNotAvailableException e) {
- // Expected.
- }
-
- checker.rebuildAndCheckChange(
- id,
- Stream.of(draftInfo.id, commentInfo.id)
- .sorted()
- .map(c -> id + ",1," + PushOneCommit.FILE_NAME + "," + c)
- .collect(
- joining(", ", "PatchLineComment.Key sets differ: [", "] only in A; [] only in B")));
- }
-
- private void assertChangesReadOnly(RestApiException e) throws Exception {
- Throwable cause = e.getCause();
- assertThat(cause).isInstanceOf(UpdateException.class);
- assertThat(cause.getCause()).isInstanceOf(OrmException.class);
- assertThat(cause.getCause()).hasMessageThat().isEqualTo(NoteDbUpdateManager.CHANGES_READ_ONLY);
- }
-
- private void setInvalidNoteDbState(Change.Id id) throws Exception {
- ReviewDb db = getUnwrappedDb();
- Change c = db.changes().get(id);
- // In reality we would have NoteDb writes enabled, which would write a real
- // state into this field. For tests however, we turn NoteDb writes off, so
- // just use a dummy state to force ChangeNotes to view the notes as
- // out-of-date.
- c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- db.changes().update(Collections.singleton(c));
- }
-
- private void assertChangeUpToDate(boolean expected, Change.Id id) throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- Change c = getUnwrappedDb().changes().get(id);
- assertThat(c).isNotNull();
- assertThat(c.getNoteDbState()).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- assertThat(state.isChangeUpToDate(new RepoRefCache(repo))).isEqualTo(expected);
- }
- }
-
- private void assertDraftsUpToDate(boolean expected, Change.Id changeId, TestAccount account)
- throws Exception {
- try (Repository repo = repoManager.openRepository(allUsers)) {
- Change c = getUnwrappedDb().changes().get(changeId);
- assertThat(c).isNotNull();
- assertThat(c.getNoteDbState()).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- assertThat(state.areDraftsUpToDate(new RepoRefCache(repo), account.getId()))
- .isEqualTo(expected);
- }
- }
-
- private ObjectId getMetaRef(Project.NameKey p, String name) throws Exception {
- try (Repository repo = repoManager.openRepository(p)) {
- Ref ref = repo.exactRef(name);
- return ref != null ? ref.getObjectId() : null;
- }
- }
-
- private CommentInfo putDraft(
- TestAccount account, Change.Id id, int line, String msg, Boolean unresolved)
- throws Exception {
- return putDraft(account, id, line, msg, unresolved, Side.REVISION);
- }
-
- private CommentInfo putDraft(
- TestAccount account, Change.Id id, int line, String msg, Boolean unresolved, Side side)
- throws Exception {
- DraftInput in = new DraftInput();
- in.side = side;
- in.line = line;
- in.message = msg;
- in.path = PushOneCommit.FILE_NAME;
- in.unresolved = unresolved;
- AcceptanceTestRequestScope.Context old = setApiUser(account);
- try {
- return gApi.changes().id(id.get()).current().createDraft(in).get();
- } finally {
- atrScope.set(old);
- }
- }
-
- private void putComment(TestAccount account, Change.Id id, int line, String msg, String inReplyTo)
- throws Exception {
- putComment(account, id, line, msg, inReplyTo, Side.REVISION);
- }
-
- private void putComment(
- TestAccount account, Change.Id id, int line, String msg, String inReplyTo, Side side)
- throws Exception {
- CommentInput in = new CommentInput();
- in.side = side;
- in.line = line;
- in.message = msg;
- in.inReplyTo = inReplyTo;
- ReviewInput rin = new ReviewInput();
- rin.comments = new HashMap<>();
- rin.comments.put(PushOneCommit.FILE_NAME, ImmutableList.of(in));
- rin.drafts = ReviewInput.DraftHandling.KEEP;
- AcceptanceTestRequestScope.Context old = setApiUser(account);
- try {
- gApi.changes().id(id.get()).current().review(rin);
- } finally {
- atrScope.set(old);
- }
- }
-
- private void publishDrafts(TestAccount account, Change.Id id) throws Exception {
- ReviewInput rin = new ReviewInput();
- rin.drafts = ReviewInput.DraftHandling.PUBLISH_ALL_REVISIONS;
- AcceptanceTestRequestScope.Context old = setApiUser(account);
- try {
- gApi.changes().id(id.get()).current().review(rin);
- } finally {
- atrScope.set(old);
- }
- }
-
- private ChangeMessage insertMessage(
- Change.Id id, PatchSet.Id psId, Account.Id author, Timestamp ts, String message)
- throws Exception {
- ChangeMessage msg =
- new ChangeMessage(new ChangeMessage.Key(id, ChangeUtil.messageUuid()), author, ts, psId);
- msg.setMessage(message);
- db.changeMessages().insert(Collections.singleton(msg));
-
- Change c = db.changes().get(id);
- if (ts.compareTo(c.getLastUpdatedOn()) > 0) {
- c.setLastUpdatedOn(ts);
- db.changes().update(Collections.singleton(c));
- }
-
- return msg;
- }
-
- private ReviewDb getUnwrappedDb() {
- ReviewDb db = dbProvider.get();
- return ReviewDbUtil.unwrapDb(db);
- }
-
- private void allowRunAs() throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- Util.allow(
- u.getConfig(),
- GlobalCapability.RUN_AS,
- systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
- u.save();
- }
- }
-
- private void removeRunAs() throws Exception {
- try (ProjectConfigUpdate u = updateProject(allProjects)) {
- Util.remove(
- u.getConfig(),
- GlobalCapability.RUN_AS,
- systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
- u.save();
- }
- }
-
- private Map<String, List<CommentInfo>> getPublishedComments(Change.Id id) throws Exception {
- return gApi.changes().id(id.get()).current().comments();
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index f4cedbb957..748c4eaf9b 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -16,7 +16,6 @@ package com.google.gerrit.acceptance.server.notedb;
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.extensions.client.ListChangesOption.MESSAGES;
import static java.util.stream.Collectors.toList;
@@ -56,7 +55,6 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.Before;
import org.junit.Test;
public class NoteDbOnlyIT extends AbstractDaemonTest {
@@ -70,14 +68,8 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
@Inject private RetryHelper retryHelper;
- @Before
- public void setUp() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
- }
-
@Test
public void updateChangeFailureRollsBackRefUpdate() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
@@ -149,7 +141,6 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
@Test
public void retryOnLockFailureWithAtomicUpdates() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
String master = "refs/heads/master";
@@ -198,8 +189,8 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
@Test
public void missingChange() throws Exception {
Change.Id changeId = new Change.Id(1234567);
- assertNoSuchChangeException(() -> notesFactory.create(db, project, changeId));
- assertNoSuchChangeException(() -> notesFactory.createChecked(db, project, changeId));
+ assertNoSuchChangeException(() -> notesFactory.create(project, changeId));
+ assertNoSuchChangeException(() -> notesFactory.createChecked(project, changeId));
}
private void assertNoSuchChangeException(Callable<?> callable) throws Exception {
@@ -285,7 +276,7 @@ public class NoteDbOnlyIT extends AbstractDaemonTest {
}
private BatchUpdate newBatchUpdate(BatchUpdate.Factory buf) {
- return buf.create(db, project, identifiedUserFactory.create(user.getId()), TimeUtil.nowTs());
+ return buf.create(project, identifiedUserFactory.create(user.id()), TimeUtil.nowTs());
}
private Optional<ObjectId> getRef(String name) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java
deleted file mode 100644
index 50125488eb..0000000000
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbPrimaryIT.java
+++ /dev/null
@@ -1,521 +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.acceptance.server.notedb;
-
-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.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
-import static com.google.gerrit.server.notedb.NoteDbUtil.formatTime;
-import static java.util.concurrent.TimeUnit.DAYS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static java.util.stream.Collectors.toList;
-
-import com.github.rholder.retry.Retryer;
-import com.github.rholder.retry.RetryerBuilder;
-import com.github.rholder.retry.StopStrategies;
-import com.google.common.base.Throwables;
-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.NoHttpd;
-import com.google.gerrit.acceptance.PushOneCommit;
-import com.google.gerrit.common.Nullable;
-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.client.ChangeStatus;
-import com.google.gerrit.extensions.common.ApprovalInfo;
-import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.common.CommentInfo;
-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.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.ChangeMessagesUtil;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.InternalUser;
-import com.google.gerrit.server.git.RepoRefCache;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeUpdate;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.PrimaryStorageMigrator;
-import com.google.gerrit.server.notedb.TestChangeRebuilderWrapper;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.update.RetryHelper;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
-import com.google.inject.Inject;
-import com.google.inject.util.Providers;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-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.eclipse.jgit.revwalk.RevWalk;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@NoHttpd
-public class NoteDbPrimaryIT extends AbstractDaemonTest {
- @ConfigSuite.Default
- public static Config defaultConfig() {
- Config cfg = new Config();
- cfg.setString("notedb", null, "concurrentWriterTimeout", "0s");
- cfg.setString("notedb", null, "primaryStorageMigrationTimeout", "1d");
- cfg.setBoolean("noteDb", null, "testRebuilderWrapper", true);
- return cfg;
- }
-
- @Inject private ChangeBundleReader bundleReader;
- @Inject private CommentsUtil commentsUtil;
- @Inject private TestChangeRebuilderWrapper rebuilderWrapper;
- @Inject private ChangeNotes.Factory changeNotesFactory;
- @Inject private ChangeUpdate.Factory updateFactory;
- @Inject private InternalUser.Factory internalUserFactory;
- @Inject private RetryHelper retryHelper;
- @Inject private ProjectCache projectCache;
-
- private PrimaryStorageMigrator migrator;
-
- @Before
- public void setUp() throws Exception {
- assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.READ_WRITE);
- db = ReviewDbUtil.unwrapDb(db);
- TestTimeUtil.resetWithClockStep(1, SECONDS);
- migrator = newMigrator(null);
- }
-
- private PrimaryStorageMigrator newMigrator(
- @Nullable Retryer<NoteDbChangeState> ensureRebuiltRetryer) {
- return new PrimaryStorageMigrator(
- cfg,
- Providers.of(db),
- repoManager,
- allUsers,
- rebuilderWrapper,
- ensureRebuiltRetryer,
- changeNotesFactory,
- queryProvider,
- updateFactory,
- internalUserFactory,
- retryHelper,
- projectCache);
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- }
-
- @Test
- public void updateChange() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- setNoteDbPrimary(id);
-
- gApi.changes().id(id.get()).current().review(ReviewInput.approve());
- gApi.changes().id(id.get()).current().submit();
-
- ChangeInfo info = gApi.changes().id(id.get()).get();
- assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
- ApprovalInfo approval = Iterables.getOnlyElement(info.labels.get("Code-Review").all);
- assertThat(approval._accountId).isEqualTo(admin.id.get());
- assertThat(approval.value).isEqualTo(2);
- assertThat(info.messages).hasSize(3);
- assertThat(Iterables.getLast(info.messages).message)
- .isEqualTo("Change has been successfully merged by " + admin.fullName);
-
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
- assertThat(notes.getChange().getNoteDbState())
- .isEqualTo(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
-
- // Writes weren't reflected in ReviewDb.
- assertThat(db.changes().get(id).getStatus()).isEqualTo(Change.Status.NEW);
- assertThat(db.patchSetApprovals().byChange(id)).isEmpty();
- assertThat(db.changeMessages().byChange(id)).hasSize(1);
- }
-
- @Test
- public void deleteDraftComment() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- setNoteDbPrimary(id);
-
- DraftInput din = new DraftInput();
- din.path = PushOneCommit.FILE_NAME;
- din.line = 1;
- din.message = "A comment";
- gApi.changes().id(id.get()).current().createDraft(din);
-
- CommentInfo di =
- Iterables.getOnlyElement(
- gApi.changes().id(id.get()).current().drafts().get(PushOneCommit.FILE_NAME));
- assertThat(di.message).isEqualTo(din.message);
-
- assertThat(db.patchComments().draftByChangeFileAuthor(id, din.path, admin.id)).isEmpty();
-
- gApi.changes().id(id.get()).current().draft(di.id).delete();
- assertThat(gApi.changes().id(id.get()).current().drafts()).isEmpty();
- }
-
- @Test
- public void deleteVote() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- setNoteDbPrimary(id);
-
- gApi.changes().id(id.get()).current().review(ReviewInput.approve());
- List<ApprovalInfo> approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
- assertThat(approvals).hasSize(1);
- assertThat(approvals.get(0).value).isEqualTo(2);
-
- gApi.changes().id(id.get()).reviewer(admin.id.toString()).deleteVote("Code-Review");
-
- approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
- assertThat(approvals).hasSize(1);
- assertThat(approvals.get(0).value).isEqualTo(0);
- }
-
- @Test
- public void deleteVoteViaReview() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- setNoteDbPrimary(id);
-
- gApi.changes().id(id.get()).current().review(ReviewInput.approve());
- List<ApprovalInfo> approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
- assertThat(approvals).hasSize(1);
- assertThat(approvals.get(0).value).isEqualTo(2);
-
- gApi.changes().id(id.get()).current().review(ReviewInput.noScore());
-
- approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
- assertThat(approvals).hasSize(1);
- assertThat(approvals.get(0).value).isEqualTo(0);
- }
-
- @Test
- public void deleteReviewer() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- setNoteDbPrimary(id);
-
- gApi.changes().id(id.get()).addReviewer(user.id.toString());
- assertThat(getReviewers(id)).containsExactly(user.id);
- gApi.changes().id(id.get()).reviewer(user.id.toString()).remove();
- assertThat(getReviewers(id)).isEmpty();
- }
-
- @Test
- public void readOnlyReviewDb() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- testReadOnly(id);
- }
-
- @Test
- public void readOnlyNoteDb() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- setNoteDbPrimary(id);
- testReadOnly(id);
- }
-
- private void testReadOnly(Change.Id id) throws Exception {
- Timestamp before = TimeUtil.nowTs();
- Timestamp until = new Timestamp(before.getTime() + 1000 * 3600);
-
- // Set read-only.
- Change c = db.changes().get(id);
- assertThat(c).named("change " + id).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- state = state.withReadOnlyUntil(until);
- c.setNoteDbState(state.toString());
- db.changes().update(Collections.singleton(c));
-
- assertThat(gApi.changes().id(id.get()).get().subject).isEqualTo(PushOneCommit.SUBJECT);
- assertThat(gApi.changes().id(id.get()).get().topic).isNull();
- try {
- gApi.changes().id(id.get()).topic("a-topic");
- fail("expected read-only exception");
- } catch (RestApiException e) {
- Optional<Throwable> oe =
- Throwables.getCausalChain(e).stream()
- .filter(x -> x instanceof OrmRuntimeException)
- .findFirst();
- assertThat(oe).named("OrmRuntimeException in causal chain of " + e).isPresent();
- assertThat(oe.get().getMessage()).contains("read-only");
- }
- assertThat(gApi.changes().id(id.get()).get().topic).isNull();
-
- TestTimeUtil.setClock(new Timestamp(until.getTime() + 1000));
- assertThat(gApi.changes().id(id.get()).get().subject).isEqualTo(PushOneCommit.SUBJECT);
- gApi.changes().id(id.get()).topic("a-topic");
- assertThat(gApi.changes().id(id.get()).get().topic).isEqualTo("a-topic");
- }
-
- @Test
- public void migrateToNoteDb() throws Exception {
- testMigrateToNoteDb(createChange().getChange().getId());
- }
-
- @Test
- public void migrateToNoteDbWithRebuildingFirst() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- Change c = db.changes().get(id);
- c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- db.changes().update(Collections.singleton(c));
- testMigrateToNoteDb(id);
- }
-
- private void testMigrateToNoteDb(Change.Id id) throws Exception {
- assertThat(PrimaryStorage.of(db.changes().get(id))).isEqualTo(PrimaryStorage.REVIEW_DB);
- migrator.migrateToNoteDbPrimary(id);
- assertNoteDbPrimary(id);
-
- gApi.changes().id(id.get()).topic("a-topic");
- assertThat(gApi.changes().id(id.get()).get().topic).isEqualTo("a-topic");
- assertThat(db.changes().get(id).getTopic()).isNull();
- }
-
- @Test
- public void migrateToNoteDbFailsRebuildingOnceAndRetries() throws Exception {
- Change.Id id = createChange().getChange().getId();
-
- Change c = db.changes().get(id);
- c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- db.changes().update(Collections.singleton(c));
- rebuilderWrapper.failNextUpdate();
-
- migrator =
- newMigrator(
- RetryerBuilder.<NoteDbChangeState>newBuilder()
- .retryIfException()
- .withStopStrategy(StopStrategies.neverStop())
- .build());
- migrator.migrateToNoteDbPrimary(id);
- assertNoteDbPrimary(id);
- }
-
- @Test
- public void migrateToNoteDbFailsRebuildingAndStops() throws Exception {
- Change.Id id = createChange().getChange().getId();
-
- Change c = db.changes().get(id);
- c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- db.changes().update(Collections.singleton(c));
- rebuilderWrapper.failNextUpdate();
-
- migrator =
- newMigrator(
- RetryerBuilder.<NoteDbChangeState>newBuilder()
- .retryIfException()
- .withStopStrategy(StopStrategies.stopAfterAttempt(1))
- .build());
- exception.expect(OrmException.class);
- exception.expectMessage("Retrying failed");
- migrator.migrateToNoteDbPrimary(id);
- }
-
- @Test
- public void migrateToNoteDbMissingOldState() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- Change c = db.changes().get(id);
- c.setNoteDbState(null);
- db.changes().update(Collections.singleton(c));
-
- exception.expect(PrimaryStorageMigrator.NoNoteDbStateException.class);
- exception.expectMessage("no note_db_state");
- migrator.migrateToNoteDbPrimary(id);
- }
-
- @Test
- public void migrateToNoteDbLeaseExpires() throws Exception {
- TestTimeUtil.resetWithClockStep(2, DAYS);
- exception.expect(OrmRuntimeException.class);
- exception.expectMessage("read-only lease");
- migrator.migrateToNoteDbPrimary(createChange().getChange().getId());
- }
-
- @Test
- public void migrateToNoteDbAlreadyReadOnly() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- Change c = db.changes().get(id);
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- Timestamp until = new Timestamp(TimeUtil.nowMs() + MILLISECONDS.convert(1, DAYS));
- state = state.withReadOnlyUntil(until);
- c.setNoteDbState(state.toString());
- db.changes().update(Collections.singleton(c));
-
- exception.expect(OrmRuntimeException.class);
- exception.expectMessage("read-only until " + until);
- migrator.migrateToNoteDbPrimary(id);
- }
-
- @Test
- public void migrateToNoteDbAlreadyMigrated() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- assertThat(PrimaryStorage.of(db.changes().get(id))).isEqualTo(PrimaryStorage.REVIEW_DB);
- migrator.migrateToNoteDbPrimary(id);
- assertNoteDbPrimary(id);
-
- migrator.migrateToNoteDbPrimary(id);
- assertNoteDbPrimary(id);
- }
-
- @Test
- public void rebuildReviewDb() throws Exception {
- Change c = createChange().getChange().change();
- Change.Id id = c.getId();
-
- CommentInput cin = new CommentInput();
- cin.line = 1;
- cin.message = "Published comment";
- ReviewInput rin = ReviewInput.approve();
- rin.comments = ImmutableMap.of(PushOneCommit.FILE_NAME, ImmutableList.of(cin));
- gApi.changes().id(id.get()).current().review(ReviewInput.approve());
-
- DraftInput din = new DraftInput();
- din.path = PushOneCommit.FILE_NAME;
- din.line = 1;
- din.message = "Draft comment";
- gApi.changes().id(id.get()).current().createDraft(din);
- gApi.changes().id(id.get()).current().review(ReviewInput.approve());
- gApi.changes().id(id.get()).current().createDraft(din);
-
- assertThat(db.changeMessages().byChange(id)).isNotEmpty();
- assertThat(db.patchSets().byChange(id)).isNotEmpty();
- assertThat(db.patchSetApprovals().byChange(id)).isNotEmpty();
- assertThat(db.patchComments().byChange(id)).isNotEmpty();
-
- ChangeBundle noteDbBundle =
- ChangeBundle.fromNotes(commentsUtil, notesFactory.create(db, project, id));
-
- setNoteDbPrimary(id);
-
- db.changeMessages().delete(db.changeMessages().byChange(id));
- db.patchSets().delete(db.patchSets().byChange(id));
- db.patchSetApprovals().delete(db.patchSetApprovals().byChange(id));
- db.patchComments().delete(db.patchComments().byChange(id));
- ChangeMessage bogusMessage =
- ChangeMessagesUtil.newMessage(
- c.currentPatchSetId(),
- identifiedUserFactory.create(admin.getId()),
- TimeUtil.nowTs(),
- "some message",
- null);
- db.changeMessages().insert(Collections.singleton(bogusMessage));
-
- rebuilderWrapper.rebuildReviewDb(db, project, id);
-
- assertThat(db.changeMessages().byChange(id)).isNotEmpty();
- assertThat(db.patchSets().byChange(id)).isNotEmpty();
- assertThat(db.patchSetApprovals().byChange(id)).isNotEmpty();
- assertThat(db.patchComments().byChange(id)).isNotEmpty();
-
- ChangeBundle reviewDbBundle = bundleReader.fromReviewDb(ReviewDbUtil.unwrapDb(db), id);
- assertThat(reviewDbBundle.differencesFrom(noteDbBundle)).isEmpty();
- }
-
- @Test
- public void migrateBackToReviewDbPrimary() throws Exception {
- Change c = createChange().getChange().change();
- Change.Id id = c.getId();
-
- migrator.migrateToNoteDbPrimary(id);
- assertNoteDbPrimary(id);
-
- gApi.changes().id(id.get()).topic("new-topic");
- assertThat(gApi.changes().id(id.get()).topic()).isEqualTo("new-topic");
- assertThat(db.changes().get(id).getTopic()).isNotEqualTo("new-topic");
-
- migrator.migrateToReviewDbPrimary(id, null);
- ObjectId metaId;
- try (Repository repo = repoManager.openRepository(c.getProject());
- RevWalk rw = new RevWalk(repo)) {
- metaId = repo.exactRef(RefNames.changeMetaRef(id)).getObjectId();
- RevCommit commit = rw.parseCommit(metaId);
- rw.parseBody(commit);
- assertThat(commit.getFullMessage())
- .contains("Read-only-until: " + formatTime(serverIdent.get(), new Timestamp(0)));
- }
- NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- assertThat(state.getChangeMetaId()).isEqualTo(metaId);
- assertThat(gApi.changes().id(id.get()).topic()).isEqualTo("new-topic");
- assertThat(db.changes().get(id).getTopic()).isEqualTo("new-topic");
-
- ChangeNotes notes = notesFactory.create(db, project, id);
- assertThat(notes.getRevision()).isEqualTo(metaId); // No rebuilding, change was up to date.
- assertThat(notes.getReadOnlyUntil()).isNotNull();
-
- gApi.changes().id(id.get()).topic("reviewdb-topic");
- assertThat(db.changes().get(id).getTopic()).isEqualTo("reviewdb-topic");
- }
-
- private void setNoteDbPrimary(Change.Id id) throws Exception {
- Change c = db.changes().get(id);
- assertThat(c).named("change " + id).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- assertThat(state.getPrimaryStorage()).named("storage of " + id).isEqualTo(REVIEW_DB);
-
- try (Repository changeRepo = repoManager.openRepository(c.getProject());
- Repository allUsersRepo = repoManager.openRepository(allUsers)) {
- assertThat(state.isUpToDate(new RepoRefCache(changeRepo), new RepoRefCache(allUsersRepo)))
- .named("change " + id + " up to date")
- .isTrue();
- }
-
- c.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
- db.changes().update(Collections.singleton(c));
- }
-
- private void assertNoteDbPrimary(Change.Id id) throws Exception {
- assertThat(PrimaryStorage.of(db.changes().get(id))).isEqualTo(PrimaryStorage.NOTE_DB);
- }
-
- private List<Account.Id> getReviewers(Change.Id id) throws Exception {
- return gApi.changes().id(id.get()).get().reviewers.values().stream()
- .flatMap(Collection::stream)
- .map(a -> new Account.Id(a._accountId))
- .collect(toList());
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
deleted file mode 100644
index b9ed0f3ac7..0000000000
--- a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ /dev/null
@@ -1,706 +0,0 @@
-// 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.acceptance.server.notedb;
-
-import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
-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.server.notedb.NoteDbChangeState.NOTE_DB_PRIMARY_STATE;
-import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY;
-import static com.google.gerrit.server.notedb.NotesMigrationState.REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigrationState.WRITE;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Comparator.naturalOrder;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.io.MoreFiles;
-import com.google.common.io.RecursiveDeleteOption;
-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.Sandboxed;
-import com.google.gerrit.acceptance.UseLocalDisk;
-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.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.notedb.ChangeBundle;
-import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.server.notedb.NotesMigrationState;
-import com.google.gerrit.server.notedb.rebuild.MigrationException;
-import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
-import com.google.gerrit.server.notedb.rebuild.NotesMigrationStateListener;
-import com.google.gerrit.server.schema.ReviewDbFactory;
-import com.google.gerrit.testing.ConfigSuite;
-import com.google.gerrit.testing.NoteDbMode;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Stream;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryCache.FileKey;
-import org.eclipse.jgit.storage.file.FileBasedConfig;
-import org.eclipse.jgit.util.FS;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-@Sandboxed
-@UseLocalDisk
-@NoHttpd
-public class OnlineNoteDbMigrationIT extends AbstractDaemonTest {
- private static final String INVALID_STATE = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
-
- @ConfigSuite.Default
- public static Config defaultConfig() {
- Config cfg = new Config();
- cfg.setInt("noteDb", "changes", "sequenceBatchSize", 10);
- cfg.setInt("noteDb", "changes", "initialSequenceGap", 500);
- return cfg;
- }
-
- // Tests in this class are generally interested in the actual ReviewDb contents, but the shifting
- // migration state may result in various kinds of wrappers showing up unexpectedly.
- @Inject @ReviewDbFactory private SchemaFactory<ReviewDb> schemaFactory;
-
- @Inject private ChangeBundleReader changeBundleReader;
- @Inject private CommentsUtil commentsUtil;
- @Inject private DynamicSet<NotesMigrationStateListener> listeners;
- @Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
- @Inject private Sequences sequences;
- @Inject private SitePaths sitePaths;
-
- private FileBasedConfig noteDbConfig;
- private List<RegistrationHandle> addedListeners;
-
- @Before
- public void setUp() throws Exception {
- assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF);
- // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
- noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
- assertNotesMigrationState(REVIEW_DB, false, false);
- addedListeners = new ArrayList<>();
- }
-
- @After
- public void tearDown() throws Exception {
- if (addedListeners != null) {
- addedListeners.forEach(RegistrationHandle::remove);
- addedListeners = null;
- }
- }
-
- @Test
- public void preconditionsFail() throws Exception {
- List<Change.Id> cs = ImmutableList.of(new Change.Id(1));
- List<Project.NameKey> ps = ImmutableList.of(new Project.NameKey("p"));
- assertMigrationException(
- "Cannot rebuild without noteDb.changes.write=true", b -> b, NoteDbMigrator::rebuild);
- assertMigrationException(
- "Cannot combine changes, projects and skipProjects",
- b -> b.setChanges(cs).setProjects(ps),
- m -> {});
- assertMigrationException(
- "Cannot combine changes, projects and skipProjects",
- b -> b.setChanges(cs).setSkipProjects(ps),
- m -> {});
- assertMigrationException(
- "Cannot combine changes, projects and skipProjects",
- b -> b.setProjects(ps).setSkipProjects(ps),
- m -> {});
- assertMigrationException(
- "Cannot set changes or projects or skipProjects during full migration",
- b -> b.setChanges(cs),
- NoteDbMigrator::migrate);
- assertMigrationException(
- "Cannot set changes or projects or skipProjects during full migration",
- b -> b.setProjects(ps),
- NoteDbMigrator::migrate);
- assertMigrationException(
- "Cannot set changes or projects or skipProjects during full migration",
- b -> b.setSkipProjects(ps),
- NoteDbMigrator::migrate);
-
- setNotesMigrationState(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);
- assertMigrationException(
- "Migration has already progressed past the endpoint of the \"trial mode\" state",
- b -> b.setTrialMode(true),
- NoteDbMigrator::migrate);
-
- setNotesMigrationState(READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY);
- assertMigrationException(
- "Cannot force rebuild changes; NoteDb is already the primary storage for some changes",
- b -> b.setForceRebuild(true),
- NoteDbMigrator::migrate);
- }
-
- @Test
- @GerritConfig(name = "noteDb.changes.initialSequenceGap", value = "-7")
- public void initialSequenceGapMustBeNonNegative() throws Exception {
- setNotesMigrationState(READ_WRITE_NO_SEQUENCE);
- assertMigrationException("Sequence gap must be non-negative: -7", b -> b, m -> {});
- }
-
- @Test
- public void rebuildOneChangeTrialModeAndForceRebuild() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- migrate(b -> b.setTrialMode(true));
- assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true);
-
- ObjectId oldMetaId;
- try (Repository repo = repoManager.openRepository(project);
- ReviewDb db = schemaFactory.open()) {
- Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
- assertThat(ref).isNotNull();
- oldMetaId = ref.getObjectId();
-
- Change c = db.changes().get(id);
- assertThat(c).isNotNull();
- NoteDbChangeState state = NoteDbChangeState.parse(c);
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- assertThat(state.getRefState()).hasValue(RefState.create(oldMetaId, ImmutableMap.of()));
-
- // Force change to be out of date, and change topic so it will get rebuilt as something other
- // than oldMetaId.
- c.setNoteDbState(INVALID_STATE);
- c.setTopic(name("a-new-topic"));
- db.changes().update(ImmutableList.of(c));
- }
-
- migrate(b -> b.setTrialMode(true));
- assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true);
-
- try (Repository repo = repoManager.openRepository(project);
- ReviewDb db = schemaFactory.open()) {
- // Change is out of date, but was not rebuilt without forceRebuild.
- assertThat(repo.exactRef(RefNames.changeMetaRef(id)).getObjectId()).isEqualTo(oldMetaId);
- Change c = db.changes().get(id);
- assertThat(c.getNoteDbState()).isEqualTo(INVALID_STATE);
- }
-
- migrate(b -> b.setTrialMode(true).setForceRebuild(true));
- assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true);
-
- try (Repository repo = repoManager.openRepository(project);
- ReviewDb db = schemaFactory.open()) {
- Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
- assertThat(ref).isNotNull();
- ObjectId newMetaId = ref.getObjectId();
- assertThat(newMetaId).isNotEqualTo(oldMetaId);
-
- NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- assertThat(state.getRefState()).hasValue(RefState.create(newMetaId, ImmutableMap.of()));
- }
- }
-
- @Test
- public void autoMigrateTrialMode() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- migrate(b -> b.setAutoMigrate(true).setTrialMode(true).setStopAtStateForTesting(WRITE));
- assertNotesMigrationState(WRITE, true, true);
-
- migrate(b -> b);
- // autoMigrate is still enabled so that we can continue the migration by only unsetting trial.
- assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, true, true);
-
- ObjectId metaId;
- try (Repository repo = repoManager.openRepository(project);
- ReviewDb db = schemaFactory.open()) {
- Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
- assertThat(ref).isNotNull();
- metaId = ref.getObjectId();
- NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- assertThat(state.getRefState()).hasValue(RefState.create(metaId, ImmutableMap.of()));
- }
-
- // Unset trial mode and the next migration runs to completion.
- noteDbConfig.load();
- NoteDbMigrator.setTrialMode(noteDbConfig, false);
- noteDbConfig.save();
-
- migrate(b -> b);
- assertNotesMigrationState(NOTE_DB, false, false);
-
- try (Repository repo = repoManager.openRepository(project);
- ReviewDb db = schemaFactory.open()) {
- Ref ref = repo.exactRef(RefNames.changeMetaRef(id));
- assertThat(ref).isNotNull();
- assertThat(ref.getObjectId()).isEqualTo(metaId);
- NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id));
- assertThat(state).isNotNull();
- assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.NOTE_DB);
- }
- }
-
- @Test
- public void rebuildSubsetOfChanges() throws Exception {
- setNotesMigrationState(WRITE);
-
- PushOneCommit.Result r1 = createChange();
- PushOneCommit.Result r2 = createChange();
- Change.Id id1 = r1.getChange().getId();
- Change.Id id2 = r2.getChange().getId();
-
- invalidateNoteDbState(id1, id2);
- migrate(b -> b.setChanges(ImmutableList.of(id2)), NoteDbMigrator::rebuild);
- assertNotRebuilt(id1);
- assertRebuilt(id2);
- }
-
- @Test
- public void rebuildSubsetOfProjects() throws Exception {
- setNotesMigrationState(WRITE);
-
- Project.NameKey p2 = createProject("project2");
- TestRepository<?> tr2 = cloneProject(p2, admin);
-
- PushOneCommit.Result r1 = createChange();
- PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master");
- Change.Id id1 = r1.getChange().getId();
- Change.Id id2 = r2.getChange().getId();
-
- invalidateNoteDbState(id1, id2);
- migrate(b -> b.setProjects(ImmutableList.of(p2)), NoteDbMigrator::rebuild);
- assertNotRebuilt(id1);
- assertRebuilt(id2);
- }
-
- @Test
- public void rebuildNonSkippedProjects() throws Exception {
- setNotesMigrationState(WRITE);
-
- Project.NameKey p2 = createProject("project2");
- TestRepository<?> tr2 = cloneProject(p2, admin);
- Project.NameKey p3 = createProject("project3");
- TestRepository<?> tr3 = cloneProject(p3, admin);
-
- PushOneCommit.Result r1 = createChange();
- PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master");
- PushOneCommit.Result r3 = pushFactory.create(db, admin.getIdent(), tr3).to("refs/for/master");
- Change.Id id1 = r1.getChange().getId();
- Change.Id id2 = r2.getChange().getId();
- Change.Id id3 = r3.getChange().getId();
-
- invalidateNoteDbState(id1, id2, id3);
- migrate(b -> b.setSkipProjects(ImmutableList.of(p3)), NoteDbMigrator::rebuild);
- assertRebuilt(id1, id2);
- assertNotRebuilt(id3);
- }
-
- private void invalidateNoteDbState(Change.Id... ids) throws OrmException {
- List<Change> list = new ArrayList<>(ids.length);
- try (ReviewDb db = schemaFactory.open()) {
- for (Change.Id id : ids) {
- Change c = db.changes().get(id);
- c.setNoteDbState(INVALID_STATE);
- list.add(c);
- }
- db.changes().update(list);
- }
- }
-
- private void assertRebuilt(Change.Id... ids) throws OrmException {
- try (ReviewDb db = schemaFactory.open()) {
- for (Change.Id id : ids) {
- NoteDbChangeState s = NoteDbChangeState.parse(db.changes().get(id));
- assertThat(s.getChangeMetaId().name()).isNotEqualTo(INVALID_STATE);
- }
- }
- }
-
- private void assertNotRebuilt(Change.Id... ids) throws OrmException {
- try (ReviewDb db = schemaFactory.open()) {
- for (Change.Id id : ids) {
- NoteDbChangeState s = NoteDbChangeState.parse(db.changes().get(id));
- assertThat(s.getChangeMetaId().name()).isEqualTo(INVALID_STATE);
- }
- }
- }
-
- @Test
- public void enableSequencesNoGap() throws Exception {
- testEnableSequences(0, 3, "13");
- }
-
- @Test
- public void enableSequencesWithGap() throws Exception {
- testEnableSequences(-1, 502, "512");
- }
-
- private void testEnableSequences(int builderOption, int expectedFirstId, String expectedRefValue)
- throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
- assertThat(id.get()).isEqualTo(1);
-
- migrate(
- b ->
- b.setSequenceGap(builderOption)
- .setStopAtStateForTesting(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY));
-
- assertThat(sequences.nextChangeId()).isEqualTo(expectedFirstId);
- assertThat(sequences.nextChangeId()).isEqualTo(expectedFirstId + 1);
-
- try (Repository repo = repoManager.openRepository(allProjects);
- ObjectReader reader = repo.newObjectReader()) {
- Ref ref = repo.exactRef("refs/sequences/changes");
- assertThat(ref).isNotNull();
- ObjectLoader loader = reader.open(ref.getObjectId());
- assertThat(loader.getType()).isEqualTo(Constants.OBJ_BLOB);
- // Acquired a block of 10 to serve the first nextChangeId call after migration.
- assertThat(new String(loader.getCachedBytes(), UTF_8)).isEqualTo(expectedRefValue);
- }
-
- try (ReviewDb db = schemaFactory.open()) {
- // Underlying, unused ReviewDb is still on its own sequence.
- @SuppressWarnings("deprecation")
- int nextFromReviewDb = db.nextChangeId();
- assertThat(nextFromReviewDb).isEqualTo(3);
- }
- }
-
- @Test
- public void fullMigrationSameThread() throws Exception {
- testFullMigration(1);
- }
-
- @Test
- public void fullMigrationMultipleThreads() throws Exception {
- testFullMigration(2);
- }
-
- private void testFullMigration(int threads) throws Exception {
- PushOneCommit.Result r1 = createChange();
- PushOneCommit.Result r2 = createChange();
- Change.Id id1 = r1.getChange().getId();
- Change.Id id2 = r2.getChange().getId();
-
- Set<String> objectFiles = getObjectFiles(project);
- assertThat(objectFiles).isNotEmpty();
-
- migrate(b -> b.setThreads(threads));
-
- assertNotesMigrationState(NOTE_DB, false, false);
- assertThat(sequences.nextChangeId()).isEqualTo(503);
- assertThat(getObjectFiles(project)).containsExactlyElementsIn(objectFiles);
-
- ObjectId oldMetaId = null;
- int rowVersion = 0;
- try (ReviewDb db = schemaFactory.open();
- Repository repo = repoManager.openRepository(project)) {
- for (Change.Id id : ImmutableList.of(id1, id2)) {
- String refName = RefNames.changeMetaRef(id);
- Ref ref = repo.exactRef(refName);
- assertThat(ref).named(refName).isNotNull();
-
- Change c = db.changes().get(id);
- assertThat(c.getTopic()).named("topic of change %s", id).isNull();
- NoteDbChangeState s = NoteDbChangeState.parse(c);
- assertThat(s.getPrimaryStorage())
- .named("primary storage of change %s", id)
- .isEqualTo(PrimaryStorage.NOTE_DB);
- assertThat(s.getRefState()).named("ref state of change %s").isEmpty();
-
- if (id.equals(id1)) {
- oldMetaId = ref.getObjectId();
- rowVersion = c.getRowVersion();
- }
- }
- }
-
- // Do not open a new context, to simulate races with other threads that opened a context earlier
- // in the migration process; this needs to work.
- gApi.changes().id(id1.get()).topic(name("a-topic"));
-
- // Of course, it should also work with a new context.
- resetCurrentApiUser();
- gApi.changes().id(id1.get()).topic(name("another-topic"));
-
- try (ReviewDb db = schemaFactory.open();
- Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef(RefNames.changeMetaRef(id1)).getObjectId()).isNotEqualTo(oldMetaId);
-
- Change c = db.changes().get(id1);
- assertThat(c.getTopic()).isNull();
- assertThat(c.getRowVersion()).isEqualTo(rowVersion);
- }
- }
-
- @Test
- public void fullMigrationOneChangeWithNoPatchSets() throws Exception {
- PushOneCommit.Result r1 = createChange();
- PushOneCommit.Result r2 = createChange();
- Change.Id id1 = r1.getChange().getId();
- Change.Id id2 = r2.getChange().getId();
-
- db.changes().beginTransaction(id2);
- try {
- db.patchSets().delete(db.patchSets().byChange(id2));
- db.commit();
- } finally {
- db.rollback();
- }
-
- migrate(b -> b);
- assertNotesMigrationState(NOTE_DB, false, false);
-
- try (ReviewDb db = schemaFactory.open();
- Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef(RefNames.changeMetaRef(id1))).isNotNull();
- assertThat(db.changes().get(id1).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE);
-
- // A change with no patch sets is so corrupt that it is completely skipped by the migration
- // process.
- assertThat(repo.exactRef(RefNames.changeMetaRef(id2))).isNull();
- assertThat(db.changes().get(id2).getNoteDbState()).isNull();
- }
- }
-
- @Test
- public void fullMigrationOneChangeWithNoProject() throws Exception {
- PushOneCommit.Result r1 = createChange();
- Change.Id id1 = r1.getChange().getId();
-
- Project.NameKey p2 = createProject("project2");
- TestRepository<?> tr2 = cloneProject(p2, admin);
- PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master");
- Change.Id id2 = r2.getChange().getId();
-
- // TODO(davido): Find an easier way to wipe out a repository from the file system.
- MoreFiles.deleteRecursively(
- FileKey.lenient(
- sitePaths
- .resolve(cfg.getString("gerrit", null, "basePath"))
- .resolve(p2.get())
- .toFile(),
- FS.DETECTED)
- .getFile()
- .toPath(),
- RecursiveDeleteOption.ALLOW_INSECURE);
-
- migrate(b -> b);
- assertNotesMigrationState(NOTE_DB, false, false);
-
- try (ReviewDb db = schemaFactory.open();
- Repository repo = repoManager.openRepository(project)) {
- assertThat(repo.exactRef(RefNames.changeMetaRef(id1))).isNotNull();
- assertThat(db.changes().get(id1).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE);
- }
-
- // A change without project is so corrupt that it is completely skipped by the migration
- // process.
- assertThat(db.changes().get(id2).getNoteDbState()).isNull();
- }
-
- @Test
- public void fullMigrationMissingPatchSetRefs() throws Exception {
- PushOneCommit.Result r = createChange();
- Change.Id id = r.getChange().getId();
-
- try (Repository repo = repoManager.openRepository(project)) {
- RefUpdate u = repo.updateRef(new PatchSet.Id(id, 1).toRefName());
- u.setForceUpdate(true);
- assertThat(u.delete()).isEqualTo(RefUpdate.Result.FORCED);
- }
-
- ChangeBundle reviewDbBundle;
- try (ReviewDb db = schemaFactory.open()) {
- reviewDbBundle = changeBundleReader.fromReviewDb(db, id);
- }
-
- migrate(b -> b);
- assertNotesMigrationState(NOTE_DB, false, false);
-
- try (ReviewDb db = schemaFactory.open();
- Repository repo = repoManager.openRepository(project)) {
- // Change migrated successfully even though it was missing patch set refs.
- assertThat(repo.exactRef(RefNames.changeMetaRef(id))).isNotNull();
- assertThat(db.changes().get(id).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE);
-
- ChangeBundle noteDbBundle =
- ChangeBundle.fromNotes(commentsUtil, notesFactory.createChecked(db, project, id));
- assertThat(noteDbBundle.differencesFrom(reviewDbBundle)).isEmpty();
- }
- }
-
- @Test
- public void autoMigrationConfig() throws Exception {
- createChange();
-
- migrate(b -> b.setStopAtStateForTesting(WRITE));
- assertNotesMigrationState(WRITE, false, false);
-
- migrate(b -> b.setAutoMigrate(true).setStopAtStateForTesting(READ_WRITE_NO_SEQUENCE));
- assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, true, false);
-
- migrate(b -> b);
- assertNotesMigrationState(NOTE_DB, false, false);
- }
-
- @Test
- public void notesMigrationStateListener() throws Exception {
- NotesMigrationStateListener listener = createStrictMock(NotesMigrationStateListener.class);
- listener.preStateChange(REVIEW_DB, WRITE);
- expectLastCall();
- listener.preStateChange(WRITE, READ_WRITE_NO_SEQUENCE);
- expectLastCall();
- listener.preStateChange(READ_WRITE_NO_SEQUENCE, READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);
- expectLastCall();
- listener.preStateChange(
- READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY, READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY);
- listener.preStateChange(READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY, NOTE_DB);
- expectLastCall();
- replay(listener);
- addListener(listener);
-
- createChange();
- migrate(b -> b);
- assertNotesMigrationState(NOTE_DB, false, false);
- verify(listener);
- }
-
- @Test
- public void notesMigrationStateListenerFails() throws Exception {
- NotesMigrationStateListener listener = createStrictMock(NotesMigrationStateListener.class);
- listener.preStateChange(REVIEW_DB, WRITE);
- expectLastCall();
- listener.preStateChange(WRITE, READ_WRITE_NO_SEQUENCE);
- IOException listenerException = new IOException("Listener failed");
- expectLastCall().andThrow(listenerException);
- replay(listener);
- addListener(listener);
-
- createChange();
- try {
- migrate(b -> b);
- fail("expected IOException");
- } catch (IOException e) {
- assertThat(e).isSameAs(listenerException);
- }
- assertNotesMigrationState(WRITE, false, false);
- verify(listener);
- }
-
- private void assertNotesMigrationState(
- NotesMigrationState expected, boolean autoMigrate, boolean trialMode) throws Exception {
- assertThat(NotesMigrationState.forNotesMigration(notesMigration)).hasValue(expected);
- noteDbConfig.load();
- assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected);
- assertThat(NoteDbMigrator.getAutoMigrate(noteDbConfig))
- .named("noteDb.changes.autoMigrate")
- .isEqualTo(autoMigrate);
- assertThat(NoteDbMigrator.getTrialMode(noteDbConfig))
- .named("noteDb.changes.trial")
- .isEqualTo(trialMode);
- }
-
- private void setNotesMigrationState(NotesMigrationState state) throws Exception {
- noteDbConfig.load();
- state.setConfigValues(noteDbConfig);
- noteDbConfig.save();
- notesMigration.setFrom(state);
- }
-
- @FunctionalInterface
- interface PrepareBuilder {
- NoteDbMigrator.Builder prepare(NoteDbMigrator.Builder b) throws Exception;
- }
-
- @FunctionalInterface
- interface RunMigration {
- void run(NoteDbMigrator m) throws Exception;
- }
-
- private void migrate(PrepareBuilder b) throws Exception {
- migrate(b, NoteDbMigrator::migrate);
- }
-
- private void migrate(PrepareBuilder b, RunMigration m) throws Exception {
- try (NoteDbMigrator migrator = b.prepare(migratorBuilderProvider.get()).build()) {
- m.run(migrator);
- }
- }
-
- private void assertMigrationException(
- String expectMessageContains, PrepareBuilder b, RunMigration m) throws Exception {
- try {
- migrate(b, m);
- fail("expected MigrationException");
- } catch (MigrationException e) {
- assertThat(e).hasMessageThat().contains(expectMessageContains);
- }
- }
-
- private void addListener(NotesMigrationStateListener listener) {
- addedListeners.add(listeners.add("gerrit", listener));
- }
-
- private ImmutableSortedSet<String> getObjectFiles(Project.NameKey project) throws Exception {
- try (Repository repo = repoManager.openRepository(project);
- Stream<Path> paths =
- Files.walk(((FileRepository) repo).getObjectDatabase().getDirectory().toPath())) {
- return paths
- .filter(path -> !Files.isDirectory(path))
- .map(Path::toString)
- .filter(name -> !name.endsWith(".pack") && !name.endsWith(".idx"))
- .collect(toImmutableSortedSet(naturalOrder()));
- }
- }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
index 720eeeda17..2919e5fe97 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
@@ -34,6 +35,7 @@ import org.junit.Test;
public class PermissionBackendConditionIT extends AbstractDaemonTest {
@Inject PermissionBackend pb;
+ @Inject ProjectOperations projectOperations;
@Test
public void globalPermissions_sameUserAndPermissionEquals() throws Exception {
@@ -110,7 +112,7 @@ public class PermissionBackendConditionIT extends AbstractDaemonTest {
@Test
public void projectPermissions_differentResourceSameUserDoesNotEqual() throws Exception {
- Project.NameKey project2 = createProject("p2");
+ Project.NameKey project2 = projectOperations.newProject().create();
BooleanCondition cond1 = pb.user(user()).project(project).testCond(ProjectPermission.READ);
BooleanCondition cond2 = pb.user(user()).project(project2).testCond(ProjectPermission.READ);
@@ -152,7 +154,7 @@ 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(createProject("p2"), "branch");
+ Branch.NameKey branch2 = new Branch.NameKey(projectOperations.newProject().create(), "branch");
BooleanCondition cond1 = pb.user(user()).ref(branch1).testCond(RefPermission.READ);
BooleanCondition cond2 = pb.user(user()).ref(branch2).testCond(RefPermission.READ);
@@ -161,10 +163,10 @@ public class PermissionBackendConditionIT extends AbstractDaemonTest {
}
private CurrentUser user() {
- return identifiedUserFactory.create(user.id);
+ return identifiedUserFactory.create(user.id());
}
private CurrentUser admin() {
- return identifiedUserFactory.create(admin.id);
+ return identifiedUserFactory.create(admin.id());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
index 8b0c56bc65..6cbe40e56b 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -80,21 +80,12 @@ public class CustomLabelIT extends AbstractDaemonTest {
u.save();
}
- eventListenerRegistration =
- source.add(
- "gerrit",
- new CommentAddedListener() {
- @Override
- public void onCommentAdded(Event event) {
- lastCommentAddedEvent = event;
- }
- });
+ eventListenerRegistration = source.add("gerrit", event -> lastCommentAddedEvent = event);
}
@After
public void cleanup() {
eventListenerRegistration.remove();
- db.close();
}
@Test
@@ -187,7 +178,7 @@ public class CustomLabelIT extends AbstractDaemonTest {
saveLabelConfig();
PushOneCommit.Result r = createChange();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(r.getChangeId()).addReviewer(in);
ReviewInput input = new ReviewInput().label(P.getName(), 0);
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index cfdd781a42..4ed16eead6 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -22,6 +22,8 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
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.Permission;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.StarsInput;
@@ -32,6 +34,7 @@ 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;
+import com.google.inject.Inject;
import java.util.EnumSet;
import java.util.List;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -40,6 +43,9 @@ import org.junit.Test;
@NoHttpd
public class ProjectWatchIT extends AbstractDaemonTest {
+ @Inject private ProjectOperations projectOperations;
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void newPatchSetsNotifyConfig() throws Exception {
Address addr = new Address("Watcher", "watcher@example.com");
@@ -57,20 +63,19 @@ public class ProjectWatchIT extends AbstractDaemonTest {
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), testRepo, "original subject", "a", "a1")
+ .create(admin.newIdent(), testRepo, "original subject", "a", "a1")
.to("refs/for/master");
r.assertOkStatus();
r =
pushFactory
- .create(
- db, admin.getIdent(), testRepo, "super sekret subject", "a", "a2", r.getChangeId())
+ .create(admin.newIdent(), testRepo, "super sekret subject", "a", "a2", r.getChangeId())
.to("refs/for/master");
r.assertOkStatus();
r =
pushFactory
- .create(db, admin.getIdent(), testRepo, "back to original subject", "a", "a3")
+ .create(admin.newIdent(), testRepo, "back to original subject", "a", "a3")
.to("refs/for/master");
r.assertOkStatus();
@@ -99,13 +104,13 @@ public class ProjectWatchIT extends AbstractDaemonTest {
sender.clear();
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), testRepo, "private change", "a", "a1")
+ .create(admin.newIdent(), testRepo, "private change", "a", "a1")
.to("refs/for/master%private");
r.assertOkStatus();
assertThat(sender.getMessages()).isEmpty();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ReviewInput in = new ReviewInput();
in.message = "comment";
gApi.changes().id(r.getChangeId()).current().review(in);
@@ -129,16 +134,14 @@ public class ProjectWatchIT extends AbstractDaemonTest {
}
PushOneCommit.Result r =
- pushFactory
- .create(db, admin.getIdent(), testRepo, "subject", "a", "a1")
- .to("refs/for/master");
+ pushFactory.create(admin.newIdent(), testRepo, "subject", "a", "a1").to("refs/for/master");
r.assertOkStatus();
sender.clear();
r =
pushFactory
- .create(db, admin.getIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
+ .create(admin.newIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
.to("refs/for/master%private");
r.assertOkStatus();
@@ -162,13 +165,13 @@ public class ProjectWatchIT extends AbstractDaemonTest {
sender.clear();
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), testRepo, "wip change", "a", "a1")
+ .create(admin.newIdent(), testRepo, "wip change", "a", "a1")
.to("refs/for/master%wip");
r.assertOkStatus();
assertThat(sender.getMessages()).isEmpty();
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ReviewInput in = new ReviewInput();
in.message = "comment";
gApi.changes().id(r.getChangeId()).current().review(in);
@@ -191,16 +194,14 @@ public class ProjectWatchIT extends AbstractDaemonTest {
}
PushOneCommit.Result r =
- pushFactory
- .create(db, admin.getIdent(), testRepo, "subject", "a", "a1")
- .to("refs/for/master");
+ pushFactory.create(admin.newIdent(), testRepo, "subject", "a", "a1").to("refs/for/master");
r.assertOkStatus();
sender.clear();
r =
pushFactory
- .create(db, admin.getIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
+ .create(admin.newIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
.to("refs/for/master%wip");
r.assertOkStatus();
@@ -210,28 +211,28 @@ public class ProjectWatchIT extends AbstractDaemonTest {
@Test
public void watchProject() throws Exception {
// watch project
- String watchedProject = createProject("watchedProject").get();
- setApiUser(user);
+ String watchedProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
watch(watchedProject);
// push a change to watched project -> should trigger email notification
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
cloneProject(new Project.NameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "TRIGGER", "a", "a1")
+ .create(admin.newIdent(), watchedRepo, "TRIGGER", "a", "a1")
.to("refs/for/master");
r.assertOkStatus();
// push a change to non-watched project -> should not trigger email
// notification
- String notWatchedProject = createProject("otherProject").get();
+ String notWatchedProject = projectOperations.newProject().create().get();
TestRepository<InMemoryRepository> notWatchedRepo =
cloneProject(new Project.NameKey(notWatchedProject), admin);
r =
pushFactory
- .create(db, admin.getIdent(), notWatchedRepo, "DONT_TRIGGER", "a", "a1")
+ .create(admin.newIdent(), notWatchedRepo, "DONT_TRIGGER", "a", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -239,16 +240,16 @@ public class ProjectWatchIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@Test
public void watchFile() throws Exception {
- String watchedProject = createProject("watchedProject").get();
- String otherWatchedProject = createProject("otherWatchedProject").get();
- setApiUser(user);
+ String watchedProject = projectOperations.newProject().create().get();
+ String otherWatchedProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
// watch file in project as user
watch(watchedProject, "file:a.txt");
@@ -258,12 +259,12 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change to watched file -> should trigger email notification for
// user
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
cloneProject(new Project.NameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "TRIGGER", "a.txt", "a1")
+ .create(admin.newIdent(), watchedRepo, "TRIGGER", "a.txt", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -271,21 +272,21 @@ public class ProjectWatchIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
// watch project as user2
TestAccount user2 = accountCreator.create("user2", "user2@test.com", "User2");
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
watch(watchedProject);
// push a change to non-watched file -> should not trigger email
// notification for user, only for user2
r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "TRIGGER_USER2", "b.txt", "b1")
+ .create(admin.newIdent(), watchedRepo, "TRIGGER_USER2", "b.txt", "b1")
.to("refs/for/master");
r.assertOkStatus();
@@ -293,26 +294,26 @@ public class ProjectWatchIT extends AbstractDaemonTest {
messages = sender.getMessages();
assertThat(messages).hasSize(1);
m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user2.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user2.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER_USER2\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@Test
public void watchKeyword() throws Exception {
- String watchedProject = createProject("watchedProject").get();
- setApiUser(user);
+ String watchedProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
// watch keyword in project as user
watch(watchedProject, "multimaster");
// push a change with keyword -> should trigger email notification
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
cloneProject(new Project.NameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "Document multimaster setup", "a.txt", "a1")
+ .create(admin.newIdent(), watchedRepo, "Document multimaster setup", "a.txt", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -320,7 +321,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("Change subject: Document multimaster setup\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -328,8 +329,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change without keyword -> should not trigger email notification
r =
pushFactory
- .create(
- db, admin.getIdent(), watchedRepo, "Cleanup cache implementation", "b.txt", "b1")
+ .create(admin.newIdent(), watchedRepo, "Cleanup cache implementation", "b.txt", "b1")
.to("refs/for/master");
r.assertOkStatus();
@@ -339,35 +339,33 @@ public class ProjectWatchIT extends AbstractDaemonTest {
@Test
public void watchAllProjects() throws Exception {
- String anyProject = createProject("anyProject").get();
- setApiUser(user);
+ String anyProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
// watch the All-Projects project to watch all projects
watch(allProjects.get());
// push a change to any project -> should trigger email notification
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> anyRepo =
cloneProject(new Project.NameKey(anyProject), admin);
PushOneCommit.Result r =
- pushFactory
- .create(db, admin.getIdent(), anyRepo, "TRIGGER", "a", "a1")
- .to("refs/for/master");
+ pushFactory.create(admin.newIdent(), anyRepo, "TRIGGER", "a", "a1").to("refs/for/master");
r.assertOkStatus();
// assert email notification
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@Test
public void watchFileAllProjects() throws Exception {
- String anyProject = createProject("anyProject").get();
- setApiUser(user);
+ String anyProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
// watch file in All-Projects project as user to watch the file in all
// projects
@@ -375,12 +373,12 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// push a change to watched file in any project -> should trigger email
// notification for user
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> anyRepo =
cloneProject(new Project.NameKey(anyProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), anyRepo, "TRIGGER", "a.txt", "a1")
+ .create(admin.newIdent(), anyRepo, "TRIGGER", "a.txt", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -388,21 +386,21 @@ public class ProjectWatchIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
// watch project as user2
TestAccount user2 = accountCreator.create("user2", "user2@test.com", "User2");
- setApiUser(user2);
+ requestScopeOperations.setApiUser(user2.id());
watch(anyProject);
// push a change to non-watched file in any project -> should not trigger
// email notification for user, only for user2
r =
pushFactory
- .create(db, admin.getIdent(), anyRepo, "TRIGGER_USER2", "b.txt", "b1")
+ .create(admin.newIdent(), anyRepo, "TRIGGER_USER2", "b.txt", "b1")
.to("refs/for/master");
r.assertOkStatus();
@@ -410,27 +408,27 @@ public class ProjectWatchIT extends AbstractDaemonTest {
messages = sender.getMessages();
assertThat(messages).hasSize(1);
m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user2.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user2.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER_USER2\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
@Test
public void watchKeywordAllProjects() throws Exception {
- String anyProject = createProject("anyProject").get();
- setApiUser(user);
+ String anyProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
// watch keyword in project as user
watch(allProjects.get(), "multimaster");
// push a change with keyword to any project -> should trigger email
// notification
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> anyRepo =
cloneProject(new Project.NameKey(anyProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), anyRepo, "Document multimaster setup", "a.txt", "a1")
+ .create(admin.newIdent(), anyRepo, "Document multimaster setup", "a.txt", "a1")
.to("refs/for/master");
r.assertOkStatus();
@@ -438,7 +436,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(user.emailAddress);
+ assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
assertThat(m.body()).contains("Change subject: Document multimaster setup\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
sender.clear();
@@ -447,7 +445,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
// notification
r =
pushFactory
- .create(db, admin.getIdent(), anyRepo, "Cleanup cache implementation", "b.txt", "b1")
+ .create(admin.newIdent(), anyRepo, "Cleanup cache implementation", "b.txt", "b1")
.to("refs/for/master");
r.assertOkStatus();
@@ -458,28 +456,28 @@ public class ProjectWatchIT extends AbstractDaemonTest {
@Test
public void watchProjectNoNotificationForIgnoredChange() throws Exception {
// watch project
- String watchedProject = createProject("watchedProject").get();
- setApiUser(user);
+ String watchedProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
watch(watchedProject);
// push a change to watched project
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
cloneProject(new Project.NameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "ignored change", "a", "a1")
+ .create(admin.newIdent(), watchedRepo, "ignored change", "a", "a1")
.to("refs/for/master");
r.assertOkStatus();
// ignore the change
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
sender.clear();
// post a comment -> should not trigger email notification since user ignored the change
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
ReviewInput in = new ReviewInput();
in.message = "comment";
gApi.changes().id(r.getChangeId()).current().review(in);
@@ -491,17 +489,17 @@ public class ProjectWatchIT extends AbstractDaemonTest {
@Test
public void watchProjectNoNotificationForPrivateChange() throws Exception {
// watch project
- String watchedProject = createProject("watchedProject").get();
- setApiUser(user);
+ String watchedProject = projectOperations.newProject().create().get();
+ requestScopeOperations.setApiUser(user.id());
watch(watchedProject);
// push a private change to watched project -> should not trigger email notification
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
cloneProject(new Project.NameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "private change", "a", "a1")
+ .create(admin.newIdent(), watchedRepo, "private change", "a", "a1")
.to("refs/for/master%private");
r.assertOkStatus();
@@ -511,7 +509,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
@Test
public void watchProjectNotifyOnPrivateChange() throws Exception {
- String watchedProject = createProject("watchedProject").get();
+ String watchedProject = projectOperations.newProject().create().get();
// create group that can view all private changes
GroupInfo groupThatCanViewPrivateChanges =
@@ -524,24 +522,24 @@ public class ProjectWatchIT extends AbstractDaemonTest {
new AccountGroup.UUID(groupThatCanViewPrivateChanges.id));
// watch project as user that can't view private changes
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
watch(watchedProject);
// watch project as user that can view all private change
TestAccount userThatCanViewPrivateChanges =
accountCreator.create(
"user2", "user2@test.com", "User2", groupThatCanViewPrivateChanges.name);
- setApiUser(userThatCanViewPrivateChanges);
+ requestScopeOperations.setApiUser(userThatCanViewPrivateChanges.id());
watch(watchedProject);
// push a private change to watched project -> should trigger email notification for
// userThatCanViewPrivateChanges, but not for user
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
TestRepository<InMemoryRepository> watchedRepo =
cloneProject(new Project.NameKey(watchedProject), admin);
PushOneCommit.Result r =
pushFactory
- .create(db, admin.getIdent(), watchedRepo, "TRIGGER", "a", "a1")
+ .create(admin.newIdent(), watchedRepo, "TRIGGER", "a", "a1")
.to("refs/for/master%private");
r.assertOkStatus();
@@ -549,7 +547,7 @@ public class ProjectWatchIT extends AbstractDaemonTest {
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
Message m = messages.get(0);
- assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.emailAddress);
+ assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.getEmailAddress());
assertThat(m.body()).contains("Change subject: TRIGGER\n");
assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
}
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
index 8abb59d537..da3a257915 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
@@ -15,12 +15,12 @@
package com.google.gerrit.acceptance.server.project;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.groups.GroupApi;
@@ -30,6 +30,7 @@ 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;
import org.eclipse.jgit.lib.ReflogEntry;
@@ -38,9 +39,10 @@ import org.junit.Test;
@UseLocalDisk
public class ReflogIT extends AbstractDaemonTest {
+ @Inject private RequestScopeOperations requestScopeOperations;
+
@Test
public void guessRestApiInReflog() throws Exception {
- assume().that(notesMigration.disableChangeReviewDb()).isTrue();
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
@@ -82,7 +84,7 @@ public class ReflogIT extends AbstractDaemonTest {
@Test
public void regularUserIsNotAllowedToGetReflog() throws Exception {
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
exception.expect(AuthException.class);
gApi.projects().name(project.get()).branch("master").reflog();
}
@@ -98,13 +100,13 @@ public class ReflogIT extends AbstractDaemonTest {
u.save();
}
- setApiUser(user);
+ requestScopeOperations.setApiUser(user.id());
gApi.projects().name(project.get()).branch("master").reflog();
}
@Test
public void adminUserIsAllowedToGetReflog() throws Exception {
- setApiUser(admin);
+ requestScopeOperations.setApiUser(admin.id());
gApi.projects().name(project.get()).branch("master").reflog();
}
}
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/BUILD b/javatests/com/google/gerrit/acceptance/server/quota/BUILD
new file mode 100644
index 0000000000..1988b227f2
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/quota/BUILD
@@ -0,0 +1,7 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+
+acceptance_tests(
+ srcs = glob(["*IT.java"]),
+ group = "server_quota",
+ labels = ["server"],
+)
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
new file mode 100644
index 0000000000..adc7807101
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
@@ -0,0 +1,239 @@
+// 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.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 com.google.gerrit.acceptance.AbstractDaemonTest;
+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;
+import com.google.gerrit.server.quota.QuotaException;
+import com.google.gerrit.server.quota.QuotaRequestContext;
+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 IdentifiedUser identifiedAdmin;
+ @Inject private QuotaBackend quotaBackend;
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ bind(QuotaEnforcer.class)
+ .annotatedWith(Exports.named("TestQuotaEnforcer"))
+ .toProvider(() -> quotaEnforcer);
+ }
+ };
+ }
+
+ @Before
+ public void setUp() {
+ identifiedAdmin = identifiedUserFactory.create(admin.id());
+ resetToStrict(quotaEnforcer);
+ }
+
+ @Test
+ public void requestTokenForUser() {
+ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
+ expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
+ .isEqualTo(singletonAggregation(QuotaResponse.ok()));
+ }
+
+ @Test
+ public void requestTokenForUserAndAccount() {
+ QuotaRequestContext ctx =
+ QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build();
+ expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).requestToken("testGroup"))
+ .isEqualTo(singletonAggregation(QuotaResponse.ok()));
+ }
+
+ @Test
+ public void requestTokenForUserAndProject() {
+ QuotaRequestContext ctx =
+ QuotaRequestContext.builder().user(identifiedAdmin).project(project).build();
+ expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).project(project).requestToken("testGroup"))
+ .isEqualTo(singletonAggregation(QuotaResponse.ok()));
+ }
+
+ @Test
+ public void requestTokenForUserAndChange() throws Exception {
+ Change.Id changeId = retrieveChangeId();
+ QuotaRequestContext ctx =
+ QuotaRequestContext.builder()
+ .user(identifiedAdmin)
+ .change(changeId)
+ .project(project)
+ .build();
+ expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
+ replay(quotaEnforcer);
+ assertThat(
+ quotaBackend.user(identifiedAdmin).change(changeId, project).requestToken("testGroup"))
+ .isEqualTo(singletonAggregation(QuotaResponse.ok()));
+ }
+
+ @Test
+ public void requestTokens() {
+ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
+ expect(quotaEnforcer.requestTokens("testGroup", ctx, 123)).andReturn(QuotaResponse.ok());
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).requestTokens("testGroup", 123))
+ .isEqualTo(singletonAggregation(QuotaResponse.ok()));
+ }
+
+ @Test
+ public void dryRun() {
+ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
+ expect(quotaEnforcer.dryRun("testGroup", ctx, 123)).andReturn(QuotaResponse.ok());
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).dryRun("testGroup", 123))
+ .isEqualTo(singletonAggregation(QuotaResponse.ok()));
+ }
+
+ @Test
+ public void availableTokensForUserAndAccount() {
+ QuotaRequestContext ctx =
+ QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build();
+ QuotaResponse r = QuotaResponse.ok(10L);
+ expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).availableTokens("testGroup"))
+ .isEqualTo(singletonAggregation(r));
+ }
+
+ @Test
+ public void availableTokensForUserAndProject() {
+ QuotaRequestContext ctx =
+ QuotaRequestContext.builder().user(identifiedAdmin).project(project).build();
+ QuotaResponse r = QuotaResponse.ok(10L);
+ expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).project(project).availableTokens("testGroup"))
+ .isEqualTo(singletonAggregation(r));
+ }
+
+ @Test
+ public void availableTokensForUserAndChange() throws Exception {
+ Change.Id changeId = retrieveChangeId();
+ QuotaRequestContext ctx =
+ QuotaRequestContext.builder()
+ .user(identifiedAdmin)
+ .change(changeId)
+ .project(project)
+ .build();
+ QuotaResponse r = QuotaResponse.ok(10L);
+ expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
+ replay(quotaEnforcer);
+ assertThat(
+ quotaBackend
+ .user(identifiedAdmin)
+ .change(changeId, project)
+ .availableTokens("testGroup"))
+ .isEqualTo(singletonAggregation(r));
+ }
+
+ @Test
+ public void availableTokens() {
+ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
+ QuotaResponse r = QuotaResponse.ok(10L);
+ expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r);
+ replay(quotaEnforcer);
+ assertThat(quotaBackend.user(identifiedAdmin).availableTokens("testGroup"))
+ .isEqualTo(singletonAggregation(r));
+ }
+
+ @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);
+
+ QuotaResponse.Aggregated result = quotaBackend.user(identifiedAdmin).requestToken("testGroup");
+ assertThat(result).isEqualTo(singletonAggregation(QuotaResponse.error("failed")));
+ exception.expect(QuotaException.class);
+ exception.expectMessage("failed");
+ result.throwOnError();
+ }
+
+ @Test
+ public void availableTokensError() throws Exception {
+ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
+ expect(quotaEnforcer.availableTokens("testGroup", ctx))
+ .andReturn(QuotaResponse.error("failed"));
+ replay(quotaEnforcer);
+ QuotaResponse.Aggregated result =
+ quotaBackend.user(identifiedAdmin).availableTokens("testGroup");
+ assertThat(result).isEqualTo(singletonAggregation(QuotaResponse.error("failed")));
+ exception.expect(QuotaException.class);
+ exception.expectMessage("failed");
+ result.throwOnError();
+ }
+
+ @Test
+ public void requestTokenPluginThrowsAndRethrows() {
+ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
+ expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andThrow(new NullPointerException());
+ replay(quotaEnforcer);
+
+ exception.expect(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);
+
+ exception.expect(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);
+ }
+
+ private static QuotaResponse.Aggregated singletonAggregation(QuotaResponse response) {
+ return QuotaResponse.Aggregated.create(Collections.singleton(response));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
new file mode 100644
index 0000000000..f5f11610c6
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
@@ -0,0 +1,157 @@
+// 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.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 com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.quota.QuotaBackend;
+import com.google.gerrit.server.quota.QuotaEnforcer;
+import com.google.gerrit.server.quota.QuotaRequestContext;
+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 IdentifiedUser identifiedAdmin;
+ @Inject private QuotaBackend quotaBackend;
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ bind(QuotaEnforcer.class)
+ .annotatedWith(Exports.named("TestQuotaEnforcerA"))
+ .toProvider(() -> quotaEnforcerA);
+
+ bind(QuotaEnforcer.class)
+ .annotatedWith(Exports.named("TestQuotaEnforcerB"))
+ .toProvider(() -> quotaEnforcerB);
+ }
+ };
+ }
+
+ @Before
+ public void setUp() {
+ identifiedAdmin = identifiedUserFactory.create(admin.id());
+ resetToStrict(quotaEnforcerA);
+ resetToStrict(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);
+
+ assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
+ .isEqualTo(
+ QuotaResponse.Aggregated.create(
+ ImmutableList.of(QuotaResponse.ok(), QuotaResponse.error("fail"))));
+ }
+
+ @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);
+ }
+
+ @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);
+
+ assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
+ .isEqualTo(
+ QuotaResponse.Aggregated.create(
+ ImmutableList.of(QuotaResponse.error("fail"), QuotaResponse.noOp())));
+ }
+
+ @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);
+
+ OptionalLong tokens =
+ quotaBackend.user(identifiedAdmin).availableTokens("testGroup").availableTokens();
+ assertThat(tokens.isPresent()).isTrue();
+ assertThat(tokens.getAsLong()).isEqualTo(10L);
+ }
+
+ @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);
+
+ OptionalLong tokens =
+ quotaBackend.user(identifiedAdmin).availableTokens("testGroup").availableTokens();
+ assertThat(tokens.isPresent()).isTrue();
+ assertThat(tokens.getAsLong()).isEqualTo(20L);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java
new file mode 100644
index 0000000000..0814230e30
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java
@@ -0,0 +1,140 @@
+// 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.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 com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.extensions.config.FactoryModule;
+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.eclipse.jgit.api.errors.TooLargeObjectInPackException;
+import org.eclipse.jgit.api.errors.TransportException;
+import org.junit.Before;
+import org.junit.Test;
+
+@UseLocalDisk
+public class RepositorySizeQuotaIT extends AbstractDaemonTest {
+ private static final QuotaBackend.WithResource quotaBackendWithResource =
+ EasyMock.createStrictMock(QuotaBackend.WithResource.class);
+ private static final QuotaBackend.WithUser quotaBackendWithUser =
+ EasyMock.createStrictMock(QuotaBackend.WithUser.class);
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ bind(QuotaBackend.class)
+ .toInstance(
+ new QuotaBackend() {
+ @Override
+ public WithUser currentUser() {
+ return quotaBackendWithUser;
+ }
+
+ @Override
+ public WithUser user(CurrentUser user) {
+ return quotaBackendWithUser;
+ }
+ });
+ }
+ };
+ }
+
+ @Before
+ public void setUp() {
+ resetToStrict(quotaBackendWithResource);
+ resetToStrict(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);
+ pushCommit();
+ verify(quotaBackendWithUser);
+ verify(quotaBackendWithResource);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ private void pushCommit() throws Exception {
+ createCommitAndPush(testRepo, "refs/heads/master", "test 01", "file.test", "some content");
+ }
+
+ private static QuotaResponse.Aggregated singletonAggregation(QuotaResponse response) {
+ return QuotaResponse.Aggregated.create(Collections.singleton(response));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
new file mode 100644
index 0000000000..dc082feda9
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
@@ -0,0 +1,144 @@
+// 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.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 com.google.gerrit.acceptance.AbstractDaemonTest;
+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);
+ private static final QuotaBackend.WithUser quotaBackendWithUser =
+ EasyMock.createStrictMock(QuotaBackend.WithUser.class);
+
+ @Override
+ public Module createModule() {
+ return new FactoryModule() {
+ @Override
+ public void configure() {
+ bind(QuotaBackend.class)
+ .toInstance(
+ new QuotaBackend() {
+ @Override
+ public WithUser currentUser() {
+ return quotaBackendWithUser;
+ }
+
+ @Override
+ public WithUser user(CurrentUser user) {
+ return quotaBackendWithUser;
+ }
+ });
+ }
+ };
+ }
+
+ @Before
+ public void setUp() {
+ reset(quotaBackendWithResource);
+ reset(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);
+ adminRestSession.get("/changes/" + changeId + "/detail").assertOK();
+ verify(quotaBackendWithUser);
+ verify(quotaBackendWithResource);
+ }
+
+ @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);
+ adminRestSession.get("/changes/" + changeId + "/revisions/current/actions").assertOK();
+ verify(quotaBackendWithUser);
+ verify(quotaBackendWithResource);
+ }
+
+ @Test
+ public void createChangePost() throws Exception {
+ expect(quotaBackendWithUser.requestToken("/restapi/changes:POST"))
+ .andReturn(singletonAggregation(QuotaResponse.ok()));
+ replay(quotaBackendWithUser);
+ ChangeInput changeInput = new ChangeInput(project.get(), "master", "test");
+ adminRestSession.post("/changes/", changeInput).assertCreated();
+ verify(quotaBackendWithUser);
+ }
+
+ @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);
+ adminRestSession.get("/accounts/self/detail").assertOK();
+ verify(quotaBackendWithUser);
+ verify(quotaBackendWithResource);
+ }
+
+ @Test
+ public void config() throws Exception {
+ expect(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
+ .andReturn(singletonAggregation(QuotaResponse.ok()));
+ replay(quotaBackendWithUser);
+ adminRestSession.get("/config/server/version").assertOK();
+ }
+
+ @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);
+ }
+
+ 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);
+ }
+
+ private static QuotaResponse.Aggregated singletonAggregation(QuotaResponse response) {
+ return QuotaResponse.Aggregated.create(Collections.singleton(response));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
index d1b05e319b..83782c912e 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
@@ -63,7 +63,7 @@ public class IgnoreSelfApprovalRuleIT extends AbstractDaemonTest {
// Create change as user
TestRepository<InMemoryRepository> userTestRepo = cloneProject(project, user);
- PushOneCommit push = pushFactory.create(db, user.getIdent(), userTestRepo);
+ PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
PushOneCommit.Result r = push.to("refs/for/master");
// Approve as admin
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
index 8fc32b400f..fa13be499b 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
@@ -87,17 +87,17 @@ public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
SubmitRecord.Label submitRecordLabel1 = new SubmitRecord.Label();
submitRecordLabel1.label = "Verified";
submitRecordLabel1.status = SubmitRecord.Label.Status.REJECT;
- submitRecordLabel1.appliedBy = admin.id;
+ submitRecordLabel1.appliedBy = admin.id();
SubmitRecord.Label submitRecordLabel2 = new SubmitRecord.Label();
submitRecordLabel2.label = "Code-Review";
submitRecordLabel2.status = SubmitRecord.Label.Status.OK;
- submitRecordLabel2.appliedBy = admin.id;
+ submitRecordLabel2.appliedBy = admin.id();
SubmitRecord.Label submitRecordLabel3 = new SubmitRecord.Label();
submitRecordLabel3.label = "Any-Label-Name";
submitRecordLabel3.status = SubmitRecord.Label.Status.REJECT;
- submitRecordLabel3.appliedBy = user.id;
+ submitRecordLabel3.appliedBy = user.id();
List<Term> terms = new ArrayList<>();
@@ -140,7 +140,7 @@ public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
}
private static StructureTerm makeLabel(String name, String status, TestAccount account) {
- StructureTerm user = new StructureTerm("user", new IntegerTerm(account.id.get()));
+ StructureTerm user = new StructureTerm("user", new IntegerTerm(account.id().get()));
return new StructureTerm("label", new StructureTerm(name), new StructureTerm(status, user));
}
@@ -150,7 +150,7 @@ public class PrologRuleEvaluatorIT extends AbstractDaemonTest {
private ChangeData makeChangeData() {
ChangeData cd = ChangeData.createForTest(project, new Change.Id(1), 1);
- cd.setChange(TestChanges.newChange(project, admin.id));
+ cd.setChange(TestChanges.newChange(project, admin.id()));
return cd;
}
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
index 98dca3ef31..536cbbbd16 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
@@ -26,7 +26,6 @@ import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import java.util.Collection;
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
@@ -74,7 +73,7 @@ public class RulesIT extends AbstractDaemonTest {
modifySubmitRules(
String.format(
"gerrit:commit_author(user(%d), '%s', '%s')",
- user.getId().get(), user.fullName, user.email));
+ user.id().get(), user.fullName(), user.email()));
assertThat(statusForRule()).isEqualTo(SubmitRecord.Status.OK);
}
@@ -87,7 +86,7 @@ public class RulesIT extends AbstractDaemonTest {
private SubmitRecord.Status statusForRule() throws Exception {
String oldHead = getRemoteHead().name();
PushOneCommit.Result result1 =
- pushFactory.create(db, user.getIdent(), testRepo).to("refs/for/master");
+ pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
testRepo.reset(oldHead);
ChangeData cd = result1.getChange();
@@ -107,13 +106,13 @@ public class RulesIT extends AbstractDaemonTest {
private void modifySubmitRules(String ruleTested) throws Exception {
String newContent = String.format(RULE_TEMPLATE, ruleTested);
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<?> testRepo = new TestRepository<>((InMemoryRepository) repo);
+ try (Repository repo = repoManager.openRepository(project);
+ TestRepository<Repository> testRepo = new TestRepository<>(repo)) {
testRepo
.branch(RefNames.REFS_CONFIG)
.commit()
- .author(admin.getIdent())
- .committer(admin.getIdent())
+ .author(admin.newIdent())
+ .committer(admin.newIdent())
.add("rules.pl", newContent)
.message("Modify rules.pl")
.create();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
index 0403cab0d2..9fb7c2d8cb 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
@@ -45,7 +45,7 @@ public abstract class AbstractIndexTests extends AbstractDaemonTest {
private RegistrationHandle changeIndexedCounterHandle;
/** @param injector injector */
- public abstract void configureIndex(Injector injector) throws Exception;
+ public void configureIndex(Injector injector) {}
@Before
public void addChangeIndexedCounter() {
diff --git a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
index 74f25e42b9..339e8aed54 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
@@ -31,11 +31,11 @@ public class ElasticIndexIT extends AbstractIndexTests {
@ConfigSuite.Config
public static Config elasticsearchV7() {
- return getConfig(ElasticVersion.V7_7);
+ return getConfig(ElasticVersion.V7_8);
}
@Override
- public void configureIndex(Injector injector) throws Exception {
+ public void configureIndex(Injector injector) {
createAllIndexes(injector);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java b/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
index 4384ab5202..c23f889dce 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/GarbageCollectionIT.java
@@ -21,6 +21,7 @@ import com.google.gerrit.acceptance.GcAssert;
import com.google.gerrit.acceptance.NoHttpd;
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.server.git.GarbageCollection;
@@ -40,14 +41,15 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
@Inject private GarbageCollectionQueue gcQueue;
@Inject private GcAssert gcAssert;
+ @Inject private ProjectOperations projectOperations;
private Project.NameKey project2;
private Project.NameKey project3;
@Before
public void setUp() throws Exception {
- project2 = createProject("p2");
- project3 = createProject("p3");
+ project2 = projectOperations.newProject().create();
+ project3 = projectOperations.newProject().create();
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java
index 196a1e5e2d..370fb72e4b 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/IndexIT.java
@@ -14,10 +14,4 @@
package com.google.gerrit.acceptance.ssh;
-import com.google.inject.Injector;
-
-public class IndexIT extends AbstractIndexTests {
-
- @Override
- public void configureIndex(Injector injector) throws Exception {}
-}
+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
new file mode 100644
index 0000000000..e61e2cc407
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/PluginChangeFieldsIT.java
@@ -0,0 +1,88 @@
+// 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 static com.google.common.truth.Truth.assertThat;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableListMultimap;
+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.server.query.change.OutputStreamQuery;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.junit.Test;
+
+@UseSsh
+public class PluginChangeFieldsIT extends AbstractPluginFieldsTest {
+ // No tests for getting a single change over SSH, since the only API is the query API.
+
+ private static final Gson GSON = OutputStreamQuery.GSON;
+
+ @Test
+ public void queryChangeWithNullAttribute() throws Exception {
+ getChangeWithNullAttribute(
+ id -> pluginInfoFromSingletonList(adminSshSession.exec(changeQueryCmd(id))));
+ }
+
+ @Test
+ public void queryChangeWithSimpleAttribute() throws Exception {
+ getChangeWithSimpleAttribute(
+ id -> pluginInfoFromSingletonList(adminSshSession.exec(changeQueryCmd(id))));
+ }
+
+ @Test
+ public void queryChangeWithOption() throws Exception {
+ getChangeWithOption(
+ id -> pluginInfoFromSingletonList(adminSshSession.exec(changeQueryCmd(id))),
+ (id, opts) -> pluginInfoFromSingletonList(adminSshSession.exec(changeQueryCmd(id, opts))));
+ }
+
+ private String changeQueryCmd(Change.Id id) {
+ return changeQueryCmd(id, ImmutableListMultimap.of());
+ }
+
+ private String changeQueryCmd(Change.Id id, ImmutableListMultimap<String, String> pluginOptions) {
+ return "gerrit query --format json "
+ + pluginOptions.entries().stream()
+ .flatMap(e -> Stream.of("--" + e.getKey(), e.getValue()))
+ .collect(joining(" "))
+ + " "
+ + id;
+ }
+
+ @Nullable
+ private static List<MyInfo> pluginInfoFromSingletonList(String sshOutput) throws Exception {
+ List<Map<String, Object>> changeAttrs = new ArrayList<>();
+ for (String line : CharStreams.readLines(new StringReader(sshOutput))) {
+ Map<String, Object> changeAttr =
+ GSON.fromJson(line, new TypeToken<Map<String, Object>>() {}.getType());
+ if (!"stats".equals(changeAttr.get("type"))) {
+ changeAttrs.add(changeAttr);
+ }
+ }
+
+ assertThat(changeAttrs).hasSize(1);
+ return decodeRawPluginsList(GSON, changeAttrs.get(0).get("plugins"));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java b/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
index 50b2a78d77..0f47a4ab2c 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
@@ -84,7 +84,7 @@ public class QueryIT extends AbstractDaemonTest {
public void allReviewersOptionJSON() throws Exception {
String changeId = createChange().getChangeId();
AddReviewerInput in = new AddReviewerInput();
- in.reviewer = user.email;
+ in.reviewer = user.email();
gApi.changes().id(changeId).addReviewer(in);
List<ChangeAttribute> changes = executeSuccessfulQuery(changeId);
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
index 9108f68059..6998a0ada9 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.Id;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.testing.ConfigSuite;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
@@ -62,13 +62,14 @@ public class SetReviewersIT extends AbstractDaemonTest {
}
private void setReviewer(boolean add, String id) throws Exception {
- session.exec(String.format("gerrit set-reviewers -%s %s %s", add ? "a" : "r", user.email, id));
+ session.exec(
+ String.format("gerrit set-reviewers -%s %s %s", add ? "a" : "r", user.email(), id));
session.assertSuccess();
- ImmutableSet<Id> reviewers = change.getChange().getReviewers().all();
+ ImmutableSet<Account.Id> reviewers = change.getChange().getReviewers().all();
if (add) {
- assertThat(reviewers).contains(user.id);
+ assertThat(reviewers).contains(user.id());
} else {
- assertThat(reviewers).doesNotContain(user.id);
+ assertThat(reviewers).doesNotContain(user.id());
}
}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
index eb79c180aa..4e9c4a49e9 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
@@ -17,7 +17,6 @@ package com.google.gerrit.acceptance.ssh;
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.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -27,7 +26,6 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.UseSsh;
-import com.google.gerrit.common.data.GlobalCapability;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -64,7 +62,6 @@ public class SshCommandsIT extends AbstractDaemonTest {
"create-branch",
"create-group",
"create-project",
- "gsql",
"index",
"query",
"receive-pack",
@@ -113,9 +110,6 @@ public class SshCommandsIT extends AbstractDaemonTest {
@Test
@Sandboxed
public void sshCommandCanBeExecuted() throws Exception {
- // Access Database capability is required to run the "gerrit gsql" command
- allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
-
testCommandExecution(MASTER_COMMANDS);
restartAsSlave();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java
new file mode 100644
index 0000000000..827c192765
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshDaemonIT.java
@@ -0,0 +1,100 @@
+// 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.ssh;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.Sandboxed;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.restapi.config.ListTasks;
+import com.google.gerrit.testing.ConfigSuite;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import java.time.LocalDateTime;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@NoHttpd
+@UseSsh
+@Sandboxed
+@RunWith(ConfigSuite.class)
+@SuppressWarnings("unused")
+public class SshDaemonIT extends AbstractDaemonTest {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Inject private ListTasks listTasks;
+ @Inject private SitePaths gerritSitePath;
+
+ @ConfigSuite.Parameter protected Config config;
+
+ @ConfigSuite.Config
+ public static Config gracefulConfig() {
+ Config config = new Config();
+ config.setString("sshd", null, "gracefulStopTimeout", "10s");
+ return config;
+ }
+
+ @Override
+ public Module createSshModule() {
+ return new TestSshCommandModule();
+ }
+
+ public Future<Integer> startCommand(String command) throws Exception {
+ Callable<Integer> gracefulSession =
+ () -> {
+ int returnCode = -1;
+ logger.atFine().log("Before Command");
+ returnCode = userSshSession.execAndReturnStatus(command);
+ logger.atFine().log("After Command");
+ return returnCode;
+ };
+
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ Future<Integer> future = executor.submit(gracefulSession);
+
+ LocalDateTime timeout = LocalDateTime.now().plusSeconds(10);
+
+ TestCommand.syncPoint.await();
+
+ return future;
+ }
+
+ @Test
+ public void NonGracefulCommandIsStoppedImmediately() throws Exception {
+ Future<Integer> future = startCommand("non-graceful -d 5");
+ restart();
+ Assert.assertTrue(future.get() == -1);
+ }
+
+ @Test
+ public void GracefulCommandIsStoppedGracefully() throws Exception {
+ Future<Integer> future = startCommand("graceful -d 5");
+ restart();
+ if (cfg.getTimeUnit("sshd", null, "gracefulStopTimeout", 0, TimeUnit.SECONDS) == 0) {
+ Assert.assertTrue(future.get() == -1);
+ } else {
+ Assert.assertTrue(future.get() == 0);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
index 899b0cfc9e..9c1e23d0a8 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
@@ -1,3 +1,17 @@
+// 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.ssh;
import static com.google.common.truth.Truth.assertThat;
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
index 954b0e65f3..e0d6593fb3 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth8.assertThat;
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.extensions.api.groups.GroupInput;
@@ -31,14 +32,10 @@ import com.google.inject.Inject;
import java.sql.Timestamp;
import java.util.Objects;
import java.util.Optional;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
public class GroupOperationsImplTest extends AbstractDaemonTest {
- @Rule public ExpectedException expectedException = ExpectedException.none();
-
@Inject private AccountOperations accountOperations;
@Inject private GroupOperationsImpl groupOperations;
@@ -231,7 +228,7 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
public void retrievingNotExistingGroupFails() throws Exception {
AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
- expectedException.expect(IllegalStateException.class);
+ exception.expect(IllegalStateException.class);
groupOperations.group(notExistingGroupUuid).get();
}
@@ -617,40 +614,34 @@ public class GroupOperationsImplTest extends AbstractDaemonTest {
}
private static Correspondence<AccountInfo, Account.Id> getAccountToIdCorrespondence() {
- return new Correspondence<AccountInfo, Account.Id>() {
- @Override
- public boolean compare(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);
- }
-
- @Override
- public String toString() {
- return "has ID";
- }
- };
+ 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);
+ }
+ },
+ "has ID");
}
private static Correspondence<GroupInfo, AccountGroup.UUID> getGroupToUuidCorrespondence() {
- return new Correspondence<GroupInfo, AccountGroup.UUID>() {
- @Override
- public boolean compare(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);
- }
-
- @Override
- public String toString() {
- return "has UUID";
- }
- };
+ 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);
+ }
+ },
+ "has UUID");
}
}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
new file mode 100644
index 0000000000..3f537c077c
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
@@ -0,0 +1,56 @@
+// 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.testsuite.project;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.extensions.api.projects.BranchInfo;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+import java.util.List;
+import org.junit.Test;
+
+public class ProjectOperationsImplTest extends AbstractDaemonTest {
+
+ @Inject private ProjectOperations projectOperations;
+
+ @Test
+ public void defaultName() throws Exception {
+ Project.NameKey name = projectOperations.newProject().create();
+ // check that the project was created (throws exception if not found.)
+ gApi.projects().name(name.get());
+ Project.NameKey name2 = projectOperations.newProject().create();
+ assertThat(name2).isNotEqualTo(name);
+ }
+
+ @Test
+ public void specifiedName() throws Exception {
+ String name = "somename";
+ Project.NameKey key = projectOperations.newProject().name(name).create();
+ assertThat(key.get()).isEqualTo(name);
+ }
+
+ @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"));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
new file mode 100644
index 0000000000..5cbed1b0e1
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
@@ -0,0 +1,147 @@
+// 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.request;
+
+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 com.google.common.collect.ImmutableSet;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+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.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;
+import com.google.gerrit.server.notedb.Sequences;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.junit.Test;
+
+@UseSsh
+public class RequestScopeOperationsImplTest extends AbstractDaemonTest {
+ private static final AtomicInteger changeCounter = new AtomicInteger();
+
+ @Inject private AccountOperations accountOperations;
+ @Inject private Provider<CurrentUser> userProvider;
+ @Inject private RequestScopeOperationsImpl requestScopeOperations;
+ @Inject private Sequences sequences;
+
+ @Test
+ public void setApiUserToExistingUserById() throws Exception {
+ fastCheckCurrentUser(admin.id());
+ AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(user.id());
+ assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.id());
+ checkCurrentUser(user.id());
+ }
+
+ @Test
+ public void setApiUserToExistingUserByTestAccount() throws Exception {
+ fastCheckCurrentUser(admin.id());
+ TestAccount testAccount =
+ accountOperations.account(accountOperations.newAccount().username("tester").create()).get();
+ AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(testAccount);
+ assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.id());
+ checkCurrentUser(testAccount.accountId());
+ }
+
+ @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.
+ }
+ checkCurrentUser(admin.id());
+ }
+
+ @Test
+ public void resetCurrentApiUserClearsCachedState() throws Exception {
+ requestScopeOperations.setApiUser(user.id());
+ PropertyKey<String> key = PropertyKey.create();
+ atrScope.get().getUser().put(key, "foo");
+ assertThat(atrScope.get().getUser().get(key)).hasValue("foo");
+
+ AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.resetCurrentApiUser();
+ checkCurrentUser(user.id());
+ assertThat(atrScope.get().getUser().get(key)).isEmpty();
+ assertThat(oldCtx.getUser().get(key)).hasValue("foo");
+ }
+
+ @Test
+ public void setApiUserAnonymousSetsAnonymousUser() throws Exception {
+ fastCheckCurrentUser(admin.id());
+ requestScopeOperations.setApiUserAnonymous();
+ assertThat(userProvider.get()).isInstanceOf(AnonymousUser.class);
+ }
+
+ 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")
+ .isTrue();
+ assertThat(userProvider.get().getAccountId()).named("user from provider").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")
+ .isEqualTo(expected.get());
+ AcceptanceTestRequestScope.Context ctx = atrScope.get();
+ assertThat(ctx.getUser().isIdentifiedUser())
+ .named("user from AcceptanceTestRequestScope.Context is an IdentifiedUser")
+ .isTrue();
+ assertThat(ctx.getUser().getAccountId())
+ .named("user from AcceptanceTestRequestScope.Context")
+ .isEqualTo(expected);
+ checkSshUser(expected);
+ }
+
+ private void checkSshUser(Account.Id expected) throws Exception {
+ // No "gerrit whoami" command, so the simplest way to check who the user is over SSH is to query
+ // for owner:self.
+ ChangeInput cin = new ChangeInput();
+ cin.project = project.get();
+ cin.branch = "master";
+ cin.subject = "Test change " + changeCounter.incrementAndGet();
+ String changeId = gApi.changes().create(cin).get().changeId;
+ 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)
+ .containsExactly(changeId);
+ }
+
+ private static ImmutableSet<String> findDistinct(String input, String pattern) {
+ Matcher m = Pattern.compile(pattern).matcher(input);
+ ImmutableSet.Builder<String> b = ImmutableSet.builder();
+ while (m.find()) {
+ b.add(m.group(0));
+ }
+ return b.build();
+ }
+}
diff --git a/javatests/com/google/gerrit/common/AutoValueTest.java b/javatests/com/google/gerrit/common/AutoValueTest.java
index 947fe4aa82..89d7bf4f98 100644
--- a/javatests/com/google/gerrit/common/AutoValueTest.java
+++ b/javatests/com/google/gerrit/common/AutoValueTest.java
@@ -17,9 +17,10 @@ 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 {
+public class AutoValueTest extends GerritBaseTests {
@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 c4f2c0a424..18ececd41f 100644
--- a/javatests/com/google/gerrit/common/BUILD
+++ b/javatests/com/google/gerrit/common/BUILD
@@ -1,14 +1,14 @@
load("//tools/bzl:junit.bzl", "junit_tests")
junit_tests(
- name = "common_tests",
- srcs = glob(["**/*.java"]),
+ name = "server_tests",
+ srcs = glob(["*.java"]),
tags = ["no_windows"],
deps = [
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/common:version",
"//java/com/google/gerrit/launcher",
- "//java/com/google/gerrit/reviewdb:server",
+ "//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 a59c015c25..faf9d6c752 100644
--- a/javatests/com/google/gerrit/common/data/AccessSectionTest.java
+++ b/javatests/com/google/gerrit/common/data/AccessSectionTest.java
@@ -17,17 +17,14 @@ 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 java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class AccessSectionTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class AccessSectionTest extends GerritBaseTests {
private static final String REF_PATTERN = "refs/heads/master";
private AccessSection accessSection;
@@ -155,21 +152,6 @@ public class AccessSectionTest {
}
@Test
- public void cannotAddPermissionByModifyingListThatWasRetrievedFromAccessSection() {
- Permission submitPermission = new Permission(Permission.SUBMIT);
- accessSection.getPermissions().add(submitPermission);
- assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
-
- List<Permission> permissions = new ArrayList<>();
- permissions.add(new Permission(Permission.ABANDON));
- permissions.add(new Permission(Permission.REBASE));
- accessSection.setPermissions(permissions);
- assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
- accessSection.getPermissions().add(submitPermission);
- assertThat(accessSection.getPermission(Permission.SUBMIT)).isNull();
- }
-
- @Test
public void removePermission() {
Permission abandonPermission = new Permission(Permission.ABANDON);
Permission rebasePermission = new Permission(Permission.REBASE);
diff --git a/javatests/com/google/gerrit/common/data/BUILD b/javatests/com/google/gerrit/common/data/BUILD
new file mode 100644
index 0000000000..776a5e0250
--- /dev/null
+++ b/javatests/com/google/gerrit/common/data/BUILD
@@ -0,0 +1,13 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "data_tests",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/reviewdb:server",
+ "//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 dcd3c054c2..3dd2db3a5a 100644
--- a/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
+++ b/javatests/com/google/gerrit/common/data/EncodePathSeparatorTest.java
@@ -16,9 +16,10 @@ 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 {
+public class EncodePathSeparatorTest extends GerritBaseTests {
@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 ec71e05a16..055f57d3cc 100644
--- a/javatests/com/google/gerrit/common/data/FilenameComparatorTest.java
+++ b/javatests/com/google/gerrit/common/data/FilenameComparatorTest.java
@@ -16,9 +16,10 @@ 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 {
+public class FilenameComparatorTest extends GerritBaseTests {
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 fdc9dc678d..8cf486b118 100644
--- a/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
+++ b/javatests/com/google/gerrit/common/data/GroupReferenceTest.java
@@ -18,13 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
-import org.junit.Rule;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class GroupReferenceTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class GroupReferenceTest extends GerritBaseTests {
@Test
public void forGroupDescription() {
String name = "foo";
diff --git a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
index 985f514bef..a534a9e8f8 100644
--- a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
+++ b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
@@ -21,15 +21,15 @@ 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.PatchSet.Id;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.testing.GerritBaseTests;
import java.sql.Date;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
-public class LabelFunctionTest {
+public class LabelFunctionTest extends GerritBaseTests {
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);
@@ -102,7 +102,8 @@ public class LabelFunctionTest {
return new PatchSetApproval(key, (short) value, Date.from(Instant.now()));
}
- private static PatchSetApproval.Key makeKey(Id psId, Account.Id accountId, LabelId labelId) {
+ private static PatchSetApproval.Key makeKey(
+ PatchSet.Id psId, Account.Id accountId, LabelId labelId) {
return new PatchSetApproval.Key(psId, accountId, labelId);
}
diff --git a/javatests/com/google/gerrit/common/data/LabelTypeTest.java b/javatests/com/google/gerrit/common/data/LabelTypeTest.java
index 6c3befb1ac..db0df2e695 100644
--- a/javatests/com/google/gerrit/common/data/LabelTypeTest.java
+++ b/javatests/com/google/gerrit/common/data/LabelTypeTest.java
@@ -17,9 +17,10 @@ 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 {
+public class LabelTypeTest extends GerritBaseTests {
@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 b646d2bce0..b22a51151f 100644
--- a/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
+++ b/javatests/com/google/gerrit/common/data/ParameterizedStringTest.java
@@ -17,11 +17,12 @@ 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 {
+public class ParameterizedStringTest extends GerritBaseTests {
@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 14c47b4f10..f442f3930f 100644
--- a/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
+++ b/javatests/com/google/gerrit/common/data/PermissionRuleTest.java
@@ -18,14 +18,11 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class PermissionRuleTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class PermissionRuleTest extends GerritBaseTests {
private GroupReference groupReference;
private PermissionRule permissionRule;
diff --git a/javatests/com/google/gerrit/common/data/PermissionTest.java b/javatests/com/google/gerrit/common/data/PermissionTest.java
index f76323facc..23380e7f79 100644
--- a/javatests/com/google/gerrit/common/data/PermissionTest.java
+++ b/javatests/com/google/gerrit/common/data/PermissionTest.java
@@ -18,12 +18,13 @@ 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 java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
-public class PermissionTest {
+public class PermissionTest extends GerritBaseTests {
private static final String PERMISSION_NAME = "foo";
private Permission permission;
@@ -186,22 +187,6 @@ public class PermissionTest {
}
@Test
- public void cannotAddPermissionByModifyingListThatWasRetrievedFromAccessSection() {
- GroupReference groupReference1 = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
- PermissionRule permissionRule1 = new PermissionRule(groupReference1);
- permission.getRules().add(permissionRule1);
- assertThat(permission.getRule(groupReference1)).isNull();
-
- List<PermissionRule> rules = new ArrayList<>();
- rules.add(new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-2"), "group2")));
- rules.add(new PermissionRule(new GroupReference(new AccountGroup.UUID("uuid-3"), "group3")));
- permission.setRules(rules);
- assertThat(permission.getRule(groupReference1)).isNull();
- permission.getRules().add(permissionRule1);
- assertThat(permission.getRule(groupReference1)).isNull();
- }
-
- @Test
public void getNonExistingRule() {
GroupReference groupReference = new GroupReference(new AccountGroup.UUID("uuid-1"), "group1");
assertThat(permission.getRule(groupReference)).isNull();
diff --git a/javatests/com/google/gerrit/common/data/SubmitRecordTest.java b/javatests/com/google/gerrit/common/data/SubmitRecordTest.java
index 5386b873e6..5b9fde7cc0 100644
--- a/javatests/com/google/gerrit/common/data/SubmitRecordTest.java
+++ b/javatests/com/google/gerrit/common/data/SubmitRecordTest.java
@@ -16,11 +16,12 @@ 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 {
+public class SubmitRecordTest extends GerritBaseTests {
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 b6575450e2..c7ae0d0a5f 100644
--- a/javatests/com/google/gerrit/elasticsearch/BUILD
+++ b/javatests/com/google/gerrit/elasticsearch/BUILD
@@ -84,8 +84,10 @@ junit_tests(
tags = ["elastic"],
deps = [
"//java/com/google/gerrit/elasticsearch",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//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 ff7b5ca874..9ce1456e09 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticConfigurationTest.java
@@ -24,16 +24,14 @@ import static com.google.gerrit.elasticsearch.ElasticConfiguration.SECTION_ELAST
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.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class ElasticConfigurationTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class ElasticConfigurationTest extends GerritBaseTests {
@Test
public void singleServerNoOtherConfig() throws Exception {
Config cfg = newConfig();
@@ -119,7 +117,7 @@ public class ElasticConfigurationTest {
}
private void assertHosts(ElasticConfiguration cfg, Object... hostURIs) throws Exception {
- assertThat(Arrays.asList(cfg.getHosts()).stream().map(h -> h.toURI()).collect(toList()))
+ assertThat(Arrays.asList(cfg.getHosts()).stream().map(HttpHost::toURI).collect(toList()))
.containsExactly(hostURIs);
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index 15094fdd1e..86829b98a4 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -38,12 +38,8 @@ public class ElasticContainer extends ElasticsearchContainer {
private static String getImageName(ElasticVersion version) {
switch (version) {
- case V6_6:
- return "blacktop/elasticsearch:6.6.2";
- case V6_7:
- return "blacktop/elasticsearch:6.7.2";
case V6_8:
- return "blacktop/elasticsearch:6.8.9";
+ return "blacktop/elasticsearch:6.8.12";
case V7_0:
return "blacktop/elasticsearch:7.0.1";
case V7_1:
@@ -59,7 +55,9 @@ public class ElasticContainer extends ElasticsearchContainer {
case V7_6:
return "blacktop/elasticsearch:7.6.2";
case V7_7:
- return "blacktop/elasticsearch:7.7.0";
+ return "blacktop/elasticsearch:7.7.1";
+ case V7_8:
+ return "blacktop/elasticsearch:7.8.1";
}
throw new IllegalStateException("No tests for version: " + version.name());
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
index a976aae495..0203a22df9 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -19,7 +19,6 @@ import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
-import java.io.IOException;
import java.util.Collection;
import java.util.UUID;
import org.eclipse.jgit.lib.Config;
@@ -34,7 +33,7 @@ public final class ElasticTestUtils {
config.setInt("index", null, "maxLimit", 10000);
}
- public static void createAllIndexes(Injector injector) throws IOException {
+ public static void createAllIndexes(Injector injector) {
Collection<IndexDefinition<?, ?, ?>> indexDefs =
injector.getInstance(Key.get(new TypeLiteral<Collection<IndexDefinition<?, ?, ?>>>() {}));
for (IndexDefinition<?, ?, ?> indexDef : indexDefs) {
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
index bb79655219..d4d321d395 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
@@ -59,6 +59,6 @@ public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 3f59d30573..68c4b710f9 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
@@ -82,6 +82,6 @@ public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 a966dfaded..99c07f4486 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
@@ -59,6 +59,6 @@ public class ElasticV6QueryGroupsTest extends AbstractQueryGroupsTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 40bee586cf..89c77748b7 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
@@ -59,6 +59,6 @@ public class ElasticV6QueryProjectsTest extends AbstractQueryProjectsTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 bbddaa277a..91d366e711 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryAccountsTest.java
@@ -36,7 +36,7 @@ public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
public static void startIndexService() {
if (container == null) {
// Only start Elasticsearch once
- container = ElasticContainer.createAndStart(ElasticVersion.V7_7);
+ container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
}
}
@@ -59,6 +59,6 @@ public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 e12df9c029..f11dc5b97d 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryChangesTest.java
@@ -42,7 +42,7 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
public static void startIndexService() {
if (container == null) {
// Only start Elasticsearch once
- container = ElasticContainer.createAndStart(ElasticVersion.V7_7);
+ container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
client = HttpAsyncClients.createDefault();
client.start();
}
@@ -82,6 +82,6 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 5fec685dff..282763a7dd 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryGroupsTest.java
@@ -36,7 +36,7 @@ public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
public static void startIndexService() {
if (container == null) {
// Only start Elasticsearch once
- container = ElasticContainer.createAndStart(ElasticVersion.V7_7);
+ container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
}
}
@@ -59,6 +59,6 @@ public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 3e8bab4bd3..4d83cf572c 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV7QueryProjectsTest.java
@@ -36,7 +36,7 @@ public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
public static void startIndexService() {
if (container == null) {
// Only start Elasticsearch once
- container = ElasticContainer.createAndStart(ElasticVersion.V7_7);
+ container = ElasticContainer.createAndStart(ElasticVersion.V7_8);
}
}
@@ -59,6 +59,6 @@ public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = getSanitizedMethodName();
ElasticTestUtils.configure(elasticsearchConfig, container, indicesPrefix);
- return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
+ 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 309c63a00e..0a7a4222a6 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
@@ -16,21 +16,12 @@ package com.google.gerrit.elasticsearch;
import static com.google.common.truth.Truth.assertThat;
-import org.junit.Rule;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class ElasticVersionTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class ElasticVersionTest extends GerritBaseTests {
@Test
public void supportedVersion() throws Exception {
- assertThat(ElasticVersion.forVersion("6.6.0")).isEqualTo(ElasticVersion.V6_6);
- assertThat(ElasticVersion.forVersion("6.6.1")).isEqualTo(ElasticVersion.V6_6);
-
- assertThat(ElasticVersion.forVersion("6.7.0")).isEqualTo(ElasticVersion.V6_7);
- assertThat(ElasticVersion.forVersion("6.7.1")).isEqualTo(ElasticVersion.V6_7);
-
assertThat(ElasticVersion.forVersion("6.8.0")).isEqualTo(ElasticVersion.V6_8);
assertThat(ElasticVersion.forVersion("6.8.1")).isEqualTo(ElasticVersion.V6_8);
@@ -57,6 +48,9 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.forVersion("7.7.0")).isEqualTo(ElasticVersion.V7_7);
assertThat(ElasticVersion.forVersion("7.7.1")).isEqualTo(ElasticVersion.V7_7);
+
+ assertThat(ElasticVersion.forVersion("7.8.0")).isEqualTo(ElasticVersion.V7_8);
+ assertThat(ElasticVersion.forVersion("7.8.1")).isEqualTo(ElasticVersion.V7_8);
}
@Test
@@ -69,23 +63,20 @@ public class ElasticVersionTest {
@Test
public void atLeastMinorVersion() throws Exception {
- assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue();
assertThat(ElasticVersion.V6_8.isAtLeastMinorVersion(ElasticVersion.V6_8)).isTrue();
- assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_3.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_4.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_5.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
- assertThat(ElasticVersion.V7_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
+ assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_3.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_4.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_5.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_6.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_7.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
+ assertThat(ElasticVersion.V7_8.isAtLeastMinorVersion(ElasticVersion.V6_8)).isFalse();
}
@Test
public void version6OrLater() throws Exception {
- assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
- assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_8.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue();
@@ -95,12 +86,11 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V7_5.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_6.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_7.isV6OrLater()).isTrue();
+ assertThat(ElasticVersion.V7_8.isV6OrLater()).isTrue();
}
@Test
public void version7OrLater() throws Exception {
- assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse();
- assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_8.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue();
@@ -110,5 +100,6 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V7_5.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_6.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_7.isV7OrLater()).isTrue();
+ assertThat(ElasticVersion.V7_8.isV7OrLater()).isTrue();
}
}
diff --git a/javatests/com/google/gerrit/extensions/BUILD b/javatests/com/google/gerrit/extensions/BUILD
index 069c915024..94e433c346 100644
--- a/javatests/com/google/gerrit/extensions/BUILD
+++ b/javatests/com/google/gerrit/extensions/BUILD
@@ -7,6 +7,8 @@ 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 0be10eecdc..86dce049d3 100644
--- a/javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
+++ b/javatests/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
@@ -16,11 +16,12 @@ 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 {
+public class LfsDefinitionsTest extends GerritBaseTests {
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
new file mode 100644
index 0000000000..4bb91079cb
--- /dev/null
+++ b/javatests/com/google/gerrit/extensions/client/ListOptionTest.java
@@ -0,0 +1,72 @@
+// 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.client;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.extensions.client.ListOptionTest.MyOption.BAR;
+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 {
+ enum MyOption implements ListOption {
+ FOO(0),
+ BAR(1),
+ BAZ(17);
+
+ private final int value;
+
+ MyOption(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public int getValue() {
+ return value;
+ }
+ }
+
+ @Test
+ public void fromBits() {
+ assertThat(IntMath.pow(2, BAZ.getValue())).isEqualTo(131072);
+ assertThat(ListOption.fromBits(MyOption.class, 0)).isEmpty();
+ assertThat(ListOption.fromBits(MyOption.class, 1)).containsExactly(FOO);
+ assertThat(ListOption.fromBits(MyOption.class, 2)).containsExactly(BAR);
+ assertThat(ListOption.fromBits(MyOption.class, 131072)).containsExactly(BAZ);
+ assertThat(ListOption.fromBits(MyOption.class, 3)).containsExactly(FOO, BAR);
+ assertThat(ListOption.fromBits(MyOption.class, 131073)).containsExactly(FOO, BAZ);
+ assertThat(ListOption.fromBits(MyOption.class, 131074)).containsExactly(BAR, BAZ);
+ assertThat(ListOption.fromBits(MyOption.class, 131075)).containsExactly(FOO, BAR, BAZ);
+
+ assertFromBitsFails(4);
+ assertFromBitsFails(8);
+ assertFromBitsFails(16);
+ assertFromBitsFails(250);
+ }
+
+ private void assertFromBitsFails(int v) {
+ try {
+ EnumSet<MyOption> opts = ListOption.fromBits(MyOption.class, v);
+ assertWithMessage("expected RuntimeException for fromBits(%s), got: %s", v, opts).fail();
+ } catch (RuntimeException e) {
+ // Expected.
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/extensions/client/RangeTest.java b/javatests/com/google/gerrit/extensions/client/RangeTest.java
index b8938aa50c..2c713b5dae 100644
--- a/javatests/com/google/gerrit/extensions/client/RangeTest.java
+++ b/javatests/com/google/gerrit/extensions/client/RangeTest.java
@@ -16,9 +16,10 @@ 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 {
+public class RangeTest extends GerritBaseTests {
@Test
public void rangeOverMultipleLinesWithSmallerEndCharacterIsValid() {
diff --git a/javatests/com/google/gerrit/extensions/conditions/BUILD b/javatests/com/google/gerrit/extensions/conditions/BUILD
index e2d595113f..7ad2ad3239 100644
--- a/javatests/com/google/gerrit/extensions/conditions/BUILD
+++ b/javatests/com/google/gerrit/extensions/conditions/BUILD
@@ -5,6 +5,7 @@ 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 f9f1fa85fc..81cb719e81 100644
--- a/javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java
+++ b/javatests/com/google/gerrit/extensions/conditions/BooleanConditionTest.java
@@ -20,9 +20,10 @@ 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 {
+public class BooleanConditionTest extends GerritBaseTests {
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 0542c35d1c..d9502247ac 100644
--- a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
+++ b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
@@ -17,13 +17,14 @@ 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 {
+public class DynamicSetTest extends GerritBaseTests {
// 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
new file mode 100644
index 0000000000..d57d73fdcf
--- /dev/null
+++ b/javatests/com/google/gerrit/git/BUILD
@@ -0,0 +1,36 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+MEDIUM_TESTS = ["RefUpdateUtilRepoTest.java"]
+
+junit_tests(
+ name = "medium_tests",
+ size = "medium",
+ timeout = "short",
+ srcs = MEDIUM_TESTS,
+ tags = ["no_windows"],
+ deps = [
+ "//java/com/google/gerrit/git",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:guava",
+ "//lib:junit",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib/jgit/org.eclipse.jgit.junit:junit",
+ "//lib/truth",
+ ],
+)
+
+junit_tests(
+ name = "small_tests",
+ size = "small",
+ srcs = glob(
+ ["*.java"],
+ exclude = MEDIUM_TESTS,
+ ),
+ deps = [
+ "//java/com/google/gerrit/git",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:guava",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib/truth",
+ ],
+)
diff --git a/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java b/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java
new file mode 100644
index 0000000000..59961ff3e7
--- /dev/null
+++ b/javatests/com/google/gerrit/git/RefUpdateUtilRepoTest.java
@@ -0,0 +1,121 @@
+// 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.git;
+
+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;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RefUpdateUtilRepoTest extends GerritBaseTests {
+ public enum RepoSetup {
+ LOCAL_DISK {
+ @Override
+ Repository setUpRepo() throws Exception {
+ Path p = Files.createTempDirectory("gerrit_repo_");
+ try {
+ Repository repo = new FileRepository(p.toFile());
+ repo.create(true);
+ return repo;
+ } catch (Exception e) {
+ delete(p);
+ throw e;
+ }
+ }
+
+ @Override
+ void tearDownRepo(Repository repo) throws Exception {
+ delete(repo.getDirectory().toPath());
+ }
+
+ private void delete(Path p) throws Exception {
+ MoreFiles.deleteRecursively(p, RecursiveDeleteOption.ALLOW_INSECURE);
+ }
+ },
+
+ IN_MEMORY {
+ @Override
+ Repository setUpRepo() {
+ return new InMemoryRepository(new DfsRepositoryDescription("repo"));
+ }
+
+ @Override
+ void tearDownRepo(Repository repo) {}
+ };
+
+ abstract Repository setUpRepo() throws Exception;
+
+ abstract void tearDownRepo(Repository repo) throws Exception;
+ }
+
+ @Parameters(name = "{0}")
+ public static ImmutableList<RepoSetup[]> data() {
+ return ImmutableList.copyOf(new RepoSetup[][] {{RepoSetup.LOCAL_DISK}, {RepoSetup.IN_MEMORY}});
+ }
+
+ @Parameter public RepoSetup repoSetup;
+
+ private Repository repo;
+
+ @Before
+ public void setUp() throws Exception {
+ repo = repoSetup.setUpRepo();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (repo != null) {
+ repoSetup.tearDownRepo(repo);
+ repo = null;
+ }
+ }
+
+ @Test
+ public void deleteRefNoOp() throws Exception {
+ String ref = "refs/heads/foo";
+ assertThat(repo.exactRef(ref)).isNull();
+ RefUpdateUtil.deleteChecked(repo, "refs/heads/foo");
+ assertThat(repo.exactRef(ref)).isNull();
+ }
+
+ @Test
+ public void deleteRef() throws Exception {
+ String ref = "refs/heads/foo";
+ try (TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch(ref).commit().create();
+ }
+
+ assertThat(repo.exactRef(ref)).isNotNull();
+ RefUpdateUtil.deleteChecked(repo, "refs/heads/foo");
+ assertThat(repo.exactRef(ref)).isNull();
+ }
+}
diff --git a/javatests/com/google/gerrit/git/RefUpdateUtilTest.java b/javatests/com/google/gerrit/git/RefUpdateUtilTest.java
new file mode 100644
index 0000000000..429583a588
--- /dev/null
+++ b/javatests/com/google/gerrit/git/RefUpdateUtilTest.java
@@ -0,0 +1,119 @@
+// 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.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 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;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RefUpdateUtilTest extends GerritBaseTests {
+ 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);
+ private static final Consumer<ReceiveCommand> REJECTED =
+ c -> c.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON);
+ private static final Consumer<ReceiveCommand> ABORTED =
+ c -> {
+ c.setResult(ReceiveCommand.Result.NOT_ATTEMPTED);
+ ReceiveCommand.abort(ImmutableList.of(c));
+ checkState(
+ c.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED
+ && c.getResult() != ReceiveCommand.Result.LOCK_FAILURE
+ && c.getResult() != ReceiveCommand.Result.OK,
+ "unexpected state after abort: %s",
+ c);
+ };
+
+ @Test
+ public void checkBatchRefUpdateResults() throws Exception {
+ checkResults();
+ checkResults(OK);
+ checkResults(OK, OK);
+
+ assertIoException(REJECTED);
+ assertIoException(OK, REJECTED);
+ assertIoException(LOCK_FAILURE, REJECTED);
+ assertIoException(LOCK_FAILURE, OK);
+ assertIoException(LOCK_FAILURE, REJECTED, OK);
+ assertIoException(LOCK_FAILURE, LOCK_FAILURE, REJECTED);
+ assertIoException(LOCK_FAILURE, ABORTED, REJECTED);
+ assertIoException(LOCK_FAILURE, ABORTED, OK);
+
+ assertLockFailureException(LOCK_FAILURE);
+ assertLockFailureException(LOCK_FAILURE, LOCK_FAILURE);
+ assertLockFailureException(LOCK_FAILURE, LOCK_FAILURE, ABORTED);
+ assertLockFailureException(LOCK_FAILURE, LOCK_FAILURE, ABORTED, ABORTED);
+ assertLockFailureException(ABORTED);
+ assertLockFailureException(ABORTED, ABORTED);
+ }
+
+ @SafeVarargs
+ private static void checkResults(Consumer<ReceiveCommand>... resultSetters) throws Exception {
+ RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters));
+ }
+
+ @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);
+ }
+ }
+
+ @SafeVarargs
+ private static void assertLockFailureException(Consumer<ReceiveCommand>... resultSetters)
+ throws Exception {
+ try {
+ RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters));
+ assert_().fail("expected LockFailureException");
+ } catch (LockFailureException e) {
+ // Expected.
+ }
+ }
+
+ @SafeVarargs
+ private static BatchRefUpdate newBatchRefUpdate(Consumer<ReceiveCommand>... resultSetters) {
+ try (Repository repo = new InMemoryRepository(new DfsRepositoryDescription("repo"))) {
+ BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
+ for (int i = 0; i < resultSetters.length; i++) {
+ ReceiveCommand cmd =
+ new ReceiveCommand(
+ ObjectId.fromString(String.format("%039x1", i)),
+ ObjectId.fromString(String.format("%039x2", i)),
+ "refs/heads/branch" + i);
+ bru.addCommand(cmd);
+ resultSetters[i].accept(cmd);
+ }
+ return bru;
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/git/testing/BUILD b/javatests/com/google/gerrit/git/testing/BUILD
index 56e9ec29c0..13091853b2 100644
--- a/javatests/com/google/gerrit/git/testing/BUILD
+++ b/javatests/com/google/gerrit/git/testing/BUILD
@@ -5,6 +5,7 @@ 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 3bf815b211..5ab52d4e01 100644
--- a/javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java
+++ b/javatests/com/google/gerrit/git/testing/PushResultSubjectTest.java
@@ -18,9 +18,10 @@ 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 {
+public class PushResultSubjectTest extends GerritBaseTests {
@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 c22e3eda04..f73208d8b6 100644
--- a/javatests/com/google/gerrit/gpg/BUILD
+++ b/javatests/com/google/gerrit/gpg/BUILD
@@ -15,7 +15,6 @@ junit_tests(
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
- "//lib:gwtorm",
"//lib/bouncycastle:bcpg",
"//lib/bouncycastle:bcpg-neverlink",
"//lib/bouncycastle:bcprov",
diff --git a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index 685f42de42..220361e205 100644
--- a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -33,8 +33,6 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountManager;
@@ -42,16 +40,13 @@ import com.google.gerrit.server.account.AccountsUpdate;
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.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gerrit.testing.InMemoryDatabase;
+import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
-import com.google.gerrit.testing.NoteDbMode;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -69,7 +64,7 @@ import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link GerritPublicKeyChecker}. */
-public class GerritPublicKeyCheckerTest {
+public class GerritPublicKeyCheckerTest extends GerritBaseTests {
@Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdateProvider;
@Inject private AccountManager accountManager;
@@ -78,14 +73,11 @@ public class GerritPublicKeyCheckerTest {
@Inject private IdentifiedUser.GenericFactory userFactory;
- @Inject private InMemoryDatabase schemaFactory;
-
@Inject private SchemaCreator schemaCreator;
@Inject private ThreadLocalRequestContext requestContext;
private LifecycleManager lifecycle;
- private ReviewDb db;
private Account.Id userId;
private IdentifiedUser user;
private Repository storeRepo;
@@ -102,16 +94,14 @@ public class GerritPublicKeyCheckerTest {
ImmutableList.of(
Fingerprint.toString(keyB().getPublicKey().getFingerprint()),
Fingerprint.toString(keyD().getPublicKey().getFingerprint())));
- Injector injector =
- Guice.createInjector(new InMemoryModule(cfg, NoteDbMode.newNotesMigrationFromEnv()));
+ Injector injector = Guice.createInjector(new InMemoryModule(cfg));
lifecycle = new LifecycleManager();
lifecycle.add(injector);
injector.injectMembers(this);
lifecycle.start();
- db = schemaFactory.open();
- schemaCreator.create(db);
+ schemaCreator.create();
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
// Note: does not match any key in TestKeys.
accountsUpdateProvider
@@ -119,18 +109,7 @@ public class GerritPublicKeyCheckerTest {
.update("Set Preferred Email", userId, u -> u.setPreferredEmail("user@example.com"));
user = reloadUser();
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(() -> user);
storeRepo = new InMemoryRepository(new DfsRepositoryDescription("repo"));
store = new PublicKeyStore(storeRepo);
@@ -158,10 +137,6 @@ public class GerritPublicKeyCheckerTest {
if (lifecycle != null) {
lifecycle.stop();
}
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(schemaFactory);
}
@Test
diff --git a/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java
index 48d5266d35..145b9cff3f 100644
--- a/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/PublicKeyCheckerTest.java
@@ -38,6 +38,7 @@ 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;
@@ -57,13 +58,9 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class PublicKeyCheckerTest {
- @Rule public ExpectedException thrown = ExpectedException.none();
+public class PublicKeyCheckerTest extends GerritBaseTests {
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 2fc06a6b41..be657528b4 100644
--- a/javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java
+++ b/javatests/com/google/gerrit/gpg/PublicKeyStoreTest.java
@@ -21,6 +21,7 @@ import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithExpiration;
import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithSecondUserId;
import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpiration;
+import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpirationWithSubkeyWithExpiration;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -28,6 +29,7 @@ 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;
@@ -49,7 +51,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
-public class PublicKeyStoreTest {
+public class PublicKeyStoreTest extends GerritBaseTests {
private TestRepository<?> tr;
private PublicKeyStore store;
@@ -100,6 +102,25 @@ public class PublicKeyStoreTest {
}
@Test
+ public void getSubkeyReturnsMasterKey() throws Exception {
+ TestKey key1 = validKeyWithoutExpirationWithSubkeyWithExpiration();
+ PGPPublicKeyRing keyRing = key1.getPublicKeyRing();
+ store.add(keyRing);
+
+ assertEquals(RefUpdate.Result.NEW, store.save(newCommitBuilder()));
+
+ long masterKeyId = key1.getKeyId();
+ long subKeyId = 0;
+ for (PGPPublicKey key : keyRing) {
+ if (masterKeyId != subKeyId) {
+ subKeyId = key.getKeyID();
+ }
+ }
+
+ assertKeys(subKeyId, key1);
+ }
+
+ @Test
public void getMultiple() throws Exception {
TestKey key1 = validKeyWithoutExpiration();
TestKey key2 = validKeyWithExpiration();
@@ -201,6 +222,29 @@ public class PublicKeyStoreTest {
}
@Test
+ public void removeMasterKeyRemovesSubkey() throws Exception {
+ TestKey key1 = validKeyWithoutExpirationWithSubkeyWithExpiration();
+ PGPPublicKeyRing keyRing = key1.getPublicKeyRing();
+ store.add(keyRing);
+
+ assertEquals(RefUpdate.Result.NEW, store.save(newCommitBuilder()));
+
+ long masterKeyId = key1.getKeyId();
+ long subKeyId = 0;
+ for (PGPPublicKey key : keyRing) {
+ if (masterKeyId != subKeyId) {
+ subKeyId = key.getKeyID();
+ }
+ }
+
+ store.remove(key1.getPublicKey().getFingerprint());
+ assertEquals(RefUpdate.Result.FAST_FORWARD, store.save(newCommitBuilder()));
+
+ assertKeys(masterKeyId);
+ assertKeys(subKeyId);
+ }
+
+ @Test
public void removeNonexisting() throws Exception {
TestKey key1 = validKeyWithoutExpiration();
store.add(key1.getPublicKeyRing());
diff --git a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
index 266f8684fa..67bf0505c3 100644
--- a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
@@ -23,6 +23,7 @@ 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;
@@ -53,7 +54,7 @@ import org.eclipse.jgit.transport.SignedPushConfig;
import org.junit.Before;
import org.junit.Test;
-public class PushCertificateCheckerTest {
+public class PushCertificateCheckerTest extends GerritBaseTests {
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 1c6559b002..e2c58d810f 100644
--- a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
+++ b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
@@ -21,6 +21,7 @@ import static org.easymock.EasyMock.eq;
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;
@@ -35,7 +36,7 @@ import org.easymock.IMocksControl;
import org.junit.Before;
import org.junit.Test;
-public class AllRequestFilterFilterProxyTest {
+public class AllRequestFilterFilterProxyTest extends GerritBaseTests {
/**
* Set of filters for FilterProxy
*
diff --git a/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java b/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java
new file mode 100644
index 0000000000..0d6d482032
--- /dev/null
+++ b/javatests/com/google/gerrit/httpd/AllowRenderInFrameFilterTest.java
@@ -0,0 +1,169 @@
+// 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.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 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;
+
+public class AllowRenderInFrameFilterTest extends GerritBaseTests {
+
+ 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);
+ }
+
+ @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();
+ }
+
+ @Test
+ public void shouldDenyInFrameRenderingWhenCanRenderInFrameIsFalseAndXFormOptionIsSAMEORIGIN()
+ throws IOException, ServletException {
+ 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();
+ }
+
+ @Test
+ public void shouldDenyInFrameRenderingWhenCanRenderInFrameIsFalseAndXFormOptionIsALLOW()
+ throws IOException, ServletException {
+ 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();
+ }
+
+ @Test
+ public void shouldRestrictAccessToSAMEORIGINWhenCanRenderInFrameIsTrue()
+ 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();
+ }
+
+ @Test
+ public void shouldSkipHeaderWhenCanRenderInFrameIsTrueAndXFormOptionIsALLOW()
+ 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();
+ }
+
+ @Test
+ public void shouldRestrictAccessToSAMEORIGINWhenCanRenderInFrameIsTrueAndXFormOptionIsSAMEORIGIN()
+ throws IOException, ServletException {
+ 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();
+ }
+
+ @Test
+ public void shouldIgnoreXFrameOriginCaseSensitivity() throws IOException, ServletException {
+ 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();
+ }
+
+ @Test
+ public void shouldThrowExceptionWhenUnknownXFormOptionValue() {
+ cfg.setBoolean("gerrit", null, "canLoadInIFrame", true);
+ cfg.setString("gerrit", null, "xframeOption", "unsupported value");
+
+ IllegalArgumentException e =
+ assertThrows(IllegalArgumentException.class, () -> new AllowRenderInFrameFilter(cfg));
+ assertThat(e).hasMessageThat().contains("gerrit.xframeOption=unsupported value");
+ }
+}
diff --git a/javatests/com/google/gerrit/httpd/BUILD b/javatests/com/google/gerrit/httpd/BUILD
index d58e816ec9..f2c8ded82a 100644
--- a/javatests/com/google/gerrit/httpd/BUILD
+++ b/javatests/com/google/gerrit/httpd/BUILD
@@ -7,13 +7,14 @@ junit_tests(
"//java/com/google/gerrit/extensions:api",
"//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:gwtorm",
"//lib:jimfs",
"//lib:junit",
- "//lib:servlet-api-3_1-without-neverlink",
+ "//lib:servlet-api-without-neverlink",
"//lib:soy",
"//lib/easymock",
"//lib/guice",
diff --git a/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java b/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
index f012ee34ac..e19085d46e 100644
--- a/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
+++ b/javatests/com/google/gerrit/httpd/RemoteUserUtilTest.java
@@ -17,9 +17,10 @@ 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 {
+public class RemoteUserUtilTest extends GerritBaseTests {
@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 684a2415bd..2de3788992 100644
--- a/javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java
+++ b/javatests/com/google/gerrit/httpd/plugins/ContextMapperTest.java
@@ -16,11 +16,12 @@ 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 {
+public class ContextMapperTest extends GerritBaseTests {
private static final String CONTEXT = "/context";
private static final String PLUGIN_NAME = "my-plugin";
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
index 307a23e5fd..340f690d76 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
@@ -17,11 +17,12 @@ package com.google.gerrit.httpd.raw;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.template.soy.data.SoyMapData;
+import com.google.gerrit.testing.GerritBaseTests;
import java.net.URISyntaxException;
+import java.util.Map;
import org.junit.Test;
-public class IndexServletTest {
+public class IndexServletTest extends GerritBaseTests {
static class TestIndexServlet extends IndexServlet {
private static final long serialVersionUID = 1L;
@@ -37,35 +38,34 @@ public class IndexServletTest {
@Test
public void noPathAndNoCDN() throws URISyntaxException {
- SoyMapData data = IndexServlet.getTemplateData("http://example.com/", null, null);
- assertThat(data.getSingle("canonicalPath").stringValue()).isEqualTo("");
- assertThat(data.getSingle("staticResourcePath").stringValue()).isEqualTo("");
+ Map<String, Object> data = IndexServlet.getTemplateData("http://example.com/", null, null);
+ assertThat(data.get("canonicalPath")).isEqualTo("");
+ assertThat(data.get("staticResourcePath").toString()).isEqualTo("");
}
@Test
public void pathAndNoCDN() throws URISyntaxException {
- SoyMapData data = IndexServlet.getTemplateData("http://example.com/gerrit/", null, null);
- assertThat(data.getSingle("canonicalPath").stringValue()).isEqualTo("/gerrit");
- assertThat(data.getSingle("staticResourcePath").stringValue()).isEqualTo("/gerrit");
+ 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");
}
@Test
public void noPathAndCDN() throws URISyntaxException {
- SoyMapData data =
+ Map<String, Object> data =
IndexServlet.getTemplateData("http://example.com/", "http://my-cdn.com/foo/bar/", null);
- assertThat(data.getSingle("canonicalPath").stringValue()).isEqualTo("");
- assertThat(data.getSingle("staticResourcePath").stringValue())
- .isEqualTo("http://my-cdn.com/foo/bar/");
+ assertThat(data.get("canonicalPath")).isEqualTo("");
+ assertThat(data.get("staticResourcePath").toString()).isEqualTo("http://my-cdn.com/foo/bar/");
}
@Test
public void pathAndCDN() throws URISyntaxException {
- SoyMapData data =
+ Map<String, Object> data =
IndexServlet.getTemplateData(
"http://example.com/gerrit", "http://my-cdn.com/foo/bar/", null);
- assertThat(data.getSingle("canonicalPath").stringValue()).isEqualTo("/gerrit");
- assertThat(data.getSingle("staticResourcePath").stringValue())
- .isEqualTo("http://my-cdn.com/foo/bar/");
+ assertThat(data.get("canonicalPath")).isEqualTo("/gerrit");
+ assertThat(data.get("staticResourcePath").toString()).isEqualTo("http://my-cdn.com/foo/bar/");
}
@Test
diff --git a/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java b/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java
index 6dd15bccc4..cfcc1d0984 100644
--- a/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/ResourceServletTest.java
@@ -28,6 +28,7 @@ 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;
@@ -44,7 +45,7 @@ import java.util.zip.GZIPInputStream;
import org.junit.Before;
import org.junit.Test;
-public class ResourceServletTest {
+public class ResourceServletTest extends GerritBaseTests {
private static Cache<Path, Resource> newCache(int size) {
return CacheBuilder.newBuilder().maximumSize(size).recordStats().build();
}
diff --git a/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java b/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java
index fb1ebd9551..fa3eaea5e2 100644
--- a/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java
+++ b/javatests/com/google/gerrit/httpd/restapi/HttpLogRedactTest.java
@@ -16,9 +16,10 @@ 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 {
+public class HttpLogRedactTest extends GerritBaseTests {
@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 13732b071f..30d318b654 100644
--- a/javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java
+++ b/javatests/com/google/gerrit/httpd/restapi/ParameterParserTest.java
@@ -22,13 +22,14 @@ 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 {
+public class ParameterParserTest extends GerritBaseTests {
@Test
public void convertFormToJson() throws BadRequestException {
JsonObject obj =
diff --git a/javatests/com/google/gerrit/index/BUILD b/javatests/com/google/gerrit/index/BUILD
index 5597ed1bb3..a1f60de66b 100644
--- a/javatests/com/google/gerrit/index/BUILD
+++ b/javatests/com/google/gerrit/index/BUILD
@@ -9,6 +9,8 @@ junit_tests(
"//antlr3:query_parser",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
+ "//java/com/google/gerrit/index/query/testing",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
"//lib:junit",
"//lib/antlr:java-runtime",
diff --git a/javatests/com/google/gerrit/index/SchemaUtilTest.java b/javatests/com/google/gerrit/index/SchemaUtilTest.java
index 3c0bbe0e82..d6b8421c3a 100644
--- a/javatests/com/google/gerrit/index/SchemaUtilTest.java
+++ b/javatests/com/google/gerrit/index/SchemaUtilTest.java
@@ -19,15 +19,12 @@ 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 com.google.gerrit.testing.GerritBaseTests;
import java.util.Map;
import org.eclipse.jgit.lib.PersonIdent;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class SchemaUtilTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class SchemaUtilTest extends GerritBaseTests {
static class TestSchemas {
static final Schema<String> V1 = schema();
static final Schema<String> V2 = schema();
diff --git a/javatests/com/google/gerrit/index/query/FieldPredicateTest.java b/javatests/com/google/gerrit/index/query/FieldPredicateTest.java
index 8fe90fceeb..805f31c194 100644
--- a/javatests/com/google/gerrit/index/query/FieldPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/FieldPredicateTest.java
@@ -58,7 +58,7 @@ public class FieldPredicateTest extends PredicateTest {
@Test
public void testCopy() {
final OperatorPredicate<String> f = f("author", "alice");
- assertSame(f, f.copy(Collections.<Predicate<String>>emptyList()));
+ assertSame(f, f.copy(Collections.emptyList()));
assertSame(f, f.copy(f.getChildren()));
exception.expect(IllegalArgumentException.class);
diff --git a/javatests/com/google/gerrit/index/query/NotPredicateTest.java b/javatests/com/google/gerrit/index/query/NotPredicateTest.java
index 74eac6153c..d10d2df498 100644
--- a/javatests/com/google/gerrit/index/query/NotPredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/NotPredicateTest.java
@@ -113,7 +113,7 @@ public class NotPredicateTest extends PredicateTest {
assertEquals(sb, n.copy(sb).getChildren());
try {
- n.copy(Collections.<Predicate>emptyList());
+ n.copy(Collections.emptyList());
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertEquals("Expected exactly one child", e.getMessage());
diff --git a/javatests/com/google/gerrit/index/query/PredicateTest.java b/javatests/com/google/gerrit/index/query/PredicateTest.java
index 6979d826d7..2295a60004 100644
--- a/javatests/com/google/gerrit/index/query/PredicateTest.java
+++ b/javatests/com/google/gerrit/index/query/PredicateTest.java
@@ -14,14 +14,11 @@
package com.google.gerrit.index.query;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.rules.ExpectedException;
@Ignore
-public abstract class PredicateTest {
- @Rule public ExpectedException exception = ExpectedException.none();
-
+public abstract class PredicateTest extends GerritBaseTests {
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
new file mode 100644
index 0000000000..6a397dca89
--- /dev/null
+++ b/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
@@ -0,0 +1,123 @@
+// 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.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 {
+ private static class TestPredicate extends Predicate<Object> {
+ private final String field;
+ private final String value;
+
+ TestPredicate(String field, String value) {
+ this.field = field;
+ this.value = value;
+ }
+
+ @Override
+ public Predicate<Object> copy(Collection<? extends Predicate<Object>> children) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(field, value);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof TestPredicate)) {
+ return false;
+ }
+ TestPredicate p = (TestPredicate) o;
+ return Objects.equals(field, p.field) && Objects.equals(value, p.value);
+ }
+ }
+
+ private static class TestQueryBuilder extends QueryBuilder<Object, TestQueryBuilder> {
+ TestQueryBuilder() {
+ super(new QueryBuilder.Definition<>(TestQueryBuilder.class), null);
+ }
+
+ @Operator
+ public Predicate<Object> a(String value) {
+ return new TestPredicate("a", value);
+ }
+ }
+
+ @Test
+ public void fieldNameAndValue() throws Exception {
+ assertThat(parse("a:foo")).isEqualTo(new TestPredicate("a", "foo"));
+ }
+
+ @Test
+ public void fieldWithParenthesizedValues() throws Exception {
+ assertThatParseException("a:(foo bar)").hasMessageThat().contains("no viable alternative");
+ }
+
+ @Test
+ public void fieldNameAndValueThatLooksLikeFieldNameColonValue() throws Exception {
+ assertThat(parse("a:foo:bar")).isEqualTo(new TestPredicate("a", "foo:bar"));
+ }
+
+ @Test
+ public void fieldNameAndValueThatLooksLikeWordColonValue() throws Exception {
+ assertThat(parse("a:*:bar")).isEqualTo(new TestPredicate("a", "*:bar"));
+ }
+
+ @Test
+ public void fieldNameAndValueWithMultipleColons() throws Exception {
+ assertThat(parse("a:*:*:*")).isEqualTo(new TestPredicate("a", "*:*:*"));
+ }
+
+ @Test
+ public void exactPhraseWithQuotes() throws Exception {
+ assertThat(parse("a:\"foo bar\"")).isEqualTo(new TestPredicate("a", "foo bar"));
+ }
+
+ @Test
+ public void exactPhraseWithQuotesAndColon() throws Exception {
+ assertThat(parse("a:\"foo:bar\"")).isEqualTo(new TestPredicate("a", "foo:bar"));
+ }
+
+ @Test
+ public void exactPhraseWithBraces() throws Exception {
+ assertThat(parse("a:{foo bar}")).isEqualTo(new TestPredicate("a", "foo bar"));
+ }
+
+ @Test
+ public void exactPhraseWithBracesAndColon() throws Exception {
+ assertThat(parse("a:{foo:bar}")).isEqualTo(new TestPredicate("a", "foo:bar"));
+ }
+
+ private static Predicate<Object> parse(String query) throws Exception {
+ return new TestQueryBuilder().parse(query);
+ }
+
+ private static ThrowableSubject assertThatParseException(String query) {
+ try {
+ new TestQueryBuilder().parse(query);
+ throw new AssertionError("expected QueryParseException for " + query);
+ } catch (QueryParseException e) {
+ return assertThat(e);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/index/query/QueryParserTest.java b/javatests/com/google/gerrit/index/query/QueryParserTest.java
index 448f292ef6..b4dd1ee0c1 100644
--- a/javatests/com/google/gerrit/index/query/QueryParserTest.java
+++ b/javatests/com/google/gerrit/index/query/QueryParserTest.java
@@ -14,34 +14,203 @@
package com.google.gerrit.index.query;
-import static org.junit.Assert.assertEquals;
+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;
+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 com.google.gerrit.testing.GerritBaseTests;
import org.antlr.runtime.tree.Tree;
import org.junit.Test;
-public class QueryParserTest {
+public class QueryParserTest extends GerritBaseTests {
@Test
- public void projectBare() throws QueryParseException {
- Tree r;
+ public void fieldNameAndValue() throws Exception {
+ Tree r = parse("project:tools/gerrit");
+ assertThat(r).hasType(FIELD_NAME);
+ assertThat(r).hasText("project");
+ assertThat(r).hasChildCount(1);
+ assertThat(r).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).hasText("tools/gerrit");
+ assertThat(r).child(0).hasNoChildren();
+ }
+
+ @Test
+ public void fieldNameAndValueThatLooksLikeFieldNameColon() throws Exception {
+ // This should work, but doesn't due to a known issue.
+ assertParseFails("project:foo:");
+ }
- r = parse("project:tools/gerrit");
- assertSingleWord("project", "tools/gerrit", r);
+ @Test
+ public void fieldNameAndValueThatLooksLikeFieldNameColonValue() throws Exception {
+ Tree r = parse("project:foo:bar");
+ assertThat(r).hasType(FIELD_NAME);
+ assertThat(r).hasText("project");
+ assertThat(r).hasChildCount(3);
+ assertThat(r).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).hasText("foo");
+ assertThat(r).child(0).hasNoChildren();
+ assertThat(r).child(1).hasType(COLON);
+ assertThat(r).child(1).hasText(":");
+ assertThat(r).child(1).hasNoChildren();
+ assertThat(r).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(2).hasText("bar");
+ assertThat(r).child(2).hasNoChildren();
+ }
- r = parse("project:tools/*");
- assertSingleWord("project", "tools/*", r);
+ @Test
+ public void fieldNameAndValueThatLooksLikeWordColonValue() throws Exception {
+ Tree r = parse("project:x*y:a*b");
+ assertThat(r).hasType(FIELD_NAME);
+ assertThat(r).hasText("project");
+ assertThat(r).hasChildCount(3);
+ assertThat(r).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).hasText("x*y");
+ assertThat(r).child(0).hasNoChildren();
+ assertThat(r).child(1).hasType(COLON);
+ assertThat(r).child(1).hasText(":");
+ assertThat(r).child(1).hasNoChildren();
+ assertThat(r).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(2).hasText("a*b");
+ assertThat(r).child(2).hasNoChildren();
}
- private static void assertSingleWord(String name, String value, Tree r) {
- assertEquals(QueryParser.FIELD_NAME, r.getType());
- assertEquals(name, r.getText());
- assertEquals(1, r.getChildCount());
- final Tree c = r.getChild(0);
- assertEquals(QueryParser.SINGLE_WORD, c.getType());
- assertEquals(value, c.getText());
- assertEquals(0, c.getChildCount());
+ @Test
+ public void fieldNameAndValueThatLooksLikeWordColon() throws Exception {
+ // This should work, but doesn't due to a known issue.
+ assertParseFails("project:x*y:");
+ }
+
+ @Test
+ public void fieldNameAndValueWithMultipleColons() throws Exception {
+ Tree r = parse("project:*:*:*");
+ assertThat(r).hasType(FIELD_NAME);
+ assertThat(r).hasText("project");
+ assertThat(r).hasChildCount(5);
+ assertThat(r).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).hasText("*");
+ assertThat(r).child(0).hasNoChildren();
+ assertThat(r).child(1).hasType(COLON);
+ assertThat(r).child(1).hasText(":");
+ assertThat(r).child(1).hasNoChildren();
+ assertThat(r).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(2).hasText("*");
+ assertThat(r).child(2).hasNoChildren();
+ assertThat(r).child(3).hasType(COLON);
+ assertThat(r).child(3).hasText(":");
+ assertThat(r).child(3).hasNoChildren();
+ assertThat(r).child(4).hasType(SINGLE_WORD);
+ assertThat(r).child(4).hasText("*");
+ assertThat(r).child(4).hasNoChildren();
+ }
+
+ @Test
+ public void fieldNameAndValueWithColonFollowedByAnotherField() throws Exception {
+ Tree r = parse("project:foo:bar file:baz");
+ assertThat(r).hasType(AND);
+ assertThat(r).hasChildCount(2);
+
+ assertThat(r).child(0).hasType(FIELD_NAME);
+ assertThat(r).child(0).hasText("project");
+ assertThat(r).child(0).hasChildCount(3);
+ assertThat(r).child(0).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).child(0).hasText("foo");
+ assertThat(r).child(0).child(0).hasNoChildren();
+ assertThat(r).child(0).child(1).hasType(COLON);
+ assertThat(r).child(0).child(1).hasText(":");
+ assertThat(r).child(0).child(1).hasNoChildren();
+ assertThat(r).child(0).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(0).child(2).hasText("bar");
+ assertThat(r).child(0).child(2).hasNoChildren();
+
+ assertThat(r).child(1).hasType(FIELD_NAME);
+ assertThat(r).child(1).hasText("file");
+ assertThat(r).child(1).hasChildCount(1);
+ assertThat(r).child(1).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(1).child(0).hasText("baz");
+ assertThat(r).child(1).child(0).hasNoChildren();
+ }
+
+ @Test
+ public void fieldNameAndValueWithColonFollowedByOpenParen() throws Exception {
+ Tree r = parse("project:foo:bar (file:baz)");
+ assertThat(r).hasType(AND);
+ assertThat(r).hasChildCount(2);
+
+ assertThat(r).child(0).hasType(FIELD_NAME);
+ assertThat(r).child(0).hasText("project");
+ assertThat(r).child(0).hasChildCount(3);
+ assertThat(r).child(0).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).child(0).hasText("foo");
+ assertThat(r).child(0).child(0).hasNoChildren();
+ assertThat(r).child(0).child(1).hasType(COLON);
+ assertThat(r).child(0).child(1).hasText(":");
+ assertThat(r).child(0).child(1).hasNoChildren();
+ assertThat(r).child(0).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(0).child(2).hasText("bar");
+ assertThat(r).child(0).child(2).hasNoChildren();
+
+ assertThat(r).child(1).hasType(FIELD_NAME);
+ assertThat(r).child(1).hasText("file");
+ assertThat(r).child(1).hasChildCount(1);
+ assertThat(r).child(1).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(1).child(0).hasText("baz");
+ assertThat(r).child(1).child(0).hasNoChildren();
+ }
+
+ @Test
+ public void fieldNameAndValueWithColonFollowedByCloseParen() throws Exception {
+ Tree r = parse("(project:foo:bar) file:baz");
+ assertThat(r).hasType(AND);
+ assertThat(r).hasChildCount(2);
+
+ assertThat(r).child(0).hasType(FIELD_NAME);
+ assertThat(r).child(0).hasText("project");
+ assertThat(r).child(0).hasChildCount(3);
+ assertThat(r).child(0).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).child(0).hasText("foo");
+ assertThat(r).child(0).child(0).hasNoChildren();
+ assertThat(r).child(0).child(1).hasType(COLON);
+ assertThat(r).child(0).child(1).hasText(":");
+ assertThat(r).child(0).child(1).hasNoChildren();
+ assertThat(r).child(0).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(0).child(2).hasText("bar");
+ assertThat(r).child(0).child(2).hasNoChildren();
+
+ assertThat(r).child(1).hasType(FIELD_NAME);
+ assertThat(r).child(1).hasText("file");
+ assertThat(r).child(1).hasChildCount(1);
+ assertThat(r).child(1).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(1).child(0).hasText("baz");
+ assertThat(r).child(1).child(0).hasNoChildren();
+ }
+
+ @Test
+ public void defaultFieldWithColon() throws Exception {
+ Tree r = parse("CodeReview:+2");
+ assertThat(r).hasType(DEFAULT_FIELD);
+ assertThat(r).hasChildCount(3);
+ assertThat(r).child(0).hasType(SINGLE_WORD);
+ assertThat(r).child(0).hasText("CodeReview");
+ assertThat(r).child(0).hasNoChildren();
+ assertThat(r).child(1).hasType(COLON);
+ assertThat(r).child(1).hasText(":");
+ assertThat(r).child(1).hasNoChildren();
+ assertThat(r).child(2).hasType(SINGLE_WORD);
+ assertThat(r).child(2).hasText("+2");
+ assertThat(r).child(2).hasNoChildren();
}
- private static Tree parse(String str) throws QueryParseException {
- return QueryParser.parse(str);
+ private static void assertParseFails(String query) {
+ try {
+ parse(query);
+ assert_().fail("expected parse to fail: %s", query);
+ } catch (QueryParseException e) {
+ // Expected.
+ }
}
}
diff --git a/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java b/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java
index e7a1a68418..18a78b0664 100644
--- a/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java
+++ b/javatests/com/google/gerrit/integration/git/UploadArchiveIT.java
@@ -22,6 +22,7 @@ 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.GerritConfig;
+import com.google.gerrit.acceptance.GerritServer.TestSshServerAddress;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.StandaloneSiteTest;
import com.google.gerrit.acceptance.UseSsh;
@@ -35,6 +36,7 @@ import com.google.inject.Inject;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
@@ -57,7 +59,10 @@ public class UploadArchiveIT extends StandaloneSiteTest {
private static final String GIT_SSH_COMMAND =
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o 'IdentitiesOnly yes' -i";
private static final String ARCHIVE = "archive";
+
@Inject private GerritApi gApi;
+ @Inject private @TestSshServerAddress InetSocketAddress sshAddress;
+
private String sshDestination;
private String identityPath;
private Project.NameKey project;
@@ -134,10 +139,7 @@ public class UploadArchiveIT extends StandaloneSiteTest {
sshDestination =
String.format(
"ssh://%s@%s:%s/%s",
- "admin",
- ctx.getServer().getSshdAddress().getHostName(),
- ctx.getServer().getSshdAddress().getPort(),
- project.get());
+ "admin", sshAddress.getHostName(), sshAddress.getPort(), project.get());
identityPath = sitePaths.data_dir.resolve(String.format("id_rsa_%s", "admin")).toString();
}
diff --git a/javatests/com/google/gerrit/json/BUILD b/javatests/com/google/gerrit/json/BUILD
new file mode 100644
index 0000000000..4894cdb466
--- /dev/null
+++ b/javatests/com/google/gerrit/json/BUILD
@@ -0,0 +1,11 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "json_tests",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/json",
+ "//lib:guava",
+ "//lib/truth",
+ ],
+)
diff --git a/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java b/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java
new file mode 100644
index 0000000000..a8488a96eb
--- /dev/null
+++ b/javatests/com/google/gerrit/json/JavaSqlTimestampHelperTest.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2014 Google Inc. All Rights Reserved.
+//
+// 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 static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.json.JavaSqlTimestampHelper.parseTimestamp;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class JavaSqlTimestampHelperTest {
+ private SimpleDateFormat format;
+ private TimeZone systemTimeZone;
+
+ @Before
+ public void setUp() throws Exception {
+ synchronized (TimeZone.class) {
+ systemTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT-5:00"));
+ format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
+ }
+ }
+
+ @After
+ public void resetTimeZone() {
+ TimeZone.setDefault(systemTimeZone);
+ }
+
+ @Test
+ public void parseFullTimestamp() {
+ assertThat(reformat("2006-01-02 20:04:05.789000000"))
+ .isEqualTo("2006-01-02 15:04:05.789 -0500");
+ assertThat(reformat("2006-01-02 20:04:05")).isEqualTo("2006-01-02 15:04:05.000 -0500");
+ }
+
+ @Test
+ public void parseDateOnly() {
+ assertThat(reformat("2006-01-02")).isEqualTo("2006-01-01 19:00:00.000 -0500");
+ }
+
+ @Test
+ public void parseTimeZone() {
+ assertThat(reformat("2006-01-02 15:04:05.789 -0100"))
+ .isEqualTo("2006-01-02 11:04:05.789 -0500");
+ assertThat(reformat("2006-01-02 15:04:05.789 -0000"))
+ .isEqualTo("2006-01-02 10:04:05.789 -0500");
+ assertThat(reformat("2006-01-02 15:04:05.789 +0100"))
+ .isEqualTo("2006-01-02 09:04:05.789 -0500");
+ }
+
+ @Test
+ public void parseInvalidTimestamps() {
+ assertInvalid("2006-01-02-15:04:05.789000000");
+ assertInvalid("2006-01-02T15:04:05.789000000");
+ assertInvalid("15:04:05");
+ assertInvalid("15:04:05.999000000");
+ }
+
+ private static void assertInvalid(String input) {
+ try {
+ parseTimestamp(input);
+ assert_().fail("Expected IllegalArgumentException for: " + input);
+ } catch (IllegalArgumentException e) {
+ // Expected;
+ }
+ }
+
+ private String reformat(String input) {
+ return format.format(parseTimestamp(input));
+ }
+}
diff --git a/javatests/com/google/gerrit/mail/AbstractParserTest.java b/javatests/com/google/gerrit/mail/AbstractParserTest.java
index 6ab4ca2a2a..bcff6a75b2 100644
--- a/javatests/com/google/gerrit/mail/AbstractParserTest.java
+++ b/javatests/com/google/gerrit/mail/AbstractParserTest.java
@@ -18,6 +18,7 @@ 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 java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
@@ -25,7 +26,7 @@ import java.util.List;
import org.junit.Ignore;
@Ignore
-public class AbstractParserTest {
+public class AbstractParserTest extends GerritBaseTests {
protected static final String CHANGE_URL =
"https://gerrit-review.googlesource.com/c/project/+/123";
@@ -40,7 +41,7 @@ public class AbstractParserTest {
String message, MailComment comment, Comment inReplyTo) {
assertThat(comment.fileName).isNull();
assertThat(comment.message).isEqualTo(message);
- assertThat(comment.inReplyTo).isEqualTo(inReplyTo);
+ assertThat(comment.inReplyTo.key).isEqualTo(inReplyTo.key);
assertThat(comment.type).isEqualTo(MailComment.CommentType.INLINE_COMMENT);
}
diff --git a/javatests/com/google/gerrit/mail/BUILD b/javatests/com/google/gerrit/mail/BUILD
index 04feeac2d7..b1c97120d0 100644
--- a/javatests/com/google/gerrit/mail/BUILD
+++ b/javatests/com/google/gerrit/mail/BUILD
@@ -13,7 +13,6 @@ junit_tests(
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:gson",
"//lib:guava-retrying",
- "//lib:gwtorm",
"//lib/commons:codec",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
diff --git a/javatests/com/google/gerrit/mail/MailHeaderParserTest.java b/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
index 2d2c2ea778..cdc8d7ad6e 100644
--- a/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
+++ b/javatests/com/google/gerrit/mail/MailHeaderParserTest.java
@@ -16,13 +16,14 @@ 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 {
+public class MailHeaderParserTest extends GerritBaseTests {
@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 47a53673f8..ed40a57701 100644
--- a/javatests/com/google/gerrit/mail/ParserUtilTest.java
+++ b/javatests/com/google/gerrit/mail/ParserUtilTest.java
@@ -16,9 +16,10 @@ 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 {
+public class ParserUtilTest extends GerritBaseTests {
@Test
public void trimQuotationLineOnMessageWithoutQuoatationLine() throws Exception {
assertThat(ParserUtil.trimQuotation("One line")).isEqualTo("One line");
diff --git a/javatests/com/google/gerrit/metrics/dropwizard/BUILD b/javatests/com/google/gerrit/metrics/dropwizard/BUILD
index 98d12b2e7b..63d4452193 100644
--- a/javatests/com/google/gerrit/metrics/dropwizard/BUILD
+++ b/javatests/com/google/gerrit/metrics/dropwizard/BUILD
@@ -7,6 +7,7 @@ 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 9b21bf6c01..d6bcb625a8 100644
--- a/javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java
+++ b/javatests/com/google/gerrit/metrics/dropwizard/DropWizardMetricMakerTest.java
@@ -16,9 +16,10 @@ 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 {
+public class DropWizardMetricMakerTest extends GerritBaseTests {
DropWizardMetricMaker metrics =
new DropWizardMetricMaker(null /* MetricRegistry unused in tests */);
diff --git a/javatests/com/google/gerrit/metrics/proc/BUILD b/javatests/com/google/gerrit/metrics/proc/BUILD
index 91e5cf61dc..7d8af90df2 100644
--- a/javatests/com/google/gerrit/metrics/proc/BUILD
+++ b/javatests/com/google/gerrit/metrics/proc/BUILD
@@ -9,6 +9,7 @@ junit_tests(
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/metrics/dropwizard",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib/dropwizard:dropwizard-core",
"//lib/guice",
"//lib/truth",
diff --git a/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java b/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java
index 91b01f6ab1..0a5dabf68a 100644
--- a/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java
+++ b/javatests/com/google/gerrit/metrics/proc/ProcMetricModuleTest.java
@@ -30,19 +30,16 @@ 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;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class ProcMetricModuleTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class ProcMetricModuleTest extends GerritBaseTests {
@Inject MetricMaker metrics;
@Inject MetricRegistry registry;
diff --git a/javatests/com/google/gerrit/pgm/BUILD b/javatests/com/google/gerrit/pgm/BUILD
index 3fc1d1d58b..e82b8848a3 100644
--- a/javatests/com/google/gerrit/pgm/BUILD
+++ b/javatests/com/google/gerrit/pgm/BUILD
@@ -10,6 +10,8 @@ junit_tests(
"//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:junit",
"//lib/easymock",
diff --git a/javatests/com/google/gerrit/pgm/init/InitTestCase.java b/javatests/com/google/gerrit/pgm/init/InitTestCase.java
deleted file mode 100644
index 35c093720f..0000000000
--- a/javatests/com/google/gerrit/pgm/init/InitTestCase.java
+++ /dev/null
@@ -1,27 +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.pgm.init;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
-import org.junit.Ignore;
-
-@Ignore
-public abstract class InitTestCase extends LocalDiskRepositoryTestCase {
- protected Path newSitePath() throws IOException {
- return createWorkRepository().getWorkTree().toPath().resolve("test_site");
- }
-}
diff --git a/javatests/com/google/gerrit/pgm/init/LibrariesTest.java b/javatests/com/google/gerrit/pgm/init/LibrariesTest.java
deleted file mode 100644
index af21ad0762..0000000000
--- a/javatests/com/google/gerrit/pgm/init/LibrariesTest.java
+++ /dev/null
@@ -1,53 +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.pgm.init;
-
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertNotNull;
-
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Provider;
-import java.nio.file.Paths;
-import java.util.Collections;
-import org.junit.Test;
-
-public class LibrariesTest {
- @Test
- public void create() throws Exception {
- final SitePaths site = new SitePaths(Paths.get("."));
- final ConsoleUI ui = createStrictMock(ConsoleUI.class);
- final StaleLibraryRemover remover = createStrictMock(StaleLibraryRemover.class);
-
- replay(ui);
-
- Libraries lib =
- new Libraries(
- new Provider<LibraryDownloader>() {
- @Override
- public LibraryDownloader get() {
- return new LibraryDownloader(ui, site, remover);
- }
- },
- Collections.<String>emptyList(),
- false);
-
- assertNotNull(lib.mysqlDriver);
-
- verify(ui);
- }
-}
diff --git a/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java b/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java
new file mode 100644
index 0000000000..a34007edbf
--- /dev/null
+++ b/javatests/com/google/gerrit/pgm/init/api/AllProjectsConfigTest.java
@@ -0,0 +1,114 @@
+// 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.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;
+import com.google.gerrit.server.securestore.testing.InMemorySecureStore;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+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.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class AllProjectsConfigTest {
+ private static final String ALL_PROJECTS = "All-The-Projects";
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private SitePaths sitePaths;
+ private AllProjectsConfig allProjectsConfig;
+ private File allProjectsRepoFile;
+
+ @Before
+ public void setUp() throws Exception {
+ sitePaths = new SitePaths(temporaryFolder.newFolder().toPath());
+ Files.createDirectories(sitePaths.etc_dir);
+
+ Path gitPath = sitePaths.resolve("git");
+
+ StoredConfig gerritConfig =
+ new FileBasedConfig(
+ sitePaths.resolve("etc").resolve("gerrit.config").toFile(), FS.DETECTED);
+ gerritConfig.load();
+ gerritConfig.setString("gerrit", null, "basePath", gitPath.toAbsolutePath().toString());
+ gerritConfig.setString("gerrit", null, "allProjects", ALL_PROJECTS);
+ gerritConfig.save();
+
+ Files.createDirectories(sitePaths.resolve("git"));
+ allProjectsRepoFile = gitPath.resolve("All-The-Projects.git").toFile();
+ try (Repository repo = new FileRepository(allProjectsRepoFile)) {
+ repo.create(true);
+ }
+
+ 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 =
+ new AllProjectsConfig(new AllProjectsNameOnInitProvider(sections), sitePaths, flags);
+ }
+
+ @Test
+ public void noBaseConfig() throws Exception {
+ assertThat(getConfig().getString("foo", null, "bar")).isNull();
+
+ try (Repository repo = new FileRepository(allProjectsRepoFile);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch("refs/meta/config").commit().add("project.config", "[foo]\nbar = baz").create();
+ }
+
+ assertThat(getConfig().getString("foo", null, "bar")).isEqualTo("baz");
+ }
+
+ @Test
+ public void baseConfig() throws Exception {
+ assertThat(getConfig().getString("foo", null, "bar")).isNull();
+
+ Path baseConfigPath = sitePaths.etc_dir.resolve(ALL_PROJECTS).resolve("project.config");
+ Files.createDirectories(baseConfigPath.getParent());
+ Files.write(baseConfigPath, ImmutableList.of("[foo]", "bar = base"));
+
+ assertThat(getConfig().getString("foo", null, "bar")).isEqualTo("base");
+
+ try (Repository repo = new FileRepository(allProjectsRepoFile);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch("refs/meta/config").commit().add("project.config", "[foo]\nbar = baz").create();
+ }
+
+ assertThat(getConfig().getString("foo", null, "bar")).isEqualTo("baz");
+ }
+
+ private Config getConfig() throws IOException, ConfigInvalidException {
+ return allProjectsConfig.load().getConfig();
+ }
+}
diff --git a/javatests/com/google/gerrit/proto/BUILD b/javatests/com/google/gerrit/proto/BUILD
index 0940f6b333..6b98b72a50 100644
--- a/javatests/com/google/gerrit/proto/BUILD
+++ b/javatests/com/google/gerrit/proto/BUILD
@@ -4,14 +4,11 @@ junit_tests(
name = "proto_tests",
srcs = glob(["*.java"]),
deps = [
- "//lib/truth:truth-proto-extension",
- "//proto:reviewdb_java_proto",
-
- # TODO(dborowitz): These are already runtime_deps of
- # truth-proto-extension, but either omitting them or adding them as
- # runtime_deps to this target fails with:
- # class file for com.google.common.collect.Multimap not found
- "//lib:guava",
+ "//java/com/google/gerrit/proto",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:protobuf",
"//lib/truth",
+ "//lib/truth:truth-proto-extension",
+ "//proto:cache_java_proto",
],
)
diff --git a/javatests/com/google/gerrit/proto/ProtosTest.java b/javatests/com/google/gerrit/proto/ProtosTest.java
new file mode 100644
index 0000000000..29e8fe073b
--- /dev/null
+++ b/javatests/com/google/gerrit/proto/ProtosTest.java
@@ -0,0 +1,156 @@
+// 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.proto;
+
+import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+
+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 {
+ @Test
+ public void parseUncheckedByteArrayWrongProtoType() {
+ ChangeNotesKeyProto proto =
+ ChangeNotesKeyProto.newBuilder()
+ .setProject("project")
+ .setChangeId(1234)
+ .setId(ByteString.copyFromUtf8("foo"))
+ .build();
+ byte[] bytes = Protos.toByteArray(proto);
+ try {
+ Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes);
+ assert_().fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void parseUncheckedByteArrayInvalidData() {
+ byte[] bytes = new byte[] {0x00};
+ try {
+ Protos.parseUnchecked(ChangeNotesStateProto.parser(), bytes);
+ assert_().fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void parseUncheckedByteArray() {
+ ChangeNotesKeyProto proto =
+ ChangeNotesKeyProto.newBuilder()
+ .setProject("project")
+ .setChangeId(1234)
+ .setId(ByteString.copyFromUtf8("foo"))
+ .build();
+ byte[] bytes = Protos.toByteArray(proto);
+ assertThat(Protos.parseUnchecked(ChangeNotesKeyProto.parser(), bytes)).isEqualTo(proto);
+ }
+
+ @Test
+ public void parseUncheckedSegmentOfByteArrayWrongProtoType() {
+ ChangeNotesKeyProto proto =
+ ChangeNotesKeyProto.newBuilder()
+ .setProject("project")
+ .setChangeId(1234)
+ .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.
+ }
+ }
+
+ @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.
+ }
+ }
+
+ @Test
+ public void parseUncheckedSegmentOfByteArray() {
+ ChangeNotesKeyProto proto =
+ ChangeNotesKeyProto.newBuilder()
+ .setProject("project")
+ .setChangeId(1234)
+ .setId(ByteString.copyFromUtf8("foo"))
+ .build();
+ byte[] protoBytes = Protos.toByteArray(proto);
+ int offset = 3;
+ int length = protoBytes.length;
+ byte[] bytes = new byte[length + 20];
+ Arrays.fill(bytes, (byte) 1);
+ System.arraycopy(protoBytes, 0, bytes, offset, length);
+
+ ChangeNotesKeyProto parsedProto =
+ Protos.parseUnchecked(ChangeNotesKeyProto.parser(), bytes, offset, length);
+
+ assertThat(parsedProto).isEqualTo(proto);
+ }
+
+ @Test
+ public void parseUncheckedByteStringWrongProtoType() {
+ ChangeNotesKeyProto proto =
+ ChangeNotesKeyProto.newBuilder()
+ .setProject("project")
+ .setChangeId(1234)
+ .setId(ByteString.copyFromUtf8("foo"))
+ .build();
+ ByteString byteString = Protos.toByteString(proto);
+ try {
+ Protos.parseUnchecked(ChangeNotesStateProto.parser(), byteString);
+ assert_().fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ }
+
+ @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.
+ }
+ }
+
+ @Test
+ public void parseUncheckedByteString() {
+ ChangeNotesKeyProto proto =
+ ChangeNotesKeyProto.newBuilder()
+ .setProject("project")
+ .setChangeId(1234)
+ .setId(ByteString.copyFromUtf8("foo"))
+ .build();
+ ByteString byteString = Protos.toByteString(proto);
+ assertThat(Protos.parseUnchecked(ChangeNotesKeyProto.parser(), byteString)).isEqualTo(proto);
+ }
+}
diff --git a/javatests/com/google/gerrit/proto/ReviewDbProtoTest.java b/javatests/com/google/gerrit/proto/ReviewDbProtoTest.java
deleted file mode 100644
index 49cd292164..0000000000
--- a/javatests/com/google/gerrit/proto/ReviewDbProtoTest.java
+++ /dev/null
@@ -1,31 +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.proto;
-
-import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-
-import com.google.gerrit.proto.reviewdb.Reviewdb.Change;
-import com.google.gerrit.proto.reviewdb.Reviewdb.Change_Id;
-import org.junit.Test;
-
-public class ReviewDbProtoTest {
- @Test
- public void generatedProtoApi() {
- Change c1 = Change.newBuilder().setChangeId(Change_Id.newBuilder().setId(1234).build()).build();
- Change c2 = Change.newBuilder().setChangeId(Change_Id.newBuilder().setId(5678).build()).build();
- assertThat(c1).isEqualTo(c1);
- assertThat(c1).isNotEqualTo(c2);
- }
-}
diff --git a/javatests/com/google/gerrit/reviewdb/BUILD b/javatests/com/google/gerrit/reviewdb/BUILD
deleted file mode 100644
index f93026219a..0000000000
--- a/javatests/com/google/gerrit/reviewdb/BUILD
+++ /dev/null
@@ -1,13 +0,0 @@
-load("//tools/bzl:junit.bzl", "junit_tests")
-
-junit_tests(
- name = "client_tests",
- srcs = glob(["client/**/*.java"]),
- deps = [
- "//java/com/google/gerrit/reviewdb:client",
- "//java/com/google/gerrit/testing:gerrit-test-util",
- "//lib:guava",
- "//lib:gwtorm",
- "//lib/truth",
- ],
-)
diff --git a/javatests/com/google/gerrit/reviewdb/client/BUILD b/javatests/com/google/gerrit/reviewdb/client/BUILD
new file mode 100644
index 0000000000..bc993d53e1
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/client/BUILD
@@ -0,0 +1,13 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "client_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/testing:gerrit-test-util",
+ "//lib:guava",
+ "//lib/truth",
+ ],
+)
diff --git a/javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java
new file mode 100644
index 0000000000..123a973ede
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/AccountIdProtoConverterTest.java
@@ -0,0 +1,67 @@
+// 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.Account;
+import com.google.protobuf.Parser;
+import org.junit.Test;
+
+public class AccountIdProtoConverterTest {
+ private final AccountIdProtoConverter accountIdProtoConverter = AccountIdProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ Account.Id accountId = new Account.Id(24);
+
+ Entities.Account_Id proto = accountIdProtoConverter.toProto(accountId);
+
+ Entities.Account_Id expectedProto = Entities.Account_Id.newBuilder().setId(24).build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ Account.Id accountId = new Account.Id(34832);
+
+ Account.Id convertedAccountId =
+ accountIdProtoConverter.fromProto(accountIdProtoConverter.toProto(accountId));
+
+ assertThat(convertedAccountId).isEqualTo(accountId);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.Account_Id proto = Entities.Account_Id.newBuilder().setId(24).build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.Account_Id> parser = accountIdProtoConverter.getParser();
+ Entities.Account_Id 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(Account.Id.class).hasFields(ImmutableMap.of("id", int.class));
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/BUILD b/javatests/com/google/gerrit/reviewdb/converter/BUILD
new file mode 100644
index 0000000000..9cc941c50b
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/BUILD
@@ -0,0 +1,15 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+
+junit_tests(
+ name = "proto_converter_tests",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/proto/testing",
+ "//java/com/google/gerrit/reviewdb:server",
+ "//lib:guava",
+ "//lib:protobuf",
+ "//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/reviewdb/converter/BranchNameKeyProtoConverterTest.java
new file mode 100644
index 0000000000..412641f6aa
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/BranchNameKeyProtoConverterTest.java
@@ -0,0 +1,83 @@
+// 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.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.protobuf.Parser;
+import java.lang.reflect.Type;
+import org.junit.Test;
+
+public class BranchNameKeyProtoConverterTest {
+ private final BranchNameKeyProtoConverter branchNameKeyProtoConverter =
+ BranchNameKeyProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ Branch.NameKey nameKey = new Branch.NameKey(new 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")
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ Branch.NameKey nameKey = new Branch.NameKey(new Project.NameKey("project-52"), "branch 14");
+
+ Branch.NameKey convertedNameKey =
+ branchNameKeyProtoConverter.fromProto(branchNameKeyProtoConverter.toProto(nameKey));
+
+ assertThat(convertedNameKey).isEqualTo(nameKey);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.Branch_NameKey proto =
+ Entities.Branch_NameKey.newBuilder()
+ .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 1"))
+ .setBranchName("branch 36")
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.Branch_NameKey> parser = branchNameKeyProtoConverter.getParser();
+ Entities.Branch_NameKey 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(Branch.NameKey.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("projectName", Project.NameKey.class)
+ .put("branchName", String.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java
new file mode 100644
index 0000000000..d5ebb515b6
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/ChangeIdProtoConverterTest.java
@@ -0,0 +1,67 @@
+// 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.Change;
+import com.google.protobuf.Parser;
+import org.junit.Test;
+
+public class ChangeIdProtoConverterTest {
+ private final ChangeIdProtoConverter changeIdProtoConverter = ChangeIdProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ Change.Id changeId = new Change.Id(94);
+
+ Entities.Change_Id proto = changeIdProtoConverter.toProto(changeId);
+
+ Entities.Change_Id expectedProto = Entities.Change_Id.newBuilder().setId(94).build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ Change.Id changeId = new Change.Id(2903482);
+
+ Change.Id convertedChangeId =
+ changeIdProtoConverter.fromProto(changeIdProtoConverter.toProto(changeId));
+
+ assertThat(convertedChangeId).isEqualTo(changeId);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.Change_Id proto = Entities.Change_Id.newBuilder().setId(94).build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.Change_Id> parser = changeIdProtoConverter.getParser();
+ Entities.Change_Id 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(Change.Id.class).hasFields(ImmutableMap.of("id", int.class));
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java
new file mode 100644
index 0000000000..d94870682b
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/ChangeKeyProtoConverterTest.java
@@ -0,0 +1,67 @@
+// 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.Change;
+import com.google.protobuf.Parser;
+import org.junit.Test;
+
+public class ChangeKeyProtoConverterTest {
+ private final ChangeKeyProtoConverter changeKeyProtoConverter = ChangeKeyProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ Change.Key changeKey = new Change.Key("change-1");
+
+ Entities.Change_Key proto = changeKeyProtoConverter.toProto(changeKey);
+
+ Entities.Change_Key expectedProto = Entities.Change_Key.newBuilder().setId("change-1").build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ Change.Key changeKey = new Change.Key("change-52");
+
+ Change.Key convertedChangeKey =
+ changeKeyProtoConverter.fromProto(changeKeyProtoConverter.toProto(changeKey));
+
+ assertThat(convertedChangeKey).isEqualTo(changeKey);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.Change_Key proto = Entities.Change_Key.newBuilder().setId("change 36").build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.Change_Key> parser = changeKeyProtoConverter.getParser();
+ Entities.Change_Key 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(Change.Key.class).hasFields(ImmutableMap.of("id", String.class));
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java
new file mode 100644
index 0000000000..c8bb2ed799
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageKeyProtoConverterTest.java
@@ -0,0 +1,83 @@
+// 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.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.protobuf.Parser;
+import java.lang.reflect.Type;
+import org.junit.Test;
+
+public class ChangeMessageKeyProtoConverterTest {
+ private final ChangeMessageKeyProtoConverter messageKeyProtoConverter =
+ ChangeMessageKeyProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ ChangeMessage.Key messageKey = new ChangeMessage.Key(new Change.Id(704), "aabbcc");
+
+ Entities.ChangeMessage_Key proto = messageKeyProtoConverter.toProto(messageKey);
+
+ Entities.ChangeMessage_Key expectedProto =
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(704))
+ .setUuid("aabbcc")
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ ChangeMessage.Key messageKey = new ChangeMessage.Key(new Change.Id(704), "aabbcc");
+
+ ChangeMessage.Key convertedMessageKey =
+ messageKeyProtoConverter.fromProto(messageKeyProtoConverter.toProto(messageKey));
+
+ assertThat(convertedMessageKey).isEqualTo(messageKey);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.ChangeMessage_Key proto =
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(704))
+ .setUuid("aabbcc")
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.ChangeMessage_Key> parser = messageKeyProtoConverter.getParser();
+ Entities.ChangeMessage_Key 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(ChangeMessage.Key.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("changeId", Change.Id.class)
+ .put("uuid", String.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java
new file mode 100644
index 0000000000..65bdfbbc3d
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/ChangeMessageProtoConverterTest.java
@@ -0,0 +1,214 @@
+// 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.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;
+import org.junit.Test;
+
+public class ChangeMessageProtoConverterTest {
+ private final ChangeMessageProtoConverter changeMessageProtoConverter =
+ ChangeMessageProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ ChangeMessage changeMessage =
+ new ChangeMessage(
+ new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
+ new Account.Id(63),
+ new Timestamp(9876543),
+ new PatchSet.Id(new Change.Id(34), 13));
+ changeMessage.setMessage("This is a change message.");
+ changeMessage.setTag("An arbitrary tag.");
+ changeMessage.setRealAuthor(new Account.Id(10003));
+
+ Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
+
+ Entities.ChangeMessage expectedProto =
+ Entities.ChangeMessage.newBuilder()
+ .setKey(
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(543))
+ .setUuid("change-message-21"))
+ .setAuthorId(Entities.Account_Id.newBuilder().setId(63))
+ .setWrittenOn(9876543)
+ .setMessage("This is a change message.")
+ .setPatchset(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(34))
+ .setPatchSetId(13))
+ .setTag("An arbitrary tag.")
+ .setRealAuthor(Entities.Account_Id.newBuilder().setId(10003))
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void mainValuesConvertedToProto() {
+ ChangeMessage changeMessage =
+ new ChangeMessage(
+ new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
+ new Account.Id(63),
+ new Timestamp(9876543),
+ new PatchSet.Id(new Change.Id(34), 13));
+
+ Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
+
+ Entities.ChangeMessage expectedProto =
+ Entities.ChangeMessage.newBuilder()
+ .setKey(
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(543))
+ .setUuid("change-message-21"))
+ .setAuthorId(Entities.Account_Id.newBuilder().setId(63))
+ .setWrittenOn(9876543)
+ .setPatchset(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(34))
+ .setPatchSetId(13))
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ // This test documents a special behavior which is necessary to ensure binary compatibility.
+ @Test
+ public void realAuthorIsNotAutomaticallySetToAuthorWhenConvertedToProto() {
+ ChangeMessage changeMessage =
+ new ChangeMessage(
+ new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
+ new Account.Id(63),
+ null,
+ null);
+
+ Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
+
+ Entities.ChangeMessage expectedProto =
+ Entities.ChangeMessage.newBuilder()
+ .setKey(
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(543))
+ .setUuid("change-message-21"))
+ .setAuthorId(Entities.Account_Id.newBuilder().setId(63))
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void mandatoryValuesConvertedToProto() {
+ // 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);
+
+ Entities.ChangeMessage proto = changeMessageProtoConverter.toProto(changeMessage);
+
+ Entities.ChangeMessage expectedProto =
+ Entities.ChangeMessage.newBuilder()
+ .setKey(
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(543))
+ .setUuid("change-message-21"))
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ ChangeMessage changeMessage =
+ new ChangeMessage(
+ new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
+ new Account.Id(63),
+ new Timestamp(9876543),
+ new PatchSet.Id(new Change.Id(34), 13));
+ changeMessage.setMessage("This is a change message.");
+ changeMessage.setTag("An arbitrary tag.");
+ changeMessage.setRealAuthor(new Account.Id(10003));
+
+ ChangeMessage convertedChangeMessage =
+ changeMessageProtoConverter.fromProto(changeMessageProtoConverter.toProto(changeMessage));
+ assertThat(convertedChangeMessage).isEqualTo(changeMessage);
+ }
+
+ @Test
+ public void mainValuesConvertedToProtoAndBackAgain() {
+ ChangeMessage changeMessage =
+ new ChangeMessage(
+ new ChangeMessage.Key(new Change.Id(543), "change-message-21"),
+ new Account.Id(63),
+ new Timestamp(9876543),
+ new PatchSet.Id(new Change.Id(34), 13));
+
+ ChangeMessage convertedChangeMessage =
+ changeMessageProtoConverter.fromProto(changeMessageProtoConverter.toProto(changeMessage));
+ assertThat(convertedChangeMessage).isEqualTo(changeMessage);
+ }
+
+ @Test
+ public void mandatoryValuesConvertedToProtoAndBackAgain() {
+ ChangeMessage changeMessage =
+ new ChangeMessage(
+ new ChangeMessage.Key(new Change.Id(543), "change-message-21"), null, null, null);
+
+ ChangeMessage convertedChangeMessage =
+ changeMessageProtoConverter.fromProto(changeMessageProtoConverter.toProto(changeMessage));
+ assertThat(convertedChangeMessage).isEqualTo(changeMessage);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.ChangeMessage proto =
+ Entities.ChangeMessage.newBuilder()
+ .setKey(
+ Entities.ChangeMessage_Key.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(543))
+ .setUuid("change-message-21"))
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.ChangeMessage> parser = changeMessageProtoConverter.getParser();
+ Entities.ChangeMessage 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(ChangeMessage.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("key", ChangeMessage.Key.class)
+ .put("author", Account.Id.class)
+ .put("writtenOn", Timestamp.class)
+ .put("message", String.class)
+ .put("patchset", PatchSet.Id.class)
+ .put("tag", String.class)
+ .put("realAuthor", Account.Id.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java
new file mode 100644
index 0000000000..61bf105d8b
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/ChangeProtoConverterTest.java
@@ -0,0 +1,363 @@
+// 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.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;
+import org.junit.Test;
+
+public class ChangeProtoConverterTest {
+ private final ChangeProtoConverter changeProtoConverter = ChangeProtoConverter.INSTANCE;
+
+ @Test
+ 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"),
+ 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");
+ change.setTopic("my topic");
+ change.setSubmissionId("submission ID 234");
+ change.setAssignee(new Account.Id(100001));
+ change.setPrivate(true);
+ change.setWorkInProgress(true);
+ change.setReviewStarted(true);
+ change.setRevertOf(new Change.Id(180));
+
+ Entities.Change proto = changeProtoConverter.toProto(change);
+
+ Entities.Change expectedProto =
+ Entities.Change.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(14))
+ .setChangeKey(Entities.Change_Key.newBuilder().setId("change 1"))
+ .setRowVersion(0)
+ .setCreatedOn(987654L)
+ .setLastUpdatedOn(1234567L)
+ .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"))
+ .setStatus(Change.STATUS_MERGED)
+ .setCurrentPatchSetId(23)
+ .setSubject("subject XYZ")
+ .setTopic("my topic")
+ .setOriginalSubject("original subject ABC")
+ .setSubmissionId("submission ID 234")
+ .setAssignee(Entities.Account_Id.newBuilder().setId(100001))
+ .setIsPrivate(true)
+ .setWorkInProgress(true)
+ .setReviewStarted(true)
+ .setRevertOf(Entities.Change_Id.newBuilder().setId(180))
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ 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"),
+ new Timestamp(987654L));
+
+ Entities.Change proto = changeProtoConverter.toProto(change);
+
+ Entities.Change expectedProto =
+ Entities.Change.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(14))
+ .setChangeKey(Entities.Change_Key.newBuilder().setId("change 1"))
+ .setCreatedOn(987654L)
+ // Defaults to createdOn if not set.
+ .setLastUpdatedOn(987654L)
+ .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"))
+ // Default values which can't be unset.
+ .setCurrentPatchSetId(0)
+ .setRowVersion(0)
+ .setStatus(Change.STATUS_NEW)
+ .setIsPrivate(false)
+ .setWorkInProgress(false)
+ .setReviewStarted(false)
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ // This test documents a special behavior which is necessary to ensure binary compatibility.
+ @Test
+ 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"),
+ 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);
+
+ Entities.Change proto = changeProtoConverter.toProto(change);
+
+ Entities.Change expectedProto =
+ Entities.Change.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(14))
+ .setChangeKey(Entities.Change_Key.newBuilder().setId("change 1"))
+ .setCreatedOn(987654L)
+ // Defaults to createdOn if not set.
+ .setLastUpdatedOn(987654L)
+ .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"))
+ .setCurrentPatchSetId(0)
+ // Default values which can't be unset.
+ .setRowVersion(0)
+ .setStatus(Change.STATUS_NEW)
+ .setIsPrivate(false)
+ .setWorkInProgress(false)
+ .setReviewStarted(false)
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ // This test documents a special behavior which is necessary to ensure binary compatibility.
+ @Test
+ 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"),
+ new Timestamp(987654L));
+ change.setCurrentPatchSet(new PatchSet.Id(new Change.Id(14), 23), "subject ABC", null);
+
+ Entities.Change proto = changeProtoConverter.toProto(change);
+
+ Entities.Change expectedProto =
+ Entities.Change.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(14))
+ .setChangeKey(Entities.Change_Key.newBuilder().setId("change 1"))
+ .setCreatedOn(987654L)
+ // Defaults to createdOn if not set.
+ .setLastUpdatedOn(987654L)
+ .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"))
+ .setCurrentPatchSetId(23)
+ .setSubject("subject ABC")
+ // Default values which can't be unset.
+ .setRowVersion(0)
+ .setStatus(Change.STATUS_NEW)
+ .setIsPrivate(false)
+ .setWorkInProgress(false)
+ .setReviewStarted(false)
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ 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"),
+ 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");
+ change.setTopic("my topic");
+ change.setSubmissionId("submission ID 234");
+ change.setAssignee(new Account.Id(100001));
+ change.setPrivate(true);
+ change.setWorkInProgress(true);
+ change.setReviewStarted(true);
+ change.setRevertOf(new Change.Id(180));
+
+ Change convertedChange = changeProtoConverter.fromProto(changeProtoConverter.toProto(change));
+ assertEqualChange(convertedChange, change);
+ }
+
+ @Test
+ 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"),
+ new Timestamp(987654L));
+
+ Change convertedChange = changeProtoConverter.fromProto(changeProtoConverter.toProto(change));
+ assertEqualChange(convertedChange, change);
+ }
+
+ // We need this special test as some values are only optional in the protobuf definition but can
+ // never be unset in our entity object.
+ @Test
+ public void protoWithOnlyRequiredValuesCanBeConvertedBack() {
+ Entities.Change proto =
+ Entities.Change.newBuilder().setChangeId(Entities.Change_Id.newBuilder().setId(14)).build();
+ Change change = changeProtoConverter.fromProto(proto);
+
+ assertThat(change.getChangeId()).isEqualTo(14);
+ // Values which can't be null according to ReviewDb's column definition but which are optional.
+ assertThat(change.getKey()).isNull();
+ assertThat(change.getOwner()).isNull();
+ assertThat(change.getDest()).isNull();
+ assertThat(change.getCreatedOn()).isEqualTo(new Timestamp(0));
+ assertThat(change.getLastUpdatedOn()).isEqualTo(new Timestamp(0));
+ assertThat(change.getSubject()).isNull();
+ assertThat(change.currentPatchSetId()).isNull();
+ // Default values for unset protobuf fields which can't be unset in the entity object.
+ assertThat(change.getRowVersion()).isEqualTo(0);
+ assertThat(change.isNew()).isTrue();
+ assertThat(change.isPrivate()).isFalse();
+ assertThat(change.isWorkInProgress()).isFalse();
+ assertThat(change.hasReviewStarted()).isFalse();
+ }
+
+ @Test
+ public void unsetLastUpdatedOnIsAutomaticallySetToCreatedOnWhenConvertedBack() {
+ Entities.Change proto =
+ Entities.Change.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(14))
+ .setChangeKey(Entities.Change_Key.newBuilder().setId("change 1"))
+ .setCreatedOn(987654L)
+ .setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
+ .setDest(
+ Entities.Branch_NameKey.newBuilder()
+ .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranchName("branch 74"))
+ .build();
+ Change change = changeProtoConverter.fromProto(proto);
+
+ assertThat(change.getLastUpdatedOn()).isEqualTo(new Timestamp(987654L));
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.Change proto =
+ Entities.Change.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(14))
+ .setChangeKey(Entities.Change_Key.newBuilder().setId("change 1"))
+ .setRowVersion(0)
+ .setCreatedOn(987654L)
+ .setLastUpdatedOn(1234567L)
+ .setOwnerAccountId(Entities.Account_Id.newBuilder().setId(35))
+ .setDest(
+ Entities.Branch_NameKey.newBuilder()
+ .setProjectName(Entities.Project_NameKey.newBuilder().setName("project 67"))
+ .setBranchName("branch 74"))
+ .setStatus(Change.STATUS_MERGED)
+ .setCurrentPatchSetId(23)
+ .setSubject("subject XYZ")
+ .setTopic("my topic")
+ .setOriginalSubject("original subject ABC")
+ .setSubmissionId("submission ID 234")
+ .setAssignee(Entities.Account_Id.newBuilder().setId(100001))
+ .setIsPrivate(true)
+ .setWorkInProgress(true)
+ .setReviewStarted(true)
+ .setRevertOf(Entities.Change_Id.newBuilder().setId(180))
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.Change> parser = changeProtoConverter.getParser();
+ Entities.Change 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(Change.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("changeId", Change.Id.class)
+ .put("changeKey", Change.Key.class)
+ .put("rowVersion", int.class)
+ .put("createdOn", Timestamp.class)
+ .put("lastUpdatedOn", Timestamp.class)
+ .put("owner", Account.Id.class)
+ .put("dest", Branch.NameKey.class)
+ .put("status", char.class)
+ .put("currentPatchSetId", int.class)
+ .put("subject", String.class)
+ .put("topic", String.class)
+ .put("originalSubject", String.class)
+ .put("submissionId", String.class)
+ .put("assignee", Account.Id.class)
+ .put("isPrivate", boolean.class)
+ .put("workInProgress", boolean.class)
+ .put("reviewStarted", boolean.class)
+ .put("revertOf", Change.Id.class)
+ .build());
+ }
+
+ // Unfortunately, Change doesn't implement equals(). Remove this method when we switch Change to
+ // an AutoValue.
+ private static void assertEqualChange(Change change, Change expectedChange) {
+ assertThat(change.getChangeId()).isEqualTo(expectedChange.getChangeId());
+ assertThat(change.getKey()).isEqualTo(expectedChange.getKey());
+ assertThat(change.getRowVersion()).isEqualTo(expectedChange.getRowVersion());
+ assertThat(change.getCreatedOn()).isEqualTo(expectedChange.getCreatedOn());
+ assertThat(change.getLastUpdatedOn()).isEqualTo(expectedChange.getLastUpdatedOn());
+ assertThat(change.getOwner()).isEqualTo(expectedChange.getOwner());
+ assertThat(change.getDest()).isEqualTo(expectedChange.getDest());
+ assertThat(change.getStatus()).isEqualTo(expectedChange.getStatus());
+ assertThat(change.currentPatchSetId()).isEqualTo(expectedChange.currentPatchSetId());
+ assertThat(change.getSubject()).isEqualTo(expectedChange.getSubject());
+ assertThat(change.getTopic()).isEqualTo(expectedChange.getTopic());
+ assertThat(change.getOriginalSubject()).isEqualTo(expectedChange.getOriginalSubject());
+ assertThat(change.getSubmissionId()).isEqualTo(expectedChange.getSubmissionId());
+ assertThat(change.getAssignee()).isEqualTo(expectedChange.getAssignee());
+ assertThat(change.isPrivate()).isEqualTo(expectedChange.isPrivate());
+ assertThat(change.isWorkInProgress()).isEqualTo(expectedChange.isWorkInProgress());
+ assertThat(change.hasReviewStarted()).isEqualTo(expectedChange.hasReviewStarted());
+ assertThat(change.getRevertOf()).isEqualTo(expectedChange.getRevertOf());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java
new file mode 100644
index 0000000000..41e0f3fdb9
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/LabelIdProtoConverterTest.java
@@ -0,0 +1,67 @@
+// 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.LabelId;
+import com.google.protobuf.Parser;
+import org.junit.Test;
+
+public class LabelIdProtoConverterTest {
+ private final LabelIdProtoConverter labelIdProtoConverter = LabelIdProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ LabelId labelId = new LabelId("Label ID 42");
+
+ Entities.LabelId proto = labelIdProtoConverter.toProto(labelId);
+
+ Entities.LabelId expectedProto = Entities.LabelId.newBuilder().setId("Label ID 42").build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ LabelId labelId = new LabelId("label-5");
+
+ LabelId convertedLabelId =
+ labelIdProtoConverter.fromProto(labelIdProtoConverter.toProto(labelId));
+
+ assertThat(convertedLabelId).isEqualTo(labelId);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.LabelId proto = Entities.LabelId.newBuilder().setId("label-23").build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.LabelId> parser = labelIdProtoConverter.getParser();
+ Entities.LabelId 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(LabelId.class).hasFields(ImmutableMap.of("id", String.class));
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java
new file mode 100644
index 0000000000..d1ed419849
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalKeyProtoConverterTest.java
@@ -0,0 +1,98 @@
+// 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.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;
+
+public class PatchSetApprovalKeyProtoConverterTest {
+ private final PatchSetApprovalKeyProtoConverter protoConverter =
+ PatchSetApprovalKeyProtoConverter.INSTANCE;
+
+ @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"));
+
+ Entities.PatchSetApproval_Key proto = protoConverter.toProto(key);
+
+ Entities.PatchSetApproval_Key expectedProto =
+ Entities.PatchSetApproval_Key.newBuilder()
+ .setPatchSetId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(42))
+ .setPatchSetId(14))
+ .setAccountId(Entities.Account_Id.newBuilder().setId(100013))
+ .setCategoryId(Entities.LabelId.newBuilder().setId("label-8"))
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @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 convertedKey = protoConverter.fromProto(protoConverter.toProto(key));
+
+ assertThat(convertedKey).isEqualTo(key);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.PatchSetApproval_Key proto =
+ Entities.PatchSetApproval_Key.newBuilder()
+ .setPatchSetId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(42))
+ .setPatchSetId(14))
+ .setAccountId(Entities.Account_Id.newBuilder().setId(100013))
+ .setCategoryId(Entities.LabelId.newBuilder().setId("label-8"))
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.PatchSetApproval_Key> parser = protoConverter.getParser();
+ Entities.PatchSetApproval_Key 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(PatchSetApproval.Key.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("patchSetId", PatchSet.Id.class)
+ .put("accountId", Account.Id.class)
+ .put("categoryId", LabelId.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java
new file mode 100644
index 0000000000..80b2cc2f4d
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/PatchSetApprovalProtoConverterTest.java
@@ -0,0 +1,203 @@
+// 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.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 java.sql.Timestamp;
+import java.util.Date;
+import org.junit.Test;
+
+public class PatchSetApprovalProtoConverterTest {
+ private final PatchSetApprovalProtoConverter protoConverter =
+ PatchSetApprovalProtoConverter.INSTANCE;
+
+ @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);
+
+ Entities.PatchSetApproval proto = protoConverter.toProto(patchSetApproval);
+
+ Entities.PatchSetApproval expectedProto =
+ Entities.PatchSetApproval.newBuilder()
+ .setKey(
+ Entities.PatchSetApproval_Key.newBuilder()
+ .setPatchSetId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(42))
+ .setPatchSetId(14))
+ .setAccountId(Entities.Account_Id.newBuilder().setId(100013))
+ .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setValue(456)
+ .setGranted(987654L)
+ .setTag("tag-21")
+ .setRealAccountId(Entities.Account_Id.newBuilder().setId(612))
+ .setPostSubmit(true)
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @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));
+
+ Entities.PatchSetApproval proto = protoConverter.toProto(patchSetApproval);
+
+ Entities.PatchSetApproval expectedProto =
+ Entities.PatchSetApproval.newBuilder()
+ .setKey(
+ Entities.PatchSetApproval_Key.newBuilder()
+ .setPatchSetId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(42))
+ .setPatchSetId(14))
+ .setAccountId(Entities.Account_Id.newBuilder().setId(100013))
+ .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setValue(456)
+ .setGranted(987654L)
+ // This value can't be unset when our entity class is given.
+ .setPostSubmit(false)
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @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 convertedPatchSetApproval =
+ protoConverter.fromProto(protoConverter.toProto(patchSetApproval));
+ assertThat(convertedPatchSetApproval).isEqualTo(patchSetApproval);
+ }
+
+ @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 convertedPatchSetApproval =
+ protoConverter.fromProto(protoConverter.toProto(patchSetApproval));
+ assertThat(convertedPatchSetApproval).isEqualTo(patchSetApproval);
+ }
+
+ // We need this special test as some values are only optional in the protobuf definition but can
+ // never be unset in our entity object.
+ @Test
+ public void protoWithOnlyRequiredValuesCanBeConvertedBack() {
+ Entities.PatchSetApproval proto =
+ Entities.PatchSetApproval.newBuilder()
+ .setKey(
+ Entities.PatchSetApproval_Key.newBuilder()
+ .setPatchSetId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(42))
+ .setPatchSetId(14))
+ .setAccountId(Entities.Account_Id.newBuilder().setId(100013))
+ .setCategoryId(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"));
+ // 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);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.PatchSetApproval proto =
+ Entities.PatchSetApproval.newBuilder()
+ .setKey(
+ Entities.PatchSetApproval_Key.newBuilder()
+ .setPatchSetId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(42))
+ .setPatchSetId(14))
+ .setAccountId(Entities.Account_Id.newBuilder().setId(100013))
+ .setCategoryId(Entities.LabelId.newBuilder().setId("label-8")))
+ .setValue(456)
+ .setGranted(987654L)
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.PatchSetApproval> parser = protoConverter.getParser();
+ Entities.PatchSetApproval 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(PatchSetApproval.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("key", PatchSetApproval.Key.class)
+ .put("value", short.class)
+ .put("granted", Timestamp.class)
+ .put("tag", String.class)
+ .put("realAccountId", Account.Id.class)
+ .put("postSubmit", boolean.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java
new file mode 100644
index 0000000000..1598ef2ce5
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/PatchSetIdProtoConverterTest.java
@@ -0,0 +1,83 @@
+// 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.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.protobuf.Parser;
+import java.lang.reflect.Type;
+import org.junit.Test;
+
+public class PatchSetIdProtoConverterTest {
+ private final PatchSetIdProtoConverter patchSetIdProtoConverter =
+ PatchSetIdProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ PatchSet.Id patchSetId = new PatchSet.Id(new 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)
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ PatchSet.Id patchSetId = new PatchSet.Id(new Change.Id(20), 13);
+
+ PatchSet.Id convertedPatchSetId =
+ patchSetIdProtoConverter.fromProto(patchSetIdProtoConverter.toProto(patchSetId));
+
+ assertThat(convertedPatchSetId).isEqualTo(patchSetId);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.PatchSet_Id proto =
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(103))
+ .setPatchSetId(73)
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.PatchSet_Id> parser = patchSetIdProtoConverter.getParser();
+ Entities.PatchSet_Id 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(PatchSet.Id.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("changeId", Change.Id.class)
+ .put("patchSetId", int.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java
new file mode 100644
index 0000000000..b8d2b1eb5a
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/PatchSetProtoConverterTest.java
@@ -0,0 +1,137 @@
+// 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.extensions.proto.ProtoTruth.assertThat;
+import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.truth.Truth;
+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.protobuf.Parser;
+import java.lang.reflect.Type;
+import java.sql.Timestamp;
+import org.junit.Test;
+
+public class PatchSetProtoConverterTest {
+ private final PatchSetProtoConverter patchSetProtoConverter = PatchSetProtoConverter.INSTANCE;
+
+ @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.");
+
+ Entities.PatchSet proto = patchSetProtoConverter.toProto(patchSet);
+
+ Entities.PatchSet expectedProto =
+ Entities.PatchSet.newBuilder()
+ .setId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(103))
+ .setPatchSetId(73))
+ .setRevision(Entities.RevId.newBuilder().setId("aabbccddeeff"))
+ .setUploaderAccountId(Entities.Account_Id.newBuilder().setId(452))
+ .setCreatedOn(930349320L)
+ .setGroups("group1, group2")
+ .setPushCertificate("my push certificate")
+ .setDescription("This is a patch set description.")
+ .build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void mandatoryValuesConvertedToProto() {
+ PatchSet patchSet = new PatchSet(new PatchSet.Id(new Change.Id(103), 73));
+
+ Entities.PatchSet proto = patchSetProtoConverter.toProto(patchSet);
+
+ Entities.PatchSet expectedProto =
+ Entities.PatchSet.newBuilder()
+ .setId(
+ Entities.PatchSet_Id.newBuilder()
+ .setChangeId(Entities.Change_Id.newBuilder().setId(103))
+ .setPatchSetId(73))
+ .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 convertedPatchSet =
+ patchSetProtoConverter.fromProto(patchSetProtoConverter.toProto(patchSet));
+ Truth.assertThat(convertedPatchSet).isEqualTo(patchSet);
+ }
+
+ @Test
+ public void mandatoryValuesConvertedToProtoAndBackAgain() {
+ PatchSet patchSet = new PatchSet(new PatchSet.Id(new Change.Id(103), 73));
+
+ PatchSet convertedPatchSet =
+ patchSetProtoConverter.fromProto(patchSetProtoConverter.toProto(patchSet));
+ Truth.assertThat(convertedPatchSet).isEqualTo(patchSet);
+ }
+
+ @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))
+ .build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.PatchSet> parser = patchSetProtoConverter.getParser();
+ Entities.PatchSet 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(PatchSet.class)
+ .hasFields(
+ ImmutableMap.<String, Type>builder()
+ .put("id", PatchSet.Id.class)
+ .put("revision", RevId.class)
+ .put("uploader", Account.Id.class)
+ .put("createdOn", Timestamp.class)
+ .put("groups", String.class)
+ .put("pushCertificate", String.class)
+ .put("description", String.class)
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java
new file mode 100644
index 0000000000..2ad610712e
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/ProjectNameKeyProtoConverterTest.java
@@ -0,0 +1,71 @@
+// 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.Project;
+import com.google.protobuf.Parser;
+import org.junit.Test;
+
+public class ProjectNameKeyProtoConverterTest {
+ private final ProjectNameKeyProtoConverter projectNameKeyProtoConverter =
+ ProjectNameKeyProtoConverter.INSTANCE;
+
+ @Test
+ public void allValuesConvertedToProto() {
+ Project.NameKey nameKey = new Project.NameKey("project-72");
+
+ Entities.Project_NameKey proto = projectNameKeyProtoConverter.toProto(nameKey);
+
+ Entities.Project_NameKey expectedProto =
+ Entities.Project_NameKey.newBuilder().setName("project-72").build();
+ assertThat(proto).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void allValuesConvertedToProtoAndBackAgain() {
+ Project.NameKey nameKey = new Project.NameKey("project-52");
+
+ Project.NameKey convertedNameKey =
+ projectNameKeyProtoConverter.fromProto(projectNameKeyProtoConverter.toProto(nameKey));
+
+ assertThat(convertedNameKey).isEqualTo(nameKey);
+ }
+
+ @Test
+ public void protoCanBeParsedFromBytes() throws Exception {
+ Entities.Project_NameKey proto =
+ Entities.Project_NameKey.newBuilder().setName("project 36").build();
+ byte[] bytes = proto.toByteArray();
+
+ Parser<Entities.Project_NameKey> parser = projectNameKeyProtoConverter.getParser();
+ Entities.Project_NameKey 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(Project.NameKey.class)
+ .hasFields(ImmutableMap.of("name", String.class));
+ }
+}
diff --git a/javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java b/javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java
new file mode 100644
index 0000000000..2c354be94d
--- /dev/null
+++ b/javatests/com/google/gerrit/reviewdb/converter/RevIdProtoConverterTest.java
@@ -0,0 +1,66 @@
+// 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 e850fdc387..7b8f602106 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -37,13 +37,18 @@ junit_tests(
":custom-truth-subjects",
"//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/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/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/cache/serialize",
@@ -54,15 +59,14 @@ junit_tests(
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
+ "//java/com/google/gerrit/server/schema/testing",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/sshd",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//java/com/google/gerrit/truth",
- "//java/org/eclipse/jgit:server",
"//lib:gson",
"//lib:guava",
"//lib:guava-retrying",
- "//lib:gwtorm",
"//lib:protobuf",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/javatests/com/google/gerrit/server/ChangeUtilTest.java b/javatests/com/google/gerrit/server/ChangeUtilTest.java
index 5f73d2cdf0..5cb474d34f 100644
--- a/javatests/com/google/gerrit/server/ChangeUtilTest.java
+++ b/javatests/com/google/gerrit/server/ChangeUtilTest.java
@@ -16,10 +16,11 @@ 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 {
+public class ChangeUtilTest extends GerritBaseTests {
@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 da6c56dc0f..485de49720 100644
--- a/javatests/com/google/gerrit/server/IdentifiedUserTest.java
+++ b/javatests/com/google/gerrit/server/IdentifiedUserTest.java
@@ -25,12 +25,13 @@ import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.DisableReverseDnsLookup;
+import com.google.gerrit.server.config.EnableReverseDnsLookup;
import com.google.gerrit.server.config.GerritServerConfig;
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;
@@ -44,7 +45,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(ConfigSuite.class)
-public class IdentifiedUserTest {
+public class IdentifiedUserTest extends GerritBaseTests {
@ConfigSuite.Parameter public Config config;
private IdentifiedUser identifiedUser;
@@ -80,8 +81,8 @@ public class IdentifiedUserTest {
@Override
protected void configure() {
bind(Boolean.class)
- .annotatedWith(DisableReverseDnsLookup.class)
- .toInstance(Boolean.FALSE);
+ .annotatedWith(EnableReverseDnsLookup.class)
+ .toInstance(Boolean.TRUE);
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(config);
bind(String.class)
.annotatedWith(AnonymousCowardName.class)
diff --git a/javatests/com/google/gerrit/server/account/AccountResolverTest.java b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
new file mode 100644
index 0000000000..c37a9454fc
--- /dev/null
+++ b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
@@ -0,0 +1,394 @@
+// 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.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 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.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 {
+ private static class TestSearcher extends StringSearcher {
+ private final String pattern;
+ private final boolean shortCircuit;
+ private final ImmutableList<AccountState> accounts;
+ private boolean assumeVisible;
+ private boolean filterInactive;
+
+ private TestSearcher(String pattern, boolean shortCircuit, AccountState... accounts) {
+ this.pattern = pattern;
+ this.shortCircuit = shortCircuit;
+ this.accounts = ImmutableList.copyOf(accounts);
+ }
+
+ @Override
+ protected boolean matches(String input) {
+ return input.matches(pattern);
+ }
+
+ @Override
+ public Stream<AccountState> search(String input) {
+ return accounts.stream();
+ }
+
+ @Override
+ public boolean shortCircuitIfNoResults() {
+ return shortCircuit;
+ }
+
+ @Override
+ public boolean callerMayAssumeCandidatesAreVisible() {
+ return assumeVisible;
+ }
+
+ void setCallerMayAssumeCandidatesAreVisible() {
+ this.assumeVisible = true;
+ }
+
+ @Override
+ public boolean callerShouldFilterOutInactiveCandidates() {
+ return filterInactive;
+ }
+
+ void setCallerShouldFilterOutInactiveCandidates() {
+ this.filterInactive = true;
+ }
+
+ @Override
+ public String toString() {
+ return accounts.stream()
+ .map(a -> a.getAccount().getId().toString())
+ .collect(joining(",", pattern + "(", ")"));
+ }
+ }
+
+ @Test
+ public void noShortCircuit() throws Exception {
+ ImmutableList<Searcher<?>> searchers =
+ ImmutableList.of(
+ new TestSearcher("foo", false, newAccount(1)),
+ new TestSearcher("bar", false, newAccount(2), newAccount(3)));
+
+ Result result = search("foo", searchers, allVisible());
+ assertThat(result.input()).isEqualTo("foo");
+ assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
+
+ result = search("bar", searchers, allVisible());
+ assertThat(result.input()).isEqualTo("bar");
+ assertThat(result.asIdSet()).containsExactlyElementsIn(ids(2, 3));
+
+ result = search("baz", searchers, allVisible());
+ assertThat(result.input()).isEqualTo("baz");
+ assertThat(result.asIdSet()).isEmpty();
+ }
+
+ @Test
+ public void shortCircuit() throws Exception {
+ ImmutableList<Searcher<?>> searchers =
+ ImmutableList.of(
+ new TestSearcher("f.*", true), new TestSearcher("foo|bar", false, newAccount(1)));
+
+ Result result = search("foo", searchers, allVisible());
+ assertThat(result.input()).isEqualTo("foo");
+ assertThat(result.asIdSet()).isEmpty();
+
+ result = search("bar", searchers, allVisible());
+ assertThat(result.input()).isEqualTo("bar");
+ assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
+ }
+
+ @Test
+ public void filterInvisible() throws Exception {
+ ImmutableList<Searcher<?>> searchers =
+ ImmutableList.of(new TestSearcher("foo", false, newAccount(1), newAccount(2)));
+
+ assertThat(search("foo", searchers, allVisible()).asIdSet())
+ .containsExactlyElementsIn(ids(1, 2));
+ assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(2));
+ }
+
+ @Test
+ public void skipVisibilityCheck() throws Exception {
+ TestSearcher searcher = new TestSearcher("foo", false, newAccount(1), newAccount(2));
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher);
+
+ assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(2));
+
+ searcher.setCallerMayAssumeCandidatesAreVisible();
+ assertThat(search("foo", searchers, only(2)).asIdSet()).containsExactlyElementsIn(ids(1, 2));
+ }
+
+ @Test
+ public void dontFilterInactive() throws Exception {
+ ImmutableList<Searcher<?>> searchers =
+ ImmutableList.of(
+ new TestSearcher("foo", false, newInactiveAccount(1)),
+ new TestSearcher("f.*", false, newInactiveAccount(2)));
+
+ Result result = search("foo", searchers, allVisible());
+ // 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(filteredInactiveIds(result)).isEmpty();
+ }
+
+ @Test
+ public void shouldUseCustomAccountActivityPredicate() throws Exception {
+ TestSearcher searcher1 = new TestSearcher("foo", false, newInactiveAccount(1));
+ searcher1.setCallerShouldFilterOutInactiveCandidates();
+ TestSearcher searcher2 = new TestSearcher("f.*", false, newInactiveAccount(2));
+ searcher2.setCallerShouldFilterOutInactiveCandidates();
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher1, searcher2);
+
+ Result result = search("foo", searchers, allVisible(), (a) -> true);
+ // 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(filteredInactiveIds(result)).isEmpty();
+ }
+
+ @Test
+ public void filterInactiveEventuallyFindingResults() throws Exception {
+ TestSearcher searcher1 = new TestSearcher("foo", false, newInactiveAccount(1));
+ searcher1.setCallerShouldFilterOutInactiveCandidates();
+ TestSearcher searcher2 = new TestSearcher("f.*", false, newAccount(2));
+ searcher2.setCallerShouldFilterOutInactiveCandidates();
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher1, searcher2);
+
+ Result result = search("foo", searchers, allVisible());
+ assertThat(search("foo", searchers, allVisible()).asIdSet()).containsExactlyElementsIn(ids(2));
+ // No info about inactive results exposed if there was at least one active result.
+ assertThat(filteredInactiveIds(result)).isEmpty();
+ }
+
+ @Test
+ public void filterInactiveEventuallyFindingNoResults() throws Exception {
+ TestSearcher searcher1 = new TestSearcher("foo", false, newInactiveAccount(1));
+ searcher1.setCallerShouldFilterOutInactiveCandidates();
+ TestSearcher searcher2 = new TestSearcher("f.*", false, newInactiveAccount(2));
+ searcher2.setCallerShouldFilterOutInactiveCandidates();
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher1, searcher2);
+
+ Result result = search("foo", searchers, allVisible());
+ assertThat(result.asIdSet()).isEmpty();
+ assertThat(filteredInactiveIds(result)).containsExactlyElementsIn(ids(1, 2));
+ }
+
+ @Test
+ public void dontShortCircuitAfterFilteringInactiveCandidatesResultsInEmptyList()
+ throws Exception {
+ AccountState account1 = newAccount(1);
+ AccountState account2 = newInactiveAccount(2);
+ TestSearcher searcher1 = new TestSearcher("foo", false, account2);
+ searcher1.setCallerShouldFilterOutInactiveCandidates();
+
+ TestSearcher searcher2 = new TestSearcher("foo", false, account1, account2);
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher1, searcher2);
+
+ // searcher1 matched, but filtered out all candidates because account2 is inactive. Actual
+ // result came from searcher2 instead.
+ Result result = search("foo", searchers, allVisible());
+ assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1, 2));
+ }
+
+ @Test
+ public void shortCircuitAfterFilteringInactiveCandidatesResultsInEmptyList() throws Exception {
+ AccountState account1 = newAccount(1);
+ AccountState account2 = newInactiveAccount(2);
+ TestSearcher searcher1 = new TestSearcher("foo", true, account2);
+ searcher1.setCallerShouldFilterOutInactiveCandidates();
+
+ TestSearcher searcher2 = new TestSearcher("foo", false, account1, account2);
+ ImmutableList<Searcher<?>> searchers = ImmutableList.of(searcher1, searcher2);
+
+ // searcher1 matched and then filtered out all candidates because account2 is inactive, but
+ // still short-circuited.
+ Result result = search("foo", searchers, allVisible());
+ assertThat(result.asIdSet()).isEmpty();
+ assertThat(filteredInactiveIds(result)).containsExactlyElementsIn(ids(2));
+ }
+
+ @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");
+ }
+ }
+
+ @Test
+ public void asUniqueWithOneResult() throws Exception {
+ 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());
+ }
+
+ @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)");
+ }
+ }
+
+ @Test
+ public void exceptionMessageNotFound() throws Exception {
+ AccountResolver resolver = newAccountResolver();
+ assertThat(
+ new UnresolvableAccountException(
+ resolver.new Result("foo", ImmutableList.of(), ImmutableList.of())))
+ .hasMessageThat()
+ .isEqualTo("Account 'foo' not found");
+ }
+
+ @Test
+ public void exceptionMessageSelf() throws Exception {
+ AccountResolver resolver = newAccountResolver();
+ UnresolvableAccountException e =
+ new UnresolvableAccountException(
+ resolver.new Result("self", ImmutableList.of(), ImmutableList.of()));
+ assertThat(e.isSelf()).isTrue();
+ assertThat(e).hasMessageThat().isEqualTo("Resolving account 'self' requires login");
+ }
+
+ @Test
+ public void exceptionMessageMe() throws Exception {
+ AccountResolver resolver = newAccountResolver();
+ UnresolvableAccountException e =
+ new UnresolvableAccountException(
+ resolver.new Result("me", ImmutableList.of(), ImmutableList.of()));
+ assertThat(e.isSelf()).isTrue();
+ assertThat(e).hasMessageThat().isEqualTo("Resolving account 'me' requires login");
+ }
+
+ @Test
+ public void exceptionMessageAmbiguous() throws Exception {
+ AccountResolver resolver = newAccountResolver();
+ assertThat(
+ new UnresolvableAccountException(
+ resolver
+ .new Result(
+ "foo", ImmutableList.of(newAccount(3), newAccount(1)), ImmutableList.of())))
+ .hasMessageThat()
+ .isEqualTo("Account 'foo' is ambiguous:\n1: Anonymous Name (1)\n3: Anonymous Name (3)");
+ }
+
+ @Test
+ public void exceptionMessageOnlyInactive() throws Exception {
+ AccountResolver resolver = newAccountResolver();
+ assertThat(
+ new UnresolvableAccountException(
+ resolver
+ .new Result(
+ "foo",
+ ImmutableList.of(),
+ ImmutableList.of(newInactiveAccount(3), newInactiveAccount(1)))))
+ .hasMessageThat()
+ .isEqualTo(
+ "Account 'foo' only matches inactive accounts. To use an inactive account, retry"
+ + " with one of the following exact account IDs:\n"
+ + "1: Anonymous Name (1)\n"
+ + "3: Anonymous Name (3)");
+ }
+
+ private Result search(
+ String input,
+ ImmutableList<Searcher<?>> searchers,
+ Supplier<Predicate<AccountState>> visibilitySupplier)
+ throws Exception {
+ return search(input, searchers, visibilitySupplier, activityPrediate());
+ }
+
+ private Result search(
+ String input,
+ ImmutableList<Searcher<?>> searchers,
+ Supplier<Predicate<AccountState>> visibilitySupplier,
+ Predicate<AccountState> activityPredicate)
+ throws Exception {
+ return newAccountResolver().searchImpl(input, searchers, visibilitySupplier, activityPredicate);
+ }
+
+ private static AccountResolver newAccountResolver() {
+ return new AccountResolver(null, null, null, null, null, null, null, "Anonymous Name");
+ }
+
+ private AccountState newAccount(int id) {
+ return AccountState.forAccount(
+ new AllUsersName("All-Users"), new Account(new Account.Id(id), TimeUtil.nowTs()));
+ }
+
+ private AccountState newInactiveAccount(int id) {
+ Account a = new Account(new Account.Id(id), TimeUtil.nowTs());
+ a.setActive(false);
+ return AccountState.forAccount(new AllUsersName("All-Users"), a);
+ }
+
+ private static ImmutableSet<Account.Id> ids(int... ids) {
+ return Arrays.stream(ids).mapToObj(Account.Id::new).collect(toImmutableSet());
+ }
+
+ private static Supplier<Predicate<AccountState>> allVisible() {
+ return () -> a -> true;
+ }
+
+ private Predicate<AccountState> activityPrediate() {
+ return (AccountState accountState) -> accountState.getAccount().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());
+ }
+
+ private static ImmutableSet<Account.Id> filteredInactiveIds(Result result) {
+ return result.filteredInactive().stream()
+ .map(a -> a.getAccount().getId())
+ .collect(toImmutableSet());
+ }
+}
diff --git a/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java b/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java
index 80a15a3c3c..51a34f5f1a 100644
--- a/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java
+++ b/javatests/com/google/gerrit/server/account/AuthorizedKeysTest.java
@@ -17,12 +17,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 java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
-public class AuthorizedKeysTest {
+public class AuthorizedKeysTest extends GerritBaseTests {
private static final String KEY1 =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCgug5VyMXQGnem2H1KVC4/HcRcD4zzBqS"
+ "uJBRWVonSSoz3RoAZ7bWXCVVGwchtXwUURD689wFYdiPecOrWOUgeeyRq754YWRhU+W28"
@@ -140,7 +141,7 @@ public class AuthorizedKeysTest {
}
private static String toWindowsLineEndings(String s) {
- return s.replaceAll("\n", "\r\n");
+ return s.replace("\n", "\r\n");
}
private static void assertSerialization(
diff --git a/javatests/com/google/gerrit/server/account/DestinationListTest.java b/javatests/com/google/gerrit/server/account/DestinationListTest.java
index 1f6ed6098c..e51b041569 100644
--- a/javatests/com/google/gerrit/server/account/DestinationListTest.java
+++ b/javatests/com/google/gerrit/server/account/DestinationListTest.java
@@ -15,19 +15,19 @@
package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.replay;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.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;
+import java.util.List;
import java.util.Set;
-import junit.framework.TestCase;
import org.junit.Test;
-public class DestinationListTest extends TestCase {
+public class DestinationListTest extends GerritBaseTests {
public static final String R_FOO = "refs/heads/foo";
public static final String R_BAR = "refs/heads/bar";
@@ -128,11 +128,12 @@ public class DestinationListTest extends TestCase {
assertThat(branches).contains(B_COMPLEX);
}
- @Test(expected = IOException.class)
+ @Test
public void testParseBad() throws IOException {
- ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
- replay(sink);
- new DestinationList().parseLabel(LABEL, L_BAD, sink);
+ List<ValidationError> errors = new ArrayList<>();
+ new DestinationList().parseLabel(LABEL, L_BAD, errors::add);
+ assertThat(errors)
+ .containsExactly(new ValidationError("destinationslabel", 1, "missing tab delimiter"));
}
@Test
diff --git a/javatests/com/google/gerrit/server/account/GroupUUIDTest.java b/javatests/com/google/gerrit/server/account/GroupUUIDTest.java
index 3b72b08181..70887e6c38 100644
--- a/javatests/com/google/gerrit/server/account/GroupUUIDTest.java
+++ b/javatests/com/google/gerrit/server/account/GroupUUIDTest.java
@@ -17,10 +17,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 org.eclipse.jgit.lib.PersonIdent;
import org.junit.Test;
-public class GroupUUIDTest {
+public class GroupUUIDTest extends GerritBaseTests {
@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 4955c0654b..9a0c9cb972 100644
--- a/javatests/com/google/gerrit/server/account/HashedPasswordTest.java
+++ b/javatests/com/google/gerrit/server/account/HashedPasswordTest.java
@@ -17,10 +17,11 @@ package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Strings;
+import com.google.gerrit.testing.GerritBaseTests;
import org.apache.commons.codec.DecoderException;
import org.junit.Test;
-public class HashedPasswordTest {
+public class HashedPasswordTest extends GerritBaseTests {
@Test
public void encodeOneLine() throws Exception {
diff --git a/javatests/com/google/gerrit/server/account/QueryListTest.java b/javatests/com/google/gerrit/server/account/QueryListTest.java
index 2792de85df..a0876e1ad9 100644
--- a/javatests/com/google/gerrit/server/account/QueryListTest.java
+++ b/javatests/com/google/gerrit/server/account/QueryListTest.java
@@ -15,15 +15,14 @@
package com.google.gerrit.server.account;
import static com.google.common.truth.Truth.assertThat;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.replay;
import com.google.gerrit.server.git.ValidationError;
-import java.io.IOException;
-import junit.framework.TestCase;
+import com.google.gerrit.testing.GerritBaseTests;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Test;
-public class QueryListTest extends TestCase {
+public class QueryListTest extends GerritBaseTests {
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'";
@@ -99,11 +98,11 @@ public class QueryListTest extends TestCase {
assertThat(ql.getQuery(N_FOO)).isEqualTo(Q_COMPLEX);
}
- @Test(expected = IOException.class)
+ @Test
public void testParseBad() throws Exception {
- ValidationError.Sink sink = createNiceMock(ValidationError.Sink.class);
- replay(sink);
- QueryList.parse(L_BAD, sink);
+ List<ValidationError> errors = new ArrayList<>();
+ assertThat(QueryList.parse(L_BAD, errors::add).asText()).isNull();
+ assertThat(errors).containsExactly(new ValidationError("queries", 1, "missing tab delimiter"));
}
@Test
diff --git a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
index f69bafa7a2..334c627a96 100644
--- a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
+++ b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
@@ -39,7 +39,6 @@ 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.easymock.IAnswer;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
@@ -113,16 +112,13 @@ public class UniversalGroupBackendTest extends GerritBaseTests {
expect(backend.handles(not(eq(handled)))).andStubReturn(false);
expect(backend.membershipsOf(anyObject(IdentifiedUser.class)))
.andStubAnswer(
- new IAnswer<GroupMembership>() {
- @Override
- public GroupMembership answer() throws Throwable {
- 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;
- }
+ () -> {
+ 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;
});
replay(member, notMember, backend);
diff --git a/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java b/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
index f29ff1fb91..d757f7135b 100644
--- a/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
+++ b/javatests/com/google/gerrit/server/account/externalids/AllExternalIdsTest.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.account.externalids;
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 static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
@@ -25,12 +25,13 @@ import com.google.gerrit.reviewdb.client.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 {
+public class AllExternalIdsTest extends GerritBaseTests {
@Test
public void serializeEmptyExternalIds() throws Exception {
assertRoundTrip(allExternalIds(), AllExternalIdsProto.getDefaultInstance());
diff --git a/javatests/com/google/gerrit/server/auth/oauth/OAuthTokenCacheTest.java b/javatests/com/google/gerrit/server/auth/oauth/OAuthTokenCacheTest.java
index 81fd6d7157..3c7e4929c9 100644
--- a/javatests/com/google/gerrit/server/auth/oauth/OAuthTokenCacheTest.java
+++ b/javatests/com/google/gerrit/server/auth/oauth/OAuthTokenCacheTest.java
@@ -1,11 +1,26 @@
+// 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.auth.oauth;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
+import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
+import com.google.gerrit.proto.testing.SerializedClassSubject;
import com.google.gerrit.server.cache.proto.Cache.OAuthTokenProto;
import com.google.gerrit.server.cache.serialize.CacheSerializer;
import java.lang.reflect.Type;
@@ -56,10 +71,7 @@ public final class OAuthTokenCacheTest {
assertThat(s.deserialize(serializedWithEmptyString)).isEqualTo(tokenWithNull);
}
- /**
- * See {@link com.google.gerrit.server.cache.testing.SerializedClassSubject} for background and
- * what to do if this test fails.
- */
+ /** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
public void oAuthTokenFields() throws Exception {
assertThatSerializedClass(OAuthToken.class)
diff --git a/javatests/com/google/gerrit/server/cache/BUILD b/javatests/com/google/gerrit/server/cache/BUILD
index 495026615b..c255e619aa 100644
--- a/javatests/com/google/gerrit/server/cache/BUILD
+++ b/javatests/com/google/gerrit/server/cache/BUILD
@@ -5,6 +5,7 @@ junit_tests(
srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:junit",
"//lib/truth",
],
diff --git a/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java b/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
index cfb5f3f361..6a42577d1e 100644
--- a/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
+++ b/javatests/com/google/gerrit/server/cache/PerThreadCacheTest.java
@@ -16,14 +16,11 @@ package com.google.gerrit.server.cache;
import static com.google.common.truth.Truth.assertThat;
+import com.google.gerrit.testing.GerritBaseTests;
import java.util.function.Supplier;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class PerThreadCacheTest {
- @Rule public ExpectedException exception = ExpectedException.none();
+public class PerThreadCacheTest extends GerritBaseTests {
@Test
public void key_respectsClass() {
assertThat(PerThreadCache.Key.create(String.class))
diff --git a/javatests/com/google/gerrit/server/cache/serialize/BUILD b/javatests/com/google/gerrit/server/cache/serialize/BUILD
index 35d8527c72..a0d5ea6767 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/BUILD
+++ b/javatests/com/google/gerrit/server/cache/serialize/BUILD
@@ -6,8 +6,9 @@ junit_tests(
deps = [
"//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:gwtorm",
"//lib:junit",
"//lib:protobuf",
"//lib/auto:auto-value",
@@ -16,5 +17,6 @@ junit_tests(
"//lib/truth",
"//lib/truth:truth-proto-extension",
"//proto:cache_java_proto",
+ "//proto/testing:test_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 75048507b1..c634a785bc 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/BooleanCacheSerializerTest.java
@@ -18,10 +18,11 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
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 {
+public class BooleanCacheSerializerTest extends GerritBaseTests {
@Test
public void serialize() throws Exception {
assertThat(BooleanCacheSerializer.INSTANCE.serialize(true))
diff --git a/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java
index 0b80fc7bbe..c6efc21513 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/EnumCacheSerializerTest.java
@@ -18,9 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class EnumCacheSerializerTest {
+public class EnumCacheSerializerTest extends GerritBaseTests {
@Test
public void serialize() throws Exception {
assertRoundTrip(MyEnum.FOO);
diff --git a/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java
index 987a62a996..56dd6ad5a2 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/IntKeyCacheSerializerTest.java
@@ -17,11 +17,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 com.google.gerrit.testing.GerritBaseTests;
import com.google.gwtorm.client.IntKey;
import com.google.gwtorm.client.Key;
import org.junit.Test;
-public class IntKeyCacheSerializerTest {
+public class IntKeyCacheSerializerTest extends GerritBaseTests {
private static class MyIntKey extends IntKey<Key<?>> {
private static final long serialVersionUID = 1L;
diff --git a/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java
index c2db8082eb..1d540100ae 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/IntegerCacheSerializerTest.java
@@ -19,10 +19,11 @@ import static com.google.common.truth.Truth.assert_;
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 {
+public class IntegerCacheSerializerTest extends GerritBaseTests {
@Test
public void serialize() throws Exception {
for (int i :
diff --git a/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java
index 6596730c10..9fcb8a4688 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/JavaCacheSerializerTest.java
@@ -17,10 +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 java.io.Serializable;
import org.junit.Test;
-public class JavaCacheSerializerTest {
+public class JavaCacheSerializerTest extends GerritBaseTests {
@Test
public void builtInTypes() throws Exception {
assertRoundTrip("foo");
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java
index c56f8f86c7..257be54bdf 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdCacheSerializerTest.java
@@ -18,10 +18,11 @@ 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 com.google.gerrit.testing.GerritBaseTests;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ObjectIdCacheSerializerTest {
+public class ObjectIdCacheSerializerTest extends GerritBaseTests {
@Test
public void serialize() {
ObjectId id = ObjectId.fromString("aabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java
new file mode 100644
index 0000000000..c5ea2ea2c3
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/ObjectIdConverterTest.java
@@ -0,0 +1,72 @@
+// 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 static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
+
+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 {
+ @Test
+ public void objectIdFromByteString() {
+ ObjectIdConverter idConverter = ObjectIdConverter.create();
+ assertThat(
+ idConverter.fromByteString(
+ byteString(
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa)))
+ .isEqualTo(ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+ assertThat(
+ idConverter.fromByteString(
+ byteString(
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb)))
+ .isEqualTo(ObjectId.fromString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
+ }
+
+ @Test
+ public void objectIdFromByteStringWrongSize() {
+ try {
+ ObjectIdConverter.create().fromByteString(ByteString.copyFromUtf8("foo"));
+ assert_().fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void objectIdToByteString() {
+ ObjectIdConverter idConverter = ObjectIdConverter.create();
+ assertThat(
+ idConverter.toByteString(
+ ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
+ .isEqualTo(
+ byteString(
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa));
+ assertThat(
+ idConverter.toByteString(
+ ObjectId.fromString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")))
+ .isEqualTo(
+ byteString(
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb));
+ }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ProtoCacheSerializersTest.java b/javatests/com/google/gerrit/server/cache/serialize/ProtoCacheSerializersTest.java
deleted file mode 100644
index 69694fe6aa..0000000000
--- a/javatests/com/google/gerrit/server/cache/serialize/ProtoCacheSerializersTest.java
+++ /dev/null
@@ -1,116 +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 static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-
-import com.google.gerrit.server.cache.proto.Cache.ChangeNotesKeyProto;
-import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
-import com.google.protobuf.ByteString;
-import org.eclipse.jgit.lib.ObjectId;
-import org.junit.Test;
-
-public class ProtoCacheSerializersTest {
- @Test
- public void objectIdFromByteString() {
- ObjectIdConverter idConverter = ObjectIdConverter.create();
- assertThat(
- idConverter.fromByteString(
- byteString(
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa)))
- .isEqualTo(ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
- assertThat(
- idConverter.fromByteString(
- byteString(
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb)))
- .isEqualTo(ObjectId.fromString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
- }
-
- @Test
- public void objectIdFromByteStringWrongSize() {
- try {
- ObjectIdConverter.create().fromByteString(ByteString.copyFromUtf8("foo"));
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- @Test
- public void objectIdToByteString() {
- ObjectIdConverter idConverter = ObjectIdConverter.create();
- assertThat(
- idConverter.toByteString(
- ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
- .isEqualTo(
- byteString(
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa));
- assertThat(
- idConverter.toByteString(
- ObjectId.fromString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")))
- .isEqualTo(
- byteString(
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb));
- }
-
- @Test
- public void parseUncheckedWrongProtoType() {
- ChangeNotesKeyProto proto =
- ChangeNotesKeyProto.newBuilder()
- .setProject("project")
- .setChangeId(1234)
- .setId(ByteString.copyFromUtf8("foo"))
- .build();
- byte[] bytes = ProtoCacheSerializers.toByteArray(proto);
- try {
- ProtoCacheSerializers.parseUnchecked(ChangeNotesStateProto.parser(), bytes);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- @Test
- public void parseUncheckedInvalidData() {
- byte[] bytes = new byte[] {0x00};
- try {
- ProtoCacheSerializers.parseUnchecked(ChangeNotesStateProto.parser(), bytes);
- assert_().fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- @Test
- public void parseUnchecked() {
- ChangeNotesKeyProto proto =
- ChangeNotesKeyProto.newBuilder()
- .setProject("project")
- .setChangeId(1234)
- .setId(ByteString.copyFromUtf8("foo"))
- .build();
- byte[] bytes = ProtoCacheSerializers.toByteArray(proto);
- assertThat(ProtoCacheSerializers.parseUnchecked(ChangeNotesKeyProto.parser(), bytes))
- .isEqualTo(proto);
- }
-}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.java
new file mode 100644
index 0000000000..845da9b700
--- /dev/null
+++ b/javatests/com/google/gerrit/server/cache/serialize/ProtobufSerializerTest.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.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 {
+ @Test
+ public void requiredAndOptionalTypes() {
+ assertRoundTrip(SerializableProto.newBuilder().setId(123));
+ assertRoundTrip(SerializableProto.newBuilder().setId(123).setText("foo bar"));
+ }
+
+ @Test
+ public void exactByteSequence() {
+ ProtobufSerializer<SerializableProto> s = new ProtobufSerializer<>(SerializableProto.parser());
+ SerializableProto proto = SerializableProto.newBuilder().setId(123).setText("foo bar").build();
+ byte[] serialized = s.serialize(proto);
+ // Hard-code byte sequence to detect library changes
+ assertThat(serialized).isEqualTo(new byte[] {8, 123, 18, 7, 102, 111, 111, 32, 98, 97, 114});
+ }
+
+ private static void assertRoundTrip(SerializableProto.Builder input) {
+ ProtobufSerializer<SerializableProto> s = new ProtobufSerializer<>(SerializableProto.parser());
+ assertThat(s.deserialize(s.serialize(input.build()))).isEqualTo(input.build());
+ }
+}
diff --git a/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java
index fa3b7d73ff..ff0cf9af04 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/StringCacheSerializerTest.java
@@ -17,11 +17,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 com.google.gerrit.testing.GerritBaseTests;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
-public class StringCacheSerializerTest {
+public class StringCacheSerializerTest extends GerritBaseTests {
@Test
public void serialize() {
assertThat(StringCacheSerializer.INSTANCE.serialize("")).isEmpty();
diff --git a/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java b/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
index b847ed7db4..fffb1da213 100644
--- a/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
+++ b/javatests/com/google/gerrit/server/change/ChangeKindCacheImplTest.java
@@ -16,17 +16,19 @@ package com.google.gerrit.server.change;
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 static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
+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 {
+public class ChangeKindCacheImplTest extends GerritBaseTests {
@Test
public void keySerializer() throws Exception {
ChangeKindCacheImpl.Key key =
@@ -49,10 +51,7 @@ public class ChangeKindCacheImplTest {
assertThat(s.deserialize(serialized)).isEqualTo(key);
}
- /**
- * See {@link com.google.gerrit.server.cache.testing.SerializedClassSubject} for background and
- * what to do if this test fails.
- */
+ /** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
public void keyFields() throws Exception {
assertThatSerializedClass(ChangeKindCacheImpl.Key.class)
diff --git a/javatests/com/google/gerrit/server/change/HashtagsTest.java b/javatests/com/google/gerrit/server/change/HashtagsTest.java
index 780ac71929..49d295263c 100644
--- a/javatests/com/google/gerrit/server/change/HashtagsTest.java
+++ b/javatests/com/google/gerrit/server/change/HashtagsTest.java
@@ -17,9 +17,10 @@ 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 {
+public class HashtagsTest extends GerritBaseTests {
@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 dca2dcb23b..0cfe483d0e 100644
--- a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
+++ b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
@@ -15,22 +15,19 @@
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 java.io.IOException;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
+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;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class IncludedInResolverTest extends RepositoryTestCase {
-
+public class IncludedInResolverTest extends GerritBaseTests {
// Branch names
private static final String BRANCH_MASTER = "master";
private static final String BRANCH_1_0 = "rel-1.0";
@@ -53,12 +50,11 @@ public class IncludedInResolverTest extends RepositoryTestCase {
private RevCommit commit_v1_3;
private RevCommit commit_v2_5;
- private RevWalk revWalk;
+ private TestRepository<?> tr;
- @Override
@Before
public void setUp() throws Exception {
- super.setUp();
+ tr = new TestRepository<>(new InMemoryRepository(new DfsRepositoryDescription("repo")));
/*- The following graph will be created.
@@ -78,55 +74,37 @@ public class IncludedInResolverTest extends RepositoryTestCase {
*/
- // TODO(dborowitz): Use try/finally when this doesn't double-close the repo.
- @SuppressWarnings("resource")
- Git git = new Git(db);
- revWalk = new RevWalk(db);
// Version 1.0
- commit_initial = git.commit().setMessage("c1").call();
- git.commit().setMessage("c2").call();
- RevCommit commit_v1_0 = git.commit().setMessage("version 1.0").call();
- git.tag().setName(TAG_1_0).setObjectId(commit_v1_0).call();
- RevCommit c3 = git.commit().setMessage("c3").call();
+ commit_initial = tr.branch(BRANCH_MASTER).commit().message("c1").create();
+ tr.branch(BRANCH_MASTER).commit().message("c2").create();
+ RevCommit commit_v1_0 = tr.branch(BRANCH_MASTER).commit().message("version 1.0").create();
+ tag(TAG_1_0, commit_v1_0);
+ RevCommit c3 = tr.branch(BRANCH_MASTER).commit().message("c3").create();
+
// Version 1.01
- createAndCheckoutBranch(commit_v1_0, BRANCH_1_0);
- RevCommit commit_v1_0_1 = git.commit().setMessage("verREFS_HEADS_RELsion 1.0.1").call();
- git.tag().setName(TAG_1_0_1).setObjectId(commit_v1_0_1).call();
+ tr.branch(BRANCH_1_0).update(commit_v1_0);
+ RevCommit commit_v1_0_1 = tr.branch(BRANCH_1_0).commit().message("version 1.0.1").create();
+ tag(TAG_1_0_1, commit_v1_0_1);
+
// Version 1.3
- createAndCheckoutBranch(c3, BRANCH_1_3);
- commit_v1_3 = git.commit().setMessage("version 1.3").call();
- git.tag().setName(TAG_1_3).setObjectId(commit_v1_3).call();
+ tr.branch(BRANCH_1_3).update(c3);
+ commit_v1_3 = tr.branch(BRANCH_1_3).commit().message("version 1.3").create();
+ tag(TAG_1_3, commit_v1_3);
+
// Version 2.0
- createAndCheckoutBranch(c3, BRANCH_2_0);
- RevCommit commit_v2_0 = git.commit().setMessage("version 2.0").call();
- git.tag().setName(TAG_2_0).setObjectId(commit_v2_0).call();
- RevCommit commit_v2_0_1 = git.commit().setMessage("version 2.0.1").call();
- git.tag().setName(TAG_2_0_1).setObjectId(commit_v2_0_1).call();
+ tr.branch(BRANCH_2_0).update(c3);
+ RevCommit commit_v2_0 = tr.branch(BRANCH_2_0).commit().message("version 2.0").create();
+ tag(TAG_2_0, commit_v2_0);
+ RevCommit commit_v2_0_1 = tr.branch(BRANCH_2_0).commit().message("version 2.0.1").create();
+ tag(TAG_2_0_1, commit_v2_0_1);
// Version 2.5
- createAndCheckoutBranch(commit_v1_3, BRANCH_2_5);
- git.merge()
- .include(commit_v2_0_1)
- .setCommit(false)
- .setFastForward(FastForwardMode.NO_FF)
- .call();
- commit_v2_5 = git.commit().setMessage("version 2.5").call();
- git.tag().setName(TAG_2_5).setObjectId(commit_v2_5).setAnnotated(false).call();
- Ref ref_tag_2_5_annotated =
- git.tag().setName(TAG_2_5_ANNOTATED).setObjectId(commit_v2_5).setAnnotated(true).call();
- RevTag tag_2_5_annotated = revWalk.parseTag(ref_tag_2_5_annotated.getObjectId());
- git.tag()
- .setName(TAG_2_5_ANNOTATED_TWICE)
- .setObjectId(tag_2_5_annotated)
- .setAnnotated(true)
- .call();
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- revWalk.close();
- super.tearDown();
+ tr.branch(BRANCH_2_5).update(commit_v1_3);
+ tr.branch(BRANCH_2_5).commit().parent(commit_v2_0_1).create(); // Merge v2.0.1
+ commit_v2_5 = tr.branch(BRANCH_2_5).commit().message("version 2.5").create();
+ tr.update(REFS_TAGS + TAG_2_5, commit_v2_5);
+ RevTag tag_2_5_annotated = tag(TAG_2_5_ANNOTATED, commit_v2_5);
+ tag(TAG_2_5_ANNOTATED_TWICE, tag_2_5_annotated);
}
@Test
@@ -171,12 +149,10 @@ public class IncludedInResolverTest extends RepositoryTestCase {
}
private IncludedInResolver.Result resolve(RevCommit commit) throws Exception {
- return IncludedInResolver.resolve(db, revWalk, commit);
+ return IncludedInResolver.resolve(tr.getRepository(), tr.getRevWalk(), commit);
}
- private void createAndCheckoutBranch(ObjectId objectId, String branchName) throws IOException {
- String fullBranchName = "refs/heads/" + branchName;
- super.createBranch(objectId, fullBranchName);
- super.checkoutBranch(fullBranchName);
+ private RevTag tag(String name, RevObject dest) throws Exception {
+ return tr.update(REFS_TAGS + name, tr.tag(name, dest));
}
}
diff --git a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
index 5a067f189a..6e02d61713 100644
--- a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
+++ b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
@@ -24,16 +24,14 @@ import static org.junit.Assert.assertEquals;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.LabelType;
+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.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.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
@@ -45,16 +43,13 @@ import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectConfig;
import com.google.gerrit.server.schema.SchemaCreator;
-import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.InMemoryDatabase;
+import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
-import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.util.List;
import org.eclipse.jgit.lib.Repository;
import org.junit.After;
@@ -62,21 +57,21 @@ import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link LabelNormalizer}. */
-public class LabelNormalizerTest {
+public class LabelNormalizerTest extends GerritBaseTests {
@Inject private AccountManager accountManager;
@Inject private AllProjectsName allProjects;
@Inject private GitRepositoryManager repoManager;
@Inject private IdentifiedUser.GenericFactory userFactory;
- @Inject private InMemoryDatabase schemaFactory;
@Inject private LabelNormalizer norm;
@Inject private MetaDataUpdate.User metaDataUpdateFactory;
@Inject private ProjectCache projectCache;
@Inject private SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
@Inject private ChangeNotes.Factory changeNotesFactory;
+ @Inject private ProjectConfig.Factory projectConfigFactory;
+ @Inject private GerritApi gApi;
private LifecycleManager lifecycle;
- private ReviewDb db;
private Account.Id userId;
private IdentifiedUser user;
private Change change;
@@ -90,23 +85,11 @@ public class LabelNormalizerTest {
lifecycle.add(injector);
lifecycle.start();
- db = schemaFactory.open();
- schemaCreator.create(db);
+ schemaCreator.create();
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
user = userFactory.create(userId);
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(() -> user);
configureProject();
setUpChange();
@@ -126,18 +109,14 @@ public class LabelNormalizerTest {
}
private void setUpChange() throws Exception {
- change =
- new Change(
- new Change.Key("Iabcd1234abcd1234abcd1234abcd1234abcd1234"),
- new Change.Id(1),
- userId,
- new Branch.NameKey(allProjects, "refs/heads/master"),
- TimeUtil.nowTs());
- PatchSetInfo ps = new PatchSetInfo(new PatchSet.Id(change.getId(), 1));
- ps.setSubject("Test change");
- change.setCurrentPatchSet(ps);
- db.changes().insert(ImmutableList.of(change));
- notes = changeNotesFactory.createChecked(db, change);
+ ChangeInput input = new ChangeInput();
+ input.project = allProjects.get();
+ input.branch = "master";
+ input.newBranch = true;
+ input.subject = "Test change";
+ ChangeInfo info = gApi.changes().create(input).get();
+ notes = changeNotesFactory.createChecked(allProjects, new Change.Id(info._number));
+ change = notes.getChange();
}
@After
@@ -146,10 +125,6 @@ public class LabelNormalizerTest {
lifecycle.stop();
}
requestContext.setContext(null);
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(schemaFactory);
}
@Test
@@ -198,7 +173,7 @@ public class LabelNormalizerTest {
private ProjectConfig loadAllProjects() throws Exception {
try (Repository repo = repoManager.openRepository(allProjects)) {
- ProjectConfig pc = new ProjectConfig(allProjects);
+ ProjectConfig pc = projectConfigFactory.create(allProjects);
pc.load(repo);
return pc;
}
@@ -225,6 +200,6 @@ public class LabelNormalizerTest {
}
private static List<PatchSetApproval> list(PatchSetApproval... psas) {
- return ImmutableList.<PatchSetApproval>copyOf(psas);
+ return ImmutableList.copyOf(psas);
}
}
diff --git a/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java b/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java
index e10a236bce..46ddbc27e0 100644
--- a/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java
+++ b/javatests/com/google/gerrit/server/change/MergeabilityCacheImplTest.java
@@ -16,16 +16,18 @@ package com.google.gerrit.server.change;
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 static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
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 {
+public class MergeabilityCacheImplTest extends GerritBaseTests {
@Test
public void keySerializer() throws Exception {
MergeabilityCacheImpl.EntryKey key =
@@ -53,10 +55,7 @@ public class MergeabilityCacheImplTest {
.isEqualTo(key);
}
- /**
- * See {@link com.google.gerrit.server.cache.testing.SerializedClassSubject} for background and
- * what to do if this test fails.
- */
+ /** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
public void keyFields() throws Exception {
assertThatSerializedClass(MergeabilityCacheImpl.EntryKey.class)
diff --git a/javatests/com/google/gerrit/server/config/ConfigUtilTest.java b/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
index 231b5845ed..b1378ad265 100644
--- a/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
+++ b/javatests/com/google/gerrit/server/config/ConfigUtilTest.java
@@ -21,14 +21,14 @@ 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.extensions.client.Theme;
+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 {
+public class ConfigUtilTest extends GerritBaseTests {
private static final String SECT = "foo";
private static final String SUB = "bar";
@@ -47,8 +47,6 @@ public class ConfigUtilTest {
public String s;
public String sd;
public String nd;
- public Theme t;
- public Theme td;
public List<String> list;
public Map<String, String> map;
@@ -66,8 +64,6 @@ public class ConfigUtilTest {
i.s = "foo";
i.sd = "bar";
// i.nd = null; // Don't need to explicitly set it; it's null by default
- i.t = Theme.DEFAULT;
- i.td = Theme.DEFAULT;
return i;
}
}
@@ -85,7 +81,6 @@ public class ConfigUtilTest {
in.bb = true;
in.bd = false;
in.s = "baz";
- in.t = Theme.MIDNIGHT;
Config cfg = new Config();
ConfigUtil.storeSection(cfg, SECT, SUB, in, d);
@@ -116,8 +111,6 @@ public class ConfigUtilTest {
assertThat(out.s).isEqualTo(in.s);
assertThat(out.sd).isEqualTo(d.sd);
assertThat(out.nd).isNull();
- assertThat(out.t).isEqualTo(in.t);
- assertThat(out.td).isEqualTo(d.td);
}
@Test
diff --git a/javatests/com/google/gerrit/server/config/GitwebConfigTest.java b/javatests/com/google/gerrit/server/config/GitwebConfigTest.java
index cb6de34b76..bf7e4fd237 100644
--- a/javatests/com/google/gerrit/server/config/GitwebConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/GitwebConfigTest.java
@@ -16,9 +16,10 @@ 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 {
+public class GitwebConfigTest extends GerritBaseTests {
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 fd9c925cd9..30fabdc521 100644
--- a/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
+++ b/javatests/com/google/gerrit/server/config/ListCapabilitiesTest.java
@@ -16,15 +16,18 @@ package com.google.gerrit.server.config;
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.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.Id;
+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;
@@ -33,7 +36,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
-public class ListCapabilitiesTest {
+public class ListCapabilitiesTest extends GerritBaseTests {
private Injector injector;
@Before
@@ -43,8 +46,18 @@ public class ListCapabilitiesTest {
@Override
protected void configure() {
DynamicMap.mapOf(binder(), CapabilityDefinition.class);
+ DynamicMap.mapOf(binder(), PluginProjectPermissionDefinition.class);
bind(CapabilityDefinition.class)
- .annotatedWith(Exports.named("printHello"))
+ .annotatedWith(Exports.named("foo"))
+ .toInstance(
+ new CapabilityDefinition() {
+ @Override
+ public String getDescription() {
+ return "Print Hello";
+ }
+ });
+ bind(CapabilityDefinition.class)
+ .annotatedWith(Exports.named("bar"))
.toInstance(
new CapabilityDefinition() {
@Override
@@ -68,10 +81,11 @@ public class ListCapabilitiesTest {
assertThat(m.get(id).name).isNotNull();
}
- String pluginCapability = "gerrit-printHello";
- assertThat(m).containsKey(pluginCapability);
- assertThat(m.get(pluginCapability).id).isEqualTo(pluginCapability);
- assertThat(m.get(pluginCapability).name).isEqualTo("Print Hello");
+ for (String pluginCapability : ImmutableSet.of("gerrit-foo", "gerrit-bar")) {
+ assertThat(m).containsKey(pluginCapability);
+ assertThat(m.get(pluginCapability).id).isEqualTo(pluginCapability);
+ assertThat(m.get(pluginCapability).name).isEqualTo("Print Hello");
+ }
}
@Singleton
@@ -87,7 +101,7 @@ public class ListCapabilitiesTest {
}
@Override
- public WithUser absentUser(Id id) {
+ public WithUser absentUser(Account.Id id) {
throw new UnsupportedOperationException();
}
diff --git a/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java b/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java
index 2edcf7c523..2a473f42e5 100644
--- a/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/RepositoryConfigTest.java
@@ -19,7 +19,8 @@ import static com.google.common.truth.Truth8.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Project.NameKey;
+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;
@@ -27,7 +28,7 @@ import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
-public class RepositoryConfigTest {
+public class RepositoryConfigTest extends GerritBaseTests {
private Config cfg;
private RepositoryConfig repoCfg;
@@ -41,35 +42,35 @@ public class RepositoryConfigTest {
@Test
public void defaultSubmitTypeWhenNotConfigured() {
// Check expected value explicitly rather than depending on constant.
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.INHERIT);
}
@Test
public void defaultSubmitTypeForStarFilter() {
configureDefaultSubmitType("*", SubmitType.CHERRY_PICK);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.CHERRY_PICK);
configureDefaultSubmitType("*", SubmitType.FAST_FORWARD_ONLY);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.FAST_FORWARD_ONLY);
configureDefaultSubmitType("*", SubmitType.REBASE_IF_NECESSARY);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.REBASE_IF_NECESSARY);
configureDefaultSubmitType("*", SubmitType.REBASE_ALWAYS);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.REBASE_ALWAYS);
}
@Test
public void defaultSubmitTypeForSpecificFilter() {
configureDefaultSubmitType("someProject", SubmitType.CHERRY_PICK);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someOtherProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someOtherProject")))
.isEqualTo(RepositoryConfig.DEFAULT_SUBMIT_TYPE);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.CHERRY_PICK);
}
@@ -79,13 +80,13 @@ public class RepositoryConfigTest {
configureDefaultSubmitType("somePath/*", SubmitType.CHERRY_PICK);
configureDefaultSubmitType("*", SubmitType.MERGE_ALWAYS);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("someProject")))
.isEqualTo(SubmitType.MERGE_ALWAYS);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("somePath/someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("somePath/someProject")))
.isEqualTo(SubmitType.CHERRY_PICK);
- assertThat(repoCfg.getDefaultSubmitType(new NameKey("somePath/somePath/someProject")))
+ assertThat(repoCfg.getDefaultSubmitType(new Project.NameKey("somePath/somePath/someProject")))
.isEqualTo(SubmitType.REBASE_IF_NECESSARY);
}
@@ -99,14 +100,14 @@ public class RepositoryConfigTest {
@Test
public void ownerGroupsWhenNotConfigured() {
- assertThat(repoCfg.getOwnerGroups(new NameKey("someProject"))).isEmpty();
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject"))).isEmpty();
}
@Test
public void ownerGroupsForStarFilter() {
ImmutableList<String> ownerGroups = ImmutableList.of("group1", "group2");
configureOwnerGroups("*", ownerGroups);
- assertThat(repoCfg.getOwnerGroups(new NameKey("someProject")))
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject")))
.containsExactlyElementsIn(ownerGroups);
}
@@ -114,8 +115,8 @@ public class RepositoryConfigTest {
public void ownerGroupsForSpecificFilter() {
ImmutableList<String> ownerGroups = ImmutableList.of("group1", "group2");
configureOwnerGroups("someProject", ownerGroups);
- assertThat(repoCfg.getOwnerGroups(new NameKey("someOtherProject"))).isEmpty();
- assertThat(repoCfg.getOwnerGroups(new NameKey("someProject")))
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someOtherProject"))).isEmpty();
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject")))
.containsExactlyElementsIn(ownerGroups);
}
@@ -129,13 +130,13 @@ public class RepositoryConfigTest {
configureOwnerGroups("somePath/*", ownerGroups2);
configureOwnerGroups("somePath/somePath/*", ownerGroups3);
- assertThat(repoCfg.getOwnerGroups(new NameKey("someProject")))
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("someProject")))
.containsExactlyElementsIn(ownerGroups1);
- assertThat(repoCfg.getOwnerGroups(new NameKey("somePath/someProject")))
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("somePath/someProject")))
.containsExactlyElementsIn(ownerGroups2);
- assertThat(repoCfg.getOwnerGroups(new NameKey("somePath/somePath/someProject")))
+ assertThat(repoCfg.getOwnerGroups(new Project.NameKey("somePath/somePath/someProject")))
.containsExactlyElementsIn(ownerGroups3);
}
@@ -149,22 +150,24 @@ public class RepositoryConfigTest {
@Test
public void basePathWhenNotConfigured() {
- assertThat(repoCfg.getBasePath(new NameKey("someProject"))).isNull();
+ assertThat(repoCfg.getBasePath(new Project.NameKey("someProject"))).isNull();
}
@Test
public void basePathForStarFilter() {
String basePath = "/someAbsolutePath/someDirectory";
configureBasePath("*", basePath);
- assertThat(repoCfg.getBasePath(new NameKey("someProject")).toString()).isEqualTo(basePath);
+ assertThat(repoCfg.getBasePath(new Project.NameKey("someProject")).toString())
+ .isEqualTo(basePath);
}
@Test
public void basePathForSpecificFilter() {
String basePath = "/someAbsolutePath/someDirectory";
configureBasePath("someProject", basePath);
- assertThat(repoCfg.getBasePath(new NameKey("someOtherProject"))).isNull();
- assertThat(repoCfg.getBasePath(new NameKey("someProject")).toString()).isEqualTo(basePath);
+ assertThat(repoCfg.getBasePath(new Project.NameKey("someOtherProject"))).isNull();
+ assertThat(repoCfg.getBasePath(new Project.NameKey("someProject")).toString())
+ .isEqualTo(basePath);
}
@Test
@@ -179,12 +182,14 @@ public class RepositoryConfigTest {
configureBasePath("project/*", basePath3);
configureBasePath("*", basePath4);
- assertThat(repoCfg.getBasePath(new NameKey("project1")).toString()).isEqualTo(basePath1);
- assertThat(repoCfg.getBasePath(new NameKey("project/project/someProject")).toString())
+ assertThat(repoCfg.getBasePath(new Project.NameKey("project1")).toString())
+ .isEqualTo(basePath1);
+ assertThat(repoCfg.getBasePath(new Project.NameKey("project/project/someProject")).toString())
.isEqualTo(basePath2);
- assertThat(repoCfg.getBasePath(new NameKey("project/someProject")).toString())
+ assertThat(repoCfg.getBasePath(new Project.NameKey("project/someProject")).toString())
.isEqualTo(basePath3);
- assertThat(repoCfg.getBasePath(new NameKey("someProject")).toString()).isEqualTo(basePath4);
+ assertThat(repoCfg.getBasePath(new 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 55f0374b56..69260523a7 100644
--- a/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
@@ -22,6 +22,7 @@ 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;
@@ -31,7 +32,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
-public class ScheduleConfigTest {
+public class ScheduleConfigTest extends GerritBaseTests {
// Friday June 13, 2014 10:00 UTC
private static final ZonedDateTime NOW =
diff --git a/javatests/com/google/gerrit/server/edit/ChangeEditTest.java b/javatests/com/google/gerrit/server/edit/ChangeEditTest.java
index 6509c4b0c0..4c0b5a18c3 100644
--- a/javatests/com/google/gerrit/server/edit/ChangeEditTest.java
+++ b/javatests/com/google/gerrit/server/edit/ChangeEditTest.java
@@ -20,9 +20,10 @@ 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 org.junit.Test;
-public class ChangeEditTest {
+public class ChangeEditTest extends GerritBaseTests {
@Test
public void changeEditRef() throws Exception {
Account.Id accountId = new Account.Id(1000042);
diff --git a/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java b/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java
index 574c795175..265b24e46e 100644
--- a/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java
+++ b/javatests/com/google/gerrit/server/edit/tree/ChangeFileContentModificationSubject.java
@@ -20,7 +20,6 @@ import com.google.common.io.CharStreams;
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.restapi.RawInput;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -41,16 +40,16 @@ public class ChangeFileContentModificationSubject
public StringSubject filePath() {
isNotNull();
- return Truth.assertThat(actual().getFilePath()).named("filePath");
+ return check("filePath()").that(actual().getFilePath());
}
public StringSubject newContent() throws IOException {
isNotNull();
RawInput newContent = actual().getNewContent();
- Truth.assertThat(newContent).named("newContent").isNotNull();
+ check("newContent()").that(newContent).isNotNull();
String contentString =
CharStreams.toString(
new InputStreamReader(newContent.getInputStream(), StandardCharsets.UTF_8));
- return Truth.assertThat(contentString).named("newContent");
+ return check("newContent()").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 59ee2b73b5..bd9d4df862 100644
--- a/javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java
+++ b/javatests/com/google/gerrit/server/edit/tree/TreeModificationSubject.java
@@ -24,12 +24,17 @@ import java.util.List;
public class TreeModificationSubject extends Subject<TreeModificationSubject, TreeModification> {
public static TreeModificationSubject assertThat(TreeModification treeModification) {
- return assertAbout(TreeModificationSubject::new).that(treeModification);
+ return assertAbout(treeModifications()).that(treeModification);
+ }
+
+ private static Factory<TreeModificationSubject, TreeModification> treeModifications() {
+ return TreeModificationSubject::new;
}
public static ListSubject<TreeModificationSubject, TreeModification> assertThatList(
List<TreeModification> treeModifications) {
- return ListSubject.assertThat(treeModifications, TreeModificationSubject::assertThat)
+ return assertAbout(ListSubject.elements())
+ .thatCustom(treeModifications, treeModifications())
.named("treeModifications");
}
diff --git a/javatests/com/google/gerrit/server/events/EventDeserializerTest.java b/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
index 997fda9191..eac8d0dcab 100644
--- a/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
+++ b/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
@@ -20,11 +20,12 @@ import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
+import com.google.gerrit.testing.GerritBaseTests;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.junit.Test;
-public class EventDeserializerTest {
+public class EventDeserializerTest extends GerritBaseTests {
@Test
public void refUpdatedEvent() {
@@ -58,12 +59,6 @@ public class EventDeserializerTest {
}
private <T> Supplier<T> createSupplier(T value) {
- return Suppliers.memoize(
- new Supplier<T>() {
- @Override
- public T get() {
- return value;
- }
- });
+ return Suppliers.memoize(() -> value);
}
}
diff --git a/javatests/com/google/gerrit/server/events/EventJsonTest.java b/javatests/com/google/gerrit/server/events/EventJsonTest.java
index fb7ffd4774..b59641d70e 100644
--- a/javatests/com/google/gerrit/server/events/EventJsonTest.java
+++ b/javatests/com/google/gerrit/server/events/EventJsonTest.java
@@ -577,10 +577,10 @@ public class EventJsonTest extends GerritBaseTests {
private Change newChange() {
return new Change(
- new Change.Key(CHANGE_ID),
- new Change.Id(CHANGE_NUM),
- new Account.Id(9999),
- new Branch.NameKey(new Project.NameKey(PROJECT), BRANCH),
+ Change.key(CHANGE_ID),
+ Change.id(CHANGE_NUM),
+ Account.id(9999),
+ Branch.nameKey(Project.nameKey(PROJECT), BRANCH),
TimeUtil.nowTs());
}
diff --git a/javatests/com/google/gerrit/server/events/EventTypesTest.java b/javatests/com/google/gerrit/server/events/EventTypesTest.java
index c822d6c68c..dd5c7f91f1 100644
--- a/javatests/com/google/gerrit/server/events/EventTypesTest.java
+++ b/javatests/com/google/gerrit/server/events/EventTypesTest.java
@@ -16,9 +16,10 @@ 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 {
+public class EventTypesTest extends GerritBaseTests {
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 08d7082518..4a1f47cd85 100644
--- a/javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java
+++ b/javatests/com/google/gerrit/server/extensions/webui/UiActionsTest.java
@@ -18,6 +18,8 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+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;
@@ -30,6 +32,7 @@ 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;
@@ -38,7 +41,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.junit.Test;
-public class UiActionsTest {
+public class UiActionsTest extends GerritBaseTests {
private static class FakeForProject extends ForProject {
private boolean allowValueQueries = true;
@@ -54,19 +57,29 @@ public class UiActionsTest {
}
@Override
- public void check(ProjectPermission perm) throws AuthException, PermissionBackendException {
+ public void check(CoreOrPluginProjectPermission perm)
+ throws AuthException, PermissionBackendException {
throw new UnsupportedOperationException("not implemented");
}
@Override
- public Set<ProjectPermission> test(Collection<ProjectPermission> permSet)
+ public <T extends CoreOrPluginProjectPermission> Set<T> test(Collection<T> permSet)
throws PermissionBackendException {
assertThat(allowValueQueries).isTrue();
- return ImmutableSet.of(ProjectPermission.READ);
+ Set<T> ok = Sets.newHashSetWithExpectedSize(permSet.size());
+ for (T perm : permSet) {
+ // Allow ProjectPermission.READ, if it was requested in the input permSet. This implies
+ // that permSet has type Collection<ProjectPermission>, otherwise no permission would
+ // compare equal to READ.
+ if (perm.equals(ProjectPermission.READ)) {
+ ok.add(perm);
+ }
+ }
+ return ok;
}
@Override
- public BooleanCondition testCond(ProjectPermission perm) {
+ public BooleanCondition testCond(CoreOrPluginProjectPermission perm) {
return new PermissionBackendCondition.ForProject(this, perm, fakeUser());
}
diff --git a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
index c1a65bb66c..cc648bfa7a 100644
--- a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
+++ b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
@@ -26,6 +26,7 @@ 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;
@@ -33,14 +34,9 @@ import org.easymock.EasyMock;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class FixReplacementInterpreterTest {
-
- @Rule public ExpectedException expectedException = ExpectedException.none();
+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);
@@ -261,7 +257,7 @@ public class FixReplacementInterpreterTest {
replay(fileContentUtil);
- expectedException.expect(ResourceConflictException.class);
+ exception.expect(ResourceConflictException.class);
toTreeModifications(fixReplacement);
}
@@ -273,7 +269,7 @@ public class FixReplacementInterpreterTest {
replay(fileContentUtil);
- expectedException.expect(ResourceConflictException.class);
+ exception.expect(ResourceConflictException.class);
toTreeModifications(fixReplacement);
}
@@ -285,7 +281,7 @@ public class FixReplacementInterpreterTest {
replay(fileContentUtil);
- expectedException.expect(ResourceConflictException.class);
+ exception.expect(ResourceConflictException.class);
toTreeModifications(fixReplacement);
}
@@ -297,7 +293,7 @@ public class FixReplacementInterpreterTest {
replay(fileContentUtil);
- expectedException.expect(ResourceConflictException.class);
+ exception.expect(ResourceConflictException.class);
toTreeModifications(fixReplacement);
}
@@ -309,7 +305,7 @@ public class FixReplacementInterpreterTest {
replay(fileContentUtil);
- expectedException.expect(ResourceConflictException.class);
+ exception.expect(ResourceConflictException.class);
toTreeModifications(fixReplacement);
}
diff --git a/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java b/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java
index f6383462ee..309f726ed2 100644
--- a/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java
+++ b/javatests/com/google/gerrit/server/fixes/LineIdentifierTest.java
@@ -16,27 +16,23 @@ package com.google.gerrit.server.fixes;
import static com.google.common.truth.Truth.assertThat;
-import org.junit.Rule;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class LineIdentifierTest {
-
- @Rule public ExpectedException expectedException = ExpectedException.none();
+public class LineIdentifierTest extends GerritBaseTests {
@Test
public void lineNumberMustBePositive() {
LineIdentifier lineIdentifier = new LineIdentifier("First line\nSecond line");
- expectedException.expect(StringIndexOutOfBoundsException.class);
- expectedException.expectMessage("positive");
+ exception.expect(StringIndexOutOfBoundsException.class);
+ exception.expectMessage("positive");
lineIdentifier.getStartIndexOfLine(0);
}
@Test
public void lineNumberMustIndicateAnAvailableLine() {
LineIdentifier lineIdentifier = new LineIdentifier("First line\nSecond line");
- expectedException.expect(StringIndexOutOfBoundsException.class);
- expectedException.expectMessage("Line 3 isn't available");
+ exception.expect(StringIndexOutOfBoundsException.class);
+ exception.expectMessage("Line 3 isn't available");
lineIdentifier.getStartIndexOfLine(3);
}
diff --git a/javatests/com/google/gerrit/server/fixes/StringModifierTest.java b/javatests/com/google/gerrit/server/fixes/StringModifierTest.java
index d23e928c8c..185b58c21c 100644
--- a/javatests/com/google/gerrit/server/fixes/StringModifierTest.java
+++ b/javatests/com/google/gerrit/server/fixes/StringModifierTest.java
@@ -16,15 +16,11 @@ package com.google.gerrit.server.fixes;
import static com.google.common.truth.Truth.assertThat;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class StringModifierTest {
-
- @Rule public ExpectedException expectedException = ExpectedException.none();
+public class StringModifierTest extends GerritBaseTests {
private final String originalString = "This is the original, unmodified string.";
private StringModifier stringModifier;
@@ -67,19 +63,19 @@ public class StringModifierTest {
@Test
public void replacedPartsMustNotOverlap() {
stringModifier.replace(0, 9, "");
- expectedException.expect(StringIndexOutOfBoundsException.class);
+ exception.expect(StringIndexOutOfBoundsException.class);
stringModifier.replace(8, 32, "The modified");
}
@Test
public void startIndexMustNotBeGreaterThanEndIndex() {
- expectedException.expect(StringIndexOutOfBoundsException.class);
+ exception.expect(StringIndexOutOfBoundsException.class);
stringModifier.replace(10, 9, "something");
}
@Test
public void startIndexMustNotBeNegative() {
- expectedException.expect(StringIndexOutOfBoundsException.class);
+ exception.expect(StringIndexOutOfBoundsException.class);
stringModifier.replace(-1, 9, "something");
}
@@ -94,13 +90,13 @@ public class StringModifierTest {
@Test
public void startIndexMustNotBeGreaterThanLengthOfString() {
- expectedException.expect(StringIndexOutOfBoundsException.class);
+ exception.expect(StringIndexOutOfBoundsException.class);
stringModifier.replace(originalString.length() + 1, originalString.length() + 1, "something");
}
@Test
public void endIndexMustNotBeGreaterThanLengthOfString() {
- expectedException.expect(StringIndexOutOfBoundsException.class);
+ exception.expect(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 59343ba798..b278bfea45 100644
--- a/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
+++ b/javatests/com/google/gerrit/server/git/DeleteZombieCommentsRefsTest.java
@@ -18,13 +18,13 @@ 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.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.update.RefUpdateUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import java.io.IOException;
diff --git a/javatests/com/google/gerrit/server/git/GroupCollectorTest.java b/javatests/com/google/gerrit/server/git/GroupCollectorTest.java
index e5e8e261e5..f6942999e5 100644
--- a/javatests/com/google/gerrit/server/git/GroupCollectorTest.java
+++ b/javatests/com/google/gerrit/server/git/GroupCollectorTest.java
@@ -21,6 +21,7 @@ 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 org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
@@ -31,7 +32,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Test;
-public class GroupCollectorTest {
+public class GroupCollectorTest extends GerritBaseTests {
private TestRepository<?> tr;
@Before
diff --git a/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java b/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
index 4e0cb0ca3d..821a6e6b10 100644
--- a/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
+++ b/javatests/com/google/gerrit/server/git/LocalDiskRepositoryManagerTest.java
@@ -20,13 +20,10 @@ import static com.google.common.truth.TruthJUnit.assume;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.ioutil.HostPlatform;
-import com.google.gerrit.testing.TempFileUtil;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
+import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import org.easymock.EasyMockSupport;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
@@ -35,13 +32,12 @@ import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.FS;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
-public class LocalDiskRepositoryManagerTest extends EasyMockSupport {
-
- static {
- KeyUtil.setEncoderImpl(new StandardKeyEncoder());
- }
+public class LocalDiskRepositoryManagerTest extends GerritBaseTests {
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
private Config cfg;
private SitePaths site;
@@ -49,7 +45,7 @@ public class LocalDiskRepositoryManagerTest extends EasyMockSupport {
@Before
public void setUp() throws Exception {
- site = new SitePaths(TempFileUtil.createTempDirectory().toPath());
+ site = new SitePaths(temporaryFolder.newFolder().toPath());
site.resolve("git").toFile().mkdir();
cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
diff --git a/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java b/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
index e848fa3248..fc79a6de5f 100644
--- a/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
+++ b/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
@@ -25,7 +25,6 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.RepositoryConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TempFileUtil;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -37,11 +36,14 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.FS;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
private Config cfg;
private SitePaths site;
private MultiBaseLocalDiskRepositoryManager repoManager;
@@ -49,7 +51,7 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
@Before
public void setUp() throws IOException {
- site = new SitePaths(TempFileUtil.createTempDirectory().toPath());
+ site = new SitePaths(temporaryFolder.newFolder().toPath());
site.resolve("git").toFile().mkdir();
cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
@@ -59,11 +61,6 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock);
}
- @After
- public void tearDown() throws IOException {
- TempFileUtil.cleanup();
- }
-
@Test
public void defaultRepositoryLocation()
throws RepositoryCaseMismatchException, RepositoryNotFoundException, IOException {
@@ -91,7 +88,7 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
@Test
public void alternateRepositoryLocation() throws IOException {
- Path alternateBasePath = TempFileUtil.createTempDirectory().toPath();
+ Path alternateBasePath = temporaryFolder.newFolder().toPath();
Project.NameKey someProjectKey = new Project.NameKey("someProject");
reset(configMock);
expect(configMock.getBasePath(someProjectKey)).andReturn(alternateBasePath).anyTimes();
@@ -124,7 +121,7 @@ public class MultiBaseLocalDiskRepositoryManagerTest extends GerritBaseTests {
Project.NameKey misplacedProject1 = new Project.NameKey("misplacedProject1");
Project.NameKey misplacedProject2 = new Project.NameKey("misplacedProject2");
- Path alternateBasePath = TempFileUtil.createTempDirectory().toPath();
+ Path alternateBasePath = temporaryFolder.newFolder().toPath();
reset(configMock);
expect(configMock.getBasePath(altPathProject)).andReturn(alternateBasePath).anyTimes();
diff --git a/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java b/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.java
new file mode 100644
index 0000000000..8c1707556e
--- /dev/null
+++ b/javatests/com/google/gerrit/server/git/PureRevertCacheKeyTest.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.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.server.cache.proto.Cache;
+import com.google.protobuf.ByteString;
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Test;
+
+public class PureRevertCacheKeyTest {
+ @Test
+ public void serialization() {
+ ObjectId revert = ObjectId.zeroId();
+ ObjectId original = ObjectId.fromString("aabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
+
+ byte[] serializedRevert =
+ new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ byte[] serializedOriginal =
+ byteArray(
+ 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);
+ assertThat(key)
+ .isEqualTo(
+ Cache.PureRevertKeyProto.newBuilder()
+ .setProject("test")
+ .setClaimedRevert(ByteString.copyFrom(serializedRevert))
+ .setClaimedOriginal(ByteString.copyFrom(serializedOriginal))
+ .build());
+ }
+}
diff --git a/javatests/com/google/gerrit/server/git/TagSetHolderTest.java b/javatests/com/google/gerrit/server/git/TagSetHolderTest.java
index 3d3e734c75..87ddc7582a 100644
--- a/javatests/com/google/gerrit/server/git/TagSetHolderTest.java
+++ b/javatests/com/google/gerrit/server/git/TagSetHolderTest.java
@@ -16,14 +16,15 @@ package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
+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.server.cache.proto.Cache.TagSetHolderProto;
+import com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class TagSetHolderTest {
+public class TagSetHolderTest extends GerritBaseTests {
@Test
public void serializerWithTagSet() throws Exception {
TagSetHolder holder = new TagSetHolder(new Project.NameKey("project"));
diff --git a/javatests/com/google/gerrit/server/git/TagSetTest.java b/javatests/com/google/gerrit/server/git/TagSetTest.java
index 1eebe75f3f..3ac72be63e 100644
--- a/javatests/com/google/gerrit/server/git/TagSetTest.java
+++ b/javatests/com/google/gerrit/server/git/TagSetTest.java
@@ -17,8 +17,8 @@ package com.google.gerrit.server.git;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
@@ -30,6 +30,7 @@ import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto.
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;
@@ -42,7 +43,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
import org.junit.Test;
-public class TagSetTest {
+public class TagSetTest extends GerritBaseTests {
@Test
public void roundTripToProto() {
HashMap<String, CachedRef> refs = new HashMap<>();
diff --git a/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java b/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java
index 7e12439ab0..dedccc23ab 100644
--- a/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java
+++ b/javatests/com/google/gerrit/server/git/meta/VersionedMetaDataTest.java
@@ -22,11 +22,12 @@ 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.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.update.RefUpdateUtil;
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;
@@ -50,7 +51,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class VersionedMetaDataTest {
+public class VersionedMetaDataTest extends GerritBaseTests {
// 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.
diff --git a/javatests/com/google/gerrit/server/group/db/BUILD b/javatests/com/google/gerrit/server/group/db/BUILD
index f3dd5d61af..b4652c9b6b 100644
--- a/javatests/com/google/gerrit/server/group/db/BUILD
+++ b/javatests/com/google/gerrit/server/group/db/BUILD
@@ -8,8 +8,10 @@ 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/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",
@@ -18,7 +20,6 @@ junit_tests(
"//java/com/google/gerrit/testing:gerrit-test-util",
"//java/com/google/gerrit/truth",
"//lib:guava",
- "//lib:gwtorm",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/jgit/org.eclipse.jgit.junit: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 a5744d1254..c9ba72ea2d 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
@@ -16,6 +16,7 @@ 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.truth.OptionalSubject.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
@@ -32,9 +33,8 @@ 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 com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
import java.io.IOException;
import java.sql.Timestamp;
import java.time.LocalDate;
@@ -53,18 +53,9 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class GroupConfigTest {
- static {
- // Necessary so that toString() methods of ReviewDb entities work correctly.
- KeyUtil.setEncoderImpl(new StandardKeyEncoder());
- }
-
- @Rule public ExpectedException expectedException = ExpectedException.none();
+public class GroupConfigTest extends GerritBaseTests {
private Project.NameKey projectName;
private Repository repository;
private TestRepository<?> testRepository;
@@ -122,8 +113,8 @@ public class GroupConfigTest {
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -135,8 +126,8 @@ public class GroupConfigTest {
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -157,8 +148,8 @@ public class GroupConfigTest {
GroupConfig groupConfig = GroupConfig.createForNewGroup(projectName, repository, groupCreation);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("ID of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("ID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -236,8 +227,8 @@ public class GroupConfigTest {
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -251,8 +242,8 @@ public class GroupConfigTest {
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -362,8 +353,8 @@ public class GroupConfigTest {
public void idInConfigMustBeDefined() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tname = users\n\townerGroupUuid = owners\n");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("ID of the group " + groupUuid);
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage("ID of the group " + groupUuid);
GroupConfig.loadForGroup(projectName, repository, groupUuid);
}
@@ -372,8 +363,8 @@ public class GroupConfigTest {
populateGroupConfig(
groupUuid, "[group]\n\tname = users\n\tid = -5\n\townerGroupUuid = owners\n");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("ID of the group " + groupUuid);
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage("ID of the group " + groupUuid);
GroupConfig.loadForGroup(projectName, repository, groupUuid);
}
@@ -398,8 +389,8 @@ public class GroupConfigTest {
public void ownerGroupUuidInConfigMustBeDefined() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tname = users\n\tid = 42\n");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("Owner UUID of the group " + groupUuid);
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage("Owner UUID of the group " + groupUuid);
GroupConfig.loadForGroup(projectName, repository, groupUuid);
}
@@ -452,8 +443,8 @@ public class GroupConfigTest {
populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
populateMembersFile(groupUuid, "One");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("Invalid file members");
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage("Invalid file members");
loadGroup(groupUuid);
}
@@ -462,8 +453,8 @@ public class GroupConfigTest {
populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
populateMembersFile(groupUuid, "1\t2");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("Invalid file members");
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage("Invalid file members");
loadGroup(groupUuid);
}
@@ -554,8 +545,8 @@ public class GroupConfigTest {
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -570,8 +561,8 @@ public class GroupConfigTest {
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -637,8 +628,8 @@ public class GroupConfigTest {
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -653,8 +644,8 @@ public class GroupConfigTest {
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group " + groupUuid);
+ exception.expectCause(instanceOf(ConfigInvalidException.class));
+ exception.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@@ -1683,6 +1674,6 @@ public class GroupConfigTest {
private static OptionalSubject<InternalGroupSubject, InternalGroup> assertThatGroup(
Optional<InternalGroup> loadedGroup) {
- return assertThat(loadedGroup, InternalGroupSubject::assertThat);
+ return assertThat(loadedGroup, internalGroups());
}
}
diff --git a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
index 42d01e2bfd..2a8d55162e 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
@@ -16,7 +16,9 @@ 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.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.truth.OptionalSubject.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -25,8 +27,10 @@ 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.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;
@@ -34,15 +38,12 @@ 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.update.RefUpdateUtil;
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;
import com.google.gerrit.truth.OptionalSubject;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.StandardKeyEncoder;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -66,21 +67,13 @@ 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;
-import org.junit.rules.ExpectedException;
-
-public class GroupNameNotesTest {
- static {
- KeyUtil.setEncoderImpl(new StandardKeyEncoder());
- }
+public class GroupNameNotesTest extends GerritBaseTests {
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");
- @Rule public ExpectedException expectedException = ExpectedException.none();
-
private final AccountGroup.UUID groupUuid = new AccountGroup.UUID("users-XYZ");
private final AccountGroup.NameKey groupName = new AccountGroup.NameKey("users");
@@ -112,13 +105,13 @@ public class GroupNameNotesTest {
@Test
public void uuidOfNewGroupMustNotBeNull() throws Exception {
- expectedException.expect(NullPointerException.class);
+ exception.expect(NullPointerException.class);
GroupNameNotes.forNewGroup(allUsersName, repo, null, groupName);
}
@Test
public void nameOfNewGroupMustNotBeNull() throws Exception {
- expectedException.expect(NullPointerException.class);
+ exception.expect(NullPointerException.class);
GroupNameNotes.forNewGroup(allUsersName, repo, groupUuid, null);
}
@@ -136,8 +129,8 @@ public class GroupNameNotesTest {
createGroup(groupUuid, groupName);
AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("AnotherGroup");
- expectedException.expect(OrmDuplicateKeyException.class);
- expectedException.expectMessage(groupName.get());
+ exception.expect(DuplicateKeyException.class);
+ exception.expectMessage(groupName.get());
GroupNameNotes.forNewGroup(allUsersName, repo, anotherGroupUuid, groupName);
}
@@ -181,7 +174,7 @@ public class GroupNameNotesTest {
public void groupCannotBeRenamedToNull() throws Exception {
createGroup(groupUuid, groupName);
- expectedException.expect(NullPointerException.class);
+ exception.expect(NullPointerException.class);
GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, null);
}
@@ -190,7 +183,7 @@ public class GroupNameNotesTest {
createGroup(groupUuid, groupName);
AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- expectedException.expect(NullPointerException.class);
+ exception.expect(NullPointerException.class);
GroupNameNotes.forRename(allUsersName, repo, groupUuid, null, anotherName);
}
@@ -200,8 +193,8 @@ public class GroupNameNotesTest {
AccountGroup.NameKey anotherOldName = new AccountGroup.NameKey("contributors");
AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage(anotherOldName.get());
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage(anotherOldName.get());
GroupNameNotes.forRename(allUsersName, repo, groupUuid, anotherOldName, anotherName);
}
@@ -212,8 +205,8 @@ public class GroupNameNotesTest {
AccountGroup.NameKey anotherGroupName = new AccountGroup.NameKey("admins");
createGroup(anotherGroupUuid, anotherGroupName);
- expectedException.expect(OrmDuplicateKeyException.class);
- expectedException.expectMessage(anotherGroupName.get());
+ exception.expect(DuplicateKeyException.class);
+ exception.expectMessage(anotherGroupName.get());
GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, anotherGroupName);
}
@@ -222,7 +215,7 @@ public class GroupNameNotesTest {
createGroup(groupUuid, groupName);
AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- expectedException.expect(NullPointerException.class);
+ exception.expect(NullPointerException.class);
GroupNameNotes.forRename(allUsersName, repo, null, groupName, anotherName);
}
@@ -232,8 +225,8 @@ public class GroupNameNotesTest {
AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC");
AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins");
- expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage(groupUuid.get());
+ exception.expect(ConfigInvalidException.class);
+ exception.expectMessage(groupUuid.get());
GroupNameNotes.forRename(allUsersName, repo, anotherGroupUuid, groupName, anotherName);
}
@@ -260,7 +253,7 @@ public class GroupNameNotesTest {
createGroup(anotherGroupUuid, anotherName);
ImmutableList<CommitInfo> commitsAfterFurtherGroup = log();
- assertThatCommits(commitsAfterFurtherGroup).containsAllIn(commitsAfterCreation);
+ assertThatCommits(commitsAfterFurtherGroup).containsAtLeastElementsIn(commitsAfterCreation);
assertThatCommits(commitsAfterFurtherGroup).lastElement().isNotIn(commitsAfterCreation);
}
@@ -273,7 +266,7 @@ public class GroupNameNotesTest {
renameGroup(groupUuid, groupName, anotherName);
ImmutableList<CommitInfo> commitsAfterRename = log();
- assertThatCommits(commitsAfterRename).containsAllIn(commitsAfterCreation);
+ assertThatCommits(commitsAfterRename).containsAtLeastElementsIn(commitsAfterCreation);
assertThatCommits(commitsAfterRename).lastElement().isNotIn(commitsAfterCreation);
}
@@ -431,35 +424,36 @@ public class GroupNameNotesTest {
GroupReference g1 = newGroup("a");
GroupReference g2 = newGroup("b");
- TestRepository<?> tr = new TestRepository<>(repo);
- ObjectId k1 = getNoteKey(g1);
- ObjectId k2 = getNoteKey(g2);
- ObjectId k3 = GroupNameNotes.getNoteKey(new AccountGroup.NameKey("c"));
- PersonIdent ident = newPersonIdent();
- ObjectId origCommitId =
- tr.branch(REFS_GROUPNAMES)
- .commit()
- .message("Prepopulate group name")
- .author(ident)
- .committer(ident)
- .add(k1.name(), "[group]\n\tuuid = a-1\n\tname = a\nanotherKey = foo\n")
- .add(k2.name(), "[group]\n\tuuid = a-1\n\tname = b\n")
- .add(k3.name(), "[group]\n\tuuid = c-3\n\tname = c\n")
- .create()
- .copy();
-
- ident = newPersonIdent();
- updateAllGroups(ident, g1, g2);
-
- assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(g1, g2);
-
- ImmutableList<CommitInfo> log = log();
- assertThat(log).hasSize(2);
- assertThat(log.get(0)).commit().isEqualTo(origCommitId.name());
-
- assertThat(log.get(1)).message().isEqualTo("Store 2 group names");
- assertThat(log.get(1)).author().matches(ident);
- assertThat(log.get(1)).committer().matches(ident);
+ try (TestRepository<?> tr = new TestRepository<>(repo)) {
+ ObjectId k1 = getNoteKey(g1);
+ ObjectId k2 = getNoteKey(g2);
+ ObjectId k3 = GroupNameNotes.getNoteKey(new AccountGroup.NameKey("c"));
+ PersonIdent ident = newPersonIdent();
+ ObjectId origCommitId =
+ tr.branch(REFS_GROUPNAMES)
+ .commit()
+ .message("Prepopulate group name")
+ .author(ident)
+ .committer(ident)
+ .add(k1.name(), "[group]\n\tuuid = a-1\n\tname = a\nanotherKey = foo\n")
+ .add(k2.name(), "[group]\n\tuuid = a-1\n\tname = b\n")
+ .add(k3.name(), "[group]\n\tuuid = c-3\n\tname = c\n")
+ .create()
+ .copy();
+
+ ident = newPersonIdent();
+ updateAllGroups(ident, g1, g2);
+
+ assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(g1, g2);
+
+ ImmutableList<CommitInfo> log = log();
+ assertThat(log).hasSize(2);
+ assertThat(log.get(0)).commit().isEqualTo(origCommitId.name());
+
+ assertThat(log.get(1)).message().isEqualTo("Store 2 group names");
+ assertThat(log.get(1)).author().matches(ident);
+ assertThat(log.get(1)).committer().matches(ident);
+ }
// Old note content was overwritten.
assertThat(readNameNote(g1)).isEqualTo("[group]\n\tuuid = a-1\n\tname = a\n");
@@ -593,11 +587,11 @@ public class GroupNameNotesTest {
private static OptionalSubject<GroupReferenceSubject, GroupReference> assertThatGroup(
Optional<GroupReference> group) {
- return assertThat(group, GroupReferenceSubject::assertThat);
+ return assertThat(group, groupReferences());
}
private static ListSubject<CommitInfoSubject, CommitInfo> assertThatCommits(
List<CommitInfo> commits) {
- return ListSubject.assertThat(commits, CommitInfoSubject::assertThat);
+ return ListSubject.assertThat(commits, commits());
}
}
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
index 53994a61b9..fd23da3679 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
@@ -68,7 +68,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
public void nonIndexPredicate() throws Exception {
Predicate<ChangeData> in = parse("foo:a");
Predicate<ChangeData> out = rewrite(in);
- assertThat(AndChangeSource.class).isSameAs(out.getClass());
+ assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
assertThat(out.getChildren())
.containsExactly(
query(Predicate.or(ChangeStatusPredicate.open(), ChangeStatusPredicate.closed())), in)
@@ -85,7 +85,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
public void nonIndexPredicates() throws Exception {
Predicate<ChangeData> in = parse("foo:a OR foo:b");
Predicate<ChangeData> out = rewrite(in);
- assertThat(AndChangeSource.class).isSameAs(out.getClass());
+ assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
assertThat(out.getChildren())
.containsExactly(
query(Predicate.or(ChangeStatusPredicate.open(), ChangeStatusPredicate.closed())), in)
@@ -96,7 +96,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
public void oneIndexPredicate() throws Exception {
Predicate<ChangeData> in = parse("foo:a file:b");
Predicate<ChangeData> out = rewrite(in);
- assertThat(AndChangeSource.class).isSameAs(out.getClass());
+ assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
assertThat(out.getChildren()).containsExactly(query(in.getChild(1)), in.getChild(0)).inOrder();
}
@@ -110,7 +110,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
public void threeLevelTreeWithSomeIndexPredicates() throws Exception {
Predicate<ChangeData> in = parse("-foo:a (file:b OR file:c)");
Predicate<ChangeData> out = rewrite(in);
- assertThat(out.getClass()).isSameAs(AndChangeSource.class);
+ assertThat(out.getClass()).isSameInstanceAs(AndChangeSource.class);
assertThat(out.getChildren()).containsExactly(query(in.getChild(1)), in.getChild(0)).inOrder();
}
@@ -118,7 +118,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
public void multipleIndexPredicates() throws Exception {
Predicate<ChangeData> in = parse("file:a OR foo:b OR file:c OR foo:d");
Predicate<ChangeData> out = rewrite(in);
- assertThat(out.getClass()).isSameAs(OrSource.class);
+ assertThat(out.getClass()).isSameInstanceAs(OrSource.class);
assertThat(out.getChildren())
.containsExactly(query(or(in.getChild(0), in.getChild(2))), in.getChild(1), in.getChild(3))
.inOrder();
@@ -128,7 +128,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
public void indexAndNonIndexPredicates() throws Exception {
Predicate<ChangeData> in = parse("status:new bar:p file:a");
Predicate<ChangeData> out = rewrite(in);
- assertThat(AndChangeSource.class).isSameAs(out.getClass());
+ assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
assertThat(out.getChildren())
.containsExactly(query(and(in.getChild(0), in.getChild(2))), in.getChild(1))
.inOrder();
@@ -255,7 +255,7 @@ public class ChangeIndexRewriterTest extends GerritBaseTests {
}
private static QueryOptions options(int start, int limit) {
- return IndexedChangeQuery.createOptions(CONFIG, start, limit, ImmutableSet.<String>of());
+ return IndexedChangeQuery.createOptions(CONFIG, start, limit, ImmutableSet.of());
}
private Set<Change.Status> status(String query) throws QueryParseException {
diff --git a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
index d4ecb6df39..34c5717f10 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -15,23 +15,20 @@
package com.google.gerrit.server.index.change;
import com.google.common.collect.ImmutableList;
-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;
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 com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import org.junit.Ignore;
@Ignore
public class FakeChangeIndex implements ChangeIndex {
- static final Schema<ChangeData> V1 =
- new Schema<>(1, ImmutableList.<FieldDef<ChangeData, ?>>of(ChangeField.STATUS));
+ static final Schema<ChangeData> V1 = new Schema<>(1, ImmutableList.of(ChangeField.STATUS));
static final Schema<ChangeData> V2 =
new Schema<>(2, ImmutableList.of(ChangeField.STATUS, ChangeField.PATH, ChangeField.UPDATED));
@@ -54,12 +51,12 @@ public class FakeChangeIndex implements ChangeIndex {
}
@Override
- public ResultSet<ChangeData> read() throws OrmException {
+ public ResultSet<ChangeData> read() {
throw new UnsupportedOperationException();
}
@Override
- public ResultSet<FieldBundle> readRaw() throws OrmException {
+ public ResultSet<FieldBundle> readRaw() {
throw new UnsupportedOperationException("not implemented");
}
diff --git a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
index b5255049d4..a713221a45 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
@@ -24,10 +24,10 @@ import org.junit.Ignore;
public class FakeQueryBuilder extends ChangeQueryBuilder {
FakeQueryBuilder(ChangeIndexCollection indexes) {
super(
- new FakeQueryBuilder.Definition<>(FakeQueryBuilder.class),
+ new ChangeQueryBuilder.Definition<>(FakeQueryBuilder.class),
new ChangeQueryBuilder.Arguments(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, indexes, null, null, null, null, null, null, null, null));
+ null, null, null, indexes, null, null, null, null, null, null, null));
}
@Operator
diff --git a/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java b/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
index 51bda661e5..a38eabe036 100644
--- a/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
+++ b/javatests/com/google/gerrit/server/index/change/StalenessCheckerTest.java
@@ -17,7 +17,6 @@ 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.TestChanges.newChange;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
@@ -25,16 +24,12 @@ import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ListMultimap;
import com.google.gerrit.index.RefState;
-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.git.GitRepositoryManager;
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gwtorm.protobuf.CodecFactory;
-import com.google.gwtorm.protobuf.ProtobufCodec;
import java.util.stream.Stream;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.ObjectId;
@@ -51,8 +46,6 @@ public class StalenessCheckerTest extends GerritBaseTests {
private static final Change.Id C = new Change.Id(1234);
- private static final ProtobufCodec<Change> CHANGE_CODEC = CodecFactory.encoder(Change.class);
-
private GitRepositoryManager repoManager;
private Repository r1;
private Repository r2;
@@ -316,29 +309,7 @@ public class StalenessCheckerTest extends GerritBaseTests {
.isFalse();
}
- @Test
- public void reviewDbChangeIsStale() throws Exception {
- Change indexChange = newChange(P1, new Account.Id(1));
- indexChange.setNoteDbState(SHA1);
-
- // Change is missing from ReviewDb but present in index.
- assertThat(StalenessChecker.reviewDbChangeIsStale(indexChange, null)).isTrue();
-
- // Change differs only in primary storage.
- Change noteDbPrimary = clone(indexChange);
- noteDbPrimary.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
- assertThat(StalenessChecker.reviewDbChangeIsStale(indexChange, noteDbPrimary)).isTrue();
-
- assertThat(StalenessChecker.reviewDbChangeIsStale(indexChange, clone(indexChange))).isFalse();
-
- // Can't easily change row version to check true case.
- }
-
private static Iterable<byte[]> byteArrays(String... strs) {
return Stream.of(strs).map(s -> s != null ? s.getBytes(UTF_8) : null).collect(toList());
}
-
- private static Change clone(Change change) {
- return CHANGE_CODEC.decode(CHANGE_CODEC.encodeToByteArray(change));
- }
}
diff --git a/javatests/com/google/gerrit/server/ioutil/BUILD b/javatests/com/google/gerrit/server/ioutil/BUILD
index ac9530f6fa..ef0224397e 100644
--- a/javatests/com/google/gerrit/server/ioutil/BUILD
+++ b/javatests/com/google/gerrit/server/ioutil/BUILD
@@ -10,6 +10,7 @@ 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 fae8559c31..c4f32c73b3 100644
--- a/javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/BasicSerializationTest.java
@@ -23,13 +23,14 @@ 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 {
+public class BasicSerializationTest extends GerritBaseTests {
@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 fe642ba945..9f5e60a3bf 100644
--- a/javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
@@ -14,12 +14,13 @@
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 {
+public class ColumnFormatterTest extends GerritBaseTests {
/**
* 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 9bb6951501..40fd71f616 100644
--- a/javatests/com/google/gerrit/server/ioutil/HexFormatTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/HexFormatTest.java
@@ -16,9 +16,10 @@ 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 {
+public class HexFormatTest extends GerritBaseTests {
@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 3043985740..33b1c4f27e 100644
--- a/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/RegexListSearcherTest.java
@@ -18,10 +18,11 @@ 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 {
+public class RegexListSearcherTest extends GerritBaseTests {
private static final ImmutableList<String> EMPTY = ImmutableList.of();
@Test
diff --git a/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java b/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java
index 04f806d804..817b317b5e 100644
--- a/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java
+++ b/javatests/com/google/gerrit/server/ioutil/StringUtilTest.java
@@ -16,9 +16,10 @@ 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 {
+public class StringUtilTest extends GerritBaseTests {
/** 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 5117c01971..463decf8cd 100644
--- a/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
+++ b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
@@ -17,6 +17,7 @@ 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 java.util.SortedMap;
import java.util.SortedSet;
import java.util.concurrent.ExecutorService;
@@ -24,7 +25,7 @@ import java.util.concurrent.Executors;
import org.junit.Rule;
import org.junit.Test;
-public class LoggingContextAwareExecutorServiceTest {
+public class LoggingContextAwareExecutorServiceTest extends GerritBaseTests {
@Rule public final Expect expect = Expect.create();
@Test
diff --git a/javatests/com/google/gerrit/server/logging/MutableTagsTest.java b/javatests/com/google/gerrit/server/logging/MutableTagsTest.java
index 4fadbb4af8..113f26c3d6 100644
--- a/javatests/com/google/gerrit/server/logging/MutableTagsTest.java
+++ b/javatests/com/google/gerrit/server/logging/MutableTagsTest.java
@@ -20,13 +20,14 @@ import static com.google.common.truth.Truth.assert_;
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 {
+public class MutableTagsTest extends GerritBaseTests {
private MutableTags tags;
@Before
diff --git a/javatests/com/google/gerrit/server/logging/TraceContextTest.java b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
index 044d2378e3..19b2eeb322 100644
--- a/javatests/com/google/gerrit/server/logging/TraceContextTest.java
+++ b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
@@ -19,13 +19,14 @@ import static com.google.common.truth.Truth.assertThat;
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 {
+public class TraceContextTest extends GerritBaseTests {
@After
public void cleanup() {
LoggingContext.getInstance().clearTags();
diff --git a/javatests/com/google/gerrit/server/mail/SignedTokenTest.java b/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
new file mode 100644
index 0000000000..41d8d69852
--- /dev/null
+++ b/javatests/com/google/gerrit/server/mail/SignedTokenTest.java
@@ -0,0 +1,162 @@
+// 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.mail;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import java.util.Random;
+import java.util.regex.Pattern;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SignedTokenTest {
+
+ private static final Pattern URL_UNSAFE_CHARS = Pattern.compile("(\\+|/)");
+ private static final String REGISTER_EMAIL_PRIVATE_KEY = "TGMv3/bTC42jUKQndTQrXyHhHYMP0t69i/4=";
+ private static final int maxAge = 5;
+ private static final String TEXT = "This is a text";
+ private static final String FORGED_TEXT = "This is a forged text";
+ private static final String FORGED_TOKEN = String.format("Zm9yZ2VkJTIwa2V5$%s", TEXT);
+
+ private SignedToken signedToken;
+
+ @Before
+ public void setUp() throws Exception {
+ signedToken = new SignedToken(maxAge, REGISTER_EMAIL_PRIVATE_KEY);
+ }
+
+ /** Test new token: the key is a normal BASE64 string that can be used for URL safely */
+ @Test
+ public void newTokenKeyDoesNotContainUnsafeChar() throws Exception {
+ assertThat(signedToken.newToken(TEXT)).doesNotContainMatch(URL_UNSAFE_CHARS);
+ }
+
+ /** Test new token: the key is an URL unsafe BASE64 string with index of '62'(+) */
+ @Test
+ public void newTokenWithUrlUnsafeBase64Plus() throws Exception {
+ String token = "+" + signedToken.newToken(TEXT);
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+ assertThat(thrown).hasMessageThat().contains("decoding failed");
+
+ assertThat(thrown)
+ .hasCauseThat()
+ .hasMessageThat()
+ .isEqualTo(
+ "com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: +");
+ }
+
+ /** Test new token: the key is an URL unsafe BASE64 string with '63'(/) */
+ @Test
+ public void newTokenWithUrlUnsafeBase64Slash() throws Exception {
+ String token = "/" + signedToken.newToken(TEXT);
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+ assertThat(thrown).hasMessageThat().contains("decoding failed");
+
+ assertThat(thrown)
+ .hasCauseThat()
+ .hasMessageThat()
+ .isEqualTo(
+ "com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: /");
+ }
+
+ /** Test check token: BASE64 encoding and decoding in a safe URL way */
+ @Test
+ public void checkToken() throws Exception {
+ String token = signedToken.newToken(TEXT);
+ ValidToken validToken = signedToken.checkToken(token, TEXT);
+ assertThat(validToken).isNotNull();
+ assertThat(validToken.getData()).isEqualTo(TEXT);
+ }
+
+ /** Test check token: input token string is null */
+ @Test
+ public void checkTokenInputTokenNull() throws Exception {
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(null, TEXT));
+
+ assertThat(thrown).hasMessageThat().isEqualTo("Empty token");
+ }
+
+ /** Test check token: input token string is empty */
+ @Test
+ public void checkTokenInputTokenEmpty() throws Exception {
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken("", TEXT));
+
+ assertThat(thrown).hasMessageThat().isEqualTo("Empty token");
+ }
+
+ /** Test check token: token string is not illegal with no '$' character */
+ @Test
+ public void checkTokenInputTokenNoDollarSplitChar() throws Exception {
+ String token = signedToken.newToken(TEXT).replace("$", "¥");
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(token, TEXT));
+
+ assertThat(thrown).hasMessageThat().isEqualTo("Token does not contain character '$'");
+ }
+
+ /** Test check token: token string length is match but is not a legal BASE64 string */
+ @Test
+ public void checkTokenInputTokenKeyBase64DecodeFail() throws Exception {
+ String token = signedToken.newToken(TEXT);
+ String key = randomString(token.indexOf("$") + 1);
+ String illegalBase64Token = key + "$" + TEXT;
+ CheckTokenException thrown =
+ assertThrows(
+ CheckTokenException.class, () -> signedToken.checkToken(illegalBase64Token, TEXT));
+
+ assertThat(thrown).hasMessageThat().isEqualTo("Base64 decoding failed");
+ }
+
+ /** Test check token: token is illegal with a forged key */
+ @Test
+ public void checkTokenForgedKey() throws Exception {
+ CheckTokenException thrown =
+ assertThrows(CheckTokenException.class, () -> signedToken.checkToken(FORGED_TOKEN, TEXT));
+
+ assertThat(thrown).hasMessageThat().isEqualTo("Token length mismatch");
+ }
+
+ /** Test check token: token is illegal with a forged text */
+ @Test
+ public void checkTokenForgedText() throws Exception {
+ CheckTokenException thrown =
+ assertThrows(
+ CheckTokenException.class,
+ () -> {
+ String token = signedToken.newToken(TEXT);
+ signedToken.checkToken(token, FORGED_TEXT);
+ });
+
+ assertThat(thrown).hasMessageThat().isEqualTo("Token text mismatch");
+ }
+
+ private static String randomString(int length) {
+ String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ Random random = new Random();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ int number = random.nextInt(62);
+ sb.append(str.charAt(number));
+ }
+ return sb.toString();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java b/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java
index f4fbc7846e..78116edf29 100644
--- a/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/CommentFormatterTest.java
@@ -20,10 +20,11 @@ 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 {
+public class CommentFormatterTest extends GerritBaseTests {
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 6b6632c544..0682bb3fd5 100644
--- a/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
@@ -16,13 +16,13 @@ package com.google.gerrit.server.mail.send;
import static com.google.common.truth.Truth.assertThat;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.testing.GerritBaseTests;
import java.util.Collections;
import org.junit.Test;
-public class CommentSenderTest {
+public class CommentSenderTest extends GerritBaseTests {
private static class TestSender extends CommentSender {
- TestSender() throws OrmException {
+ 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 75923b4df4..537ebff86b 100644
--- a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
@@ -28,6 +28,7 @@ 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;
@@ -36,7 +37,7 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.junit.Before;
import org.junit.Test;
-public class FromAddressGeneratorProviderTest {
+public class FromAddressGeneratorProviderTest extends GerritBaseTests {
private Config config;
private PersonIdent ident;
private AccountCache accountCache;
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index d00f96b0f0..a1f318f8a5 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -28,7 +28,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -43,7 +42,7 @@ import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.DefaultUrlFormatter;
-import com.google.gerrit.server.config.DisableReverseDnsLookup;
+import com.google.gerrit.server.config.EnableReverseDnsLookup;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -58,12 +57,9 @@ import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.TestChanges;
import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
-import com.google.inject.TypeLiteral;
import com.google.inject.util.Providers;
import java.sql.Timestamp;
import java.util.TimeZone;
@@ -137,11 +133,11 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
install(new GitModule());
install(new DefaultUrlFormatter.Module());
- install(NoteDbModule.forTest(testConfig));
+ install(NoteDbModule.forTest());
bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
bind(String.class).annotatedWith(GerritServerId.class).toInstance("gerrit");
bind(GitRepositoryManager.class).toInstance(repoManager);
- bind(ProjectCache.class).toProvider(Providers.<ProjectCache>of(null));
+ bind(ProjectCache.class).toProvider(Providers.of(null));
bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(testConfig);
bind(String.class)
.annotatedWith(AnonymousCowardName.class)
@@ -150,8 +146,8 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
.annotatedWith(CanonicalWebUrl.class)
.toInstance("http://localhost:8080/");
bind(Boolean.class)
- .annotatedWith(DisableReverseDnsLookup.class)
- .toInstance(Boolean.FALSE);
+ .annotatedWith(EnableReverseDnsLookup.class)
+ .toInstance(Boolean.TRUE);
bind(Realm.class).to(FakeRealm.class);
bind(GroupBackend.class).to(SystemGroupBackend.class).in(SINGLETON);
bind(AccountCache.class).toInstance(accountCache);
@@ -160,23 +156,6 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
.toInstance(serverIdent);
bind(GitReferenceUpdated.class).toInstance(GitReferenceUpdated.DISABLED);
bind(MetricMaker.class).to(DisabledMetricMaker.class);
- bind(ReviewDb.class).toProvider(Providers.<ReviewDb>of(null));
- MutableNotesMigration migration = MutableNotesMigration.newDisabled();
- migration.setFrom(NotesMigrationState.FINAL);
- bind(MutableNotesMigration.class).toInstance(migration);
- bind(NotesMigration.class).to(MutableNotesMigration.class);
-
- // Tests don't support ReviewDb at all, but bindings are required via NoteDbModule.
- bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {})
- .toInstance(
- () -> {
- throw new UnsupportedOperationException();
- });
- bind(ChangeBundleReader.class)
- .toInstance(
- (db, id) -> {
- throw new UnsupportedOperationException();
- });
}
});
@@ -201,7 +180,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
protected Change newChange(boolean workInProgress) throws Exception {
Change c = TestChanges.newChange(project, changeOwner.getAccountId());
- ChangeUpdate u = newUpdate(c, changeOwner);
+ ChangeUpdate u = newUpdateForNewChange(c, changeOwner);
u.setChangeId(c.getKey().get());
u.setBranch(c.getDest().get());
u.setWorkInProgress(workInProgress);
@@ -217,15 +196,24 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
return newChange(false);
}
+ protected ChangeUpdate newUpdateForNewChange(Change c, CurrentUser user) throws Exception {
+ return newUpdate(c, user, false);
+ }
+
protected ChangeUpdate newUpdate(Change c, CurrentUser user) throws Exception {
- ChangeUpdate update = TestChanges.newUpdate(injector, c, user);
+ return newUpdate(c, user, true);
+ }
+
+ protected ChangeUpdate newUpdate(Change c, CurrentUser user, boolean shouldExist)
+ throws Exception {
+ ChangeUpdate update = TestChanges.newUpdate(injector, c, user, shouldExist);
update.setPatchSetId(c.currentPatchSetId());
update.setAllowWriteToNewRef(true);
return update;
}
- protected ChangeNotes newNotes(Change c) throws OrmException {
- return new ChangeNotes(args, c).load();
+ protected ChangeNotes newNotes(Change c) {
+ return new ChangeNotes(args, c, true, null).load();
}
protected static SubmitRecord submitRecord(
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java b/javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java
deleted file mode 100644
index fc2a272082..0000000000
--- a/javatests/com/google/gerrit/server/notedb/ChangeBundleTest.java
+++ /dev/null
@@ -1,1976 +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.server.notedb;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB;
-import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB;
-import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
-import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
-import static com.google.gerrit.server.util.time.TimeUtil.truncateToSecond;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Table;
-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.LabelId;
-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.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.server.ReviewerSet;
-import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TestChanges;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.protobuf.CodecFactory;
-import com.google.gwtorm.protobuf.ProtobufCodec;
-import java.sql.Timestamp;
-import java.time.LocalDate;
-import java.time.Month;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.TimeZone;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ChangeBundleTest extends GerritBaseTests {
- private static final ProtobufCodec<Change> CHANGE_CODEC = CodecFactory.encoder(Change.class);
- private static final ProtobufCodec<ChangeMessage> CHANGE_MESSAGE_CODEC =
- CodecFactory.encoder(ChangeMessage.class);
- private static final ProtobufCodec<PatchSet> PATCH_SET_CODEC =
- CodecFactory.encoder(PatchSet.class);
- private static final ProtobufCodec<PatchSetApproval> PATCH_SET_APPROVAL_CODEC =
- CodecFactory.encoder(PatchSetApproval.class);
- private static final ProtobufCodec<PatchLineComment> PATCH_LINE_COMMENT_CODEC =
- CodecFactory.encoder(PatchLineComment.class);
- private static final String TIMEZONE_ID = "US/Eastern";
-
- private String systemTimeZoneProperty;
- private TimeZone systemTimeZone;
-
- private Project.NameKey project;
- private Account.Id accountId;
-
- @Before
- public void setUp() {
- systemTimeZoneProperty = System.setProperty("user.timezone", TIMEZONE_ID);
- systemTimeZone = TimeZone.getDefault();
- TimeZone.setDefault(TimeZone.getTimeZone(TIMEZONE_ID));
- long maxMs = ChangeRebuilderImpl.MAX_WINDOW_MS;
- assertThat(maxMs).isGreaterThan(1000L);
- TestTimeUtil.resetWithClockStep(maxMs * 2, MILLISECONDS);
- project = new Project.NameKey("project");
- accountId = new Account.Id(100);
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZoneProperty);
- TimeZone.setDefault(systemTimeZone);
- }
-
- private void superWindowResolution() {
- TestTimeUtil.setClockStep(ChangeRebuilderImpl.MAX_WINDOW_MS * 2, MILLISECONDS);
- TimeUtil.nowTs();
- }
-
- private void subWindowResolution() {
- TestTimeUtil.setClockStep(1, SECONDS);
- TimeUtil.nowTs();
- }
-
- @Test
- public void diffChangesDifferentIds() throws Exception {
- Change c1 = TestChanges.newChange(project, accountId);
- int id1 = c1.getId().get();
- Change c2 = TestChanges.newChange(project, accountId);
- int id2 = c2.getId().get();
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-
- assertDiffs(
- b1,
- b2,
- "changeId differs for Changes: {" + id1 + "} != {" + id2 + "}",
- "createdOn differs for Changes: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:06.0}",
- "effective last updated time differs for Changes:"
- + " {2009-09-30 17:00:00.0} != {2009-09-30 17:00:06.0}");
- }
-
- @Test
- public void diffChangesSameId() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- Change c2 = clone(c1);
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
-
- assertNoDiffs(b1, b2);
-
- c2.setTopic("topic");
- assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {null} != {topic}");
- }
-
- @Test
- public void diffChangesMixedSourcesAllowsSlop() throws Exception {
- subWindowResolution();
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- Change c2 = clone(c1);
- c2.setCreatedOn(TimeUtil.nowTs());
- c2.setLastUpdatedOn(TimeUtil.nowTs());
-
- // Both are ReviewDb, exact timestamp match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for Change.Id "
- + c1.getId()
- + ":"
- + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:02.0}",
- "effective last updated time differs for Change.Id "
- + c1.getId()
- + ":"
- + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:03.0}");
-
- // One NoteDb, slop is allowed.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- // But not too much slop.
- superWindowResolution();
- Change c3 = clone(c1);
- c3.setLastUpdatedOn(TimeUtil.nowTs());
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- ChangeBundle b3 =
- new ChangeBundle(
- c3, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- String msg =
- "effective last updated time differs for Change.Id "
- + c1.getId()
- + " in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:01.0} != {2009-09-30 17:00:10.0}";
- assertDiffs(b1, b3, msg);
- assertDiffs(b3, b1, msg);
- }
-
- @Test
- public void diffChangesIgnoresOriginalSubjectInReviewDb() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- c1.setCurrentPatchSet(c1.currentPatchSetId(), "Subject", "Original A");
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(c2.currentPatchSetId(), c1.getSubject(), "Original B");
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "originalSubject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Original A} != {Original B}");
-
- // Both NoteDb, exact match required.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "originalSubject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Original A} != {Original B}");
-
- // One ReviewDb, one NoteDb, original subject is ignored.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangesSanitizesSubjectsBeforeComparison() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- c1.setCurrentPatchSet(c1.currentPatchSetId(), "Subject\r\rbody", "Original");
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(c2.currentPatchSetId(), "Subject body", "Original");
-
- // Both ReviewDb, exact match required
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Subject\r\rbody} != {Subject body}");
-
- // Both NoteDb, exact match required (although it should be impossible to
- // create a NoteDb change with '\r' in the subject).
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Subject\r\rbody} != {Subject body}");
-
- // One ReviewDb, one NoteDb, '\r' is normalized to ' '.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangesConsidersEmptyReviewDbTopicEquivalentToNullInNoteDb() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- c1.setTopic("");
- Change c2 = clone(c1);
- c2.setTopic(null);
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {} != {null}");
-
- // Topic ignored if ReviewDb is empty and NoteDb is null.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
-
- // Exact match still required if NoteDb has empty value (not realistic).
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {} != {null}");
-
- // Null is not equal to a non-empty string.
- Change c3 = clone(c1);
- c3.setTopic("topic");
- b1 =
- new ChangeBundle(
- c3, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": {topic} != {null}");
-
- // Null is equal to a string that is all whitespace.
- Change c4 = clone(c1);
- c4.setTopic(" ");
- b1 =
- new ChangeBundle(
- c4, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangesIgnoresLeadingAndTrailingWhitespaceInReviewDbTopics() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- c1.setTopic(" abc ");
- Change c2 = clone(c1);
- c2.setTopic("abc");
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": { abc } != {abc}");
-
- // Leading whitespace in ReviewDb topic is ignored.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- // Must match except for the leading/trailing whitespace.
- Change c3 = clone(c1);
- c3.setTopic("cba");
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c3, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(b1, b2, "topic differs for Change.Id " + c1.getId() + ": { abc } != {cba}");
- }
-
- @Test
- public void diffChangesTakesMaxEntityTimestampFromReviewDb() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- PatchSet ps = new PatchSet(c1.currentPatchSetId());
- ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps.setUploader(accountId);
- ps.setCreatedOn(TimeUtil.nowTs());
- PatchSetApproval a =
- new PatchSetApproval(
- new PatchSetApproval.Key(c1.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- TimeUtil.nowTs());
-
- Change c2 = clone(c1);
- c2.setLastUpdatedOn(a.getGranted());
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "effective last updated time differs for Change.Id "
- + c1.getId()
- + ":"
- + " {2009-09-30 17:00:00.0} != {2009-09-30 17:00:12.0}");
-
- // NoteDb allows latest timestamp from all entities in bundle.
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- }
-
- @Test
- public void diffChangesIgnoresChangeTimestampIfAnyOtherEntitiesExist() {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- PatchSet ps = new PatchSet(c1.currentPatchSetId());
- ps.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps.setUploader(accountId);
- ps.setCreatedOn(TimeUtil.nowTs());
- PatchSetApproval a =
- new PatchSetApproval(
- new PatchSetApproval.Key(c1.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- TimeUtil.nowTs());
- c1.setLastUpdatedOn(a.getGranted());
-
- Change c2 = clone(c1);
- c2.setLastUpdatedOn(TimeUtil.nowTs());
-
- // ReviewDb has later lastUpdatedOn timestamp than NoteDb, allowed since
- // NoteDb matches the latest timestamp of a non-Change entity.
- ChangeBundle b1 =
- new ChangeBundle(
- c2, messages(), patchSets(ps), approvals(a), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c1, messages(), patchSets(ps), approvals(a), comments(), reviewers(), NOTE_DB);
- assertThat(b1.getChange().getLastUpdatedOn()).isGreaterThan(b2.getChange().getLastUpdatedOn());
- assertNoDiffs(b1, b2);
-
- // Timestamps must actually match if Change is the only entity.
- b1 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "effective last updated time differs for Change.Id "
- + c1.getId()
- + " in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:12.0} != {2009-09-30 17:00:18.0}");
- }
-
- @Test
- public void diffChangesAllowsReviewDbSubjectToBePrefixOfNoteDbSubject() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(
- c1.currentPatchSetId(), c1.getSubject().substring(0, 10), c1.getOriginalSubject());
- assertThat(c2.getSubject()).isNotEqualTo(c1.getSubject());
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id " + c1.getId() + ": {Change subject} != {Change sub}");
-
- // ReviewDb has shorter subject, allowed.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
-
- // NoteDb has shorter subject, not allowed.
- b1 =
- new ChangeBundle(
- c1, messages(), latest(c1), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(c2, messages(), latest(c2), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id " + c1.getId() + ": {Change subject} != {Change sub}");
- }
-
- @Test
- public void diffChangesTrimsLeadingSpacesFromReviewDbComparingToNoteDb() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(c1.currentPatchSetId(), " " + c1.getSubject(), c1.getOriginalSubject());
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Change subject} != { Change subject}");
-
- // ReviewDb is missing leading spaces, allowed.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangesDoesntTrimLeadingNonSpaceWhitespaceFromSubject() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(c1.currentPatchSetId(), "\t" + c1.getSubject(), c1.getOriginalSubject());
-
- // Both ReviewDb.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Change subject} != {\tChange subject}");
-
- // One NoteDb.
- b1 =
- new ChangeBundle(c1, messages(), latest(c1), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), latest(c2), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Change subject} != {\tChange subject}");
- assertDiffs(
- b2,
- b1,
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {\tChange subject} != {Change subject}");
- }
-
- @Test
- public void diffChangesHandlesBuggyJGitSubjectExtraction() throws Exception {
- Change c1 = TestChanges.newChange(project, accountId);
- String buggySubject = "Subject\r \r Rest of message.";
- c1.setCurrentPatchSet(c1.currentPatchSetId(), buggySubject, buggySubject);
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(c2.currentPatchSetId(), "Subject", "Subject");
-
- // Both ReviewDb.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "originalSubject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Subject\r \r Rest of message.} != {Subject}",
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Subject\r \r Rest of message.} != {Subject}");
-
- // NoteDb has correct subject without "\r ".
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangesIgnoresInvalidCurrentPatchSetIdInReviewDb() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- Change c2 = clone(c1);
- c2.setCurrentPatchSet(
- new PatchSet.Id(c2.getId(), 0), "Unrelated subject", c2.getOriginalSubject());
-
- // Both ReviewDb.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "currentPatchSetId differs for Change.Id " + c1.getId() + ": {1} != {0}",
- "subject differs for Change.Id "
- + c1.getId()
- + ":"
- + " {Change subject} != {Unrelated subject}");
-
- // One NoteDb.
- //
- // This is based on a real corrupt change where all patch sets were deleted
- // but the Change entity stuck around, resulting in a currentPatchSetId of 0
- // after converting to NoteDb.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangesAllowsCreatedToMatchLastUpdated() throws Exception {
- Change c1 = TestChanges.newChange(new Project.NameKey("project"), new Account.Id(100));
- c1.setCreatedOn(TimeUtil.nowTs());
- assertThat(c1.getCreatedOn()).isGreaterThan(c1.getLastUpdatedOn());
- Change c2 = clone(c1);
- c2.setCreatedOn(c2.getLastUpdatedOn());
-
- // Both ReviewDb.
- ChangeBundle b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for Change.Id "
- + c1.getId()
- + ": {2009-09-30 17:00:06.0} != {2009-09-30 17:00:00.0}");
-
- // One NoteDb.
- b1 =
- new ChangeBundle(
- c1, messages(), patchSets(), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c2, messages(), patchSets(), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffChangeMessageKeySets() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid1"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- ChangeMessage cm2 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid2"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-
- assertDiffs(
- b1,
- b2,
- "ChangeMessage.Key sets differ:"
- + " ["
- + id
- + ",uuid1] only in A; ["
- + id
- + ",uuid2] only in B");
- }
-
- @Test
- public void diffChangeMessages() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- cm1.setMessage("message 1");
- ChangeMessage cm2 = clone(cm1);
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-
- assertNoDiffs(b1, b2);
-
- cm2.setMessage("message 2");
- assertDiffs(
- b1,
- b2,
- "message differs for ChangeMessage.Key "
- + c.getId()
- + ",uuid:"
- + " {message 1} != {message 2}");
- }
-
- @Test
- public void diffChangeMessagesIgnoresUuids() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid1"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- cm1.setMessage("message 1");
- ChangeMessage cm2 = clone(cm1);
- cm2.getKey().set("uuid2");
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- // Both are ReviewDb, exact UUID match is required.
- assertDiffs(
- b1,
- b2,
- "ChangeMessage.Key sets differ:"
- + " ["
- + id
- + ",uuid1] only in A; ["
- + id
- + ",uuid2] only in B");
-
- // One NoteDb, UUIDs are ignored.
- b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- }
-
- @Test
- public void diffChangeMessagesWithDifferentCounts() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid1"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- cm1.setMessage("message 1");
- ChangeMessage cm2 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid2"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- cm1.setMessage("message 2");
-
- // Both ReviewDb: Uses same keySet diff as other types.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1, cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1, b2, "ChangeMessage.Key sets differ: [" + id + ",uuid2] only in A; [] only in B");
-
- // One NoteDb: UUIDs in keys can't be used for comparison, just diff counts.
- b1 =
- new ChangeBundle(
- c, messages(cm1, cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(b1, b2, "ChangeMessages differ for Change.Id " + id + "\nOnly in A:\n " + cm2);
- assertDiffs(b2, b1, "ChangeMessages differ for Change.Id " + id + "\nOnly in B:\n " + cm2);
- }
-
- @Test
- public void diffChangeMessagesMixedSourcesWithDifferences() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid1"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- cm1.setMessage("message 1");
- ChangeMessage cm2 = clone(cm1);
- cm2.setMessage("message 2");
- ChangeMessage cm3 = clone(cm1);
- cm3.getKey().set("uuid2"); // Differs only in UUID.
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1, cm3), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm2, cm3), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- // Implementation happens to pair up cm1 in b1 with cm3 in b2 because it
- // depends on iteration order and doesn't care about UUIDs. The important
- // thing is that there's some diff.
- assertDiffs(
- b1,
- b2,
- "ChangeMessages differ for Change.Id "
- + id
- + "\n"
- + "Only in A:\n "
- + cm3
- + "\n"
- + "Only in B:\n "
- + cm2);
- assertDiffs(
- b2,
- b1,
- "ChangeMessages differ for Change.Id "
- + id
- + "\n"
- + "Only in A:\n "
- + cm2
- + "\n"
- + "Only in B:\n "
- + cm3);
- }
-
- @Test
- public void diffChangeMessagesMixedSourcesAllowsSlop() throws Exception {
- subWindowResolution();
- Change c = TestChanges.newChange(project, accountId);
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid1"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- ChangeMessage cm2 = clone(cm1);
- cm2.setWrittenOn(TimeUtil.nowTs());
-
- // Both are ReviewDb, exact timestamp match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "writtenOn differs for ChangeMessage.Key "
- + c.getId()
- + ",uuid1:"
- + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}");
-
- // One NoteDb, slop is allowed.
- b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- // But not too much slop.
- superWindowResolution();
- ChangeMessage cm3 = clone(cm1);
- cm3.setWrittenOn(TimeUtil.nowTs());
- b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- ChangeBundle b3 =
- new ChangeBundle(
- c, messages(cm3), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- int id = c.getId().get();
- assertDiffs(
- b1,
- b3,
- "ChangeMessages differ for Change.Id "
- + id
- + "\n"
- + "Only in A:\n "
- + cm1
- + "\n"
- + "Only in B:\n "
- + cm3);
- assertDiffs(
- b3,
- b1,
- "ChangeMessages differ for Change.Id "
- + id
- + "\n"
- + "Only in A:\n "
- + cm3
- + "\n"
- + "Only in B:\n "
- + cm1);
- }
-
- @Test
- public void diffChangeMessagesAllowsNullPatchSetIdFromReviewDb() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- cm1.setMessage("message 1");
- ChangeMessage cm2 = clone(cm1);
- cm2.setPatchSetId(null);
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
-
- // Both are ReviewDb, exact patch set ID match is required.
- assertDiffs(
- b1,
- b2,
- "patchset differs for ChangeMessage.Key "
- + c.getId()
- + ",uuid:"
- + " {"
- + id
- + ",1} != {null}");
-
- // Null patch set ID on ReviewDb is ignored.
- b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
-
- // Null patch set ID on NoteDb is not ignored (but is not realistic).
- b1 =
- new ChangeBundle(
- c, messages(cm1), latest(c), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c, messages(cm2), latest(c), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "ChangeMessages differ for Change.Id "
- + id
- + "\n"
- + "Only in A:\n "
- + cm1
- + "\n"
- + "Only in B:\n "
- + cm2);
- assertDiffs(
- b2,
- b1,
- "ChangeMessages differ for Change.Id "
- + id
- + "\n"
- + "Only in A:\n "
- + cm2
- + "\n"
- + "Only in B:\n "
- + cm1);
- }
-
- @Test
- public void diffPatchSetIdSets() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- TestChanges.incrementPatchSet(c);
-
- PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
- ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps1.setUploader(accountId);
- ps1.setCreatedOn(TimeUtil.nowTs());
- PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
- ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
- ps2.setUploader(accountId);
- ps2.setCreatedOn(TimeUtil.nowTs());
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps1, ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-
- assertDiffs(b1, b2, "PatchSet.Id sets differ: [] only in A; [" + c.getId() + ",1] only in B");
- }
-
- @Test
- public void diffPatchSets() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- PatchSet ps1 = new PatchSet(c.currentPatchSetId());
- ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps1.setUploader(accountId);
- ps1.setCreatedOn(TimeUtil.nowTs());
- PatchSet ps2 = clone(ps1);
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
-
- assertNoDiffs(b1, b2);
-
- ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
- assertDiffs(
- b1,
- b2,
- "revision differs for PatchSet.Id "
- + c.getId()
- + ",1:"
- + " {RevId{deadbeefdeadbeefdeadbeefdeadbeefdeadbeef}}"
- + " != {RevId{badc0feebadc0feebadc0feebadc0feebadc0fee}}");
- }
-
- @Test
- public void diffPatchSetsMixedSourcesAllowsSlop() throws Exception {
- subWindowResolution();
- Change c = TestChanges.newChange(project, accountId);
- PatchSet ps1 = new PatchSet(c.currentPatchSetId());
- ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps1.setUploader(accountId);
- ps1.setCreatedOn(truncateToSecond(TimeUtil.nowTs()));
- PatchSet ps2 = clone(ps1);
- ps2.setCreatedOn(TimeUtil.nowTs());
-
- // Both are ReviewDb, exact timestamp match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for PatchSet.Id "
- + c.getId()
- + ",1:"
- + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}");
-
- // One NoteDb, slop is allowed.
- b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
-
- // But not too much slop.
- superWindowResolution();
- PatchSet ps3 = clone(ps1);
- ps3.setCreatedOn(TimeUtil.nowTs());
- b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB);
- ChangeBundle b3 =
- new ChangeBundle(
- c, messages(), patchSets(ps3), approvals(), comments(), reviewers(), REVIEW_DB);
- String msg =
- "createdOn differs for PatchSet.Id "
- + c.getId()
- + ",1 in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:10.0}";
- assertDiffs(b1, b3, msg);
- assertDiffs(b3, b1, msg);
- }
-
- @Test
- public void diffPatchSetsIgnoresTrailingNewlinesInPushCertificate() throws Exception {
- subWindowResolution();
- Change c = TestChanges.newChange(project, accountId);
- PatchSet ps1 = new PatchSet(c.currentPatchSetId());
- ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps1.setUploader(accountId);
- ps1.setCreatedOn(truncateToSecond(TimeUtil.nowTs()));
- ps1.setPushCertificate("some cert");
- PatchSet ps2 = clone(ps1);
- ps2.setPushCertificate(ps2.getPushCertificate() + "\n\n");
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), NOTE_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffPatchSetsGreaterThanCurrent() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
-
- PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
- ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps1.setUploader(accountId);
- ps1.setCreatedOn(TimeUtil.nowTs());
- PatchSet ps2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
- ps2.setRevision(new RevId("badc0feebadc0feebadc0feebadc0feebadc0fee"));
- ps2.setUploader(accountId);
- ps2.setCreatedOn(TimeUtil.nowTs());
- assertThat(ps2.getId().get()).isGreaterThan(c.currentPatchSetId().get());
-
- ChangeMessage cm1 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid1"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
- ChangeMessage cm2 =
- new ChangeMessage(
- new ChangeMessage.Key(c.getId(), "uuid2"),
- accountId,
- TimeUtil.nowTs(),
- c.currentPatchSetId());
-
- PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(ps1.getId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- TimeUtil.nowTs());
- PatchSetApproval a2 =
- new PatchSetApproval(
- new PatchSetApproval.Key(ps2.getId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- TimeUtil.nowTs());
-
- // Both ReviewDb.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c,
- messages(cm1, cm2),
- patchSets(ps1, ps2),
- approvals(a1, a2),
- comments(),
- reviewers(),
- REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "ChangeMessage.Key sets differ: [] only in A; [" + cm2.getKey() + "] only in B",
- "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B",
- "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B");
-
- // One NoteDb.
- b1 =
- new ChangeBundle(
- c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c,
- messages(cm1, cm2),
- patchSets(ps1, ps2),
- approvals(a1, a2),
- comments(),
- reviewers(),
- REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "ChangeMessages differ for Change.Id " + c.getId() + "\nOnly in B:\n " + cm2,
- "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B",
- "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B");
-
- // Both NoteDb.
- b1 =
- new ChangeBundle(
- c, messages(cm1), patchSets(ps1), approvals(a1), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c,
- messages(cm1, cm2),
- patchSets(ps1, ps2),
- approvals(a1, a2),
- comments(),
- reviewers(),
- NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "ChangeMessages differ for Change.Id " + c.getId() + "\nOnly in B:\n " + cm2,
- "PatchSet.Id sets differ: [] only in A; [" + ps2.getId() + "] only in B",
- "PatchSetApproval.Key sets differ: [] only in A; [" + a2.getKey() + "] only in B");
- }
-
- @Test
- public void diffPatchSetsIgnoresLeadingAndTrailingWhitespaceInReviewDbDescriptions()
- throws Exception {
- Change c = TestChanges.newChange(project, accountId);
-
- PatchSet ps1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
- ps1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- ps1.setUploader(accountId);
- ps1.setCreatedOn(TimeUtil.nowTs());
- ps1.setDescription(" abc ");
- PatchSet ps2 = clone(ps1);
- ps2.setDescription("abc");
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ": { abc } != {abc}");
-
- // Whitespace in ReviewDb description is ignored.
- b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps2), approvals(), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- // Must match except for the leading/trailing whitespace.
- PatchSet ps3 = clone(ps1);
- ps3.setDescription("cba");
- b1 =
- new ChangeBundle(
- c, messages(), patchSets(ps1), approvals(), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(
- c, messages(), patchSets(ps3), approvals(), comments(), reviewers(), NOTE_DB);
- assertDiffs(
- b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ": { abc } != {cba}");
- }
-
- @Test
- public void diffPatchSetsIgnoresCreatedOnWhenReviewDbIsNonMonotonic() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
-
- Timestamp beforePs1 = TimeUtil.nowTs();
-
- PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
- goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- goodPs1.setUploader(accountId);
- goodPs1.setCreatedOn(TimeUtil.nowTs());
- PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
- goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- goodPs2.setUploader(accountId);
- goodPs2.setCreatedOn(TimeUtil.nowTs());
- assertThat(goodPs2.getCreatedOn()).isGreaterThan(goodPs1.getCreatedOn());
-
- PatchSet badPs2 = clone(goodPs2);
- badPs2.setCreatedOn(beforePs1);
- assertThat(badPs2.getCreatedOn()).isLessThan(goodPs1.getCreatedOn());
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(goodPs1, goodPs2),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(goodPs1, badPs2),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for PatchSet.Id "
- + badPs2.getId()
- + ":"
- + " {2009-09-30 17:00:18.0} != {2009-09-30 17:00:06.0}");
-
- // Non-monotonic in ReviewDb but monotonic in NoteDb, timestamps are
- // ignored, including for ps1.
- PatchSet badPs1 = clone(goodPs1);
- badPs1.setCreatedOn(TimeUtil.nowTs());
- b1 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(badPs1, badPs2),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- b2 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(goodPs1, goodPs2),
- approvals(),
- comments(),
- reviewers(),
- NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- // Non-monotonic in NoteDb but monotonic in ReviewDb, timestamps are not
- // ignored.
- b1 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(goodPs1, goodPs2),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- b2 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(badPs1, badPs2),
- approvals(),
- comments(),
- reviewers(),
- NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for PatchSet.Id "
- + badPs1.getId()
- + " in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:24.0} != {2009-09-30 17:00:12.0}",
- "createdOn differs for PatchSet.Id "
- + badPs2.getId()
- + " in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:06.0} != {2009-09-30 17:00:18.0}");
- }
-
- @Test
- public void diffPatchSetsAllowsFirstPatchSetCreatedOnToMatchChangeCreatedOn() {
- Change c = TestChanges.newChange(project, accountId);
- c.setLastUpdatedOn(TimeUtil.nowTs());
-
- PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
- goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- goodPs1.setUploader(accountId);
- goodPs1.setCreatedOn(TimeUtil.nowTs());
- assertThat(goodPs1.getCreatedOn()).isGreaterThan(c.getCreatedOn());
-
- PatchSet ps1AtCreatedOn = clone(goodPs1);
- ps1AtCreatedOn.setCreatedOn(c.getCreatedOn());
-
- PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
- goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
- goodPs2.setUploader(accountId);
- goodPs2.setCreatedOn(TimeUtil.nowTs());
-
- PatchSet ps2AtCreatedOn = clone(goodPs2);
- ps2AtCreatedOn.setCreatedOn(c.getCreatedOn());
-
- // Both ReviewDb, exact match required.
- ChangeBundle b1 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(goodPs1, goodPs2),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(ps1AtCreatedOn, ps2AtCreatedOn),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for PatchSet.Id "
- + c.getId()
- + ",1: {2009-09-30 17:00:12.0} != {2009-09-30 17:00:00.0}",
- "createdOn differs for PatchSet.Id "
- + c.getId()
- + ",2: {2009-09-30 17:00:18.0} != {2009-09-30 17:00:00.0}");
-
- // One ReviewDb, PS1 is allowed to match change createdOn, but PS2 isn't.
- b1 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(goodPs1, goodPs2),
- approvals(),
- comments(),
- reviewers(),
- REVIEW_DB);
- b2 =
- new ChangeBundle(
- c,
- messages(),
- patchSets(ps1AtCreatedOn, ps2AtCreatedOn),
- approvals(),
- comments(),
- reviewers(),
- NOTE_DB);
- assertDiffs(
- b1,
- b2,
- "createdOn differs for PatchSet.Id "
- + c.getId()
- + ",2 in NoteDb vs. ReviewDb: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:18.0}");
- assertDiffs(
- b2,
- b1,
- "createdOn differs for PatchSet.Id "
- + c.getId()
- + ",2 in NoteDb vs. ReviewDb: {2009-09-30 17:00:00.0} != {2009-09-30 17:00:18.0}");
- }
-
- @Test
- public void diffPatchSetApprovalKeySets() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- TimeUtil.nowTs());
- PatchSetApproval a2 =
- new PatchSetApproval(
- new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Verified")),
- (short) 1,
- TimeUtil.nowTs());
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-
- assertDiffs(
- b1,
- b2,
- "PatchSetApproval.Key sets differ:"
- + " ["
- + id
- + "%2C1,100,Code-Review] only in A;"
- + " ["
- + id
- + "%2C1,100,Verified] only in B");
- }
-
- @Test
- public void diffPatchSetApprovals() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- TimeUtil.nowTs());
- PatchSetApproval a2 = clone(a1);
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
-
- assertNoDiffs(b1, b2);
-
- a2.setValue((short) -1);
- assertDiffs(
- b1,
- b2,
- "value differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review: {1} != {-1}");
- }
-
- @Test
- public void diffPatchSetApprovalsMixedSourcesAllowsSlop() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- subWindowResolution();
- PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- truncateToSecond(TimeUtil.nowTs()));
- PatchSetApproval a2 = clone(a1);
- a2.setGranted(TimeUtil.nowTs());
-
- // Both are ReviewDb, exact timestamp match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "granted differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review:"
- + " {2009-09-30 17:00:07.0} != {2009-09-30 17:00:08.0}");
-
- // One NoteDb, slop is allowed.
- b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
-
- // But not too much slop.
- superWindowResolution();
- PatchSetApproval a3 = clone(a1);
- a3.setGranted(TimeUtil.nowTs());
- b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB);
- ChangeBundle b3 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a3), comments(), reviewers(), REVIEW_DB);
- String msg =
- "granted differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:07.0} != {2009-09-30 17:00:15.0}";
- assertDiffs(b1, b3, msg);
- assertDiffs(b3, b1, msg);
- }
-
- @Test
- public void diffPatchSetApprovalsAllowsTruncatedTimestampInNoteDb() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 1,
- c.getCreatedOn());
- PatchSetApproval a2 = clone(a1);
- a2.setGranted(
- new Timestamp(
- LocalDate.of(1900, Month.JANUARY, 1)
- .atStartOfDay()
- .atZone(ZoneId.of(TIMEZONE_ID))
- .toInstant()
- .toEpochMilli()));
-
- // Both are ReviewDb, exact match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "granted differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review:"
- + " {2009-09-30 17:00:00.0} != {1900-01-01 00:00:00.0}");
-
- // Truncating NoteDb timestamp is allowed.
- b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(a1), comments(), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
- }
-
- @Test
- public void diffPatchSetApprovalsIgnoresPostSubmitBitOnZeroVote() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- c.setStatus(Change.Status.MERGED);
- PatchSetApproval a1 =
- new PatchSetApproval(
- new PatchSetApproval.Key(c.currentPatchSetId(), accountId, new LabelId("Code-Review")),
- (short) 0,
- TimeUtil.nowTs());
- a1.setPostSubmit(false);
- PatchSetApproval a2 = clone(a1);
- a2.setPostSubmit(true);
-
- // Both are ReviewDb, exact match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a2), comments(), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "postSubmit differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review:"
- + " {false} != {true}");
-
- // One NoteDb, postSubmit is ignored.
- b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(a1), comments(), reviewers(), REVIEW_DB);
- b2 =
- new ChangeBundle(c, messages(), latest(c), approvals(a2), comments(), reviewers(), NOTE_DB);
- assertNoDiffs(b1, b2);
- assertNoDiffs(b2, b1);
-
- // postSubmit is not ignored if vote isn't 0.
- a1.setValue((short) 1);
- a2.setValue((short) 1);
- assertDiffs(
- b1,
- b2,
- "postSubmit differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review:"
- + " {false} != {true}");
- assertDiffs(
- b2,
- b1,
- "postSubmit differs for PatchSetApproval.Key "
- + c.getId()
- + "%2C1,100,Code-Review:"
- + " {true} != {false}");
- }
-
- @Test
- public void diffReviewers() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- Timestamp now = TimeUtil.nowTs();
- ReviewerSet r1 = reviewers(REVIEWER, new Account.Id(1), now);
- ReviewerSet r2 = reviewers(REVIEWER, new Account.Id(2), now);
-
- ChangeBundle b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r1, REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r2, REVIEW_DB);
- assertNoDiffs(b1, b1);
- assertNoDiffs(b2, b2);
- assertDiffs(b1, b2, "reviewer sets differ: [1] only in A; [2] only in B");
- }
-
- @Test
- public void diffReviewersIgnoresStateAndTimestamp() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- ReviewerSet r1 = reviewers(REVIEWER, new Account.Id(1), TimeUtil.nowTs());
- ReviewerSet r2 = reviewers(CC, new Account.Id(1), TimeUtil.nowTs());
-
- ChangeBundle b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r1, REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(c, messages(), latest(c), approvals(), comments(), r2, REVIEW_DB);
- assertNoDiffs(b1, b1);
- assertNoDiffs(b2, b2);
- }
-
- @Test
- public void diffPatchLineCommentKeySets() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- int id = c.getId().get();
- PatchLineComment c1 =
- new PatchLineComment(
- new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename1"), "uuid1"),
- 5,
- accountId,
- null,
- TimeUtil.nowTs());
- PatchLineComment c2 =
- new PatchLineComment(
- new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename2"), "uuid2"),
- 5,
- accountId,
- null,
- TimeUtil.nowTs());
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
-
- assertDiffs(
- b1,
- b2,
- "PatchLineComment.Key sets differ:"
- + " ["
- + id
- + ",1,filename1,uuid1] only in A;"
- + " ["
- + id
- + ",1,filename2,uuid2] only in B");
- }
-
- @Test
- public void diffPatchLineComments() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- PatchLineComment c1 =
- new PatchLineComment(
- new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename"), "uuid"),
- 5,
- accountId,
- null,
- TimeUtil.nowTs());
- PatchLineComment c2 = clone(c1);
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
-
- assertNoDiffs(b1, b2);
-
- c2.setStatus(PatchLineComment.Status.PUBLISHED);
- assertDiffs(
- b1,
- b2,
- "status differs for PatchLineComment.Key " + c.getId() + ",1,filename,uuid: {d} != {P}");
- }
-
- @Test
- public void diffPatchLineCommentsMixedSourcesAllowsSlop() throws Exception {
- subWindowResolution();
- Change c = TestChanges.newChange(project, accountId);
- PatchLineComment c1 =
- new PatchLineComment(
- new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename"), "uuid"),
- 5,
- accountId,
- null,
- truncateToSecond(TimeUtil.nowTs()));
- PatchLineComment c2 = clone(c1);
- c2.setWrittenOn(TimeUtil.nowTs());
-
- // Both are ReviewDb, exact timestamp match is required.
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
- assertDiffs(
- b1,
- b2,
- "writtenOn differs for PatchLineComment.Key "
- + c.getId()
- + ",1,filename,uuid:"
- + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:03.0}");
-
- // One NoteDb, slop is allowed.
- b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(), comments(c1), reviewers(), NOTE_DB);
- b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c2), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
-
- // But not too much slop.
- superWindowResolution();
- PatchLineComment c3 = clone(c1);
- c3.setWrittenOn(TimeUtil.nowTs());
- b1 =
- new ChangeBundle(c, messages(), latest(c), approvals(), comments(c1), reviewers(), NOTE_DB);
- ChangeBundle b3 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c3), reviewers(), REVIEW_DB);
- String msg =
- "writtenOn differs for PatchLineComment.Key "
- + c.getId()
- + ",1,filename,uuid in NoteDb vs. ReviewDb:"
- + " {2009-09-30 17:00:02.0} != {2009-09-30 17:00:10.0}";
- assertDiffs(b1, b3, msg);
- assertDiffs(b3, b1, msg);
- }
-
- @Test
- public void diffPatchLineCommentsIgnoresCommentsOnInvalidPatchSet() throws Exception {
- Change c = TestChanges.newChange(project, accountId);
- PatchLineComment c1 =
- new PatchLineComment(
- new PatchLineComment.Key(new Patch.Key(c.currentPatchSetId(), "filename1"), "uuid1"),
- 5,
- accountId,
- null,
- TimeUtil.nowTs());
- PatchLineComment c2 =
- new PatchLineComment(
- new PatchLineComment.Key(
- new Patch.Key(new PatchSet.Id(c.getId(), 0), "filename2"), "uuid2"),
- 5,
- accountId,
- null,
- TimeUtil.nowTs());
-
- ChangeBundle b1 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c1, c2), reviewers(), REVIEW_DB);
- ChangeBundle b2 =
- new ChangeBundle(
- c, messages(), latest(c), approvals(), comments(c1), reviewers(), REVIEW_DB);
- assertNoDiffs(b1, b2);
- }
-
- private static void assertNoDiffs(ChangeBundle a, ChangeBundle b) {
- assertThat(a.differencesFrom(b)).isEmpty();
- assertThat(b.differencesFrom(a)).isEmpty();
- }
-
- private static void assertDiffs(ChangeBundle a, ChangeBundle b, String first, String... rest) {
- List<String> actual = a.differencesFrom(b);
- if (actual.size() == 1 && rest.length == 0) {
- // This error message is much easier to read.
- assertThat(actual.get(0)).isEqualTo(first);
- } else {
- List<String> expected = new ArrayList<>(1 + rest.length);
- expected.add(first);
- Collections.addAll(expected, rest);
- assertThat(actual).containsExactlyElementsIn(expected).inOrder();
- }
- assertThat(a).isNotEqualTo(b);
- }
-
- private static List<ChangeMessage> messages(ChangeMessage... ents) {
- return Arrays.asList(ents);
- }
-
- private static List<PatchSet> patchSets(PatchSet... ents) {
- return Arrays.asList(ents);
- }
-
- private static List<PatchSet> latest(Change c) {
- PatchSet ps = new PatchSet(c.currentPatchSetId());
- ps.setCreatedOn(c.getLastUpdatedOn());
- return ImmutableList.of(ps);
- }
-
- private static List<PatchSetApproval> approvals(PatchSetApproval... ents) {
- return Arrays.asList(ents);
- }
-
- private static ReviewerSet reviewers(Object... ents) {
- checkArgument(ents.length % 3 == 0);
- Table<ReviewerStateInternal, Account.Id, Timestamp> t = HashBasedTable.create();
- for (int i = 0; i < ents.length; i += 3) {
- t.put((ReviewerStateInternal) ents[i], (Account.Id) ents[i + 1], (Timestamp) ents[i + 2]);
- }
- return ReviewerSet.fromTable(t);
- }
-
- private static List<PatchLineComment> comments(PatchLineComment... ents) {
- return Arrays.asList(ents);
- }
-
- private static Change clone(Change ent) {
- return clone(CHANGE_CODEC, ent);
- }
-
- private static ChangeMessage clone(ChangeMessage ent) {
- return clone(CHANGE_MESSAGE_CODEC, ent);
- }
-
- private static PatchSet clone(PatchSet ent) {
- return clone(PATCH_SET_CODEC, ent);
- }
-
- private static PatchSetApproval clone(PatchSetApproval ent) {
- return clone(PATCH_SET_APPROVAL_CODEC, ent);
- }
-
- private static PatchLineComment clone(PatchLineComment ent) {
- return clone(PATCH_LINE_COMMENT_CODEC, ent);
- }
-
- private static <T> T clone(ProtobufCodec<T> codec, T obj) {
- return codec.decode(codec.encodeToByteArray(obj));
- }
-}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java
index 7b140b73f3..b4d9738a51 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesCacheTest.java
@@ -16,8 +16,8 @@ package com.google.gerrit.server.notedb;
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 static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.reviewdb.client.Change;
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index 7b41ba3624..2931b1706c 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -16,11 +16,8 @@ package com.google.gerrit.server.notedb;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.APPROVAL_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.MESSAGE_CODEC;
-import static com.google.gerrit.reviewdb.server.ReviewDbCodecs.PATCH_SET_CODEC;
-import static com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.toByteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
+import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
+import static com.google.gerrit.server.notedb.ChangeNotesState.Serializer.toByteString;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
@@ -39,6 +36,9 @@ 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.ReviewerByEmailSet;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -47,11 +47,10 @@ import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeCo
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerStatusUpdateProto;
-import com.google.gerrit.server.cache.serialize.ProtoCacheSerializers.ObjectIdConverter;
+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.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.StandardKeyEncoder;
+import com.google.gerrit.testing.GerritBaseTests;
import com.google.inject.TypeLiteral;
import com.google.protobuf.ByteString;
import java.lang.reflect.Type;
@@ -62,11 +61,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.junit.Before;
import org.junit.Test;
-public class ChangeNotesStateTest {
- static {
- KeyUtil.setEncoderImpl(new StandardKeyEncoder());
- }
-
+public class ChangeNotesStateTest extends GerritBaseTests {
private static final Change.Id ID = new Change.Id(123);
private static final ObjectId SHA =
ObjectId.fromString("1234567812345678123456781234567812345678");
@@ -345,14 +340,14 @@ public class ChangeNotesStateTest {
ps1.setUploader(new Account.Id(2000));
ps1.setRevision(new RevId("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
ps1.setCreatedOn(cols.createdOn());
- ByteString ps1Bytes = toByteString(ps1, PATCH_SET_CODEC);
+ 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());
- ByteString ps2Bytes = toByteString(ps2, PATCH_SET_CODEC);
+ ByteString ps2Bytes = toByteString(ps2, PatchSetProtoConverter.INSTANCE);
assertThat(ps2Bytes.size()).isEqualTo(66);
assertThat(ps2Bytes).isNotEqualTo(ps1Bytes);
@@ -377,7 +372,7 @@ public class ChangeNotesStateTest {
new PatchSet.Id(ID, 1), new Account.Id(2001), new LabelId("Code-Review")),
(short) 1,
new Timestamp(1212L));
- ByteString a1Bytes = toByteString(a1, APPROVAL_CODEC);
+ ByteString a1Bytes = toByteString(a1, PatchSetApprovalProtoConverter.INSTANCE);
assertThat(a1Bytes.size()).isEqualTo(43);
PatchSetApproval a2 =
@@ -386,7 +381,7 @@ public class ChangeNotesStateTest {
new PatchSet.Id(ID, 1), new Account.Id(2002), new LabelId("Verified")),
(short) -1,
new Timestamp(3434L));
- ByteString a2Bytes = toByteString(a2, APPROVAL_CODEC);
+ ByteString a2Bytes = toByteString(a2, PatchSetApprovalProtoConverter.INSTANCE);
assertThat(a2Bytes.size()).isEqualTo(49);
assertThat(a2Bytes).isNotEqualTo(a1Bytes);
@@ -644,7 +639,7 @@ public class ChangeNotesStateTest {
new Account.Id(1000),
new Timestamp(1212L),
new PatchSet.Id(ID, 1));
- ByteString m1Bytes = toByteString(m1, MESSAGE_CODEC);
+ ByteString m1Bytes = toByteString(m1, ChangeMessageProtoConverter.INSTANCE);
assertThat(m1Bytes.size()).isEqualTo(35);
ChangeMessage m2 =
@@ -653,7 +648,7 @@ public class ChangeNotesStateTest {
new Account.Id(2000),
new Timestamp(3434L),
new PatchSet.Id(ID, 2));
- ByteString m2Bytes = toByteString(m2, MESSAGE_CODEC);
+ ByteString m2Bytes = toByteString(m2, ChangeMessageProtoConverter.INSTANCE);
assertThat(m2Bytes.size()).isEqualTo(35);
assertThat(m2Bytes).isNotEqualTo(m1Bytes);
@@ -709,19 +704,6 @@ public class ChangeNotesStateTest {
}
@Test
- public void serializeReadOnlyUntil() throws Exception {
- assertRoundTrip(
- newBuilder().readOnlyUntil(new Timestamp(1212L)).build(),
- ChangeNotesStateProto.newBuilder()
- .setMetaId(SHA_BYTES)
- .setChangeId(ID.get())
- .setColumns(colsProto)
- .setReadOnlyUntil(1212L)
- .setHasReadOnlyUntil(true)
- .build());
- }
-
- @Test
public void changeNotesStateMethods() throws Exception {
assertThatSerializedClass(ChangeNotesState.class)
.hasAutoValueMethods(
@@ -751,7 +733,6 @@ public class ChangeNotesStateTest {
.put(
"publishedComments",
new TypeLiteral<ImmutableListMultimap<RevId, Comment>>() {}.getType())
- .put("readOnlyUntil", Timestamp.class)
.build());
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 549f5dbea3..a6c0224465 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -14,14 +14,15 @@
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.common.truth.Truth.assert_;
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import static com.google.gerrit.reviewdb.client.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 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;
@@ -34,6 +35,7 @@ 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.exceptions.StorageException;
import com.google.gerrit.mail.Address;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
@@ -45,7 +47,6 @@ 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.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ReviewerSet;
@@ -54,14 +55,11 @@ import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.testing.TestChanges;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.sql.Timestamp;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -408,10 +406,10 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
update.commit();
ChangeNotes notes = newNotes(c);
- List<PatchSetApproval> approvals =
- ReviewDbUtil.intKeyOrdering()
- .onResultOf(PatchSetApproval::getAccountId)
- .sortedCopy(notes.getApprovals().get(c.currentPatchSetId()));
+ ImmutableList<PatchSetApproval> approvals =
+ notes.getApprovals().get(c.currentPatchSetId()).stream()
+ .sorted(comparing(a -> a.getAccountId().get()))
+ .collect(toImmutableList());
assertThat(approvals).hasSize(2);
assertThat(approvals.get(0).getAccountId()).isEqualTo(changeOwner.getAccountId());
@@ -956,7 +954,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = TestChanges.newChange(project, changeOwner.getAccountId());
String trimmedSubj = c.getSubject();
c.setCurrentPatchSet(c.currentPatchSetId(), " " + trimmedSubj, c.getOriginalSubject());
- ChangeUpdate update = newUpdate(c, changeOwner);
+ ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
update.setBranch(c.getDest().get());
update.commit();
@@ -968,7 +966,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
c = TestChanges.newChange(project, changeOwner.getAccountId());
c.setCurrentPatchSet(c.currentPatchSetId(), tabSubj, c.getOriginalSubject());
- update = newUpdate(c, changeOwner);
+ update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
update.setBranch(c.getDest().get());
update.commit();
@@ -995,7 +993,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
try {
newNotes(c);
fail("Expected IOException");
- } catch (OrmException e) {
+ } catch (StorageException e) {
assertCause(
e,
ConfigInvalidException.class,
@@ -2605,7 +2603,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
}
// Looking at drafts directly shows the zombie comment.
- DraftCommentNotes draftNotes = draftNotesFactory.create(c, otherUserId);
+ DraftCommentNotes draftNotes = draftNotesFactory.create(c.getId(), otherUserId);
assertThat(draftNotes.load().getComments().get(rev1)).containsExactly(comment1, comment2);
// Zombie comment is filtered out of drafts via ChangeNotes.
@@ -2760,76 +2758,6 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
}
@Test
- public void readOnlyUntilExpires() throws Exception {
- Change c = newChange();
- ChangeUpdate update = newUpdate(c, changeOwner);
- Timestamp until = new Timestamp(TimeUtil.nowMs() + 10000);
- update.setReadOnlyUntil(until);
- update.commit();
-
- update = newUpdate(c, changeOwner);
- update.setTopic("failing-topic");
- try {
- update.commit();
- assert_().fail("expected OrmException");
- } catch (OrmException e) {
- assertThat(e.getMessage()).contains("read-only until");
- }
-
- ChangeNotes notes = newNotes(c);
- assertThat(notes.getChange().getTopic()).isNotEqualTo("failing-topic");
- assertThat(notes.getReadOnlyUntil()).isEqualTo(until);
-
- TestTimeUtil.incrementClock(30, TimeUnit.SECONDS);
- update = newUpdate(c, changeOwner);
- update.setTopic("succeeding-topic");
- update.commit();
-
- // Write succeeded; lease still exists, even though it's expired.
- notes = newNotes(c);
- assertThat(notes.getChange().getTopic()).isEqualTo("succeeding-topic");
- assertThat(notes.getReadOnlyUntil()).isEqualTo(until);
-
- // New lease takes precedence.
- update = newUpdate(c, changeOwner);
- until = new Timestamp(TimeUtil.nowMs() + 10000);
- update.setReadOnlyUntil(until);
- update.commit();
- assertThat(newNotes(c).getReadOnlyUntil()).isEqualTo(until);
- }
-
- @Test
- public void readOnlyUntilCleared() throws Exception {
- Change c = newChange();
- ChangeUpdate update = newUpdate(c, changeOwner);
- Timestamp until = new Timestamp(TimeUtil.nowMs() + TimeUnit.DAYS.toMillis(30));
- update.setReadOnlyUntil(until);
- update.commit();
-
- update = newUpdate(c, changeOwner);
- update.setTopic("failing-topic");
- try {
- update.commit();
- assert_().fail("expected OrmException");
- } catch (OrmException e) {
- assertThat(e.getMessage()).contains("read-only until");
- }
-
- // Sentinel timestamp of 0 can be written to clear lease.
- update = newUpdate(c, changeOwner);
- update.setReadOnlyUntil(new Timestamp(0));
- update.commit();
-
- update = newUpdate(c, changeOwner);
- update.setTopic("succeeding-topic");
- update.commit();
-
- ChangeNotes notes = newNotes(c);
- assertThat(notes.getChange().getTopic()).isEqualTo("succeeding-topic");
- assertThat(notes.getReadOnlyUntil()).isEqualTo(new Timestamp(0));
- }
-
- @Test
public void privateDefault() throws Exception {
Change c = newChange();
ChangeNotes notes = newNotes(c);
@@ -3067,7 +2995,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
public void setRevertOfPersistsValue() throws Exception {
Change changeToRevert = newChange();
Change c = TestChanges.newChange(project, changeOwner.getAccountId());
- ChangeUpdate update = newUpdate(c, changeOwner);
+ ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
update.setRevertOf(changeToRevert.getId().get());
update.commit();
@@ -3088,7 +3016,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
update.setRevertOf(newChange().getId().get());
- exception.expect(OrmException.class);
+ exception.expect(StorageException.class);
exception.expectMessage("Given ChangeUpdate is only allowed on initial commit");
update.commit();
}
diff --git a/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java b/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
deleted file mode 100644
index dee61814c1..0000000000
--- a/javatests/com/google/gerrit/server/notedb/CommentJsonMigratorTest.java
+++ /dev/null
@@ -1,527 +0,0 @@
-// 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.notedb;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Multimaps;
-import com.google.gerrit.reviewdb.client.Change;
-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.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.server.CommentsUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.notedb.CommentJsonMigrator.ProjectMigrationResult;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.TestChanges;
-import com.google.inject.Inject;
-import java.io.ByteArrayOutputStream;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.jgit.junit.TestRepository;
-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.notes.NoteMap;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.Before;
-import org.junit.Test;
-
-public class CommentJsonMigratorTest extends AbstractChangeNotesTest {
- private CommentJsonMigrator migrator;
- @Inject private ChangeNoteUtil noteUtil;
- @Inject private CommentsUtil commentsUtil;
- @Inject private LegacyChangeNoteWrite legacyChangeNoteWrite;
- @Inject private AllUsersName allUsersName;
-
- private AtomicInteger uuidCounter;
-
- @Before
- public void setUpCounter() {
- uuidCounter = new AtomicInteger();
- migrator = new CommentJsonMigrator(new ChangeNoteJson(), "gerrit", allUsersName);
- }
-
- @Test
- public void noOpIfAllCommentsAreJson() throws Exception {
- Change c = newChange();
- incrementPatchSet(c);
-
- ChangeNotes notes = newNotes(c);
- ChangeUpdate update = newUpdate(c, changeOwner);
- Comment ps1Comment = newComment(notes, 1, "comment on ps1");
- update.putComment(Status.PUBLISHED, ps1Comment);
- update.commit();
-
- notes = newNotes(c);
- update = newUpdate(c, changeOwner);
- Comment ps2Comment = newComment(notes, 2, "comment on ps2");
- update.putComment(Status.PUBLISHED, ps2Comment);
- update.commit();
-
- notes = newNotes(c);
- assertThat(getToStringRepresentations(notes.getComments()))
- .containsExactly(
- getRevId(notes, 1), ps1Comment.toString(),
- getRevId(notes, 2), ps2Comment.toString());
-
- ChangeNotes oldNotes = notes;
- checkMigrate(project, ImmutableList.of());
- assertNoDifferences(notes, oldNotes);
- assertThat(notes.getMetaId()).isEqualTo(oldNotes.getMetaId());
- }
-
- @Test
- public void migratePublishedComments() throws Exception {
- Change c = newChange();
- incrementPatchSet(c);
-
- ChangeNotes notes = newNotes(c);
-
- Comment ps1Comment1 = newComment(notes, 1, "first comment on ps1");
- Comment ps2Comment1 = newComment(notes, 2, "first comment on ps2");
- Comment ps1Comment2 = newComment(notes, 1, "second comment on ps1");
-
- // Construct legacy format 'by hand'.
- ByteArrayOutputStream out1 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(1, ps1Comment1).build(), out1);
-
- ByteArrayOutputStream out2 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(2, ps2Comment1).build(), out2);
-
- ByteArrayOutputStream out3 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder()
- .put(1, ps1Comment2)
- .put(1, ps1Comment1)
- .build(),
- out3);
-
- TestRepository<Repository> testRepository = new TestRepository<>(repo, rw);
-
- String metaRefName = RefNames.changeMetaRef(c.getId());
- testRepository
- .branch(metaRefName)
- .commit()
- .message("Review ps 1\n\nPatch-set: 1")
- .add(ps1Comment1.revId, out1.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
-
- testRepository
- .branch(metaRefName)
- .commit()
- .message("Review ps 2\n\nPatch-set: 2")
- .add(ps2Comment1.revId, out2.toString())
- .add(ps1Comment1.revId, out3.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
-
- notes = newNotes(c);
- assertThat(getToStringRepresentations(notes.getComments()))
- .containsExactly(
- getRevId(notes, 1), ps1Comment1.toString(),
- getRevId(notes, 1), ps1Comment2.toString(),
- getRevId(notes, 2), ps2Comment1.toString());
-
- // Comments at each commit all have legacy format.
- ImmutableList<RevCommit> oldLog = log(project, RefNames.changeMetaRef(c.getId()));
- assertThat(oldLog).hasSize(4);
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(0))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(1))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(2)))
- .containsExactly(ps1Comment1.key, true);
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(3)))
- .containsExactly(ps1Comment1.key, true, ps1Comment2.key, true, ps2Comment1.key, true);
-
- // Check that dryRun doesn't touch anything.
- String refName = RefNames.changeMetaRef(c.getId());
- ObjectId before = repo.getRefDatabase().getRef(refName).getObjectId();
- ProjectMigrationResult dryRunResult = migrator.migrateProject(project, repo, true);
- ObjectId after = repo.getRefDatabase().getRef(refName).getObjectId();
- assertThat(before).isEqualTo(after);
- assertThat(dryRunResult.refsUpdated).isEqualTo(ImmutableList.of(refName));
-
- ChangeNotes oldNotes = notes;
- checkMigrate(project, ImmutableList.of(refName));
-
- // Comment content is the same.
- notes = newNotes(c);
- assertNoDifferences(notes, oldNotes);
- assertThat(getToStringRepresentations(notes.getComments()))
- .containsExactly(
- getRevId(notes, 1), ps1Comment1.toString(),
- getRevId(notes, 1), ps1Comment2.toString(),
- getRevId(notes, 2), ps2Comment1.toString());
-
- // Comments at each commit all have JSON format.
- ImmutableList<RevCommit> newLog = log(project, RefNames.changeMetaRef(c.getId()));
- assertLogEqualExceptTrees(newLog, oldLog);
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(0))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(1))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(2)))
- .containsExactly(ps1Comment1.key, false);
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(3)))
- .containsExactly(ps1Comment1.key, false, ps1Comment2.key, false, ps2Comment1.key, false);
- }
-
- @Test
- public void migrateDraftComments() throws Exception {
- Change c = newChange();
- incrementPatchSet(c);
-
- ChangeNotes notes = newNotes(c);
- ObjectId origMetaId = notes.getMetaId();
-
- Comment ownerCommentPs1 = newComment(notes, 1, "owner comment on ps1", changeOwner);
- Comment ownerCommentPs2 = newComment(notes, 2, "owner comment on ps2", changeOwner);
- Comment otherCommentPs1 = newComment(notes, 1, "other user comment on ps1", otherUser);
-
- ByteArrayOutputStream out1 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(1, ownerCommentPs1).build(), out1);
-
- ByteArrayOutputStream out2 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(2, ownerCommentPs2).build(), out2);
-
- ByteArrayOutputStream out3 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(1, otherCommentPs1).build(), out3);
-
- try (Repository allUsersRepo = repoManager.openRepository(allUsers);
- RevWalk allUsersRw = new RevWalk(allUsersRepo)) {
- TestRepository<Repository> testRepository = new TestRepository<>(allUsersRepo, allUsersRw);
-
- testRepository
- .branch(RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()))
- .commit()
- .message("Review ps 1\n\nPatch-set: 1")
- .add(ownerCommentPs1.revId, out1.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
-
- testRepository
- .branch(RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()))
- .commit()
- .message("Review ps 1\n\nPatch-set: 2")
- .add(ownerCommentPs2.revId, out2.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
-
- testRepository
- .branch(RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()))
- .commit()
- .message("Review ps 2\n\nPatch-set: 2")
- .add(otherCommentPs1.revId, out3.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
- }
-
- notes = newNotes(c);
- assertThat(getToStringRepresentations(notes.getDraftComments(changeOwner.getAccountId())))
- .containsExactly(
- getRevId(notes, 1), ownerCommentPs1.toString(),
- getRevId(notes, 2), ownerCommentPs2.toString());
- assertThat(getToStringRepresentations(notes.getDraftComments(otherUser.getAccountId())))
- .containsExactly(getRevId(notes, 1), otherCommentPs1.toString());
-
- // Comments at each commit all have legacy format.
- ImmutableList<RevCommit> oldOwnerLog =
- log(allUsers, RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()));
- assertThat(oldOwnerLog).hasSize(2);
- assertThat(getLegacyFormatMapForDraftComments(notes, oldOwnerLog.get(0)))
- .containsExactly(ownerCommentPs1.key, true);
- assertThat(getLegacyFormatMapForDraftComments(notes, oldOwnerLog.get(1)))
- .containsExactly(ownerCommentPs1.key, true, ownerCommentPs2.key, true);
-
- ImmutableList<RevCommit> oldOtherLog =
- log(allUsers, RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()));
- assertThat(oldOtherLog).hasSize(1);
- assertThat(getLegacyFormatMapForDraftComments(notes, oldOtherLog.get(0)))
- .containsExactly(otherCommentPs1.key, true);
-
- ChangeNotes oldNotes = notes;
- checkMigrate(
- allUsers,
- ImmutableList.of(
- RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()),
- RefNames.refsDraftComments(c.getId(), otherUser.getAccountId())));
- assertNoDifferences(notes, oldNotes);
-
- // Migration doesn't touch change ref.
- assertThat(repo.exactRef(RefNames.changeMetaRef(c.getId())).getObjectId())
- .isEqualTo(origMetaId);
-
- // Comment content is the same.
- notes = newNotes(c);
- assertThat(getToStringRepresentations(notes.getDraftComments(changeOwner.getAccountId())))
- .containsExactly(
- getRevId(notes, 1), ownerCommentPs1.toString(),
- getRevId(notes, 2), ownerCommentPs2.toString());
- assertThat(getToStringRepresentations(notes.getDraftComments(otherUser.getAccountId())))
- .containsExactly(getRevId(notes, 1), otherCommentPs1.toString());
-
- // Comments at each commit all have JSON format.
- ImmutableList<RevCommit> newOwnerLog =
- log(allUsers, RefNames.refsDraftComments(c.getId(), changeOwner.getAccountId()));
- assertLogEqualExceptTrees(newOwnerLog, oldOwnerLog);
- assertThat(getLegacyFormatMapForDraftComments(notes, newOwnerLog.get(0)))
- .containsExactly(ownerCommentPs1.key, false);
- assertThat(getLegacyFormatMapForDraftComments(notes, newOwnerLog.get(1)))
- .containsExactly(ownerCommentPs1.key, false, ownerCommentPs2.key, false);
-
- ImmutableList<RevCommit> newOtherLog =
- log(allUsers, RefNames.refsDraftComments(c.getId(), otherUser.getAccountId()));
- assertLogEqualExceptTrees(newOtherLog, oldOtherLog);
- assertThat(getLegacyFormatMapForDraftComments(notes, newOtherLog.get(0)))
- .containsExactly(otherCommentPs1.key, false);
- }
-
- @Test
- public void migrateMixOfJsonAndLegacyComments() throws Exception {
- // 3 comments: legacy, JSON, legacy. Because adding a comment necessarily rewrites the entire
- // note, these comments need to be on separate patch sets.
- Change c = newChange();
- incrementPatchSet(c);
- incrementPatchSet(c);
-
- ChangeNotes notes = newNotes(c);
-
- Comment ps1Comment = newComment(notes, 1, "comment on ps1 (legacy)");
-
- ByteArrayOutputStream out1 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(1, ps1Comment).build(), out1);
-
- TestRepository<Repository> testRepository = new TestRepository<>(repo, rw);
-
- String metaRefName = RefNames.changeMetaRef(c.getId());
- testRepository
- .branch(metaRefName)
- .commit()
- .message("Review ps 1\n\nPatch-set: 1")
- .add(ps1Comment.revId, out1.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
-
- notes = newNotes(c);
- ChangeUpdate update = newUpdate(c, changeOwner);
- Comment ps2Comment = newComment(notes, 2, "comment on ps2 (JSON)");
- update.putComment(Status.PUBLISHED, ps2Comment);
- update.commit();
-
- Comment ps3Comment = newComment(notes, 3, "comment on ps3 (legacy)");
- ByteArrayOutputStream out3 = new ByteArrayOutputStream(0);
- legacyChangeNoteWrite.buildNote(
- ImmutableListMultimap.<Integer, Comment>builder().put(3, ps3Comment).build(), out3);
-
- testRepository
- .branch(metaRefName)
- .commit()
- .message("Review ps 3\n\nPatch-set: 3")
- .add(ps3Comment.revId, out3.toString())
- .author(serverIdent)
- .committer(serverIdent)
- .create();
-
- notes = newNotes(c);
- assertThat(getToStringRepresentations(notes.getComments()))
- .containsExactly(
- getRevId(notes, 1), ps1Comment.toString(),
- getRevId(notes, 2), ps2Comment.toString(),
- getRevId(notes, 3), ps3Comment.toString());
-
- // Comments at each commit match expected format.
- ImmutableList<RevCommit> oldLog = log(project, RefNames.changeMetaRef(c.getId()));
- assertThat(oldLog).hasSize(6);
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(0))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(1))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(2))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(3)))
- .containsExactly(ps1Comment.key, true);
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(4)))
- .containsExactly(ps1Comment.key, true, ps2Comment.key, false);
- assertThat(getLegacyFormatMapForPublishedComments(notes, oldLog.get(5)))
- .containsExactly(ps1Comment.key, true, ps2Comment.key, false, ps3Comment.key, true);
-
- ChangeNotes oldNotes = notes;
- checkMigrate(project, ImmutableList.of(RefNames.changeMetaRef(c.getId())));
- assertNoDifferences(notes, oldNotes);
-
- // Comment content is the same.
- notes = newNotes(c);
- assertThat(getToStringRepresentations(notes.getComments()))
- .containsExactly(
- getRevId(notes, 1), ps1Comment.toString(),
- getRevId(notes, 2), ps2Comment.toString(),
- getRevId(notes, 3), ps3Comment.toString());
-
- // Comments at each commit all have JSON format.
- ImmutableList<RevCommit> newLog = log(project, RefNames.changeMetaRef(c.getId()));
- assertLogEqualExceptTrees(newLog, oldLog);
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(0))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(1))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(2))).isEmpty();
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(3)))
- .containsExactly(ps1Comment.key, false);
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(4)))
- .containsExactly(ps1Comment.key, false, ps2Comment.key, false);
- assertThat(getLegacyFormatMapForPublishedComments(notes, newLog.get(5)))
- .containsExactly(ps1Comment.key, false, ps2Comment.key, false, ps3Comment.key, false);
- }
-
- private void checkMigrate(Project.NameKey project, List<String> expectedRefs) throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- ProjectMigrationResult progress = migrator.migrateProject(project, repo, false);
-
- assertThat(progress.ok).isTrue();
- assertThat(progress.refsUpdated).isEqualTo(expectedRefs);
- }
- }
-
- private Comment newComment(ChangeNotes notes, int psNum, String message) {
- return newComment(notes, psNum, message, changeOwner);
- }
-
- private Comment newComment(
- ChangeNotes notes, int psNum, String message, IdentifiedUser commenter) {
- return newComment(
- new PatchSet.Id(notes.getChangeId(), psNum),
- "filename",
- "uuid-" + uuidCounter.getAndIncrement(),
- null,
- 0,
- commenter,
- null,
- TimeUtil.nowTs(),
- message,
- (short) 1,
- getRevId(notes, psNum).get(),
- false);
- }
-
- private void incrementPatchSet(Change c) throws Exception {
- TestChanges.incrementPatchSet(c);
- RevCommit commit = tr.commit().message("PS" + c.currentPatchSetId().get()).create();
- ChangeUpdate update = newUpdate(c, changeOwner);
- update.setCommit(rw, commit);
- update.commit();
- }
-
- private static RevId getRevId(ChangeNotes notes, int psNum) {
- PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), psNum);
- PatchSet ps = notes.getPatchSets().get(psId);
- checkArgument(ps != null, "no patch set %s: %s", psNum, notes.getPatchSets());
- return ps.getRevision();
- }
-
- private static ListMultimap<RevId, String> getToStringRepresentations(
- ListMultimap<RevId, Comment> comments) {
- // Use string representation for equality comparison in this test, because Comment#equals only
- // compares keys.
- return Multimaps.transformValues(comments, Comment::toString);
- }
-
- private ImmutableMap<Comment.Key, Boolean> getLegacyFormatMapForPublishedComments(
- ChangeNotes notes, ObjectId metaId) throws Exception {
- return getLegacyFormatMap(project, notes.getChangeId(), metaId, Status.PUBLISHED);
- }
-
- private ImmutableMap<Comment.Key, Boolean> getLegacyFormatMapForDraftComments(
- ChangeNotes notes, ObjectId metaId) throws Exception {
- return getLegacyFormatMap(allUsers, notes.getChangeId(), metaId, Status.DRAFT);
- }
-
- private ImmutableMap<Comment.Key, Boolean> getLegacyFormatMap(
- Project.NameKey project, Change.Id changeId, ObjectId metaId, Status status)
- throws Exception {
- try (Repository repo = repoManager.openRepository(project);
- ObjectReader reader = repo.newObjectReader();
- RevWalk rw = new RevWalk(reader)) {
- NoteMap noteMap = NoteMap.read(reader, rw.parseCommit(metaId));
- RevisionNoteMap<ChangeRevisionNote> revNoteMap =
- RevisionNoteMap.parse(
- noteUtil.getChangeNoteJson(),
- noteUtil.getLegacyChangeNoteRead(),
- changeId,
- reader,
- noteMap,
- status);
- return revNoteMap.revisionNotes.values().stream()
- .flatMap(crn -> crn.getComments().stream())
- .collect(toImmutableMap(c -> c.key, c -> c.legacyFormat));
- }
- }
-
- private ImmutableList<RevCommit> log(Project.NameKey project, String refName) throws Exception {
- try (Repository repo = repoManager.openRepository(project);
- RevWalk rw = new RevWalk(repo)) {
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.REVERSE);
- Ref ref = repo.exactRef(refName);
- checkArgument(ref != null, "missing ref: %s", refName);
- rw.markStart(rw.parseCommit(ref.getObjectId()));
- return ImmutableList.copyOf(rw);
- }
- }
-
- private static void assertLogEqualExceptTrees(
- ImmutableList<RevCommit> actualLog, ImmutableList<RevCommit> expectedLog) {
- assertThat(actualLog).hasSize(expectedLog.size());
- for (int i = 0; i < expectedLog.size(); i++) {
- RevCommit actual = actualLog.get(i);
- RevCommit expected = expectedLog.get(i);
- assertThat(actual.getAuthorIdent())
- .named("author of entry %s", i)
- .isEqualTo(expected.getAuthorIdent());
- assertThat(actual.getCommitterIdent())
- .named("committer of entry %s", i)
- .isEqualTo(expected.getCommitterIdent());
- assertThat(actual.getFullMessage()).named("message of entry %s", i).isNotNull();
- assertThat(actual.getFullMessage())
- .named("message of entry %s", i)
- .isEqualTo(expected.getFullMessage());
- }
- }
-
- private void assertNoDifferences(ChangeNotes actual, ChangeNotes expected) throws Exception {
- assertThat(
- ChangeBundle.fromNotes(commentsUtil, actual)
- .differencesFrom(ChangeBundle.fromNotes(commentsUtil, expected)))
- .isEmpty();
- }
-}
diff --git a/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java b/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java
index e7d89562e4..4dd2005170 100644
--- a/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommentTimestampAdapterTest.java
@@ -18,6 +18,7 @@ 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.gson.Gson;
import com.google.gson.GsonBuilder;
import java.sql.Timestamp;
@@ -28,7 +29,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class CommentTimestampAdapterTest {
+public class CommentTimestampAdapterTest extends GerritBaseTests {
/** Arbitrary time outside of a DST transition, as an ISO instant. */
private static final String NON_DST_STR = "2017-02-07T10:20:30.123Z";
diff --git a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
index 1b504312a8..5552572a37 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
@@ -41,7 +41,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
@Test
public void approvalsCommitFormatSimple() throws Exception {
Change c = TestChanges.newChange(project, changeOwner.getAccountId(), 1);
- ChangeUpdate update = newUpdate(c, changeOwner);
+ ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.putApproval("Verified", (short) 1);
update.putApproval("Code-Review", (short) -1);
update.putReviewer(changeOwner.getAccount().getId(), REVIEWER);
@@ -84,7 +84,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
@Test
public void changeMessageCommitFormatSimple() throws Exception {
Change c = TestChanges.newChange(project, changeOwner.getAccountId(), 1);
- ChangeUpdate update = newUpdate(c, changeOwner);
+ ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeMessage("Just a little code change.\nHow about a new line");
update.commit();
assertThat(update.getRefName()).isEqualTo("refs/changes/01/1/meta");
@@ -110,7 +110,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
@Test
public void changeWithRevision() throws Exception {
Change c = TestChanges.newChange(project, changeOwner.getAccountId(), 1);
- ChangeUpdate update = newUpdate(c, changeOwner);
+ ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeMessage("Foo");
RevCommit commit = tr.commit().message("Subject").create();
update.setCommit(rw, commit);
@@ -309,7 +309,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
public void leadingWhitespace() throws Exception {
Change c = TestChanges.newChange(project, changeOwner.getAccountId());
c.setCurrentPatchSet(c.currentPatchSetId(), " " + c.getSubject(), c.getOriginalSubject());
- ChangeUpdate update = newUpdate(c, changeOwner);
+ ChangeUpdate update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
update.setBranch(c.getDest().get());
update.commit();
@@ -330,7 +330,7 @@ public class CommitMessageOutputTest extends AbstractChangeNotesTest {
c = TestChanges.newChange(project, changeOwner.getAccountId());
c.setCurrentPatchSet(c.currentPatchSetId(), "\t\t" + c.getSubject(), c.getOriginalSubject());
- update = newUpdate(c, changeOwner);
+ update = newUpdateForNewChange(c, changeOwner);
update.setChangeId(c.getKey().get());
update.setBranch(c.getDest().get());
update.commit();
diff --git a/javatests/com/google/gerrit/server/notedb/IntBlobTest.java b/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
new file mode 100644
index 0000000000..1cbe61de96
--- /dev/null
+++ b/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
@@ -0,0 +1,202 @@
+// 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.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.truth.OptionalSubject.assertThat;
+
+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;
+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.ObjectId;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+public class IntBlobTest {
+ // Note: Can't easily test GitRefUpdated behavior, since binding GitRefUpdated requires a thick
+ // stack of dependencies, and it's not just a simple interface or abstract class.
+
+ private Project.NameKey projectName;
+ private InMemoryRepository repo;
+ private TestRepository<InMemoryRepository> tr;
+ private RevWalk rw;
+
+ @Before
+ public void setUp() throws Exception {
+ projectName = new Project.NameKey("repo");
+ repo = new InMemoryRepository(new DfsRepositoryDescription(projectName.get()));
+ tr = new TestRepository<>(repo);
+ rw = tr.getRevWalk();
+ }
+
+ @Test
+ public void parseNoRef() throws Exception {
+ assertThat(IntBlob.parse(repo, "refs/nothing")).isEmpty();
+ }
+
+ @Test
+ 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.
+ }
+ }
+
+ @Test
+ public void parseValid() throws Exception {
+ String refName = "refs/foo";
+ ObjectId id = tr.update(refName, tr.blob("123"));
+ assertThat(IntBlob.parse(repo, refName)).value().isEqualTo(IntBlob.create(id, 123));
+ }
+
+ @Test
+ public void parseWithWhitespace() throws Exception {
+ String refName = "refs/foo";
+ ObjectId id = tr.update(refName, tr.blob(" 123 "));
+ assertThat(IntBlob.parse(repo, refName)).value().isEqualTo(IntBlob.create(id, 123));
+ }
+
+ @Test
+ 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());
+ }
+ }
+
+ @Test
+ public void tryStoreNoOldId() throws Exception {
+ String refName = "refs/foo";
+ RefUpdate ru =
+ IntBlob.tryStore(repo, rw, projectName, refName, null, 123, GitReferenceUpdated.DISABLED);
+ assertThat(ru.getResult()).isEqualTo(RefUpdate.Result.NEW);
+ assertThat(ru.getName()).isEqualTo(refName);
+ assertThat(IntBlob.parse(repo, refName))
+ .value()
+ .isEqualTo(IntBlob.create(ru.getNewObjectId(), 123));
+ }
+
+ @Test
+ public void tryStoreOldIdZero() throws Exception {
+ String refName = "refs/foo";
+ RefUpdate ru =
+ IntBlob.tryStore(
+ repo, rw, projectName, refName, ObjectId.zeroId(), 123, GitReferenceUpdated.DISABLED);
+ assertThat(ru.getResult()).isEqualTo(RefUpdate.Result.NEW);
+ assertThat(ru.getName()).isEqualTo(refName);
+ assertThat(IntBlob.parse(repo, refName))
+ .value()
+ .isEqualTo(IntBlob.create(ru.getNewObjectId(), 123));
+ }
+
+ @Test
+ public void tryStoreCorrectOldId() throws Exception {
+ String refName = "refs/foo";
+ ObjectId id = tr.update(refName, tr.blob("123"));
+ RefUpdate ru =
+ IntBlob.tryStore(repo, rw, projectName, refName, id, 456, GitReferenceUpdated.DISABLED);
+ assertThat(ru.getResult()).isEqualTo(RefUpdate.Result.FORCED);
+ assertThat(ru.getName()).isEqualTo(refName);
+ assertThat(IntBlob.parse(repo, refName))
+ .value()
+ .isEqualTo(IntBlob.create(ru.getNewObjectId(), 456));
+ }
+
+ @Test
+ public void tryStoreWrongOldId() throws Exception {
+ String refName = "refs/foo";
+ RefUpdate ru =
+ IntBlob.tryStore(
+ repo,
+ rw,
+ projectName,
+ refName,
+ ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+ 123,
+ GitReferenceUpdated.DISABLED);
+ assertThat(ru.getResult()).isEqualTo(RefUpdate.Result.LOCK_FAILURE);
+ assertThat(ru.getName()).isEqualTo(refName);
+ assertThat(IntBlob.parse(repo, refName)).isEmpty();
+ }
+
+ @Test
+ public void storeNoOldId() throws Exception {
+ String refName = "refs/foo";
+ IntBlob.store(repo, rw, projectName, refName, null, 123, GitReferenceUpdated.DISABLED);
+ assertThat(IntBlob.parse(repo, refName))
+ .value()
+ .isEqualTo(IntBlob.create(getRef(refName), 123));
+ }
+
+ @Test
+ public void storeOldIdZero() throws Exception {
+ String refName = "refs/foo";
+ IntBlob.store(
+ repo, rw, projectName, refName, ObjectId.zeroId(), 123, GitReferenceUpdated.DISABLED);
+ assertThat(IntBlob.parse(repo, refName))
+ .value()
+ .isEqualTo(IntBlob.create(getRef(refName), 123));
+ }
+
+ @Test
+ public void storeCorrectOldId() throws Exception {
+ String refName = "refs/foo";
+ ObjectId id = tr.update(refName, tr.blob("123"));
+ IntBlob.store(repo, rw, projectName, refName, id, 456, GitReferenceUpdated.DISABLED);
+ assertThat(IntBlob.parse(repo, refName))
+ .value()
+ .isEqualTo(IntBlob.create(getRef(refName), 456));
+ }
+
+ @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");
+ }
+ assertThat(IntBlob.parse(repo, refName)).isEmpty();
+ }
+
+ private ObjectId getRef(String refName) throws IOException {
+ return repo.exactRef(refName).getObjectId();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/notedb/NoteDbChangeStateTest.java b/javatests/com/google/gerrit/server/notedb/NoteDbChangeStateTest.java
deleted file mode 100644
index c82135e116..0000000000
--- a/javatests/com/google/gerrit/server/notedb/NoteDbChangeStateTest.java
+++ /dev/null
@@ -1,241 +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.server.notedb;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.NOTE_DB;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.applyDelta;
-import static com.google.gerrit.server.notedb.NoteDbChangeState.parse;
-import static com.google.gerrit.server.util.time.TimeUtil.nowTs;
-import static org.eclipse.jgit.lib.ObjectId.zeroId;
-
-import com.google.common.collect.ImmutableMap;
-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.notedb.NoteDbChangeState.Delta;
-import com.google.gerrit.server.notedb.NoteDbChangeState.RefState;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TestChanges;
-import com.google.gerrit.testing.TestTimeUtil;
-import java.sql.Timestamp;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.ObjectId;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/** Unit tests for {@link NoteDbChangeState}. */
-public class NoteDbChangeStateTest extends GerritBaseTests {
- ObjectId SHA1 = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
- ObjectId SHA2 = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
- ObjectId SHA3 = ObjectId.fromString("badc0feebadc0feebadc0feebadc0feebadc0fee");
-
- @Before
- public void setUp() {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- }
-
- @Test
- public void parseReviewDbWithoutDrafts() {
- NoteDbChangeState state = parse(new Change.Id(1), SHA1.name());
- assertThat(state.getPrimaryStorage()).isEqualTo(REVIEW_DB);
- assertThat(state.getChangeId()).isEqualTo(new Change.Id(1));
- assertThat(state.getChangeMetaId()).isEqualTo(SHA1);
- assertThat(state.getDraftIds()).isEmpty();
- assertThat(state.getReadOnlyUntil()).isEmpty();
- assertThat(state.toString()).isEqualTo(SHA1.name());
-
- state = parse(new Change.Id(1), "R," + SHA1.name());
- assertThat(state.getPrimaryStorage()).isEqualTo(REVIEW_DB);
- assertThat(state.getChangeId()).isEqualTo(new Change.Id(1));
- assertThat(state.getChangeMetaId()).isEqualTo(SHA1);
- assertThat(state.getDraftIds()).isEmpty();
- assertThat(state.getReadOnlyUntil()).isEmpty();
- assertThat(state.toString()).isEqualTo(SHA1.name());
- }
-
- @Test
- public void parseReviewDbWithDrafts() {
- String str = SHA1.name() + ",2003=" + SHA2.name() + ",1001=" + SHA3.name();
- String expected = SHA1.name() + ",1001=" + SHA3.name() + ",2003=" + SHA2.name();
- NoteDbChangeState state = parse(new Change.Id(1), str);
- assertThat(state.getPrimaryStorage()).isEqualTo(REVIEW_DB);
- assertThat(state.getChangeId()).isEqualTo(new Change.Id(1));
- assertThat(state.getChangeMetaId()).isEqualTo(SHA1);
- assertThat(state.getDraftIds())
- .containsExactly(
- new Account.Id(1001), SHA3,
- new Account.Id(2003), SHA2);
- assertThat(state.getReadOnlyUntil()).isEmpty();
- assertThat(state.toString()).isEqualTo(expected);
-
- state = parse(new Change.Id(1), "R," + str);
- assertThat(state.getPrimaryStorage()).isEqualTo(REVIEW_DB);
- assertThat(state.getChangeId()).isEqualTo(new Change.Id(1));
- assertThat(state.getChangeMetaId()).isEqualTo(SHA1);
- assertThat(state.getDraftIds())
- .containsExactly(
- new Account.Id(1001), SHA3,
- new Account.Id(2003), SHA2);
- assertThat(state.getReadOnlyUntil()).isEmpty();
- assertThat(state.toString()).isEqualTo(expected);
- }
-
- @Test
- public void parseReadOnlyUntil() {
- Timestamp ts = new Timestamp(12345);
- String str = "R=12345," + SHA1.name();
- NoteDbChangeState state = parse(new Change.Id(1), str);
- assertThat(state.getPrimaryStorage()).isEqualTo(REVIEW_DB);
- assertThat(state.getChangeId()).isEqualTo(new Change.Id(1));
- assertThat(state.getChangeMetaId()).isEqualTo(SHA1);
- assertThat(state.getReadOnlyUntil().get()).isEqualTo(ts);
- assertThat(state.toString()).isEqualTo(str);
-
- str = "N=12345";
- state = parse(new Change.Id(1), str);
- assertThat(state.getPrimaryStorage()).isEqualTo(NOTE_DB);
- assertThat(state.getChangeId()).isEqualTo(new Change.Id(1));
- assertThat(state.getRefState()).isEmpty();
- assertThat(state.getReadOnlyUntil().get()).isEqualTo(ts);
- assertThat(state.toString()).isEqualTo(str);
- }
-
- @Test
- public void applyDeltaToNullWithNoNewMetaId() throws Exception {
- Change c = newChange();
- assertThat(c.getNoteDbState()).isNull();
- applyDelta(c, Delta.create(c.getId(), noMetaId(), noDrafts()));
- assertThat(c.getNoteDbState()).isNull();
-
- applyDelta(c, Delta.create(c.getId(), noMetaId(), drafts(new Account.Id(1001), zeroId())));
- assertThat(c.getNoteDbState()).isNull();
- }
-
- @Test
- public void applyDeltaToMetaId() throws Exception {
- Change c = newChange();
- applyDelta(c, Delta.create(c.getId(), metaId(SHA1), noDrafts()));
- assertThat(c.getNoteDbState()).isEqualTo(SHA1.name());
-
- applyDelta(c, Delta.create(c.getId(), metaId(SHA2), noDrafts()));
- assertThat(c.getNoteDbState()).isEqualTo(SHA2.name());
-
- // No-op delta.
- applyDelta(c, Delta.create(c.getId(), noMetaId(), noDrafts()));
- assertThat(c.getNoteDbState()).isEqualTo(SHA2.name());
-
- // Set to zero clears the field.
- applyDelta(c, Delta.create(c.getId(), metaId(zeroId()), noDrafts()));
- assertThat(c.getNoteDbState()).isNull();
- }
-
- @Test
- public void applyDeltaToDrafts() throws Exception {
- Change c = newChange();
- applyDelta(c, Delta.create(c.getId(), metaId(SHA1), drafts(new Account.Id(1001), SHA2)));
- assertThat(c.getNoteDbState()).isEqualTo(SHA1.name() + ",1001=" + SHA2.name());
-
- applyDelta(c, Delta.create(c.getId(), noMetaId(), drafts(new Account.Id(2003), SHA3)));
- assertThat(c.getNoteDbState())
- .isEqualTo(SHA1.name() + ",1001=" + SHA2.name() + ",2003=" + SHA3.name());
-
- applyDelta(c, Delta.create(c.getId(), noMetaId(), drafts(new Account.Id(2003), zeroId())));
- assertThat(c.getNoteDbState()).isEqualTo(SHA1.name() + ",1001=" + SHA2.name());
-
- applyDelta(c, Delta.create(c.getId(), metaId(SHA3), noDrafts()));
- assertThat(c.getNoteDbState()).isEqualTo(SHA3.name() + ",1001=" + SHA2.name());
- }
-
- @Test
- public void applyDeltaToReadOnly() throws Exception {
- Timestamp ts = nowTs();
- Change c = newChange();
- NoteDbChangeState state =
- new NoteDbChangeState(
- c.getId(),
- REVIEW_DB,
- Optional.of(RefState.create(SHA1, ImmutableMap.of())),
- Optional.of(new Timestamp(ts.getTime() + 10000)));
- c.setNoteDbState(state.toString());
- Delta delta = Delta.create(c.getId(), metaId(SHA2), noDrafts());
- applyDelta(c, delta);
- assertThat(NoteDbChangeState.parse(c))
- .isEqualTo(
- new NoteDbChangeState(
- state.getChangeId(),
- state.getPrimaryStorage(),
- Optional.of(RefState.create(SHA2, ImmutableMap.of())),
- state.getReadOnlyUntil()));
- }
-
- @Test
- public void parseNoteDbPrimary() {
- NoteDbChangeState state = parse(new Change.Id(1), "N");
- assertThat(state.getPrimaryStorage()).isEqualTo(NOTE_DB);
- assertThat(state.getRefState()).isEmpty();
- assertThat(state.getReadOnlyUntil()).isEmpty();
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void parseInvalidPrimaryStorage() {
- parse(new Change.Id(1), "X");
- }
-
- @Test
- public void applyDeltaToNoteDbPrimaryIsNoOp() throws Exception {
- Change c = newChange();
- c.setNoteDbState("N");
- applyDelta(c, Delta.create(c.getId(), metaId(SHA1), drafts(new Account.Id(1001), SHA2)));
- assertThat(c.getNoteDbState()).isEqualTo("N");
- }
-
- private static Change newChange() {
- return TestChanges.newChange(new Project.NameKey("project"), new Account.Id(12345));
- }
-
- // Static factory methods to avoid type arguments when using as method args.
-
- private static Optional<ObjectId> noMetaId() {
- return Optional.empty();
- }
-
- private static Optional<ObjectId> metaId(ObjectId id) {
- return Optional.of(id);
- }
-
- private static ImmutableMap<Account.Id, ObjectId> noDrafts() {
- return ImmutableMap.of();
- }
-
- private static ImmutableMap<Account.Id, ObjectId> drafts(Object... args) {
- checkArgument(args.length % 2 == 0);
- ImmutableMap.Builder<Account.Id, ObjectId> b = ImmutableMap.builder();
- for (int i = 0; i < args.length / 2; i++) {
- b.put((Account.Id) args[2 * i], (ObjectId) args[2 * i + 1]);
- }
- return b.build();
- }
-}
diff --git a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
index a21f5bae0c..74cc507e27 100644
--- a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
+++ b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
@@ -23,11 +23,12 @@ import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.google.common.util.concurrent.Runnables;
+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 com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -40,17 +41,13 @@ 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;
-import org.junit.rules.ExpectedException;
-public class RepoSequenceTest {
+public class RepoSequenceTest extends GerritBaseTests {
// Don't sleep in tests.
- private static final Retryer<RefUpdate.Result> RETRYER =
+ private static final Retryer<RefUpdate> RETRYER =
RepoSequence.retryerBuilder().withBlockStrategy(t -> {}).build();
- @Rule public ExpectedException exception = ExpectedException.none();
-
private InMemoryRepositoryManager repoManager;
private Project.NameKey project;
@@ -70,7 +67,7 @@ public class RepoSequenceTest {
for (int i = 1; i <= max; i++) {
try {
assertThat(s.next()).named("i=" + i + " for " + name).isEqualTo(i);
- } catch (OrmException e) {
+ } catch (StorageException e) {
throw new AssertionError("failed batchSize=" + batchSize + ", i=" + i, e);
}
}
@@ -171,20 +168,20 @@ public class RepoSequenceTest {
@Test
public void failOnInvalidValue() throws Exception {
ObjectId id = writeBlob("id", "not a number");
- exception.expect(OrmException.class);
+ exception.expect(StorageException.class);
exception.expectMessage("invalid value in refs/sequences/id blob at " + id.name());
newSequence("id", 1, 3).next();
}
@Test
public void failOnWrongType() throws Exception {
- try (Repository repo = repoManager.openRepository(project)) {
- TestRepository<Repository> tr = new TestRepository<>(repo);
+ 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 (OrmException e) {
+ } catch (StorageException e) {
assertThat(e.getCause()).isInstanceOf(ExecutionException.class);
assertThat(e.getCause().getCause()).isInstanceOf(IncorrectObjectTypeException.class);
}
@@ -200,11 +197,11 @@ public class RepoSequenceTest {
1,
10,
() -> writeBlob("id", Integer.toString(bgCounter.getAndAdd(1000))),
- RetryerBuilder.<RefUpdate.Result>newBuilder()
+ RetryerBuilder.<RefUpdate>newBuilder()
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build());
- exception.expect(OrmException.class);
- exception.expectMessage("failed to update refs/sequences/id: LOCK_FAILURE");
+ exception.expect(StorageException.class);
+ exception.expectMessage("Failed to update refs/sequences/id: LOCK_FAILURE");
s.next();
}
@@ -254,95 +251,6 @@ public class RepoSequenceTest {
assertThat(s2.acquireCount).isEqualTo(1);
}
- @Test
- public void increaseTo() throws Exception {
- // Seed existing ref value.
- writeBlob("id", "1");
-
- RepoSequence s = newSequence("id", 1, 10);
-
- s.increaseTo(2);
- assertThat(s.next()).isEqualTo(2);
- }
-
- @Test
- public void increaseToLowerValueIsIgnored() throws Exception {
- // Seed existing ref value.
- writeBlob("id", "2");
-
- RepoSequence s = newSequence("id", 1, 10);
-
- s.increaseTo(1);
- assertThat(s.next()).isEqualTo(2);
- }
-
- @Test
- public void increaseToRetryOnLockFailureV1() throws Exception {
- // Seed existing ref value.
- writeBlob("id", "1");
-
- AtomicBoolean doneBgUpdate = new AtomicBoolean(false);
- Runnable bgUpdate =
- () -> {
- if (!doneBgUpdate.getAndSet(true)) {
- writeBlob("id", "2");
- }
- };
-
- RepoSequence s = newSequence("id", 1, 10, bgUpdate, RETRYER);
- assertThat(doneBgUpdate.get()).isFalse();
-
- // Increase the value to 3. The background thread increases the value to 2, which makes the
- // increase to value 3 fail once with LockFailure. The increase to 3 is then retried and is
- // expected to succeed.
- s.increaseTo(3);
- assertThat(s.next()).isEqualTo(3);
-
- assertThat(doneBgUpdate.get()).isTrue();
- }
-
- @Test
- public void increaseToRetryOnLockFailureV2() throws Exception {
- // Seed existing ref value.
- writeBlob("id", "1");
-
- AtomicBoolean doneBgUpdate = new AtomicBoolean(false);
- Runnable bgUpdate =
- () -> {
- if (!doneBgUpdate.getAndSet(true)) {
- writeBlob("id", "3");
- }
- };
-
- RepoSequence s = newSequence("id", 1, 10, bgUpdate, RETRYER);
- assertThat(doneBgUpdate.get()).isFalse();
-
- // Increase the value to 2. The background thread increases the value to 3, which makes the
- // increase to value 2 fail with LockFailure. The increase to 2 is then not retried because the
- // current value is already higher and it should be preserved.
- s.increaseTo(2);
- assertThat(s.next()).isEqualTo(3);
-
- assertThat(doneBgUpdate.get()).isTrue();
- }
-
- @Test
- public void increaseToFailAfterRetryerGivesUp() throws Exception {
- AtomicInteger bgCounter = new AtomicInteger(1234);
- RepoSequence s =
- newSequence(
- "id",
- 1,
- 10,
- () -> writeBlob("id", Integer.toString(bgCounter.getAndAdd(1000))),
- RetryerBuilder.<RefUpdate.Result>newBuilder()
- .withStopStrategy(StopStrategies.stopAfterAttempt(3))
- .build());
- exception.expect(OrmException.class);
- exception.expectMessage("failed to update refs/sequences/id: LOCK_FAILURE");
- s.increaseTo(2);
- }
-
private RepoSequence newSequence(String name, int start, int batchSize) {
return newSequence(name, start, batchSize, Runnables.doNothing(), RETRYER);
}
@@ -352,7 +260,7 @@ public class RepoSequenceTest {
final int start,
int batchSize,
Runnable afterReadRef,
- Retryer<RefUpdate.Result> retryer) {
+ Retryer<RefUpdate> retryer) {
return new RepoSequence(
repoManager,
GitReferenceUpdated.DISABLED,
diff --git a/javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java b/javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java
deleted file mode 100644
index c81a176d2c..0000000000
--- a/javatests/com/google/gerrit/server/notedb/rebuild/EventSorterTest.java
+++ /dev/null
@@ -1,231 +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.server.notedb.rebuild;
-
-import static com.google.common.truth.Truth.assertThat;
-import static java.util.stream.Collectors.toList;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-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.notedb.ChangeUpdate;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.TestTimeUtil;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
-import org.junit.Before;
-import org.junit.Test;
-
-public class EventSorterTest {
- private class TestEvent extends Event {
- protected TestEvent(Timestamp when) {
- super(
- new PatchSet.Id(new Change.Id(1), 1),
- new Account.Id(1000),
- new Account.Id(1000),
- when,
- changeCreatedOn,
- null);
- }
-
- @Override
- boolean uniquePerUpdate() {
- return false;
- }
-
- @Override
- void apply(ChangeUpdate update) {
- throw new UnsupportedOperationException();
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public String toString() {
- return "E{" + when.getSeconds() + '}';
- }
- }
-
- private Timestamp changeCreatedOn;
-
- @Before
- public void setUp() {
- TestTimeUtil.resetWithClockStep(10, TimeUnit.SECONDS);
- changeCreatedOn = TimeUtil.nowTs();
- }
-
- @Test
- public void naturalSort() {
- Event e1 = new TestEvent(TimeUtil.nowTs());
- Event e2 = new TestEvent(TimeUtil.nowTs());
- Event e3 = new TestEvent(TimeUtil.nowTs());
-
- for (List<Event> events : Collections2.permutations(events(e1, e2, e3))) {
- assertSorted(events, events(e1, e2, e3));
- }
- }
-
- @Test
- public void topoSortOneDep() {
- List<Event> es;
-
- // Input list is 0,1,2
-
- // 0 depends on 1 => 1,0,2
- es = threeEventsOneDep(0, 1);
- assertSorted(es, events(es, 1, 0, 2));
-
- // 1 depends on 0 => 0,1,2
- es = threeEventsOneDep(1, 0);
- assertSorted(es, events(es, 0, 1, 2));
-
- // 0 depends on 2 => 1,2,0
- es = threeEventsOneDep(0, 2);
- assertSorted(es, events(es, 1, 2, 0));
-
- // 2 depends on 0 => 0,1,2
- es = threeEventsOneDep(2, 0);
- assertSorted(es, events(es, 0, 1, 2));
-
- // 1 depends on 2 => 0,2,1
- es = threeEventsOneDep(1, 2);
- assertSorted(es, events(es, 0, 2, 1));
-
- // 2 depends on 1 => 0,1,2
- es = threeEventsOneDep(2, 1);
- assertSorted(es, events(es, 0, 1, 2));
- }
-
- private List<Event> threeEventsOneDep(int depFromIdx, int depOnIdx) {
- List<Event> events =
- Lists.newArrayList(
- new TestEvent(TimeUtil.nowTs()),
- new TestEvent(TimeUtil.nowTs()),
- new TestEvent(TimeUtil.nowTs()));
- events.get(depFromIdx).addDep(events.get(depOnIdx));
- return events;
- }
-
- @Test
- public void lastEventDependsOnFirstEvent() {
- List<Event> events = new ArrayList<>();
- for (int i = 0; i < 20; i++) {
- events.add(new TestEvent(TimeUtil.nowTs()));
- }
- events.get(events.size() - 1).addDep(events.get(0));
- assertSorted(events, events);
- }
-
- @Test
- public void firstEventDependsOnLastEvent() {
- List<Event> events = new ArrayList<>();
- for (int i = 0; i < 20; i++) {
- events.add(new TestEvent(TimeUtil.nowTs()));
- }
- events.get(0).addDep(events.get(events.size() - 1));
-
- List<Event> expected = new ArrayList<>();
- expected.addAll(events.subList(1, events.size()));
- expected.add(events.get(0));
- assertSorted(events, expected);
- }
-
- @Test
- public void topoSortChainOfDeps() {
- Event e1 = new TestEvent(TimeUtil.nowTs());
- Event e2 = new TestEvent(TimeUtil.nowTs());
- Event e3 = new TestEvent(TimeUtil.nowTs());
- Event e4 = new TestEvent(TimeUtil.nowTs());
- e1.addDep(e2);
- e2.addDep(e3);
- e3.addDep(e4);
-
- assertSorted(events(e1, e2, e3, e4), events(e4, e3, e2, e1));
- }
-
- @Test
- public void topoSortMultipleDeps() {
- Event e1 = new TestEvent(TimeUtil.nowTs());
- Event e2 = new TestEvent(TimeUtil.nowTs());
- Event e3 = new TestEvent(TimeUtil.nowTs());
- Event e4 = new TestEvent(TimeUtil.nowTs());
- e1.addDep(e2);
- e1.addDep(e4);
- e2.addDep(e3);
-
- // Processing 3 pops 2, processing 4 pops 1.
- assertSorted(events(e2, e3, e1, e4), events(e3, e2, e4, e1));
- }
-
- @Test
- public void topoSortMultipleDepsPreservesNaturalOrder() {
- Event e1 = new TestEvent(TimeUtil.nowTs());
- Event e2 = new TestEvent(TimeUtil.nowTs());
- Event e3 = new TestEvent(TimeUtil.nowTs());
- Event e4 = new TestEvent(TimeUtil.nowTs());
- e1.addDep(e4);
- e2.addDep(e4);
- e3.addDep(e4);
-
- // Processing 4 pops 1, 2, 3 in natural order.
- assertSorted(events(e4, e3, e2, e1), events(e4, e1, e2, e3));
- }
-
- @Test
- public void topoSortCycle() {
- Event e1 = new TestEvent(TimeUtil.nowTs());
- Event e2 = new TestEvent(TimeUtil.nowTs());
-
- // Implementation is not really defined, but infinite looping would be bad.
- // According to current implementation details, 2 pops 1, 1 pops 2 which was
- // already seen.
- assertSorted(events(e2, e1), events(e1, e2));
- }
-
- @Test
- public void topoSortDepNotInInputList() {
- Event e1 = new TestEvent(TimeUtil.nowTs());
- Event e2 = new TestEvent(TimeUtil.nowTs());
- Event e3 = new TestEvent(TimeUtil.nowTs());
- e1.addDep(e3);
-
- List<Event> events = events(e2, e1);
- try {
- new EventSorter(events).sort();
- fail("expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- private static List<Event> events(Event... es) {
- return Lists.newArrayList(es);
- }
-
- private static List<Event> events(List<Event> in, Integer... indexes) {
- return Stream.of(indexes).map(in::get).collect(toList());
- }
-
- private static void assertSorted(List<Event> unsorted, List<Event> expected) {
- List<Event> actual = new ArrayList<>(unsorted);
- new EventSorter(actual).sort();
- assertThat(actual).named("sorted" + unsorted).isEqualTo(expected);
- }
-}
diff --git a/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java b/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
index ef80d7ef33..6c63c5f6a0 100644
--- a/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
+++ b/javatests/com/google/gerrit/server/patch/IntraLineLoaderTest.java
@@ -19,13 +19,14 @@ 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.eclipse.jgit.diff.ReplaceEdit;
import org.junit.Test;
-public class IntraLineLoaderTest {
+public class IntraLineLoaderTest extends GerritBaseTests {
@Test
public void rewriteAtStartOfLineIsRecognized() throws Exception {
diff --git a/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java b/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java
index 81f03afea8..4ed5f4b299 100644
--- a/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java
+++ b/javatests/com/google/gerrit/server/patch/PatchListEntryTest.java
@@ -20,9 +20,10 @@ 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 org.junit.Test;
-public class PatchListEntryTest {
+public class PatchListEntryTest extends GerritBaseTests {
@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 6fbafb673d..ccdd040195 100644
--- a/javatests/com/google/gerrit/server/patch/PatchListTest.java
+++ b/javatests/com/google/gerrit/server/patch/PatchListTest.java
@@ -17,6 +17,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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
@@ -25,7 +26,7 @@ import java.io.ObjectOutputStream;
import java.util.Arrays;
import org.junit.Test;
-public class PatchListTest {
+public class PatchListTest extends GerritBaseTests {
@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 305e81bf1c..ff9ac411f7 100644
--- a/javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java
+++ b/javatests/com/google/gerrit/server/permissions/DefaultPermissionsMappingTest.java
@@ -18,9 +18,10 @@ 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 {
+public class DefaultPermissionsMappingTest extends GerritBaseTests {
@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
new file mode 100644
index 0000000000..f40c3bc1f3
--- /dev/null
+++ b/javatests/com/google/gerrit/server/permissions/PluginPermissionsUtilTest.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.server.permissions;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.permissions.PluginPermissionsUtil.isValidPluginPermission;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+/** Small tests for {@link PluginPermissionsUtil}. */
+public final class PluginPermissionsUtilTest {
+
+ @Test
+ public void isPluginPermissionReturnsTrueForValidName() {
+ // "-" is allowed for a plugin name. Here "foo-a" should be the name of the plugin.
+ ImmutableList<String> validPluginPermissions =
+ ImmutableList.of("plugin-foo-a", "plugin-foo-a-b");
+
+ for (String permission : validPluginPermissions) {
+ assertThat(isValidPluginPermission(permission))
+ .named("valid plugin permission: %s", permission)
+ .isTrue();
+ }
+ }
+
+ @Test
+ public void isPluginPermissionReturnsFalseForInvalidName() {
+ ImmutableList<String> invalidPluginPermissions =
+ ImmutableList.of(
+ "create",
+ "label-Code-Review",
+ "plugin-foo",
+ "plugin-foo",
+ "plugin-foo-a-",
+ "plugin-foo-a1");
+
+ for (String permission : invalidPluginPermissions) {
+ assertThat(isValidPluginPermission(permission))
+ .named("invalid plugin permission: %s", permission)
+ .isFalse();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/permissions/RefControlTest.java b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
index 0d19984427..58adc0c6c9 100644
--- a/javatests/com/google/gerrit/server/permissions/RefControlTest.java
+++ b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
@@ -41,12 +41,11 @@ 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.common.errors.InvalidNameException;
+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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.account.GroupMembership;
@@ -55,7 +54,6 @@ 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.config.SitePaths;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.index.SingleVersionModule.SingleVersionListener;
import com.google.gerrit.server.project.ProjectCache;
@@ -64,16 +62,13 @@ 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.schema.SchemaCreator;
-import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gerrit.testing.InMemoryDatabase;
+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 com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -89,7 +84,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class RefControlTest {
+public class RefControlTest extends GerritBaseTests {
private void assertAdminsAreOwnersAndDevsAreNot() {
ProjectControl uBlah = user(local, DEVS);
ProjectControl uAdmin = user(local, DEVS, ADMIN);
@@ -195,17 +190,16 @@ public class RefControlTest {
private ProjectCache projectCache;
private PermissionCollection.Factory sectionSorter;
private ChangeControl.Factory changeControlFactory;
- private ReviewDb db;
@Inject private PermissionBackend permissionBackend;
@Inject private CapabilityCollection.Factory capabilityCollectionFactory;
@Inject private SchemaCreator schemaCreator;
@Inject private SingleVersionListener singleVersionListener;
- @Inject private InMemoryDatabase schemaFactory;
@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 {
@@ -274,7 +268,8 @@ public class RefControlTest {
try {
Repository repo = repoManager.createRepository(allProjectsName);
- ProjectConfig allProjects = new ProjectConfig(new Project.NameKey(allProjectsName.get()));
+ ProjectConfig allProjects =
+ projectConfigFactory.create(new Project.NameKey(allProjectsName.get()));
allProjects.load(repo);
LabelType cr = Util.codeReview();
allProjects.getLabelSections().put(cr.getName(), cr);
@@ -283,10 +278,9 @@ public class RefControlTest {
throw new RuntimeException(e);
}
- db = schemaFactory.open();
singleVersionListener.start();
try {
- schemaCreator.create(db);
+ schemaCreator.create();
} finally {
singleVersionListener.stop();
}
@@ -295,27 +289,16 @@ public class RefControlTest {
CacheBuilder.newBuilder().build();
sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c), metricMaker);
- parent = new ProjectConfig(parentKey);
+ parent = projectConfigFactory.create(parentKey);
parent.load(newRepository(parentKey));
add(parent);
- local = new ProjectConfig(localKey);
+ local = projectConfigFactory.create(localKey);
local.load(newRepository(localKey));
add(local);
local.getProject().setParentName(parentKey);
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return null;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(() -> null);
changeControlFactory = injector.getInstance(ChangeControl.Factory.class);
}
@@ -323,10 +306,6 @@ public class RefControlTest {
@After
public void tearDown() {
requestContext.setContext(null);
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(schemaFactory);
}
@Test
@@ -455,7 +434,7 @@ public class RefControlTest {
allow(local, READ, DEVS, "refs/heads/*");
assertCanAccess(user(local, "a", ADMIN));
- local = new ProjectConfig(localKey);
+ local = projectConfigFactory.create(localKey);
local.load(newRepository(localKey));
local.getProject().setParentName(parentKey);
allow(local, READ, DEVS, "refs/*");
@@ -972,7 +951,6 @@ public class RefControlTest {
}
private InMemoryRepository add(ProjectConfig pc) {
- SitePaths sitePaths = null;
List<CommentLinkInfo> commentLinks = null;
InMemoryRepository repo;
@@ -987,7 +965,6 @@ public class RefControlTest {
all.put(
pc.getName(),
new ProjectState(
- sitePaths,
projectCache,
allProjectsName,
allUsersName,
@@ -1007,8 +984,8 @@ public class RefControlTest {
private ProjectControl user(
ProjectConfig local, @Nullable String name, AccountGroup.UUID... memberOf) {
return new ProjectControl(
- Collections.<AccountGroup.UUID>emptySet(),
- Collections.<AccountGroup.UUID>emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
sectionSorter,
changeControlFactory,
permissionBackend,
diff --git a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
index 07f64a8cae..cf6d50fab6 100644
--- a/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
+++ b/javatests/com/google/gerrit/server/project/CommitsCollectionTest.java
@@ -34,6 +34,7 @@ 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;
@@ -48,7 +49,7 @@ import org.junit.Rule;
import org.junit.Test;
/** Unit tests for {@link CommitsCollection}. */
-public class CommitsCollectionTest {
+public class CommitsCollectionTest extends GerritBaseTests {
@Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
@Inject private AccountManager accountManager;
@@ -57,6 +58,7 @@ public class CommitsCollectionTest {
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected AllProjectsName allProjects;
@Inject private CommitsCollection commits;
+ @Inject private ProjectConfig.Factory projectConfigFactory;
private TestRepository<InMemoryRepository> repo;
private ProjectConfig project;
@@ -70,7 +72,7 @@ public class CommitsCollectionTest {
Project.NameKey name = new Project.NameKey("project");
InMemoryRepository inMemoryRepo = repoManager.createRepository(name);
- project = new ProjectConfig(name);
+ project = projectConfigFactory.create(name);
project.load(inMemoryRepo);
repo = new TestRepository<>(inMemoryRepo);
}
diff --git a/javatests/com/google/gerrit/server/project/GroupListTest.java b/javatests/com/google/gerrit/server/project/GroupListTest.java
index 2249a16ccf..08aca9f987 100644
--- a/javatests/com/google/gerrit/server/project/GroupListTest.java
+++ b/javatests/com/google/gerrit/server/project/GroupListTest.java
@@ -29,6 +29,7 @@ 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.server.git.ValidationError;
+import com.google.gerrit.testing.GerritBaseTests;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
@@ -36,7 +37,7 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Test;
-public class GroupListTest {
+public class GroupListTest extends GerritBaseTests {
private static final Project.NameKey PROJECT = new Project.NameKey("project");
private static final String TEXT =
"# UUID \tGroup Name\n"
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index 0e4ba10415..3436153b41 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -16,7 +16,9 @@ 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 com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ContributorAgreement;
@@ -24,16 +26,21 @@ 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.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 java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
@@ -51,7 +58,9 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
public class ProjectConfigTest extends GerritBaseTests {
private static final String LABEL_SCORES_CONFIG =
@@ -74,15 +83,24 @@ public class ProjectConfigTest extends GerritBaseTests {
+ !LabelType.DEF_COPY_ALL_SCORES_IF_NO_CHANGE
+ "\n";
+ private static final AllProjectsName ALL_PROJECTS = new AllProjectsName("All-The-Projects");
+
+ @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");
+ private SitePaths sitePaths;
+ private ProjectConfig.Factory factory;
private Repository db;
private TestRepository<?> tr;
@Before
public void setUp() throws Exception {
+ sitePaths = new SitePaths(temporaryFolder.newFolder().toPath());
+ Files.createDirectories(sitePaths.etc_dir);
+ factory = new ProjectConfig.Factory(sitePaths, ALL_PROJECTS);
db = new InMemoryRepository(new DfsRepositoryDescription("repo"));
tr = new TestRepository<>(db);
}
@@ -104,6 +122,13 @@ public class ProjectConfigTest extends GerritBaseTests {
+ " sameGroupVisibility = block group Staff\n"
+ "[contributor-agreement \"Individual\"]\n"
+ " description = A simple description\n"
+ + " matchProjects = ^/ourproject\n"
+ + " matchProjects = ^/ourotherproject\n"
+ + " matchProjects = ^/someotherroot/ourproject\n"
+ + " excludeProjects = ^/theirproject\n"
+ + " excludeProjects = ^/theirotherproject\n"
+ + " excludeProjects = ^/someotherroot/theirproject\n"
+ + " excludeProjects = ^/someotherroot/theirotherproject\n"
+ " accepted = group Developers\n"
+ " accepted = group Staff\n"
+ " autoVerify = group Developers\n"
@@ -115,6 +140,14 @@ public class ProjectConfigTest extends GerritBaseTests {
ContributorAgreement ca = cfg.getContributorAgreement("Individual");
assertThat(ca.getName()).isEqualTo("Individual");
assertThat(ca.getDescription()).isEqualTo("A simple description");
+ assertThat(ca.getMatchProjectsRegexes())
+ .containsExactly("^/ourproject", "^/ourotherproject", "^/someotherroot/ourproject");
+ assertThat(ca.getExcludeProjectsRegexes())
+ .containsExactly(
+ "^/theirproject",
+ "^/theirotherproject",
+ "^/someotherroot/theirproject",
+ "^/someotherroot/theirotherproject");
assertThat(ca.getAgreementUrl()).isEqualTo("http://www.example.com/agree");
assertThat(ca.getAccepted()).hasSize(2);
assertThat(ca.getAccepted().get(0).getGroup()).isEqualTo(developers);
@@ -256,6 +289,7 @@ public class ProjectConfigTest extends GerritBaseTests {
+ " sameGroupVisibility = block group Staff\n"
+ "[contributor-agreement \"Individual\"]\n"
+ " description = A simple description\n"
+ + " matchProjects = ^/ourproject\n"
+ " accepted = group Developers\n"
+ " autoVerify = group Developers\n"
+ " agreementUrl = http://www.example.com/agree\n"
@@ -273,6 +307,8 @@ public class ProjectConfigTest extends GerritBaseTests {
ContributorAgreement ca = cfg.getContributorAgreement("Individual");
ca.setAccepted(Collections.singletonList(new PermissionRule(cfg.resolve(staff))));
ca.setAutoVerify(null);
+ ca.setMatchProjectsRegexes(null);
+ ca.setExcludeProjectsRegexes(Collections.singletonList("^/theirproject"));
ca.setDescription("A new description");
rev = commit(cfg);
assertThat(text(rev, "project.config"))
@@ -289,6 +325,7 @@ public class ProjectConfigTest extends GerritBaseTests {
+ " 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
@@ -385,7 +422,7 @@ public class ProjectConfigTest extends GerritBaseTests {
@Test
public void readUnexistingPluginConfig() throws Exception {
- ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
+ ProjectConfig cfg = factory.create(new Project.NameKey("test"));
cfg.load(db);
PluginConfig pluginCfg = cfg.getPluginConfig("somePlugin");
assertThat(pluginCfg.getNames()).isEmpty();
@@ -572,8 +609,46 @@ public class ProjectConfigTest extends GerritBaseTests {
+ "commentlink.bugzilla must have either link or html"));
}
+ @Test
+ public void readAllProjectsBaseConfigFromSitePaths() throws Exception {
+ ProjectConfig cfg = factory.create(ALL_PROJECTS);
+ cfg.load(db);
+ assertThat(cfg.getProject().getBooleanConfig(REQUIRE_CHANGE_ID))
+ .isEqualTo(InheritableBoolean.INHERIT);
+
+ writeDefaultAllProjectsConfig("[receive]", "requireChangeId = false");
+
+ cfg.load(db);
+ assertThat(cfg.getProject().getBooleanConfig(REQUIRE_CHANGE_ID))
+ .isEqualTo(InheritableBoolean.FALSE);
+ }
+
+ @Test
+ public void readOtherProjectIgnoresAllProjectsBaseConfig() throws Exception {
+ ProjectConfig cfg = factory.create(new Project.NameKey("test"));
+ cfg.load(db);
+ assertThat(cfg.getProject().getBooleanConfig(REQUIRE_CHANGE_ID))
+ .isEqualTo(InheritableBoolean.INHERIT);
+
+ writeDefaultAllProjectsConfig("[receive]", "requireChangeId = false");
+
+ cfg.load(db);
+ // If we went through ProjectState, then this would return FALSE, since project.config for
+ // All-Projects would inherit from all_projects.config, and this project would inherit from
+ // All-Projects. But in ProjectConfig itself, there is no inheritance from All-Projects, so this
+ // continues to return the default.
+ assertThat(cfg.getProject().getBooleanConfig(REQUIRE_CHANGE_ID))
+ .isEqualTo(InheritableBoolean.INHERIT);
+ }
+
+ private Path writeDefaultAllProjectsConfig(String... lines) throws IOException {
+ Path dir = sitePaths.etc_dir.resolve(ALL_PROJECTS.get());
+ Files.createDirectories(dir);
+ return Files.write(dir.resolve("project.config"), ImmutableList.copyOf(lines));
+ }
+
private ProjectConfig read(RevCommit rev) throws IOException, ConfigInvalidException {
- ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
+ ProjectConfig cfg = factory.create(new 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 d292e19072..a2b2866a72 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -49,7 +49,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
@@ -80,11 +79,9 @@ 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.InMemoryDatabase;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -118,8 +115,6 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
@Inject private Provider<AnonymousUser> anonymousUser;
- @Inject protected InMemoryDatabase schemaFactory;
-
@Inject protected SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
@@ -140,7 +135,6 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
protected LifecycleManager lifecycle;
protected Injector injector;
- protected ReviewDb db;
protected AccountInfo currentUserInfo;
protected CurrentUser admin;
@@ -160,12 +154,10 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
@After
public void cleanUp() {
lifecycle.stop();
- db.close();
}
protected void setUpDatabase() throws Exception {
- db = schemaFactory.open();
- schemaCreator.create(db);
+ schemaCreator.create();
Account.Id adminId = createAccount("admin", "Administrator", "admin@example.com", true);
admin = userFactory.create(adminId);
@@ -177,32 +169,11 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
protected RequestContext newRequestContext(Account.Id requestUserId) {
final CurrentUser requestUser = userFactory.create(requestUserId);
- return new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return requestUser;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- };
+ return () -> requestUser;
}
protected void setAnonymous() {
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return anonymousUser.get();
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(anonymousUser::get);
}
@After
@@ -211,10 +182,6 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
lifecycle.stop();
}
requestContext.setContext(null);
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(schemaFactory);
}
@Test
diff --git a/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
index 660c1d8b77..e36b79e6f6 100644
--- a/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/LuceneQueryAccountsTest.java
@@ -44,6 +44,6 @@ public class LuceneQueryAccountsTest extends AbstractQueryAccountsTest {
protected Injector createInjector() {
Config luceneConfig = new Config(config);
InMemoryModule.setDefaults(luceneConfig);
- return Guice.createInjector(new InMemoryModule(luceneConfig, notesMigration));
+ return Guice.createInjector(new InMemoryModule(luceneConfig));
}
}
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 8b606a032d..ff45c086ae 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -14,6 +14,7 @@
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.TruthJUnit.assume;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
@@ -51,10 +52,8 @@ import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.Changes.QueryRequest;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.HashtagsInput;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
-import com.google.gerrit.extensions.api.changes.ReviewInput.RobotCommentInput;
import com.google.gerrit.extensions.api.changes.StarsInput;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.api.projects.ConfigInput;
@@ -67,15 +66,15 @@ import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
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.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
-import com.google.gerrit.index.QueryOptions;
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.Account.Id;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
@@ -83,12 +82,10 @@ 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.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
+import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.account.AccountCache;
@@ -99,17 +96,15 @@ import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeTriplet;
+import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gerrit.server.index.change.StalenessChecker;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.NoteDbChangeState;
-import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
+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;
@@ -120,29 +115,23 @@ 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.server.util.time.TimeUtil;
-import com.google.gerrit.testing.DisabledReviewDb;
import com.google.gerrit.testing.GerritServerTests;
-import com.google.gerrit.testing.InMemoryDatabase;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.InMemoryRepositoryManager.Repo;
import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -177,6 +166,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Inject protected ChangeIndexer indexer;
@Inject protected IndexConfig indexConfig;
@Inject protected InMemoryRepositoryManager repoManager;
+ @Inject protected Provider<AnonymousUser> anonymousUserProvider;
@Inject protected Provider<InternalChangeQuery> queryProvider;
@Inject protected ChangeNotes.Factory notesFactory;
@Inject protected OneOffRequestContext oneOffRequestContext;
@@ -185,19 +175,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Inject protected ChangeNotes.Factory changeNotesFactory;
@Inject protected Provider<ChangeQueryProcessor> queryProcessorProvider;
@Inject protected SchemaCreator schemaCreator;
- @Inject protected SchemaFactory<ReviewDb> schemaFactory;
@Inject protected Sequences seq;
@Inject protected ThreadLocalRequestContext requestContext;
@Inject protected ProjectCache projectCache;
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected IdentifiedUser.GenericFactory identifiedUserFactory;
-
- // Only for use in setting up/tearing down injector; other users should use schemaFactory.
- @Inject private InMemoryDatabase inMemoryDatabase;
+ @Inject protected ProjectConfig.Factory projectConfigFactory;
protected Injector injector;
protected LifecycleManager lifecycle;
- protected ReviewDb db;
protected Account.Id userId;
protected CurrentUser user;
@@ -235,16 +221,12 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@After
public void cleanUp() {
lifecycle.stop();
- db.close();
}
protected void initAfterLifecycleStart() throws Exception {}
protected void setUpDatabase() throws Exception {
- try (ReviewDb underlyingDb = inMemoryDatabase.getDatabase().open()) {
- schemaCreator.create(underlyingDb);
- }
- db = schemaFactory.open();
+ schemaCreator.create();
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
String email = "user@example.com";
@@ -259,17 +241,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
protected RequestContext newRequestContext(Account.Id requestUserId) {
final CurrentUser requestUser = userFactory.create(requestUserId);
- return new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return requestUser;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- };
+ return () -> requestUser;
}
protected void resetUser() {
@@ -283,10 +255,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
lifecycle.stop();
}
requestContext.setContext(null);
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(inMemoryDatabase);
}
@Before
@@ -583,7 +551,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void restorePendingReviewers() throws Exception {
assume().that(getSchemaVersion()).isAtLeast(44);
- assume().that(notesMigration.readChanges()).isTrue();
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
@@ -1327,7 +1294,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("status:new", change2, change1);
gApi.changes().id(change1.getId().get()).topic("new-topic");
- change1 = notesFactory.create(db, change1.getProject(), change1.getId()).getChange();
+ change1 = notesFactory.create(change1.getProject(), change1.getId()).getChange();
assertThat(lastUpdatedMs(change1)).isGreaterThan(lastUpdatedMs(change2));
assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2))
@@ -1367,14 +1334,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byFileExact() throws Exception {
TestRepository<Repo> repo = createProject("repo");
- RevCommit commit =
- repo.parseBody(
- repo.commit()
- .message("one")
- .add("dir/file1", "contents1")
- .add("dir/file2", "contents2")
- .create());
- Change change = insert(repo, newChangeForCommit(repo, commit));
+ Change change = insert(repo, newChangeWithFiles(repo, "dir/file1", "dir/file2"));
assertQuery("file:file");
assertQuery("file:dir", change);
@@ -1387,14 +1347,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byFileRegex() throws Exception {
TestRepository<Repo> repo = createProject("repo");
- RevCommit commit =
- repo.parseBody(
- repo.commit()
- .message("one")
- .add("dir/file1", "contents1")
- .add("dir/file2", "contents2")
- .create());
- Change change = insert(repo, newChangeForCommit(repo, commit));
+ Change change = insert(repo, newChangeWithFiles(repo, "dir/file1", "dir/file2"));
assertQuery("file:.*file.*");
assertQuery("file:^file.*"); // Whole path only.
@@ -1404,14 +1357,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byPathExact() throws Exception {
TestRepository<Repo> repo = createProject("repo");
- RevCommit commit =
- repo.parseBody(
- repo.commit()
- .message("one")
- .add("dir/file1", "contents1")
- .add("dir/file2", "contents2")
- .create());
- Change change = insert(repo, newChangeForCommit(repo, commit));
+ Change change = insert(repo, newChangeWithFiles(repo, "dir/file1", "dir/file2"));
assertQuery("path:file");
assertQuery("path:dir");
@@ -1424,20 +1370,248 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byPathRegex() throws Exception {
TestRepository<Repo> repo = createProject("repo");
- RevCommit commit =
- repo.parseBody(
- repo.commit()
- .message("one")
- .add("dir/file1", "contents1")
- .add("dir/file2", "contents2")
- .create());
- Change change = insert(repo, newChangeForCommit(repo, commit));
+ Change change = insert(repo, newChangeWithFiles(repo, "dir/file1", "dir/file2"));
assertQuery("path:.*file.*");
assertQuery("path:^dir.file.*", change);
}
@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"));
+ Change change3 = insert(repo, newChangeWithFiles(repo, "dir/baz.h", "dir/baz.cc"));
+ Change change4 = insert(repo, newChangeWithFiles(repo, "Quux.java", "foo"));
+ Change change5 = insert(repo, newChangeWithFiles(repo, "foo"));
+
+ assertQuery("extension:java", change4);
+ assertQuery("ext:java", change4);
+ assertQuery("ext:.java", change4);
+ assertQuery("ext:jAvA", change4);
+ assertQuery("ext:.jAvA", change4);
+ assertQuery("ext:cc", change3, change2, change1);
+
+ if (getSchemaVersion() >= 56) {
+ // matching changes with files that have no extension is possible
+ assertQuery("ext:\"\"", change5, change4);
+ assertFailingQuery("ext:");
+ }
+ }
+
+ @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"));
+ Change change3 = insert(repo, newChangeWithFiles(repo, "foo.CC", "bar.cc"));
+ Change change4 = insert(repo, newChangeWithFiles(repo, "dir/baz.h", "dir/baz.cc"));
+ Change change5 = insert(repo, newChangeWithFiles(repo, "Quux.java"));
+ Change change6 = insert(repo, newChangeWithFiles(repo, "foo.txt", "foo"));
+ Change change7 = insert(repo, newChangeWithFiles(repo, "foo"));
+
+ // case doesn't matter
+ assertQuery("onlyextensions:cc,h", change4, change2, change1);
+ assertQuery("onlyextensions:CC,H", change4, change2, change1);
+ assertQuery("onlyextensions:cc,H", change4, change2, change1);
+ assertQuery("onlyextensions:cC,h", change4, change2, change1);
+ assertQuery("onlyextensions:cc", change3);
+ assertQuery("onlyextensions:CC", change3);
+ assertQuery("onlyexts:java", change5);
+ assertQuery("onlyexts:jAvA", change5);
+ assertQuery("onlyexts:.jAvA", change5);
+
+ // order doesn't matter
+ assertQuery("onlyextensions:h,cc", change4, change2, change1);
+ assertQuery("onlyextensions:H,CC", change4, change2, change1);
+
+ // specifying extension with '.' is okay
+ assertQuery("onlyextensions:.cc,.h", change4, change2, change1);
+ assertQuery("onlyextensions:cc,.h", change4, change2, change1);
+ assertQuery("onlyextensions:.cc,h", change4, change2, change1);
+ assertQuery("onlyexts:.java", change5);
+
+ // matching changes without extension is possible
+ assertQuery("onlyexts:txt");
+ assertQuery("onlyexts:txt,", change6);
+ assertQuery("onlyexts:,txt", change6);
+ assertQuery("onlyextensions:\"\"", change7);
+ assertQuery("onlyexts:\"\"", change7);
+ assertQuery("onlyextensions:,", change7);
+ assertQuery("onlyexts:,", change7);
+ assertFailingQuery("onlyextensions:");
+ assertFailingQuery("onlyexts:");
+
+ // inverse queries
+ assertQuery("-onlyextensions:cc,h", change7, change6, change5, change3);
+ }
+
+ @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));
+ RevCommit commit2 = repo.parseBody(repo.commit().message("Test\n\nfoo: baz").create());
+ Change change2 = insert(repo, newChangeForCommit(repo, commit2));
+ RevCommit commit3 = repo.parseBody(repo.commit().message("Test\n\nfoo: bar\nfoo:baz").create());
+ Change change3 = insert(repo, newChangeForCommit(repo, commit3));
+ RevCommit commit4 = repo.parseBody(repo.commit().message("Test\n\nfoo: bar=baz").create());
+ Change change4 = insert(repo, newChangeForCommit(repo, commit4));
+
+ // create a changes with lines that look like footers, but which are not
+ RevCommit commit5 =
+ repo.parseBody(
+ repo.commit().message("Test\n\nfoo: bar\n\nfoo=bar").insertChangeId().create());
+ Change change5 = insert(repo, newChangeForCommit(repo, commit5));
+ RevCommit commit6 = repo.parseBody(repo.commit().message("Test\n\na=b: c").create());
+ insert(repo, newChangeForCommit(repo, commit6));
+
+ // matching by 'key=value' works
+ assertQuery("footer:foo=bar", change3, change1);
+ assertQuery("footer:foo=baz", change3, change2);
+ assertQuery("footer:Change-Id=" + change5.getKey(), change5);
+ assertQuery("footer:foo=bar=baz", change4);
+
+ // case doesn't matter
+ assertQuery("footer:foo=BAR", change3, change1);
+ assertQuery("footer:FOO=bar", change3, change1);
+ assertQuery("footer:fOo=BaZ", change3, change2);
+
+ // verbatim matching of footers works
+ assertQuery("footer:\"foo: bar\"", change3, change1);
+ assertQuery("footer:\"foo: baz\"", change3, change2);
+ assertQuery("footer:\"Change-Id: " + change5.getKey() + "\"", change5);
+ assertQuery("footer:\"foo: bar=baz\"", change4);
+
+ // expect no match because 'a=b: c' of commit6 is not a valid footer (footer key cannot contain
+ // '=')
+ assertQuery("footer:a=b=c");
+ assertQuery("footer:\"a=b: c\"");
+
+ // expect empty result for invalid footers
+ assertQuery("footer:foo");
+ assertQuery("footer:foo=");
+ assertQuery("footer:=foo");
+ assertQuery("footer:=");
+ }
+
+ @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"));
+ Change change3 =
+ insert(repo, newChangeWithFiles(repo, "documentation/training/slides/README.txt"));
+ Change change4 = insert(repo, newChangeWithFiles(repo, "a.txt"));
+ Change change5 = insert(repo, newChangeWithFiles(repo, "a/b/c/d/e/foo.txt"));
+
+ // matching by directory prefix works
+ assertQuery("directory:src", change2, change1);
+ assertQuery("directory:src/java", change2);
+ assertQuery("directory:src/js", change2);
+ assertQuery("directory:documentation/", change3);
+ assertQuery("directory:documentation/training", change3);
+ assertQuery("directory:documentation/training/slides", change3);
+
+ // 'dir' alias works
+ assertQuery("dir:src", change2, change1);
+ assertQuery("dir:src/java", change2);
+
+ // case doesn't matter
+ assertQuery("directory:Documentation/TrAiNiNg/SLIDES", change3);
+
+ // leading and trailing '/' doesn't matter
+ assertQuery("directory:/documentation/training/slides", change3);
+ assertQuery("directory:documentation/training/slides/", change3);
+ assertQuery("directory:/documentation/training/slides/", change3);
+
+ // files do not match as directory
+ assertQuery("directory:src/foo.h");
+ assertQuery("directory:documentation/training/slides/README.txt");
+
+ // root directory matches all changes
+ assertQuery("directory:/", change5, change4, change3, change2, change1);
+ assertQuery("directory:\"\"", change5, change4, change3, change2, change1);
+ assertFailingQuery("directory:");
+
+ // matching single directory segments works
+ assertQuery("directory:java", change2);
+ assertQuery("directory:slides", change3);
+
+ // files do not match as directory segment
+ assertQuery("directory:foo.h");
+
+ // matching any combination of intermediate directory segments works
+ assertQuery("directory:training/slides", change3);
+ assertQuery("directory:b/c", change5);
+ assertQuery("directory:b/c/d", change5);
+ assertQuery("directory:b/c/d/e", change5);
+ assertQuery("directory:c/d", change5);
+ assertQuery("directory:c/d/e", change5);
+ assertQuery("directory:d/e", change5);
+
+ // files do not match as directory segments
+ assertQuery("directory:d/e/foo.txt");
+ assertQuery("directory:e/foo.txt");
+
+ // matching any combination of intermediate directory segments works with leading and trailing
+ // '/'
+ assertQuery("directory:/b/c", change5);
+ assertQuery("directory:/b/c/", change5);
+ assertQuery("directory:b/c/", change5);
+ }
+
+ @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 =
+ insert(repo, newChangeWithFiles(repo, "documentation/training/slides/README.txt"));
+
+ // match by regexp
+ assertQuery("directory:^.*va.*", change1);
+ assertQuery("directory:^documentation/.*/slides", change2);
+ assertQuery("directory:^train.*", change2);
+ }
+
+ @Test
public void byComment() throws Exception {
TestRepository<Repo> repo = createProject("repo");
ChangeInserter ins = newChange(repo);
@@ -1448,9 +1622,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
ReviewInput.CommentInput commentInput = new ReviewInput.CommentInput();
commentInput.line = 1;
commentInput.message = "inline";
- input.comments =
- ImmutableMap.<String, List<ReviewInput.CommentInput>>of(
- Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(commentInput));
+ input.comments = ImmutableMap.of(Patch.COMMIT_MSG, ImmutableList.of(commentInput));
gApi.changes().id(change.getId().get()).current().review(input);
Map<String, List<CommentInfo>> comments =
@@ -1610,8 +1782,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
- public void byHashtagWithNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
+ public void byHashtag() throws Exception {
List<Change> changes = setUpHashtagChanges();
assertQuery("hashtag:foo", changes.get(1), changes.get(0));
assertQuery("hashtag:bar", changes.get(1));
@@ -1625,35 +1796,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
- public void byHashtagWithoutNoteDb() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
-
- notesMigration.setWriteChanges(true);
- notesMigration.setReadChanges(true);
- db.close();
- db = schemaFactory.open();
- List<Change> changes;
- try {
- changes = setUpHashtagChanges();
- notesMigration.setWriteChanges(false);
- notesMigration.setReadChanges(false);
- } finally {
- db.close();
- }
- db = schemaFactory.open();
- for (Change c : changes) {
- indexer.index(db, c); // Reindex without hashtag field.
- }
- assertQuery("hashtag:foo");
- assertQuery("hashtag:bar");
- assertQuery("hashtag:\" bar \"");
- assertQuery("hashtag:\"a tag\"");
- assertQuery("hashtag:\" a tag \"");
- assertQuery("hashtag:#foo");
- assertQuery("hashtag:\"# #foo\"");
- }
-
- @Test
public void byDefault() throws Exception {
TestRepository<Repo> repo = createProject("repo");
@@ -1669,7 +1811,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Change change4 = insert(repo, ins4);
ReviewInput ri4 = new ReviewInput();
ri4.message = "toplevel";
- ri4.labels = ImmutableMap.<String, Short>of("Code-Review", (short) 1);
+ ri4.labels = ImmutableMap.of("Code-Review", (short) 1);
gApi.changes().id(change4.getId().get()).current().review(ri4);
ChangeInserter ins5 = newChangeWithTopic(repo, "feature5");
@@ -1690,6 +1832,8 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
Change[] expected = new Change[] {change6, change5, change4, change3, change2, change1};
assertQuery("user@example.com", expected);
assertQuery("repo", expected);
+
+ assertQuery("Code-Review:+1", change4);
}
@Test
@@ -1782,7 +1926,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
throws RepositoryNotFoundException, IOException, ConfigInvalidException {
try (MetaDataUpdate md = metaDataUpdateFactory.create(project)) {
md.setMessage(String.format("Grant %s on %s", permission, ref));
- ProjectConfig config = ProjectConfig.read(md);
+ ProjectConfig config = projectConfigFactory.read(md);
AccessSection s = config.getAccessSection(ref, true);
Permission p = s.getPermission(permission, true);
PermissionRule rule = Util.newRule(config, groupUUID);
@@ -1794,6 +1938,24 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
+ public void visibleToSelf() throws Exception {
+ TestRepository<Repo> repo = createProject("repo");
+ Change change1 = insert(repo, newChange(repo));
+ Change change2 = insert(repo, newChange(repo));
+
+ gApi.changes().id(change2.getChangeId()).setPrivate(true, "private");
+
+ String q = "project:repo";
+ assertQuery(q + " visibleto:self", change2, change1);
+ assertQuery(q + " visibleto:me", change2, change1);
+
+ // Anonymous user cannot see first user's private change.
+ requestContext.setContext(anonymousUserProvider::get);
+ assertQuery(q + " visibleto:self", change1);
+ assertQuery(q + " visibleto:me", change1);
+ }
+
+ @Test
public void byCommentBy() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo));
@@ -1807,9 +1969,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
ReviewInput.CommentInput comment = new ReviewInput.CommentInput();
comment.line = 1;
comment.message = "inline";
- input.comments =
- ImmutableMap.<String, List<ReviewInput.CommentInput>>of(
- Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment));
+ input.comments = ImmutableMap.of(Patch.COMMIT_MSG, ImmutableList.of(comment));
gApi.changes().id(change1.getId().get()).current().review(input);
input = new ReviewInput();
@@ -1850,8 +2010,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void byDraftByExcludesZombieDrafts() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
Change change = insert(repo, newChange(repo));
@@ -1866,36 +2024,25 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("draftby:" + userId, change);
assertQuery("commentby:" + userId);
- TestRepository<Repo> allUsers = new TestRepository<>(repoManager.openRepository(allUsersName));
+ try (TestRepository<Repo> allUsers =
+ new TestRepository<>(repoManager.openRepository(allUsersName))) {
+ Ref draftsRef = allUsers.getRepository().exactRef(RefNames.refsDraftComments(id, userId));
+ assertThat(draftsRef).isNotNull();
- Ref draftsRef = allUsers.getRepository().exactRef(RefNames.refsDraftComments(id, userId));
- assertThat(draftsRef).isNotNull();
+ ReviewInput rin = ReviewInput.dislike();
+ rin.drafts = DraftHandling.PUBLISH_ALL_REVISIONS;
+ gApi.changes().id(id.get()).current().review(rin);
- ReviewInput rin = ReviewInput.dislike();
- rin.drafts = DraftHandling.PUBLISH_ALL_REVISIONS;
- gApi.changes().id(id.get()).current().review(rin);
+ assertQuery("draftby:" + userId);
+ assertQuery("commentby:" + userId, change);
+ assertThat(allUsers.getRepository().exactRef(draftsRef.getName())).isNull();
- assertQuery("draftby:" + userId);
- assertQuery("commentby:" + userId, change);
- assertThat(allUsers.getRepository().exactRef(draftsRef.getName())).isNull();
-
- // Re-add drafts ref and ensure it gets filtered out during indexing.
- allUsers.update(draftsRef.getName(), draftsRef.getObjectId());
- assertThat(allUsers.getRepository().exactRef(draftsRef.getName())).isNotNull();
-
- if (PrimaryStorage.of(change) == PrimaryStorage.REVIEW_DB
- && !notesMigration.disableChangeReviewDb()) {
- // Record draft ref in noteDbState as well.
- ReviewDb db = ReviewDbUtil.unwrapDb(this.db);
- change = db.changes().get(id);
- NoteDbChangeState.applyDelta(
- change,
- NoteDbChangeState.Delta.create(
- id, Optional.empty(), ImmutableMap.of(userId, draftsRef.getObjectId())));
- db.changes().update(Collections.singleton(change));
+ // Re-add drafts ref and ensure it gets filtered out during indexing.
+ allUsers.update(draftsRef.getName(), draftsRef.getObjectId());
+ assertThat(allUsers.getRepository().exactRef(draftsRef.getName())).isNotNull();
}
- indexer.index(db, project, id);
+ indexer.index(project, id);
assertQuery("draftby:" + userId);
}
@@ -1988,9 +2135,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
ReviewInput.CommentInput comment = new ReviewInput.CommentInput();
comment.line = 1;
comment.message = "inline";
- input.comments =
- ImmutableMap.<String, List<ReviewInput.CommentInput>>of(
- Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment));
+ input.comments = ImmutableMap.of(Patch.COMMIT_MSG, ImmutableList.of(comment));
gApi.changes().id(change2.getId().get()).current().review(input);
assertQuery("from:" + userId.get(), change2, change1);
@@ -2109,17 +2254,10 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("reviewer:self", change3);
requestContext.setContext(newRequestContext(user1));
- if (notesMigration.readChanges()) {
- assertQuery("reviewer:" + user1, change1);
- assertQuery("cc:" + user1, change2);
- assertQuery("is:cc", change2);
- assertQuery("cc:self", change2);
- } else {
- assertQuery("reviewer:" + user1, change2, change1);
- assertQuery("cc:" + user1);
- assertQuery("is:cc");
- assertQuery("cc:self");
- }
+ assertQuery("reviewer:" + user1, change1);
+ assertQuery("cc:" + user1, change2);
+ assertQuery("is:cc", change2);
+ assertQuery("cc:self", change2);
}
@Test
@@ -2179,36 +2317,19 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
.collect(toList());
assertThat(members).contains(user2.toString());
- if (notesMigration.readChanges()) {
- // CC and REVIEWER are separate in NoteDB
- assertQuery("reviewerin:\"Registered Users\"", change2, change1);
- assertQuery("reviewerin:" + group, change2);
- } else {
- // CC and REVIEWER are the same in ReviewDb
- assertQuery("reviewerin:\"Registered Users\"", change3, change2, change1);
- assertQuery("reviewerin:" + group, change3, change2);
- }
+ assertQuery("reviewerin:\"Registered Users\"", change2, change1);
+ assertQuery("reviewerin:" + group, change2);
gApi.changes().id(change2.getId().get()).current().review(ReviewInput.approve());
gApi.changes().id(change2.getId().get()).current().submit();
- if (notesMigration.readChanges()) {
- // CC and REVIEWER are separate in NoteDB
- assertQuery("reviewerin:" + group, change2);
- assertQuery("project:repo reviewerin:" + group, change2);
- assertQuery("status:merged reviewerin:" + group, change2);
- } else {
- // CC and REVIEWER are the same in ReviewDb
- assertQuery("reviewerin:" + group, change2, change3);
- assertQuery("project:repo reviewerin:" + group, change2, change3);
- assertQuery("status:merged reviewerin:" + group, change2);
- }
+ assertQuery("reviewerin:" + group, change2);
+ assertQuery("project:repo reviewerin:" + group, change2);
+ assertQuery("status:merged reviewerin:" + group, change2);
}
@Test
public void reviewerAndCcByEmail() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
ConfigInput conf = new ConfigInput();
@@ -2255,8 +2376,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test
public void reviewerAndCcByEmailWithQueryForDifferentUser() throws Exception {
- assume().that(notesMigration.readChanges()).isTrue();
-
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
ConfigInput conf = new ConfigInput();
@@ -2420,7 +2539,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
for (int i = 1; i <= 11; i++) {
Iterable<ChangeData> cds =
- queryProvider.get().byCommitsOnBranchNotMerged(repo.getRepository(), db, dest, shas, i);
+ 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);
@@ -2429,73 +2548,14 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
- public void prepopulatedFields() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- TestRepository<Repo> repo = createProject("repo");
- Change change = insert(repo, newChange(repo));
-
- db = new DisabledReviewDb();
- requestContext.setContext(newRequestContext(userId));
- // Use QueryProcessor directly instead of API so we get ChangeDatas back.
- List<ChangeData> cds =
- queryProcessorProvider
- .get()
- .query(queryBuilder.parse(change.getId().toString()))
- .entities();
- assertThat(cds).hasSize(1);
-
- ChangeData cd = cds.get(0);
- cd.change();
- cd.patchSets();
- cd.currentApprovals();
- cd.changedLines();
- cd.reviewedBy();
- cd.reviewers();
- cd.unresolvedCommentCount();
-
- // TODO(dborowitz): Swap out GitRepositoryManager somehow? Will probably be
- // necessary for NoteDb anyway.
- cd.isMergeable();
-
- exception.expect(DisabledReviewDb.Disabled.class);
- cd.messages();
- }
-
- @Test
- public void prepopulateOnlyRequestedFields() throws Exception {
- assume().that(notesMigration.readChanges()).isFalse();
- TestRepository<Repo> repo = createProject("repo");
- Change change = insert(repo, newChange(repo));
-
- db = new DisabledReviewDb();
- requestContext.setContext(newRequestContext(userId));
- // Use QueryProcessor directly instead of API so we get ChangeDatas back.
- List<ChangeData> cds =
- queryProcessorProvider
- .get()
- .setRequestedFields(
- ImmutableSet.of(ChangeField.PATCH_SET.getName(), ChangeField.CHANGE.getName()))
- .query(queryBuilder.parse(change.getId().toString()))
- .entities();
- assertThat(cds).hasSize(1);
-
- ChangeData cd = cds.get(0);
- cd.change();
- cd.patchSets();
-
- exception.expect(DisabledReviewDb.Disabled.class);
- cd.currentApprovals();
- }
-
- @Test
public void reindexIfStale() throws Exception {
Account.Id user = createAccount("user");
Project.NameKey project = new Project.NameKey("repo");
TestRepository<Repo> repo = createProject(project.get());
Change change = insert(repo, newChange(repo));
String changeId = change.getKey().get();
- ChangeNotes notes = notesFactory.create(db, change.getProject(), change.getId());
- PatchSet ps = psUtil.get(db, notes, change.currentPatchSetId());
+ ChangeNotes notes = notesFactory.create(change.getProject(), change.getId());
+ PatchSet ps = psUtil.get(notes, change.currentPatchSetId());
requestContext.setContext(newRequestContext(user));
gApi.changes().id(changeId).edit().create();
@@ -2515,92 +2575,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
@Test
- public void refStateFields() throws Exception {
- // This test method manages primary storage manually.
- assume().that(notesMigration.changePrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB);
- Account.Id user = createAccount("user");
- Project.NameKey project = new Project.NameKey("repo");
- TestRepository<Repo> repo = createProject(project.get());
- String path = "file";
- RevCommit commit = repo.parseBody(repo.commit().message("one").add(path, "contents").create());
- Change change = insert(repo, newChangeForCommit(repo, commit));
- Change.Id id = change.getId();
- int c = id.get();
- String changeId = change.getKey().get();
- requestContext.setContext(newRequestContext(user));
-
- // Ensure one of each type of supported ref is present for the change. If
- // any more refs are added, update this test to reflect them.
-
- // Edit
- gApi.changes().id(changeId).edit().create();
-
- // Star
- gApi.accounts().self().starChange(change.getId().toString());
-
- if (notesMigration.readChanges()) {
- // Robot comment.
- ReviewInput rin = new ReviewInput();
- RobotCommentInput rcin = new RobotCommentInput();
- rcin.robotId = "happyRobot";
- rcin.robotRunId = "1";
- rcin.line = 1;
- rcin.message = "nit: trailing whitespace";
- rcin.path = path;
- rin.robotComments = ImmutableMap.of(path, ImmutableList.of(rcin));
- gApi.changes().id(c).current().review(rin);
- }
-
- // Draft.
- DraftInput din = new DraftInput();
- din.path = path;
- din.line = 1;
- din.message = "draft";
- gApi.changes().id(c).current().createDraft(din);
-
- if (notesMigration.readChanges()) {
- // Force NoteDb primary.
- change = ReviewDbUtil.unwrapDb(db).changes().get(id);
- change.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
- ReviewDbUtil.unwrapDb(db).changes().update(Collections.singleton(change));
- indexer.index(db, change);
- }
-
- QueryOptions opts =
- IndexedChangeQuery.createOptions(indexConfig, 0, 1, StalenessChecker.FIELDS);
- ChangeData cd = indexes.getSearchIndex().get(id, opts).get();
-
- String cs = RefNames.shard(c);
- int u = user.get();
- String us = RefNames.shard(u);
-
- List<String> expectedStates =
- Lists.newArrayList(
- "repo:refs/users/" + us + "/edit-" + c + "/1",
- "All-Users:refs/starred-changes/" + cs + "/" + u);
- if (notesMigration.readChanges()) {
- expectedStates.add("repo:refs/changes/" + cs + "/meta");
- expectedStates.add("repo:refs/changes/" + cs + "/robot-comments");
- expectedStates.add("All-Users:refs/draft-comments/" + cs + "/" + u);
- }
- assertThat(
- cd.getRefStates().stream()
- .map(String::new)
- // Omit SHA-1, we're just concerned with the project/ref names.
- .map(s -> s.substring(0, s.lastIndexOf(':')))
- .collect(toList()))
- .containsExactlyElementsIn(expectedStates);
-
- List<String> expectedPatterns = Lists.newArrayList("repo:refs/users/*/edit-" + c + "/*");
- expectedPatterns.add("All-Users:refs/starred-changes/" + cs + "/*");
- if (notesMigration.readChanges()) {
- expectedPatterns.add("All-Users:refs/draft-comments/" + cs + "/*");
- }
- assertThat(cd.getRefStatePatterns().stream().map(String::new).collect(toList()))
- .containsExactlyElementsIn(expectedPatterns);
- }
-
- @Test
public void watched() throws Exception {
TestRepository<Repo> repo = createProject("repo");
ChangeInserter ins1 = newChangeWithStatus(repo, Change.Status.NEW);
@@ -2761,7 +2735,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
return this;
}
- DashboardChangeState draftAndDeleteCommentBy(Id commenterId) {
+ DashboardChangeState draftAndDeleteCommentBy(Account.Id commenterId) {
deleteDraftCommentBy.add(commenterId);
return this;
}
@@ -3123,16 +3097,18 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
String destination4 = "refs/heads/master\trepo3";
String destination5 = "refs/heads/other\trepo1";
- TestRepository<Repo> allUsers = new TestRepository<>(repoManager.openRepository(allUsersName));
- String refsUsers = RefNames.refsUsers(userId);
- allUsers.branch(refsUsers).commit().add("destinations/destination1", destination1).create();
- allUsers.branch(refsUsers).commit().add("destinations/destination2", destination2).create();
- allUsers.branch(refsUsers).commit().add("destinations/destination3", destination3).create();
- allUsers.branch(refsUsers).commit().add("destinations/destination4", destination4).create();
- allUsers.branch(refsUsers).commit().add("destinations/destination5", destination5).create();
-
- Ref userRef = allUsers.getRepository().exactRef(refsUsers);
- assertThat(userRef).isNotNull();
+ try (TestRepository<Repo> allUsers =
+ new TestRepository<>(repoManager.openRepository(allUsersName))) {
+ String refsUsers = RefNames.refsUsers(userId);
+ allUsers.branch(refsUsers).commit().add("destinations/destination1", destination1).create();
+ allUsers.branch(refsUsers).commit().add("destinations/destination2", destination2).create();
+ allUsers.branch(refsUsers).commit().add("destinations/destination3", destination3).create();
+ allUsers.branch(refsUsers).commit().add("destinations/destination4", destination4).create();
+ allUsers.branch(refsUsers).commit().add("destinations/destination5", destination5).create();
+
+ Ref userRef = allUsers.getRepository().exactRef(refsUsers);
+ assertThat(userRef).isNotNull();
+ }
assertQuery("destination:destination1", change1);
assertQuery("destination:destination2", change2);
@@ -3153,12 +3129,14 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
+ "query3\tproject:repo branch:stable\n"
+ "query4\tproject:repo branch:other";
- TestRepository<Repo> allUsers = new TestRepository<>(repoManager.openRepository(allUsersName));
- String refsUsers = RefNames.refsUsers(userId);
- allUsers.branch(refsUsers).commit().add("queries", queries).create();
+ 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();
+ Ref userRef = allUsers.getRepository().exactRef(refsUsers);
+ assertThat(userRef).isNotNull();
+ }
assertThatQueryException("query:foo").hasMessageThat().isEqualTo("Unknown named query: foo");
@@ -3198,6 +3176,64 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("project:repo+foo", change);
}
+ @Test
+ public void selfFailsForAnonymousUser() throws Exception {
+ for (String query : ImmutableList.of("assignee:self", "starredby:self", "is:starred")) {
+ assertQuery(query);
+ RequestContext oldContext = requestContext.setContext(anonymousUserProvider::get);
+
+ try {
+ requestContext.setContext(anonymousUserProvider::get);
+ assertThatAuthException(query)
+ .hasMessageThat()
+ .isEqualTo("Must be signed-in to use this operator");
+ } finally {
+ requestContext.setContext(oldContext);
+ }
+ }
+ }
+
+ @Test
+ public void selfSucceedsForInactiveAccount() throws Exception {
+ Account.Id user2 =
+ accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+
+ TestRepository<Repo> repo = createProject("repo");
+ Change change = insert(repo, newChange(repo));
+ AssigneeInput ain = new AssigneeInput();
+ ain.assignee = user2.toString();
+ gApi.changes().id(change.getId().get()).setAssignee(ain);
+
+ RequestContext adminContext = requestContext.setContext(newRequestContext(user2));
+ assertQuery("assignee:self", change);
+
+ requestContext.setContext(adminContext);
+ gApi.accounts().id(user2.get()).setActive(false);
+
+ requestContext.setContext(newRequestContext(user2));
+ assertQuery("assignee:self", change);
+ }
+
+ @Test
+ public void none() throws Exception {
+ TestRepository<Repo> repo = createProject("repo");
+ Change change = insert(repo, newChange(repo));
+
+ assertQuery(ChangeIndexPredicate.none());
+
+ for (Predicate<ChangeData> matchingOneChange :
+ ImmutableList.of(
+ // One index query, one post-filtering query.
+ queryBuilder.parse(change.getId().toString()),
+ queryBuilder.parse("ownerin:Administrators"))) {
+ assertQuery(matchingOneChange, change);
+ assertQuery(Predicate.or(ChangeIndexPredicate.none(), matchingOneChange), change);
+ assertQuery(Predicate.and(ChangeIndexPredicate.none(), matchingOneChange));
+ assertQuery(
+ Predicate.and(Predicate.not(ChangeIndexPredicate.none()), matchingOneChange), change);
+ }
+ }
+
protected ChangeInserter newChange(TestRepository<Repo> repo) throws Exception {
return newChange(repo, null, null, null, null, false, false);
}
@@ -3207,6 +3243,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
return newChange(repo, commit, null, null, null, false, false);
}
+ protected ChangeInserter newChangeWithFiles(TestRepository<Repo> repo, String... paths)
+ throws Exception {
+ TestRepository<?>.CommitBuilder b = repo.commit().message("Change with files");
+ for (String path : paths) {
+ b.add(path, "contents of " + path);
+ }
+ return newChangeForCommit(repo, repo.parseBody(b.create()));
+ }
+
protected ChangeInserter newChangeForBranch(TestRepository<Repo> repo, String branch)
throws Exception {
return newChange(repo, null, branch, null, null, false, false);
@@ -3279,7 +3324,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
new Project.NameKey(repo.getRepository().getDescription().getRepositoryName());
Account.Id ownerId = owner != null ? owner : userId;
IdentifiedUser user = userFactory.create(ownerId);
- try (BatchUpdate bu = updateFactory.create(db, project, user, createdOn)) {
+ try (BatchUpdate bu = updateFactory.create(project, user, createdOn)) {
bu.insertChange(ins);
bu.execute();
return ins.getChange();
@@ -3295,15 +3340,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
PatchSetInserter inserter =
patchSetFactory
- .create(changeNotesFactory.createChecked(db, c), new PatchSet.Id(c.getId(), n), commit)
- .setNotify(NotifyHandling.NONE)
+ .create(changeNotesFactory.createChecked(c), new PatchSet.Id(c.getId(), n), commit)
.setFireRevisionCreated(false)
.setValidate(false);
- try (BatchUpdate bu = updateFactory.create(db, c.getProject(), user, TimeUtil.nowTs());
+ try (BatchUpdate bu = updateFactory.create(c.getProject(), user, TimeUtil.nowTs());
ObjectInserter oi = repo.getRepository().newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
bu.setRepository(repo.getRepository(), rw, oi);
+ bu.setNotify(NotifyResolver.Result.none());
bu.addOp(c.getId(), inserter);
bu.execute();
}
@@ -3324,6 +3369,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
}
}
+ protected ThrowableSubject assertThatAuthException(Object query) throws Exception {
+ try {
+ newQuery(query).get();
+ throw new AssertionError("expected AuthException for query: " + query);
+ } catch (AuthException e) {
+ return assertThat(e);
+ }
+ }
+
protected TestRepository<Repo> createProject(String name) throws Exception {
gApi.projects().create(name).get();
return new TestRepository<>(repoManager.openRepository(new Project.NameKey(name)));
@@ -3359,21 +3413,32 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
List<ChangeInfo> result = query.get();
Iterable<Change.Id> ids = ids(result);
assertThat(ids)
- .named(format(query, ids, changes))
+ .named(format(query.getQuery(), ids, changes))
.containsExactlyElementsIn(Arrays.asList(changes))
.inOrder();
return result;
}
- private String format(
- QueryRequest query, Iterable<Change.Id> actualIds, Change.Id... expectedChanges)
+ protected void assertQuery(Predicate<ChangeData> predicate, Change... changes) throws Exception {
+ ImmutableList<Change.Id> actualIds =
+ queryProvider.get().query(predicate).stream()
+ .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))
+ .containsExactlyElementsIn(expectedIds)
+ .inOrder();
+ }
+
+ private String format(String query, Iterable<Change.Id> actualIds, Change.Id... expectedChanges)
throws RestApiException {
- StringBuilder b = new StringBuilder();
- b.append("query '").append(query.getQuery()).append("' with expected changes ");
- b.append(format(Arrays.asList(expectedChanges)));
- b.append(" and result ");
- b.append(format(actualIds));
- return b.toString();
+ return "query '"
+ + query
+ + "' with expected changes "
+ + format(Arrays.asList(expectedChanges))
+ + " and result "
+ + format(actualIds);
}
private String format(Iterable<Change.Id> changeIds) throws RestApiException {
@@ -3426,9 +3491,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
comment.line = 1;
comment.message = message;
comment.unresolved = unresolved;
- input.comments =
- ImmutableMap.<String, List<ReviewInput.CommentInput>>of(
- Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment));
+ input.comments = ImmutableMap.of(Patch.COMMIT_MSG, ImmutableList.of(comment));
gApi.changes().id(changeId).current().review(input);
}
@@ -3457,12 +3520,19 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
.isFalse();
}
- protected void assertFailingQuery(String query, String expectedMessage) throws Exception {
+ protected void assertFailingQuery(String query) throws Exception {
+ assertFailingQuery(query, null);
+ }
+
+ protected void assertFailingQuery(String query, @Nullable String expectedMessage)
+ throws Exception {
try {
assertQuery(query);
fail("expected BadRequestException for query '" + query + "'");
} catch (BadRequestException e) {
- assertThat(e.getMessage()).isEqualTo(expectedMessage);
+ if (expectedMessage != null) {
+ assertThat(e.getMessage()).isEqualTo(expectedMessage);
+ }
}
}
diff --git a/javatests/com/google/gerrit/server/query/change/BUILD b/javatests/com/google/gerrit/server/query/change/BUILD
index 120bbbb565..8347484e2e 100644
--- a/javatests/com/google/gerrit/server/query/change/BUILD
+++ b/javatests/com/google/gerrit/server/query/change/BUILD
@@ -24,7 +24,6 @@ java_library(
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
- "//lib:gwtorm",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/jgit/org.eclipse.jgit.junit:junit",
@@ -46,7 +45,6 @@ junit_tests(
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
- "//lib:gwtorm",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/jgit/org.eclipse.jgit.junit:junit",
@@ -63,13 +61,14 @@ junit_tests(
),
visibility = ["//visibility:public"],
deps = [
+ "//java/com/google/gerrit/exceptions",
"//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/testing:gerrit-test-util",
"//lib:guava",
- "//lib:gwtorm",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/truth",
"//lib/truth:truth-proto-extension",
diff --git a/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java b/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
index c8637cd200..aba0018384 100644
--- a/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
+++ b/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
@@ -21,10 +21,11 @@ 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.testing.TestChanges;
import org.junit.Test;
-public class ChangeDataTest {
+public class ChangeDataTest extends GerritBaseTests {
@Test
public void setPatchSetsClearsCurrentPatchSet() throws Exception {
Project.NameKey project = new Project.NameKey("project");
@@ -36,6 +37,6 @@ public class ChangeDataTest {
PatchSet ps2 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 2));
cd.setPatchSets(ImmutableList.of(ps1, ps2));
PatchSet curr2 = cd.currentPatchSet();
- assertThat(curr2).isNotSameAs(curr1);
+ assertThat(curr2).isNotSameInstanceAs(curr1);
}
}
diff --git a/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java b/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java
index de2acf0e58..e550f8e865 100644
--- a/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java
+++ b/javatests/com/google/gerrit/server/query/change/ConflictKeyTest.java
@@ -18,16 +18,18 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.gerrit.extensions.client.SubmitType.FAST_FORWARD_ONLY;
import static com.google.gerrit.extensions.client.SubmitType.MERGE_IF_NECESSARY;
+import static com.google.gerrit.proto.testing.SerializedClassSubject.assertThatSerializedClass;
import static com.google.gerrit.server.cache.testing.CacheSerializerTestUtil.byteString;
-import static com.google.gerrit.server.cache.testing.SerializedClassSubject.assertThatSerializedClass;
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 {
+public class ConflictKeyTest extends GerritBaseTests {
@Test
public void ffOnlyPreservesInputOrder() {
ObjectId id1 = ObjectId.fromString("badc0feebadc0feebadc0feebadc0feebadc0fee");
@@ -81,10 +83,7 @@ public class ConflictKeyTest {
assertThat(ConflictKey.Serializer.INSTANCE.deserialize(serialized)).isEqualTo(key);
}
- /**
- * See {@link com.google.gerrit.server.cache.testing.SerializedClassSubject} for background and
- * what to do if this test fails.
- */
+ /** See {@link SerializedClassSubject} for background and what to do if this test fails. */
@Test
public void methods() throws Exception {
assertThatSerializedClass(ConflictKey.class)
diff --git a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
index 1dfe7df408..2ea198f6d8 100644
--- a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesTest.java
@@ -49,7 +49,7 @@ public class LuceneQueryChangesTest extends AbstractQueryChangesTest {
protected Injector createInjector() {
Config luceneConfig = new Config(config);
InMemoryModule.setDefaults(luceneConfig);
- return Guice.createInjector(new InMemoryModule(luceneConfig, notesMigration));
+ return Guice.createInjector(new InMemoryModule(luceneConfig));
}
@Test
diff --git a/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java b/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
index a13a8f73b6..135e9c268d 100644
--- a/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
+++ b/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
@@ -19,13 +19,13 @@ import static org.junit.Assert.assertTrue;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.testing.GerritBaseTests;
import java.util.Arrays;
import org.junit.Test;
-public class RegexPathPredicateTest {
+public class RegexPathPredicateTest extends GerritBaseTests {
@Test
- public void prefixOnlyOptimization() throws OrmException {
+ public void prefixOnlyOptimization() {
RegexPathPredicate p = predicate("^a/b/.*");
assertTrue(p.match(change("a/b/source.c")));
assertFalse(p.match(change("source.c")));
@@ -35,7 +35,7 @@ public class RegexPathPredicateTest {
}
@Test
- public void prefixReducesSearchSpace() throws OrmException {
+ public void prefixReducesSearchSpace() {
RegexPathPredicate p = predicate("^a/b/.*\\.[ch]");
assertTrue(p.match(change("a/b/source.c")));
assertFalse(p.match(change("a/b/source.res")));
@@ -45,7 +45,7 @@ public class RegexPathPredicateTest {
}
@Test
- public void fileExtension_Constant() throws OrmException {
+ public void fileExtension_Constant() {
RegexPathPredicate p = predicate("^.*\\.res");
assertTrue(p.match(change("test.res")));
assertTrue(p.match(change("foo/bar/test.res")));
@@ -53,7 +53,7 @@ public class RegexPathPredicateTest {
}
@Test
- public void fileExtension_CharacterGroup() throws OrmException {
+ public void fileExtension_CharacterGroup() {
RegexPathPredicate p = predicate("^.*\\.[ch]");
assertTrue(p.match(change("test.c")));
assertTrue(p.match(change("test.h")));
@@ -61,7 +61,7 @@ public class RegexPathPredicateTest {
}
@Test
- public void endOfString() throws OrmException {
+ public void endOfString() {
assertTrue(predicate("^a$").match(change("a")));
assertFalse(predicate("^a$").match(change("a$")));
@@ -70,7 +70,7 @@ public class RegexPathPredicateTest {
}
@Test
- public void exactMatch() throws OrmException {
+ public void exactMatch() {
RegexPathPredicate p = predicate("^foo.c");
assertTrue(p.match(change("foo.c")));
assertFalse(p.match(change("foo.cc")));
@@ -81,7 +81,7 @@ public class RegexPathPredicateTest {
return new RegexPathPredicate(pattern);
}
- private static ChangeData change(String... files) throws OrmException {
+ private static ChangeData change(String... files) {
Arrays.sort(files);
ChangeData cd = ChangeData.createForTest(new Project.NameKey("project"), new Change.Id(1), 1);
cd.setCurrentFilePaths(Arrays.asList(files));
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index fd65f833d6..4a3c75577d 100644
--- a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -34,7 +34,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -58,11 +57,9 @@ 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.InMemoryDatabase;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -89,8 +86,6 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@Inject private Provider<AnonymousUser> anonymousUser;
- @Inject protected InMemoryDatabase schemaFactory;
-
@Inject protected SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
@@ -109,7 +104,6 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
protected LifecycleManager lifecycle;
protected Injector injector;
- protected ReviewDb db;
protected AccountInfo currentUserInfo;
protected CurrentUser user;
@@ -129,12 +123,10 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
@After
public void cleanUp() {
lifecycle.stop();
- db.close();
}
protected void setUpDatabase() throws Exception {
- db = schemaFactory.open();
- schemaCreator.create(db);
+ schemaCreator.create();
Account.Id userId =
createAccountOutsideRequestContext("user", "User", "user@example.com", true);
@@ -147,32 +139,11 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
protected RequestContext newRequestContext(Account.Id requestUserId) {
final CurrentUser requestUser = userFactory.create(requestUserId);
- return new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return requestUser;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- };
+ return () -> requestUser;
}
protected void setAnonymous() {
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return anonymousUser.get();
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(anonymousUser::get);
}
@After
@@ -183,10 +154,6 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
if (requestContext != null) {
requestContext.setContext(null);
}
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(schemaFactory);
}
@Test
diff --git a/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java
index 83835c1a3d..2a453a0554 100644
--- a/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/LuceneQueryGroupsTest.java
@@ -43,6 +43,6 @@ public class LuceneQueryGroupsTest extends AbstractQueryGroupsTest {
protected Injector createInjector() {
Config luceneConfig = new Config(config);
InMemoryModule.setDefaults(luceneConfig);
- return Guice.createInjector(new InMemoryModule(luceneConfig, notesMigration));
+ return Guice.createInjector(new InMemoryModule(luceneConfig));
}
}
diff --git a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
index 7872b444a4..08ef2b0e05 100644
--- a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -39,7 +39,6 @@ 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.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -58,11 +57,9 @@ 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.InMemoryDatabase;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -89,8 +86,6 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
@Inject private Provider<AnonymousUser> anonymousUser;
- @Inject protected InMemoryDatabase schemaFactory;
-
@Inject protected SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
@@ -105,7 +100,6 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
protected LifecycleManager lifecycle;
protected Injector injector;
- protected ReviewDb db;
protected AccountInfo currentUserInfo;
protected CurrentUser user;
@@ -125,12 +119,10 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
@After
public void cleanUp() {
lifecycle.stop();
- db.close();
}
protected void setUpDatabase() throws Exception {
- db = schemaFactory.open();
- schemaCreator.create(db);
+ schemaCreator.create();
Account.Id userId = createAccount("user", "User", "user@example.com", true);
user = userFactory.create(userId);
@@ -142,32 +134,11 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
protected RequestContext newRequestContext(Account.Id requestUserId) {
final CurrentUser requestUser = userFactory.create(requestUserId);
- return new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return requestUser;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- };
+ return () -> requestUser;
}
protected void setAnonymous() {
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return anonymousUser.get();
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
+ requestContext.setContext(anonymousUser::get);
}
@After
@@ -176,10 +147,6 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
lifecycle.stop();
}
requestContext.setContext(null);
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(schemaFactory);
}
@Test
diff --git a/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
index 42964fa09c..77a56edde8 100644
--- a/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/LuceneQueryProjectsTest.java
@@ -45,6 +45,6 @@ public class LuceneQueryProjectsTest extends AbstractQueryProjectsTest {
protected Injector createInjector() {
Config luceneConfig = new Config(config);
InMemoryModule.setDefaults(luceneConfig);
- return Guice.createInjector(new InMemoryModule(luceneConfig, notesMigration));
+ return Guice.createInjector(new InMemoryModule(luceneConfig));
}
}
diff --git a/javatests/com/google/gerrit/server/rules/GerritCommonTest.java b/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
index 086dd65898..180c16b26d 100644
--- a/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
+++ b/javatests/com/google/gerrit/server/rules/GerritCommonTest.java
@@ -49,7 +49,7 @@ public class GerritCommonTest extends PrologTestCase {
bind(PrologEnvironment.Args.class)
.toInstance(
new PrologEnvironment.Args(
- null, null, null, null, null, null, null, cfg, null, null));
+ null, null, null, null, null, null, null, null, cfg, null, null));
}
});
}
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
index 27f4423f27..14124fa90c 100644
--- a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -24,6 +24,7 @@ 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.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
@@ -31,7 +32,7 @@ import java.util.Date;
import java.util.List;
import org.junit.Test;
-public class IgnoreSelfApprovalRuleTest {
+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);
private static final LabelType VERIFIED = makeLabel("Verified");
diff --git a/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java b/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
index 8622b32d35..6eb0747680 100644
--- a/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
+++ b/javatests/com/google/gerrit/server/rules/PrologRuleEvaluatorTest.java
@@ -16,9 +16,10 @@ 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 {
+public class PrologRuleEvaluatorTest extends GerritBaseTests {
@Test
public void validLabelNamesAreKept() {
diff --git a/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.java
new file mode 100644
index 0000000000..e5890c936b
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/AllProjectsCreatorTest.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.schema;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.schema.AllProjectsInput.getDefaultCodeReviewLabel;
+import static com.google.gerrit.server.schema.testing.AllProjectsCreatorTestUtil.assertSectionEquivalent;
+import static com.google.gerrit.server.schema.testing.AllProjectsCreatorTestUtil.assertTwoConfigsEquivalent;
+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 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.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;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AllProjectsCreatorTest extends GerritBaseTests {
+ private static final LabelType TEST_LABEL =
+ new LabelType(
+ "Test-Label",
+ ImmutableList.of(
+ new LabelValue((short) 2, "Two"),
+ new LabelValue((short) 0, "Zero"),
+ new LabelValue((short) 1, "One")));
+
+ private static final String TEST_LABEL_STRING =
+ String.join(
+ "\n",
+ ImmutableList.of(
+ "[label \"Test-Label\"]",
+ "\tfunction = MaxWithBlock",
+ "\tdefaultValue = 0",
+ "\tvalue = 0 Zero",
+ "\tvalue = +1 One",
+ "\tvalue = +2 Two"));
+
+ @Inject private AllProjectsName allProjectsName;
+
+ @Inject @GerritPersonIdent private PersonIdent serverUser;
+
+ @Inject private AllProjectsCreator allProjectsCreator;
+
+ @Inject private GitRepositoryManager repoManager;
+
+ @Before
+ public void setUp() throws Exception {
+ InMemoryModule inMemoryModule = new InMemoryModule();
+ inMemoryModule.inject(this);
+
+ // Creates an empty All-Projects.
+ try (Repository repo = repoManager.createRepository(allProjectsName)) {
+ // Intentionally empty.
+ }
+ }
+
+ @Test
+ public void createDefaultAllProjectsConfig() throws Exception {
+ // Loads the expected configs.
+ Config expectedConfig = new Config();
+ expectedConfig.fromText(getDefaultAllProjectsWithAllDefaultSections());
+
+ GroupReference adminsGroup = createGroupReference("Administrators");
+ GroupReference batchUsersGroup = createGroupReference("Non-Interactive Users");
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builder()
+ .administratorsGroup(adminsGroup)
+ .batchUsersGroup(batchUsersGroup)
+ .build();
+ allProjectsCreator.create(allProjectsInput);
+
+ Config config = readAllProjectsConfig(repoManager, allProjectsName);
+ assertTwoConfigsEquivalent(config, expectedConfig);
+ }
+
+ private GroupReference createGroupReference(String name) {
+ AccountGroup.UUID groupUuid = GroupUUID.make(name, serverUser);
+ return new GroupReference(groupUuid, name);
+ }
+
+ @Test
+ public void createAllProjectsWithNewCodeReviewLabel() throws Exception {
+ Config expectedLabelConfig = new Config();
+ expectedLabelConfig.fromText(TEST_LABEL_STRING);
+
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builder().codeReviewLabel(TEST_LABEL).build();
+ allProjectsCreator.create(allProjectsInput);
+
+ Config config = readAllProjectsConfig(repoManager, allProjectsName);
+ assertSectionEquivalent(config, expectedLabelConfig, "label");
+ }
+
+ @Test
+ public void createAllProjectsWithProjectDescription() throws Exception {
+ String testDescription = "test description";
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builder().projectDescription(testDescription).build();
+ allProjectsCreator.create(allProjectsInput);
+
+ Config config = readAllProjectsConfig(repoManager, allProjectsName);
+ assertThat(config.getString("project", null, "description")).isEqualTo(testDescription);
+ }
+
+ @Test
+ public void createAllProjectsWithBooleanConfigs() throws Exception {
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builderWithNoDefault()
+ .codeReviewLabel(getDefaultCodeReviewLabel())
+ .firstChangeIdForNoteDb(Sequences.FIRST_CHANGE_ID)
+ .addBooleanProjectConfig(
+ BooleanProjectConfig.REJECT_EMPTY_COMMIT, InheritableBoolean.TRUE)
+ .initDefaultAcls(true)
+ .build();
+ allProjectsCreator.create(allProjectsInput);
+
+ Config config = readAllProjectsConfig(repoManager, allProjectsName);
+ assertThat(config.getBoolean("submit", null, "rejectEmptyCommit", false)).isTrue();
+ }
+
+ @Test
+ public void createAllProjectsWithoutInitializingDefaultACLs() throws Exception {
+ AllProjectsInput allProjectsInput = AllProjectsInput.builder().initDefaultAcls(false).build();
+ allProjectsCreator.create(allProjectsInput);
+
+ Config expectedConfig = new Config();
+ expectedConfig.fromText(getAllProjectsWithoutDefaultAcls());
+ Config config = readAllProjectsConfig(repoManager, allProjectsName);
+ assertTwoConfigsEquivalent(config, expectedConfig);
+ }
+
+ @Test
+ public void createAllProjectsOnlyInitializingProjectDescription() throws Exception {
+ String description = "a project.config with just a project description";
+ AllProjectsInput allProjectsInput =
+ AllProjectsInput.builderWithNoDefault()
+ .firstChangeIdForNoteDb(Sequences.FIRST_CHANGE_ID)
+ .projectDescription(description)
+ .initDefaultAcls(false)
+ .build();
+ allProjectsCreator.create(allProjectsInput);
+
+ Config expectedConfig = new Config();
+ expectedConfig.setString("project", null, "description", description);
+ Config config = readAllProjectsConfig(repoManager, allProjectsName);
+ assertTwoConfigsEquivalent(config, expectedConfig);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/GroupBundleTest.java b/javatests/com/google/gerrit/server/schema/GroupBundleTest.java
deleted file mode 100644
index c1de3a3b0e..0000000000
--- a/javatests/com/google/gerrit/server/schema/GroupBundleTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.server.schema.GroupBundle.Source;
-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.TimeZone;
-import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GroupBundleTest extends GerritBaseTests {
- // This class just contains sanity checks that GroupBundle#compare correctly compares all parts of
- // the bundle. Most other test coverage should come via the slightly more realistic
- // GroupRebuilderTest.
-
- private static final String TIMEZONE_ID = "US/Eastern";
-
- private String systemTimeZoneProperty;
- private TimeZone systemTimeZone;
- private Timestamp ts;
-
- @Before
- public void setUp() {
- systemTimeZoneProperty = System.setProperty("user.timezone", TIMEZONE_ID);
- systemTimeZone = TimeZone.getDefault();
- TimeZone.setDefault(TimeZone.getTimeZone(TIMEZONE_ID));
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- ts = TimeUtil.nowTs();
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZoneProperty);
- TimeZone.setDefault(systemTimeZone);
- }
-
- @Test
- public void compareNonEqual() throws Exception {
- GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
- AccountGroup g2 = new AccountGroup(reviewDbBundle.group());
- g2.setDescription("Hello!");
- GroupBundle noteDbBundle = GroupBundle.builder().source(Source.NOTE_DB).group(g2).build();
- assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle))
- .containsExactly(
- "AccountGroups differ\n"
- + ("ReviewDb: AccountGroup{name=group, groupId=1, description=null,"
- + " visibleToAll=false, groupUUID=group-1, ownerGroupUUID=group-1,"
- + " createdOn=2009-09-30 17:00:00.0}\n")
- + ("NoteDb : AccountGroup{name=group, groupId=1, description=Hello!,"
- + " visibleToAll=false, groupUUID=group-1, ownerGroupUUID=group-1,"
- + " createdOn=2009-09-30 17:00:00.0}"),
- "AccountGroupMembers differ\n"
- + "ReviewDb: [AccountGroupMember{key=1000,1}]\n"
- + "NoteDb : []",
- "AccountGroupMemberAudits differ\n"
- + ("ReviewDb: [AccountGroupMemberAudit{key=Key{groupId=1, accountId=1000,"
- + " addedOn=2009-09-30 17:00:00.0}, addedBy=2000, removedBy=null,"
- + " removedOn=null}]\n")
- + "NoteDb : []",
- "AccountGroupByIds differ\n"
- + "ReviewDb: [AccountGroupById{key=1,subgroup}]\n"
- + "NoteDb : []",
- "AccountGroupByIdAudits differ\n"
- + ("ReviewDb: [AccountGroupByIdAud{key=Key{groupId=1, includeUUID=subgroup,"
- + " addedOn=2009-09-30 17:00:00.0}, addedBy=3000, removedBy=null,"
- + " removedOn=null}]\n")
- + "NoteDb : []");
- }
-
- @Test
- public void compareIgnoreAudits() throws Exception {
- GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
- AccountGroup group = new AccountGroup(reviewDbBundle.group());
-
- AccountGroupMember member =
- new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(1), group.getId()));
- AccountGroupMemberAudit memberAudit =
- new AccountGroupMemberAudit(member, new Account.Id(2), ts);
- AccountGroupById byId =
- new AccountGroupById(
- new AccountGroupById.Key(group.getId(), new AccountGroup.UUID("subgroup-2")));
- AccountGroupByIdAud byIdAudit = new AccountGroupByIdAud(byId, new Account.Id(3), ts);
-
- GroupBundle noteDbBundle =
- newBundle().source(Source.NOTE_DB).memberAudit(memberAudit).byIdAudit(byIdAudit).build();
-
- assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle)).isNotEmpty();
- assertThat(GroupBundle.compareWithoutAudits(reviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- @Test
- public void compareEqual() throws Exception {
- GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
- GroupBundle noteDbBundle = newBundle().source(Source.NOTE_DB).build();
- assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- private GroupBundle.Builder newBundle() {
- AccountGroup group =
- new AccountGroup(
- new AccountGroup.NameKey("group"),
- new AccountGroup.Id(1),
- new AccountGroup.UUID("group-1"),
- ts);
- AccountGroupMember member =
- new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(1000), group.getId()));
- AccountGroupMemberAudit memberAudit =
- new AccountGroupMemberAudit(member, new Account.Id(2000), ts);
- AccountGroupById byId =
- new AccountGroupById(
- new AccountGroupById.Key(group.getId(), new AccountGroup.UUID("subgroup")));
- AccountGroupByIdAud byIdAudit = new AccountGroupByIdAud(byId, new Account.Id(3000), ts);
- return GroupBundle.builder()
- .group(group)
- .members(member)
- .memberAudit(memberAudit)
- .byId(byId)
- .byIdAudit(byIdAudit);
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
deleted file mode 100644
index bf6953afcd..0000000000
--- a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
+++ /dev/null
@@ -1,747 +0,0 @@
-// 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.schema;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_GROUPNAMES;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
-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.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-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.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.AuditLogReader;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.update.RefUpdateUtil;
-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.InMemoryRepositoryManager;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import java.sql.Timestamp;
-import java.util.Optional;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.IntStream;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GroupRebuilderTest extends GerritBaseTests {
- private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
- private static final String SERVER_ID = "server-id";
- private static final String SERVER_NAME = "Gerrit Server";
- private static final String SERVER_EMAIL = "noreply@gerritcodereview.com";
-
- private AtomicInteger idCounter;
- private AllUsersName allUsersName;
- private Repository repo;
- private GroupRebuilder rebuilder;
- private GroupBundle.Factory bundleFactory;
-
- @Before
- public void setUp() throws Exception {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- idCounter = new AtomicInteger();
- allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
- repo = new InMemoryRepositoryManager().createRepository(allUsersName);
- rebuilder =
- new GroupRebuilder(
- GroupRebuilderTest.newPersonIdent(),
- allUsersName,
- // Note that the expected name/email values in tests are not necessarily realistic,
- // since they use these trivial name/email functions.
- getAuditLogFormatter());
- bundleFactory = new GroupBundle.Factory(new AuditLogReader(SERVER_ID, allUsersName));
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- }
-
- @Test
- public void minimalGroupFields() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(1);
- assertCommit(log.get(0), "Create group", SERVER_NAME, SERVER_EMAIL);
- assertThat(logGroupNames()).isEmpty();
- }
-
- @Test
- public void allGroupFields() throws Exception {
- AccountGroup g = newGroup("a");
- g.setDescription("Description");
- g.setOwnerGroupUUID(new AccountGroup.UUID("owner"));
- g.setVisibleToAll(true);
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(1);
- assertServerCommit(log.get(0), "Create group");
- }
-
- @Test
- public void emptyGroupName() throws Exception {
- AccountGroup g = newGroup("");
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- GroupBundle noteDbBundle = reload(g);
- assertMigratedCleanly(noteDbBundle, b);
- assertThat(noteDbBundle.group().getName()).isEmpty();
- }
-
- @Test
- public void nullGroupDescription() throws Exception {
- AccountGroup g = newGroup("a");
- g.setDescription(null);
- assertThat(g.getDescription()).isNull();
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- GroupBundle noteDbBundle = reload(g);
- assertMigratedCleanly(noteDbBundle, b);
- assertThat(noteDbBundle.group().getDescription()).isNull();
- }
-
- @Test
- public void emptyGroupDescription() throws Exception {
- AccountGroup g = newGroup("a");
- g.setDescription("");
- assertThat(g.getDescription()).isEmpty();
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- GroupBundle noteDbBundle = reload(g);
- assertMigratedCleanly(noteDbBundle, b);
- assertThat(noteDbBundle.group().getDescription()).isNull();
- }
-
- @Test
- public void membersAndSubgroups() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2))
- .byId(byId(g, "x"), byId(g, "y"))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(2);
- assertServerCommit(log.get(0), "Create group");
- assertServerCommit(
- log.get(1),
- "Update group\n"
- + "\n"
- + "Add-group: Group x <x>\n"
- + "Add-group: Group y <y>\n"
- + "Add: Account 1 <1@server-id>\n"
- + "Add: Account 2 <2@server-id>");
- }
-
- @Test
- public void memberAudit() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1))
- .memberAudit(addMember(g, 1, 8, t2), addAndRemoveMember(g, 2, 8, t1, 9, t3))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(4);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
- }
-
- @Test
- public void memberAuditLegacyRemoved() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 2))
- .memberAudit(
- addAndLegacyRemoveMember(g, 1, 8, TimeUtil.nowTs()),
- addMember(g, 2, 8, TimeUtil.nowTs()))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(4);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
- }
-
- @Test
- public void unauditedMembershipsAddedAtEnd() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2), member(g, 3))
- .memberAudit(addMember(g, 1, 8, TimeUtil.nowTs()))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertServerCommit(
- log.get(2), "Update group\n\nAdd: Account 2 <2@server-id>\nAdd: Account 3 <3@server-id>");
- }
-
- @Test
- public void byIdAudit() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .byId(byId(g, "x"))
- .byIdAudit(addById(g, "x", 8, t2), addAndRemoveById(g, "y", 8, t1, 9, t3))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(4);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group y <y>", "Account 8", "8@server-id");
- assertCommit(log.get(2), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nRemove-group: Group y <y>", "Account 9", "9@server-id");
- }
-
- @Test
- public void unauditedByIdAddedAtEnd() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
- .byIdAudit(addById(g, "x", 8, TimeUtil.nowTs()))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertServerCommit(
- log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
- }
-
- @Test
- public void auditsAtSameTimestampBrokenDownByType() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp ts = TimeUtil.nowTs();
- int user = 8;
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2))
- .memberAudit(
- addMember(g, 1, user, ts),
- addMember(g, 2, user, ts),
- addAndRemoveMember(g, 3, user, ts, user, ts))
- .byId(byId(g, "x"), byId(g, "y"))
- .byIdAudit(
- addById(g, "x", user, ts),
- addById(g, "y", user, ts),
- addAndRemoveById(g, "z", user, ts, user, ts))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(5);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1),
- "Update group\n"
- + "\n"
- + "Add: Account 1 <1@server-id>\n"
- + "Add: Account 2 <2@server-id>\n"
- + "Add: Account 3 <3@server-id>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 3 <3@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3),
- "Update group\n"
- + "\n"
- + "Add-group: Group x <x>\n"
- + "Add-group: Group y <y>\n"
- + "Add-group: Group z <z>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(4), "Update group\n\nRemove-group: Group z <z>", "Account 8", "8@server-id");
- }
-
- @Test
- public void auditsAtSameTimestampBrokenDownByUserAndType() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp ts = TimeUtil.nowTs();
- int user1 = 8;
- int user2 = 9;
-
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2), member(g, 3))
- .memberAudit(
- addMember(g, 1, user1, ts), addMember(g, 2, user2, ts), addMember(g, 3, user1, ts))
- .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
- .byIdAudit(
- addById(g, "x", user1, ts), addById(g, "y", user2, ts), addById(g, "z", user1, ts))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(5);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1),
- "Update group\n" + "\n" + "Add: Account 1 <1@server-id>\n" + "Add: Account 3 <3@server-id>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(2),
- "Update group\n\nAdd-group: Group x <x>\nAdd-group: Group z <z>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
- assertCommit(log.get(4), "Update group\n\nAdd-group: Group y <y>", "Account 9", "9@server-id");
- }
-
- @Test
- public void fixupCommitPostDatesAllAuditEventsEvenIfAuditEventsAreInTheFuture() throws Exception {
- AccountGroup g = newGroup("a");
- IntStream.range(0, 20).forEach(i -> TimeUtil.nowTs());
- Timestamp future = TimeUtil.nowTs();
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
-
- GroupBundle b =
- builder()
- .group(g)
- .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
- .byIdAudit(addById(g, "x", 8, future))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertServerCommit(
- log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
-
- assertThat(log.stream().map(c -> c.committer.date).collect(toImmutableList()))
- .named("%s", log)
- .isOrdered();
- assertThat(TimeUtil.nowTs()).isLessThan(future);
- }
-
- @Test
- public void redundantMemberAuditsAreIgnored() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- Timestamp t4 = TimeUtil.nowTs();
- Timestamp t5 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 2))
- .memberAudit(
- addMember(g, 1, 8, t1),
- addMember(g, 1, 8, t1),
- addMember(g, 1, 8, t3),
- addMember(g, 1, 9, t4),
- addAndRemoveMember(g, 1, 8, t2, 9, t5),
- addAndLegacyRemoveMember(g, 2, 9, t3),
- addMember(g, 2, 8, t1),
- addMember(g, 2, 9, t4),
- addMember(g, 1, 8, t5))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(5);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1),
- "Update group\n\nAdd: Account 1 <1@server-id>\nAdd: Account 2 <2@server-id>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
- assertCommit(
- log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
- }
-
- @Test
- public void additionsAndRemovalsWithinSameSecondCanBeMigrated() throws Exception {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.MILLISECONDS);
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- Timestamp t4 = TimeUtil.nowTs();
- Timestamp t5 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1))
- .memberAudit(
- addAndLegacyRemoveMember(g, 1, 8, t1),
- addMember(g, 1, 10, t2),
- addAndRemoveMember(g, 1, 8, t3, 9, t4),
- addMember(g, 1, 8, t5))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(6);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 10", "10@server-id");
- assertCommit(
- log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
- assertCommit(
- log.get(5), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- }
-
- @Test
- public void redundantByIdAuditsAreIgnored() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- Timestamp t4 = TimeUtil.nowTs();
- Timestamp t5 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .byId()
- .byIdAudit(
- addById(g, "x", 8, t1),
- addById(g, "x", 8, t3),
- addById(g, "x", 9, t4),
- addAndRemoveById(g, "x", 8, t2, 9, t5))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove-group: Group x <x>", "Account 9", "9@server-id");
- }
-
- @Test
- public void combineWithBatchGroupNameNotes() throws Exception {
- AccountGroup g1 = newGroup("a");
- AccountGroup g2 = newGroup("b");
- GroupReference gr1 = new GroupReference(g1.getGroupUUID(), g1.getName());
- GroupReference gr2 = new GroupReference(g2.getGroupUUID(), g2.getName());
-
- GroupBundle b1 = builder().group(g1).build();
- GroupBundle b2 = builder().group(g2).build();
-
- BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
-
- rebuilder.rebuild(repo, b1, bru);
- rebuilder.rebuild(repo, b2, bru);
- try (ObjectInserter inserter = repo.newObjectInserter()) {
- ImmutableList<GroupReference> refs = ImmutableList.of(gr1, gr2);
- GroupNameNotes.updateAllGroups(repo, inserter, bru, refs, newPersonIdent());
- inserter.flush();
- }
-
- assertThat(log(g1)).isEmpty();
- assertThat(log(g2)).isEmpty();
- assertThat(logGroupNames()).isEmpty();
-
- RefUpdateUtil.executeChecked(bru, repo);
-
- assertThat(log(g1)).hasSize(1);
- assertThat(log(g2)).hasSize(1);
- assertThat(logGroupNames()).hasSize(1);
- assertMigratedCleanly(reload(g1), b1);
- assertMigratedCleanly(reload(g2), b2);
-
- assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(gr1, gr2);
- }
-
- @Test
- public void groupNamesWithLeadingAndTrailingWhitespace() throws Exception {
- for (String leading : ImmutableList.of("", " ", " ")) {
- for (String trailing : ImmutableList.of("", " ", " ")) {
- AccountGroup g = newGroup(leading + "a" + trailing);
- GroupBundle b = builder().group(g).build();
- rebuilder.rebuild(repo, b, null);
- assertMigratedCleanly(reload(g), b);
- }
- }
- }
-
- @Test
- public void disallowExisting() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
- assertMigratedCleanly(reload(g), b);
- String refName = RefNames.refsGroups(g.getGroupUUID());
- ObjectId oldId = repo.exactRef(refName).getObjectId();
-
- try {
- rebuilder.rebuild(repo, b, null);
- assert_().fail("expected OrmDuplicateKeyException");
- } catch (OrmDuplicateKeyException e) {
- // Expected.
- }
-
- assertThat(repo.exactRef(refName).getObjectId()).isEqualTo(oldId);
- }
-
- private GroupBundle reload(AccountGroup g) throws Exception {
- return bundleFactory.fromNoteDb(allUsersName, repo, g.getGroupUUID());
- }
-
- private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
- assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- private AccountGroup newGroup(String name) {
- int id = idCounter.incrementAndGet();
- return new AccountGroup(
- new AccountGroup.NameKey(name),
- new AccountGroup.Id(id),
- new AccountGroup.UUID(name.trim() + "-" + id),
- TimeUtil.nowTs());
- }
-
- private AccountGroupMember member(AccountGroup g, int accountId) {
- return new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(accountId), g.getId()));
- }
-
- private AccountGroupMemberAudit addMember(
- AccountGroup g, int accountId, int adder, Timestamp addedOn) {
- return new AccountGroupMemberAudit(member(g, accountId), new Account.Id(adder), addedOn);
- }
-
- private AccountGroupMemberAudit addAndLegacyRemoveMember(
- AccountGroup g, int accountId, int adder, Timestamp addedOn) {
- AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
- a.removedLegacy();
- return a;
- }
-
- private AccountGroupMemberAudit addAndRemoveMember(
- AccountGroup g,
- int accountId,
- int adder,
- Timestamp addedOn,
- int removedBy,
- Timestamp removedOn) {
- AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
- a.removed(new Account.Id(removedBy), removedOn);
- return a;
- }
-
- private AccountGroupByIdAud addById(
- AccountGroup g, String subgroupUuid, int adder, Timestamp addedOn) {
- return new AccountGroupByIdAud(byId(g, subgroupUuid), new Account.Id(adder), addedOn);
- }
-
- private AccountGroupByIdAud addAndRemoveById(
- AccountGroup g,
- String subgroupUuid,
- int adder,
- Timestamp addedOn,
- int removedBy,
- Timestamp removedOn) {
- AccountGroupByIdAud a = addById(g, subgroupUuid, adder, addedOn);
- a.removed(new Account.Id(removedBy), removedOn);
- return a;
- }
-
- private AccountGroupById byId(AccountGroup g, String subgroupUuid) {
- return new AccountGroupById(
- new AccountGroupById.Key(g.getId(), new AccountGroup.UUID(subgroupUuid)));
- }
-
- private ImmutableList<CommitInfo> log(AccountGroup g) throws Exception {
- return GitTestUtil.log(repo, RefNames.refsGroups(g.getGroupUUID()));
- }
-
- private ImmutableList<CommitInfo> logGroupNames() throws Exception {
- return GitTestUtil.log(repo, REFS_GROUPNAMES);
- }
-
- private static GroupBundle.Builder builder() {
- return GroupBundle.builder().source(GroupBundle.Source.REVIEW_DB);
- }
-
- private static PersonIdent newPersonIdent() {
- return new PersonIdent(SERVER_NAME, SERVER_EMAIL, TimeUtil.nowTs(), TZ);
- }
-
- private static void assertServerCommit(CommitInfo commitInfo, String expectedMessage) {
- assertCommit(commitInfo, expectedMessage, SERVER_NAME, SERVER_EMAIL);
- }
-
- private static void assertCommit(
- CommitInfo commitInfo, String expectedMessage, String expectedName, String expectedEmail) {
- assertThat(commitInfo).message().isEqualTo(expectedMessage);
- assertThat(commitInfo).author().name().isEqualTo(expectedName);
- assertThat(commitInfo).author().email().isEqualTo(expectedEmail);
-
- // Committer should always be the server, regardless of author.
- assertThat(commitInfo).committer().name().isEqualTo(SERVER_NAME);
- assertThat(commitInfo).committer().email().isEqualTo(SERVER_EMAIL);
- assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
- assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
- }
-
- private static AuditLogFormatter getAuditLogFormatter() {
- return AuditLogFormatter.create(
- GroupRebuilderTest::getAccount, GroupRebuilderTest::getGroup, SERVER_ID);
- }
-
- private static Optional<Account> getAccount(Account.Id id) {
- Account account = new Account(id, TimeUtil.nowTs());
- account.setFullName("Account " + id);
- return Optional.of(account);
- }
-
- private static Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
- GroupDescription.Basic group =
- new GroupDescription.Basic() {
- @Override
- public AccountGroup.UUID getGroupUUID() {
- return uuid;
- }
-
- @Override
- public String getName() {
- return "Group " + uuid;
- }
-
- @Nullable
- @Override
- public String getEmailAddress() {
- return null;
- }
-
- @Nullable
- @Override
- public String getUrl() {
- return null;
- }
- };
- return Optional.of(group);
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/HANATest.java b/javatests/com/google/gerrit/server/schema/HANATest.java
deleted file mode 100644
index ac5813486f..0000000000
--- a/javatests/com/google/gerrit/server/schema/HANATest.java
+++ /dev/null
@@ -1,44 +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
-
-package com.google.gerrit.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.eclipse.jgit.lib.Config;
-import org.junit.Before;
-import org.junit.Test;
-
-public class HANATest {
-
- private HANA hana;
- private Config config;
-
- @Before
- public void setup() {
- config = new Config();
- config.setString("database", null, "hostname", "my.host");
- hana = new HANA(config);
- }
-
- @Test
- public void getUrl() throws Exception {
- config.setString("database", null, "port", "4242");
- assertThat(hana.getUrl()).isEqualTo("jdbc:sap://my.host:4242");
- }
-
- @Test
- public void getIndexScript() throws Exception {
- assertThat(hana.getIndexScript()).isSameAs(ScriptRunner.NOOP);
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
new file mode 100644
index 0000000000..5c1b20152f
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
@@ -0,0 +1,297 @@
+// 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.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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.ImmutableSortedSet;
+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;
+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;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Test;
+
+public class NoteDbSchemaUpdaterTest extends GerritBaseTests {
+ @Test
+ public void requiredUpgradesFromNoVersion() throws Exception {
+ assertThat(requiredUpgrades(0, versions(10))).containsExactly(10).inOrder();
+ assertThat(requiredUpgrades(0, versions(10, 11, 12))).containsExactly(10, 11, 12).inOrder();
+ }
+
+ @Test
+ public void requiredUpgradesFromExistingVersion() throws Exception {
+ ImmutableSortedSet<Integer> versions = versions(10, 11, 12, 13);
+ assertThat(requiredUpgrades(10, versions)).containsExactly(11, 12, 13).inOrder();
+ assertThat(requiredUpgrades(11, versions)).containsExactly(12, 13).inOrder();
+ assertThat(requiredUpgrades(12, versions)).containsExactly(13).inOrder();
+ assertThat(requiredUpgrades(13, versions)).isEmpty();
+ }
+
+ @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");
+ }
+ }
+
+ @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");
+ }
+ }
+
+ private static class TestUpdate {
+ protected final Config cfg;
+ protected final AllProjectsName allProjectsName;
+ protected final AllUsersName allUsersName;
+ protected final NoteDbSchemaUpdater updater;
+ protected final GitRepositoryManager repoManager;
+ protected final NoteDbSchemaVersion.Arguments args;
+ private final List<String> messages;
+
+ TestUpdate(Optional<Integer> initialVersion) {
+ cfg = new Config();
+ allProjectsName = new AllProjectsName("The-Projects");
+ allUsersName = new AllUsersName("The-Users");
+ repoManager = new InMemoryRepositoryManager();
+
+ args = new NoteDbSchemaVersion.Arguments(repoManager, allProjectsName, allUsersName);
+ NoteDbSchemaVersionManager versionManager =
+ new NoteDbSchemaVersionManager(allProjectsName, repoManager);
+ updater =
+ new NoteDbSchemaUpdater(
+ cfg,
+ allUsersName,
+ repoManager,
+ new TestSchemaCreator(initialVersion),
+ versionManager,
+ args,
+ ImmutableSortedMap.of(10, TestSchema_10.class, 11, TestSchema_11.class));
+ messages = new ArrayList<>();
+ }
+
+ private class TestSchemaCreator implements SchemaCreator {
+ private final Optional<Integer> initialVersion;
+
+ TestSchemaCreator(Optional<Integer> initialVersion) {
+ this.initialVersion = initialVersion;
+ }
+
+ @Override
+ public void create() throws IOException {
+ try (Repository repo = repoManager.createRepository(allProjectsName);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ if (initialVersion.isPresent()) {
+ tr.update(RefNames.REFS_VERSION, tr.blob(initialVersion.get().toString()));
+ }
+ } catch (Exception e) {
+ throw new StorageException(e);
+ }
+ repoManager.createRepository(allUsersName).close();
+ setUp();
+ }
+
+ @Override
+ public void ensureCreated() throws IOException {
+ try {
+ repoManager.openRepository(allProjectsName).close();
+ } catch (RepositoryNotFoundException e) {
+ create();
+ }
+ }
+ }
+
+ protected void setNotesMigrationConfig() {
+ cfg.setString("noteDb", "changes", "write", "true");
+ cfg.setString("noteDb", "changes", "read", "true");
+ cfg.setString("noteDb", "changes", "primaryStorage", "NOTE_DB");
+ cfg.setString("noteDb", "changes", "disableReviewDb", "true");
+ }
+
+ protected void seedGroupSequenceRef() {
+ new RepoSequence(
+ repoManager,
+ GitReferenceUpdated.DISABLED,
+ allUsersName,
+ Sequences.NAME_GROUPS,
+ () -> 1,
+ 1)
+ .next();
+ }
+
+ /** Test-specific setup. */
+ protected void setUp() {}
+
+ ImmutableList<String> update() throws Exception {
+ updater.update(
+ new TestUpdateUI() {
+ @Override
+ public void message(String m) {
+ messages.add(m);
+ }
+ });
+ return getMessages();
+ }
+
+ ImmutableList<String> getMessages() {
+ return ImmutableList.copyOf(messages);
+ }
+
+ Optional<Integer> readVersion() throws Exception {
+ try (Repository repo = repoManager.openRepository(allProjectsName)) {
+ return IntBlob.parse(repo, RefNames.REFS_VERSION).map(IntBlob::value);
+ }
+ }
+
+ static class TestSchema_10 implements NoteDbSchemaVersion {
+ @Override
+ public void upgrade(Arguments args, UpdateUI ui) {
+ ui.message("body of 10");
+ }
+ }
+
+ static class TestSchema_11 implements NoteDbSchemaVersion {
+ @Override
+ public void upgrade(Arguments args, UpdateUI ui) {
+ ui.message("BODY OF 11");
+ }
+ }
+ }
+
+ @Test
+ public void bootstrapUpdateWith216Prerequisites() throws Exception {
+ TestUpdate u =
+ new TestUpdate(Optional.empty()) {
+ @Override
+ public void setUp() {
+ setNotesMigrationConfig();
+ seedGroupSequenceRef();
+ }
+ };
+ assertThat(u.update())
+ .containsExactly(
+ "Migrating data to schema 10 ...",
+ "body of 10",
+ "Migrating data to schema 11 ...",
+ "BODY OF 11")
+ .inOrder();
+ assertThat(u.readVersion()).hasValue(11);
+ }
+
+ @Test
+ public void bootstrapUpdateFailsWithoutNotesMigrationConfig() throws Exception {
+ TestUpdate u =
+ new TestUpdate(Optional.empty()) {
+ @Override
+ public void setUp() {
+ seedGroupSequenceRef();
+ }
+ };
+ try {
+ u.update();
+ assert_().fail("expected StorageException");
+ } catch (StorageException e) {
+ assertThat(e).hasMessageThat().contains("NoteDb change migration was not completed");
+ }
+ assertThat(u.getMessages()).isEmpty();
+ assertThat(u.readVersion()).isEmpty();
+ }
+
+ @Test
+ public void bootstrapUpdateFailsWithoutGroupSequenceRef() throws Exception {
+ TestUpdate u =
+ new TestUpdate(Optional.empty()) {
+ @Override
+ public void setUp() {
+ setNotesMigrationConfig();
+ }
+ };
+ try {
+ u.update();
+ assert_().fail("expected StorageException");
+ } catch (StorageException e) {
+ assertThat(e).hasMessageThat().contains("upgrade to 2.16.x first");
+ }
+ assertThat(u.getMessages()).isEmpty();
+ assertThat(u.readVersion()).isEmpty();
+ }
+
+ @Test
+ public void updateTwoVersions() throws Exception {
+ TestUpdate u = new TestUpdate(Optional.of(9));
+ assertThat(u.update())
+ .containsExactly(
+ "Migrating data to schema 10 ...",
+ "body of 10",
+ "Migrating data to schema 11 ...",
+ "BODY OF 11")
+ .inOrder();
+ assertThat(u.readVersion()).hasValue(11);
+ }
+
+ @Test
+ public void updateOneVersion() throws Exception {
+ TestUpdate u = new TestUpdate(Optional.of(10));
+ assertThat(u.update())
+ .containsExactly("Migrating data to schema 11 ...", "BODY OF 11")
+ .inOrder();
+ assertThat(u.readVersion()).hasValue(11);
+ }
+
+ @Test
+ public void updateNoOp() throws Exception {
+ // This test covers the state when running the updater after initializing a new 3.x site, which
+ // seeds the schema version ref with the latest version.
+ TestUpdate u = new TestUpdate(Optional.of(11));
+ assertThat(u.update()).isEmpty();
+ assertThat(u.readVersion()).hasValue(11);
+ }
+
+ private static ImmutableSortedSet<Integer> versions(Integer... versions) {
+ return ImmutableSortedSet.copyOf(versions);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
new file mode 100644
index 0000000000..9c62d7f870
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
@@ -0,0 +1,93 @@
+// 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.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 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 {
+ private NoteDbSchemaVersionManager manager;
+ private TestRepository<?> tr;
+
+ @Before
+ public void setUp() throws Exception {
+ AllProjectsName allProjectsName = new AllProjectsName("The-Projects");
+ GitRepositoryManager repoManager = new InMemoryRepositoryManager();
+ tr = new TestRepository<>(repoManager.createRepository(allProjectsName));
+ manager = new NoteDbSchemaVersionManager(allProjectsName, repoManager);
+ }
+
+ @Test
+ public void readMissing() throws Exception {
+ assertThat(manager.read()).isEqualTo(0);
+ }
+
+ @Test
+ public void read() throws Exception {
+ tr.update(REFS_VERSION, tr.blob("123"));
+ assertThat(manager.read()).isEqualTo(123);
+ }
+
+ @Test
+ 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());
+ }
+ }
+
+ @Test
+ public void incrementFromMissing() throws Exception {
+ manager.increment(123);
+ assertThat(manager.read()).isEqualTo(124);
+ }
+
+ @Test
+ public void increment() throws Exception {
+ tr.update(REFS_VERSION, tr.blob("123"));
+ manager.increment(123);
+ assertThat(manager.read()).isEqualTo(124);
+ }
+
+ @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");
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
new file mode 100644
index 0000000000..7bc3848f6d
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionsTest.java
@@ -0,0 +1,74 @@
+// 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.schema;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.gerrit.server.schema.NoteDbSchemaVersions.guessVersion;
+
+import com.google.common.collect.ImmutableList;
+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 {
+ @Test
+ public void testGuessVersion() {
+ assertThat(guessVersion(getClass())).isEmpty();
+ assertThat(guessVersion(Schema_180.class)).hasValue(180);
+ }
+
+ @Test
+ public void contiguousVersions() {
+ ImmutableSortedSet<Integer> keys = NoteDbSchemaVersions.ALL.keySet();
+ ImmutableList<Integer> expected =
+ IntStream.rangeClosed(keys.first(), keys.last()).boxed().collect(toImmutableList());
+ assertThat(keys).containsExactlyElementsIn(expected).inOrder();
+ }
+
+ @Test
+ public void exceedsReviewDbVersion() {
+ assertThat(NoteDbSchemaVersions.ALL.firstKey()).isGreaterThan(170);
+ }
+
+ @Test
+ public void containsAllNoteDbSchemas() throws Exception {
+ int minNoteDbVersion = 180;
+ ImmutableList<Integer> allSchemaVersions =
+ ClassPath.from(getClass().getClassLoader())
+ .getTopLevelClasses(getClass().getPackage().getName()).stream()
+ .map(ClassInfo::load)
+ .map(NoteDbSchemaVersions::guessVersion)
+ .flatMap(Streams::stream)
+ .filter(v -> v >= minNoteDbVersion)
+ .sorted()
+ .collect(toImmutableList());
+ assertThat(NoteDbSchemaVersions.ALL.keySet())
+ .containsExactlyElementsIn(allSchemaVersions)
+ .inOrder();
+ }
+
+ @Test
+ public void schemaConstructors() throws Exception {
+ for (int version : NoteDbSchemaVersions.ALL.keySet()) {
+ NoteDbSchemaVersions.get(NoteDbSchemaVersions.ALL, version);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java b/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java
new file mode 100644
index 0000000000..c5c956c997
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/ProjectConfigSchemaUpdateTest.java
@@ -0,0 +1,110 @@
+// 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.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.server.config.AllProjectsName;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+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.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class ProjectConfigSchemaUpdateTest {
+ private static final String ALL_PROJECTS = "All-The-Projects";
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private SitePaths sitePaths;
+ private ProjectConfigSchemaUpdate.Factory factory;
+ private File allProjectsRepoFile;
+
+ @Before
+ public void setUp() throws Exception {
+ sitePaths = new SitePaths(temporaryFolder.newFolder().toPath());
+ Files.createDirectories(sitePaths.etc_dir);
+
+ Path gitPath = sitePaths.resolve("git");
+
+ StoredConfig gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED);
+ gerritConfig.load();
+ gerritConfig.setString("gerrit", null, "basePath", gitPath.toAbsolutePath().toString());
+ gerritConfig.setString("gerrit", null, "allProjects", ALL_PROJECTS);
+ gerritConfig.save();
+
+ Files.createDirectories(sitePaths.resolve("git"));
+ allProjectsRepoFile = gitPath.resolve("All-The-Projects.git").toFile();
+ try (Repository repo = new FileRepository(allProjectsRepoFile)) {
+ repo.create(true);
+ }
+
+ factory = new ProjectConfigSchemaUpdate.Factory(sitePaths, new AllProjectsName(ALL_PROJECTS));
+ }
+
+ @Test
+ public void noBaseConfig() throws Exception {
+ assertThat(getConfig().getString("foo", null, "bar")).isNull();
+
+ try (Repository repo = new FileRepository(allProjectsRepoFile);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch("refs/meta/config").commit().add("project.config", "[foo]\nbar = baz").create();
+ }
+
+ assertThat(getConfig().getString("foo", null, "bar")).isEqualTo("baz");
+ }
+
+ @Test
+ public void baseConfig() throws Exception {
+ assertThat(getConfig().getString("foo", null, "bar")).isNull();
+
+ Path baseConfigPath = sitePaths.etc_dir.resolve(ALL_PROJECTS).resolve("project.config");
+ Files.createDirectories(baseConfigPath.getParent());
+ Files.write(baseConfigPath, ImmutableList.of("[foo]", "bar = base"));
+
+ assertThat(getConfig().getString("foo", null, "bar")).isEqualTo("base");
+
+ try (Repository repo = new FileRepository(allProjectsRepoFile);
+ TestRepository<Repository> tr = new TestRepository<>(repo)) {
+ tr.branch("refs/meta/config").commit().add("project.config", "[foo]\nbar = baz").create();
+ }
+
+ assertThat(getConfig().getString("foo", null, "bar")).isEqualTo("baz");
+ }
+
+ private Config getConfig() throws Exception {
+ try (Repository repo = new FileRepository(allProjectsRepoFile)) {
+ return factory
+ .read(
+ new MetaDataUpdate(
+ GitReferenceUpdated.DISABLED, new 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
new file mode 100644
index 0000000000..35f580c771
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/SchemaCreatorImplTest.java
@@ -0,0 +1,97 @@
+// 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.server.schema;
+
+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.common.data.LabelFunction;
+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.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;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SchemaCreatorImplTest extends GerritBaseTests {
+ @Inject private AllProjectsName allProjects;
+
+ @Inject private GitRepositoryManager repoManager;
+
+ @Inject private SchemaCreator schemaCreator;
+
+ @Inject private ProjectConfig.Factory projectConfigFactory;
+
+ @Before
+ public void setUp() throws Exception {
+ new InMemoryModule().inject(this);
+ schemaCreator.create();
+ }
+
+ private LabelTypes getLabelTypes() throws Exception {
+ ProjectConfig c = projectConfigFactory.create(allProjects);
+ try (Repository repo = repoManager.openRepository(allProjects)) {
+ c.load(repo);
+ return new LabelTypes(ImmutableList.copyOf(c.getLabelSections().values()));
+ }
+ }
+
+ @Test
+ public void createSchema_LabelTypes() throws Exception {
+ List<String> labels = new ArrayList<>();
+ for (LabelType label : getLabelTypes().getLabelTypes()) {
+ labels.add(label.getName());
+ }
+ assertThat(labels).containsExactly("Code-Review");
+ }
+
+ @Test
+ public void createSchema_Label_CodeReview() throws Exception {
+ LabelType codeReview = getLabelTypes().byLabel("Code-Review");
+ assertThat(codeReview).isNotNull();
+ assertThat(codeReview.getName()).isEqualTo("Code-Review");
+ assertThat(codeReview.getDefaultValue()).isEqualTo(0);
+ assertThat(codeReview.getFunction()).isEqualTo(LabelFunction.MAX_WITH_BLOCK);
+ assertThat(codeReview.isCopyMinScore()).isTrue();
+ assertValueRange(codeReview, -2, -1, 0, 1, 2);
+ }
+
+ private void assertValueRange(LabelType label, Integer... range) {
+ List<Integer> rangeList = Arrays.asList(range);
+ assertThat(rangeList).isNotEmpty();
+ assertThat(rangeList).isStrictlyOrdered();
+
+ assertThat(label.getValues().stream().map(v -> (int) v.getValue()))
+ .containsExactlyElementsIn(rangeList)
+ .inOrder();
+ assertThat(label.getMax().getValue()).isEqualTo(Collections.max(rangeList));
+ assertThat(label.getMin().getValue()).isEqualTo(Collections.min(rangeList));
+ for (LabelValue v : label.getValues()) {
+ assertThat(v.getText()).isNotNull();
+ assertThat(v.getText()).isNotEmpty();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java b/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java
deleted file mode 100644
index 9569745306..0000000000
--- a/javatests/com/google/gerrit/server/schema/SchemaCreatorTest.java
+++ /dev/null
@@ -1,128 +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.server.schema;
-
-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.common.data.LabelFunction;
-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.server.config.AllProjectsName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.testing.InMemoryDatabase;
-import com.google.gerrit.testing.InMemoryModule;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import java.io.File;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SchemaCreatorTest {
- @Inject private AllProjectsName allProjects;
-
- @Inject private GitRepositoryManager repoManager;
-
- @Inject private InMemoryDatabase db;
-
- @Before
- public void setUp() throws Exception {
- new InMemoryModule().inject(this);
- }
-
- @After
- public void tearDown() throws Exception {
- InMemoryDatabase.drop(db);
- }
-
- @Test
- public void getCauses_CreateSchema() throws OrmException, SQLException {
- // Initially the schema should be empty.
- String[] types = {"TABLE", "VIEW"};
- try (JdbcSchema d = (JdbcSchema) db.open();
- ResultSet rs = d.getConnection().getMetaData().getTables(null, null, null, types)) {
- assertThat(rs.next()).isFalse();
- }
-
- // Create the schema using the current schema version.
- //
- db.create();
- db.assertSchemaVersion();
-
- // By default sitePath is set to the current working directory.
- //
- File sitePath = new File(".").getAbsoluteFile();
- if (sitePath.getName().equals(".")) {
- sitePath = sitePath.getParentFile();
- }
- }
-
- private LabelTypes getLabelTypes() throws Exception {
- db.create();
- ProjectConfig c = new ProjectConfig(allProjects);
- try (Repository repo = repoManager.openRepository(allProjects)) {
- c.load(repo);
- return new LabelTypes(ImmutableList.copyOf(c.getLabelSections().values()));
- }
- }
-
- @Test
- public void createSchema_LabelTypes() throws Exception {
- List<String> labels = new ArrayList<>();
- for (LabelType label : getLabelTypes().getLabelTypes()) {
- labels.add(label.getName());
- }
- assertThat(labels).containsExactly("Code-Review");
- }
-
- @Test
- public void createSchema_Label_CodeReview() throws Exception {
- LabelType codeReview = getLabelTypes().byLabel("Code-Review");
- assertThat(codeReview).isNotNull();
- assertThat(codeReview.getName()).isEqualTo("Code-Review");
- assertThat(codeReview.getDefaultValue()).isEqualTo(0);
- assertThat(codeReview.getFunction()).isEqualTo(LabelFunction.MAX_WITH_BLOCK);
- assertThat(codeReview.isCopyMinScore()).isTrue();
- assertValueRange(codeReview, -2, -1, 0, 1, 2);
- }
-
- private void assertValueRange(LabelType label, Integer... range) {
- List<Integer> rangeList = Arrays.asList(range);
- assertThat(rangeList).isNotEmpty();
- assertThat(rangeList).isStrictlyOrdered();
-
- assertThat(label.getValues().stream().map(v -> (int) v.getValue()))
- .containsExactlyElementsIn(rangeList)
- .inOrder();
- assertThat(label.getMax().getValue()).isEqualTo(Collections.max(rangeList));
- assertThat(label.getMin().getValue()).isEqualTo(Collections.min(rangeList));
- for (LabelValue v : label.getValues()) {
- assertThat(v.getText()).isNotNull();
- assertThat(v.getText()).isNotEmpty();
- }
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
deleted file mode 100644
index 7ea4d93232..0000000000
--- a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ /dev/null
@@ -1,149 +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.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.metrics.DisabledMetricMaker;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.GerritPersonIdentProvider;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AnonymousCowardName;
-import com.google.gerrit.server.config.AnonymousCowardNameProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.testing.InMemoryDatabase;
-import com.google.gerrit.testing.InMemoryH2Type;
-import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Guice;
-import com.google.inject.Key;
-import com.google.inject.ProvisionException;
-import com.google.inject.TypeLiteral;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.UUID;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class SchemaUpdaterTest {
- private LifecycleManager lifecycle;
- private InMemoryDatabase db;
-
- @Before
- public void setUp() throws Exception {
- lifecycle = new LifecycleManager();
- db = InMemoryDatabase.newDatabase(lifecycle);
- lifecycle.start();
- }
-
- @After
- public void tearDown() throws Exception {
- if (lifecycle != null) {
- lifecycle.stop();
- }
- InMemoryDatabase.drop(db);
- }
-
- @Test
- public void update() throws OrmException, FileNotFoundException, IOException {
- db.create();
-
- final Path site = Paths.get(UUID.randomUUID().toString());
- final SitePaths paths = new SitePaths(site);
- SchemaUpdater u =
- Guice.createInjector(
- new FactoryModule() {
- @Override
- protected void configure() {
- TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
- new TypeLiteral<SchemaFactory<ReviewDb>>() {};
- bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
- bind(Key.get(schemaFactory, ReviewDbFactory.class)).toInstance(db);
- bind(SitePaths.class).toInstance(paths);
-
- Config cfg = new Config();
- cfg.setString("user", null, "name", "Gerrit Code Review");
- cfg.setString("user", null, "email", "gerrit@localhost");
- cfg.setString(
- GerritServerIdProvider.SECTION,
- null,
- GerritServerIdProvider.KEY,
- "1234567");
- bind(Config.class) //
- .annotatedWith(GerritServerConfig.class) //
- .toInstance(cfg);
-
- bind(PersonIdent.class) //
- .annotatedWith(GerritPersonIdent.class) //
- .toProvider(GerritPersonIdentProvider.class);
-
- bind(String.class).annotatedWith(GerritServerId.class).toInstance("gerrit");
-
- bind(AllProjectsName.class).toInstance(new AllProjectsName("All-Projects"));
- bind(AllUsersName.class).toInstance(new AllUsersName("All-Users"));
-
- bind(GitRepositoryManager.class).toInstance(new InMemoryRepositoryManager());
-
- bind(String.class) //
- .annotatedWith(AnonymousCowardName.class) //
- .toProvider(AnonymousCowardNameProvider.class);
-
- bind(DataSourceType.class).to(InMemoryH2Type.class);
-
- bind(SystemGroupBackend.class);
- install(new NotesMigration.Module());
- bind(MetricMaker.class).to(DisabledMetricMaker.class);
- }
- })
- .getInstance(SchemaUpdater.class);
-
- for (SchemaVersion s = u.getLatestSchemaVersion(); s.getVersionNbr() > 1; s = s.getPrior()) {
- try {
- assertThat(s.getPrior().getVersionNbr())
- .named(
- "schema %s has prior version %s. Not true that",
- s.getVersionNbr(), s.getPrior().getVersionNbr())
- .isEqualTo(s.getVersionNbr() - 1);
- } catch (ProvisionException e) {
- // Ignored
- // The oldest supported schema version doesn't have a prior schema
- // version.
- break;
- }
- }
-
- u.update(new TestUpdateUI());
-
- db.assertSchemaVersion();
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java b/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
deleted file mode 100644
index 2e268ee620..0000000000
--- a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
+++ /dev/null
@@ -1,265 +0,0 @@
-// 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.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.server.schema.Schema_151.createdOnColumnExists;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroup.Id;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.GroupUUID;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.Month;
-import java.time.ZoneOffset;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_150_to_151_Test {
-
- @Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
-
- @Inject private Schema_151 schema151;
- @Inject private ReviewDb db;
- @Inject private IdentifiedUser currentUser;
- @Inject private @GerritPersonIdent Provider<PersonIdent> serverIdent;
- @Inject private Sequences seq;
-
- private Connection connection;
- private PreparedStatement createdOnRetrieval;
- private PreparedStatement createdOnUpdate;
- private PreparedStatement auditEntryDeletion;
- private JdbcSchema jdbcSchema;
-
- @Before
- public void unwrapDb() {
- jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
- }
-
- @Before
- public void setUp() throws Exception {
- assume().that(db instanceof JdbcSchema).isTrue();
-
- connection = ((JdbcSchema) db).getConnection();
-
- try (Statement stmt = connection.createStatement()) {
- stmt.execute(
- "CREATE TABLE account_groups ("
- + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " name varchar(255) DEFAULT '' NOT NULL,"
- + " created_on TIMESTAMP,"
- + " description CLOB,"
- + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
- + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
- + ")");
-
- stmt.execute(
- "CREATE TABLE account_group_members ("
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " account_id INTEGER DEFAULT 0 NOT NULL"
- + ")");
-
- stmt.execute(
- "CREATE TABLE account_group_members_audit ("
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " account_id INTEGER DEFAULT 0 NOT NULL,"
- + " added_by INTEGER DEFAULT 0 NOT NULL,"
- + " added_on TIMESTAMP,"
- + " removed_by INTEGER,"
- + " removed_on TIMESTAMP"
- + ")");
- }
-
- createdOnRetrieval =
- connection.prepareStatement("SELECT created_on FROM account_groups WHERE group_id = ?");
- createdOnUpdate =
- connection.prepareStatement("UPDATE account_groups SET created_on = ? WHERE group_id = ?");
- auditEntryDeletion =
- connection.prepareStatement("DELETE FROM account_group_members_audit WHERE group_id = ?");
- }
-
- @After
- public void tearDown() throws Exception {
- if (auditEntryDeletion != null) {
- auditEntryDeletion.close();
- }
- if (createdOnUpdate != null) {
- createdOnUpdate.close();
- }
- if (createdOnRetrieval != null) {
- createdOnRetrieval.close();
- }
- if (connection != null) {
- connection.close();
- }
- }
-
- @Test
- public void createdOnIsPopulatedForGroupsCreatedAfterAudit() throws Exception {
- Timestamp testStartTime = TimeUtil.nowTs();
- AccountGroup.Id groupId = createGroupInReviewDb("Group for schema migration");
- setCreatedOnToVeryOldTimestamp(groupId);
-
- schema151.migrateData(db, new TestUpdateUI());
-
- Timestamp createdOn = getCreatedOn(groupId);
- assertThat(createdOn).isAtLeast(testStartTime);
- }
-
- @Test
- public void createdOnIsPopulatedForGroupsCreatedBeforeAudit() throws Exception {
- AccountGroup.Id groupId = createGroupInReviewDb("Ancient group for schema migration");
- setCreatedOnToVeryOldTimestamp(groupId);
- removeAuditEntriesFor(groupId);
-
- schema151.migrateData(db, new TestUpdateUI());
-
- Timestamp createdOn = getCreatedOn(groupId);
- assertThat(createdOn).isEqualTo(AccountGroup.auditCreationInstantTs());
- }
-
- @Test
- public void createdOnIsAddedWhenItIsMissing() throws Exception {
- assertThat(createdOnColumnExists(connection)).isTrue();
- try (Statement deleteColumn = connection.createStatement()) {
- deleteColumn.execute("ALTER TABLE account_groups DROP COLUMN created_on");
- }
- assertThat(createdOnColumnExists(connection)).isFalse();
- schema151.migrateData(db, new TestUpdateUI());
- assertThat(createdOnColumnExists(connection)).isTrue();
- }
-
- private AccountGroup.Id createGroupInReviewDb(String name) throws Exception {
- AccountGroup group =
- new AccountGroup(
- new AccountGroup.NameKey(name),
- new AccountGroup.Id(seq.nextGroupId()),
- GroupUUID.make(name, serverIdent.get()),
- TimeUtil.nowTs());
- storeInReviewDb(group);
- addMembersInReviewDb(group.getId(), currentUser.getAccountId());
- return group.getId();
- }
-
- private Timestamp getCreatedOn(Id groupId) throws Exception {
- createdOnRetrieval.setInt(1, groupId.get());
- try (ResultSet results = createdOnRetrieval.executeQuery()) {
- if (results.first()) {
- return results.getTimestamp(1);
- }
- }
- return null;
- }
-
- private void setCreatedOnToVeryOldTimestamp(Id groupId) throws Exception {
- createdOnUpdate.setInt(1, groupId.get());
- Instant instant = LocalDateTime.of(1800, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC);
- createdOnUpdate.setTimestamp(1, Timestamp.from(instant));
- createdOnUpdate.setInt(2, groupId.get());
- createdOnUpdate.executeUpdate();
- }
-
- private void removeAuditEntriesFor(AccountGroup.Id groupId) throws Exception {
- auditEntryDeletion.setInt(1, groupId.get());
- auditEntryDeletion.executeUpdate();
- }
-
- private void storeInReviewDb(AccountGroup... groups) throws Exception {
- try (PreparedStatement stmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_groups"
- + " (group_uuid,"
- + " group_id,"
- + " name,"
- + " description,"
- + " created_on,"
- + " owner_group_uuid,"
- + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
- for (AccountGroup group : groups) {
- stmt.setString(1, group.getGroupUUID().get());
- stmt.setInt(2, group.getId().get());
- stmt.setString(3, group.getName());
- stmt.setString(4, group.getDescription());
- stmt.setTimestamp(5, group.getCreatedOn());
- stmt.setString(6, group.getOwnerGroupUUID().get());
- stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
- stmt.addBatch();
- }
- stmt.executeBatch();
- }
- }
-
- private void addMembersInReviewDb(AccountGroup.Id groupId, Account.Id... memberIds)
- throws Exception {
- try (PreparedStatement addMemberStmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_group_members"
- + " (group_id,"
- + " account_id) VALUES ("
- + groupId.get()
- + ", ?)");
- PreparedStatement addMemberAuditStmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_group_members_audit"
- + " (group_id,"
- + " account_id,"
- + " added_by,"
- + " added_on) VALUES ("
- + groupId.get()
- + ", ?, "
- + currentUser.getAccountId().get()
- + ", ?)")) {
- Timestamp addedOn = TimeUtil.nowTs();
- for (Account.Id memberId : memberIds) {
- addMemberStmt.setInt(1, memberId.get());
- addMemberStmt.addBatch();
-
- addMemberAuditStmt.setInt(1, memberId.get());
- addMemberAuditStmt.setTimestamp(2, addedOn);
- addMemberAuditStmt.addBatch();
- }
- addMemberStmt.executeBatch();
- addMemberAuditStmt.executeBatch();
- }
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java b/javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java
deleted file mode 100644
index 9bd20841b4..0000000000
--- a/javatests/com/google/gerrit/server/schema/Schema_159_to_160_Test.java
+++ /dev/null
@@ -1,222 +0,0 @@
-// 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.schema;
-
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-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_USERS_DEFAULT;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static com.google.gerrit.server.git.UserConfigSections.MY;
-import static com.google.gerrit.server.schema.Schema_160.DEFAULT_DRAFT_ITEMS;
-import static com.google.gerrit.server.schema.VersionedAccountPreferences.PREFERENCES;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
-import com.google.gerrit.extensions.client.MenuItem;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.index.account.AccountIndexer;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Supplier;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.BlobBasedConfig;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_159_to_160_Test {
- @Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
-
- @Inject private AccountCache accountCache;
- @Inject private AccountIndexer accountIndexer;
- @Inject private AllUsersName allUsersName;
- @Inject private GerritApi gApi;
- @Inject private GitRepositoryManager repoManager;
- @Inject private Provider<IdentifiedUser> userProvider;
- @Inject private ReviewDb db;
- @Inject private Schema_160 schema160;
-
- private Account.Id accountId;
-
- @Before
- public void setUp() throws Exception {
- accountId = userProvider.get().getAccountId();
- }
-
- @Test
- public void skipUnmodified() throws Exception {
- ObjectId oldMetaId = metaRef(accountId);
- ImmutableSet<String> fromNoteDb = myMenusFromNoteDb(accountId);
- ImmutableSet<String> fromApi = myMenusFromApi(accountId);
- for (String item : DEFAULT_DRAFT_ITEMS) {
- assertThat(fromNoteDb).doesNotContain(item);
- assertThat(fromApi).doesNotContain(item);
- }
-
- schema160.migrateData(db, new TestUpdateUI());
-
- assertThat(metaRef(accountId)).isEqualTo(oldMetaId);
- }
-
- @Test
- public void deleteItems() throws Exception {
- ObjectId oldMetaId = metaRef(accountId);
- ImmutableSet<String> defaultNames = myMenusFromApi(accountId);
-
- GeneralPreferencesInfo prefs = gApi.accounts().id(accountId.get()).getPreferences();
- prefs.my.add(0, new MenuItem("Something else", DEFAULT_DRAFT_ITEMS.get(0) + "+is:mergeable"));
- for (int i = 0; i < DEFAULT_DRAFT_ITEMS.size(); i++) {
- prefs.my.add(new MenuItem("Draft entry " + i, DEFAULT_DRAFT_ITEMS.get(i)));
- }
- gApi.accounts().id(accountId.get()).setPreferences(prefs);
-
- List<String> oldNames =
- ImmutableList.<String>builder()
- .add("Something else")
- .addAll(defaultNames)
- .add("Draft entry 0")
- .add("Draft entry 1")
- .add("Draft entry 2")
- .add("Draft entry 3")
- .build();
- assertThat(myMenusFromApi(accountId)).containsExactlyElementsIn(oldNames).inOrder();
-
- schema160.migrateData(db, new TestUpdateUI());
- accountCache.evict(accountId);
- accountIndexer.index(accountId);
- testEnv.setApiUser(accountId);
-
- assertThat(metaRef(accountId)).isNotEqualTo(oldMetaId);
-
- List<String> newNames =
- ImmutableList.<String>builder().add("Something else").addAll(defaultNames).build();
- assertThat(myMenusFromNoteDb(accountId)).containsExactlyElementsIn(newNames).inOrder();
- assertThat(myMenusFromApi(accountId)).containsExactlyElementsIn(newNames).inOrder();
- }
-
- @Test
- public void skipNonExistentRefsUsersDefault() throws Exception {
- assertThat(readRef(REFS_USERS_DEFAULT)).isEmpty();
- schema160.migrateData(db, new TestUpdateUI());
- assertThat(readRef(REFS_USERS_DEFAULT)).isEmpty();
- }
-
- @Test
- public void deleteDefaultItem() throws Exception {
- assertThat(readRef(REFS_USERS_DEFAULT)).isEmpty();
- ImmutableSet<String> defaultNames = defaultMenusFromApi();
-
- // Setting *any* preference causes preferences.config to contain the full set of "my" sections.
- // This mimics real-world behavior prior to the 2.15 upgrade; see Issue 8439 for details.
- GeneralPreferencesInfo prefs = gApi.config().server().getDefaultPreferences();
- prefs.signedOffBy = !firstNonNull(prefs.signedOffBy, false);
- gApi.config().server().setDefaultPreferences(prefs);
-
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- Config cfg = new BlobBasedConfig(null, repo, readRef(REFS_USERS_DEFAULT).get(), PREFERENCES);
- assertThat(cfg.getSubsections("my")).containsExactlyElementsIn(defaultNames).inOrder();
-
- // Add more defaults directly in git, the SetPreferences endpoint doesn't respect the "my"
- // field in the input in 2.15 and earlier.
- cfg.setString("my", "Drafts", "url", "#/q/owner:self+is:draft");
- cfg.setString("my", "Something else", "url", "#/q/owner:self+is:draft+is:mergeable");
- cfg.setString("my", "Totally not drafts", "url", "#/q/owner:self+is:draft");
- new TestRepository<>(repo)
- .branch(REFS_USERS_DEFAULT)
- .commit()
- .add(PREFERENCES, cfg.toText())
- .create();
- }
-
- List<String> oldNames =
- ImmutableList.<String>builder()
- .addAll(defaultNames)
- .add("Drafts")
- .add("Something else")
- .add("Totally not drafts")
- .build();
- assertThat(defaultMenusFromApi()).containsExactlyElementsIn(oldNames).inOrder();
-
- schema160.migrateData(db, new TestUpdateUI());
-
- assertThat(readRef(REFS_USERS_DEFAULT)).isPresent();
-
- List<String> newNames =
- ImmutableList.<String>builder().addAll(defaultNames).add("Something else").build();
- assertThat(myMenusFromNoteDb(VersionedAccountPreferences::forDefault).keySet())
- .containsExactlyElementsIn(newNames)
- .inOrder();
- assertThat(defaultMenusFromApi()).containsExactlyElementsIn(newNames).inOrder();
- }
-
- private ImmutableSet<String> myMenusFromNoteDb(Account.Id id) throws Exception {
- return myMenusFromNoteDb(() -> VersionedAccountPreferences.forUser(id)).keySet();
- }
-
- // Raw config values, bypassing the defaults set by PreferencesConfig.
- private ImmutableMap<String, String> myMenusFromNoteDb(
- Supplier<VersionedAccountPreferences> prefsSupplier) throws Exception {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- VersionedAccountPreferences prefs = prefsSupplier.get();
- prefs.load(allUsersName, repo);
- Config cfg = prefs.getConfig();
- return cfg.getSubsections(MY).stream()
- .collect(toImmutableMap(i -> i, i -> cfg.getString(MY, i, KEY_URL)));
- }
- }
-
- private ImmutableSet<String> myMenusFromApi(Account.Id id) throws Exception {
- return myMenus(gApi.accounts().id(id.get()).getPreferences()).keySet();
- }
-
- private ImmutableSet<String> defaultMenusFromApi() throws Exception {
- return myMenus(gApi.config().server().getDefaultPreferences()).keySet();
- }
-
- private static ImmutableMap<String, String> myMenus(GeneralPreferencesInfo prefs) {
-
- return prefs.my.stream().collect(toImmutableMap(i -> i.name, i -> i.url));
- }
-
- private ObjectId metaRef(Account.Id id) throws Exception {
- return readRef(RefNames.refsUsers(id))
- .orElseThrow(() -> new AssertionError("missing ref for account " + id));
- }
-
- private Optional<ObjectId> readRef(String ref) throws Exception {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- return Optional.ofNullable(repo.exactRef(ref)).map(Ref::getObjectId);
- }
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java b/javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java
deleted file mode 100644
index 67d071d0e3..0000000000
--- a/javatests/com/google/gerrit/server/schema/Schema_161_to_162_Test.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.config.AllProjectsName;
-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.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_161_to_162_Test {
- @Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
-
- @Inject private AllProjectsName allProjectsName;
- @Inject private AllUsersName allUsersName;
- @Inject private GerritApi gApi;
- @Inject private GitRepositoryManager repoManager;
- @Inject private Schema_162 schema162;
- @Inject private ReviewDb db;
- @Inject @GerritPersonIdent private PersonIdent serverUser;
-
- @Test
- public void skipCorrectInheritance() throws Exception {
- assertThatAllUsersInheritsFrom(allProjectsName.get());
- ObjectId oldHead;
- try (Repository git = repoManager.openRepository(allUsersName)) {
- oldHead = git.findRef(RefNames.REFS_CONFIG).getObjectId();
- }
-
- schema162.migrateData(db, new TestUpdateUI());
-
- // Check that the parent remained unchanged and that no commit was made
- assertThatAllUsersInheritsFrom(allProjectsName.get());
- try (Repository git = repoManager.openRepository(allUsersName)) {
- assertThat(oldHead).isEqualTo(git.findRef(RefNames.REFS_CONFIG).getObjectId());
- }
- }
-
- @Test
- public void fixIncorrectInheritance() throws Exception {
- String testProject = gApi.projects().create("test").get().name;
- assertThatAllUsersInheritsFrom(allProjectsName.get());
-
- try (Repository git = repoManager.openRepository(allUsersName);
- MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, git)) {
- ProjectConfig cfg = ProjectConfig.read(md);
- cfg.getProject().setParentName(testProject);
- md.getCommitBuilder().setCommitter(serverUser);
- md.getCommitBuilder().setAuthor(serverUser);
- md.setMessage("Test");
- cfg.commit(md);
- } catch (ConfigInvalidException | IOException ex) {
- throw new OrmException(ex);
- }
- assertThatAllUsersInheritsFrom(testProject);
-
- schema162.migrateData(db, new TestUpdateUI());
-
- assertThatAllUsersInheritsFrom(allProjectsName.get());
- }
-
- private void assertThatAllUsersInheritsFrom(String parent) throws Exception {
- assertThat(gApi.projects().name(allUsersName.get()).access().inheritsFrom.name)
- .isEqualTo(parent);
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java
deleted file mode 100644
index 57689b3520..0000000000
--- a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java
+++ /dev/null
@@ -1,227 +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.schema;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.ServerInitiated;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.group.db.GroupsUpdate;
-import com.google.gerrit.server.group.db.InternalGroupCreation;
-import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import java.io.IOException;
-import java.sql.PreparedStatement;
-import java.sql.Statement;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_166_to_167_WithGroupsInNoteDbTest {
- private static Config createConfig() {
- Config config = new Config();
- config.setString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY, "1234567");
-
- // Disable groups in ReviewDb. This means the primary storage for groups is NoteDb.
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, true);
-
- return config;
- }
-
- @Rule
- public InMemoryTestEnvironment testEnv =
- new InMemoryTestEnvironment(Schema_166_to_167_WithGroupsInNoteDbTest::createConfig);
-
- @Inject private Schema_167 schema167;
- @Inject private ReviewDb db;
- @Inject private GitRepositoryManager gitRepoManager;
- @Inject private AllUsersName allUsersName;
- @Inject private @ServerInitiated GroupsUpdate groupsUpdate;
- @Inject private Sequences seq;
-
- private JdbcSchema jdbcSchema;
-
- @Before
- public void initDb() throws Exception {
- jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
-
- try (Statement stmt = jdbcSchema.getConnection().createStatement()) {
- stmt.execute(
- "CREATE TABLE account_groups ("
- + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " name varchar(255) DEFAULT '' NOT NULL,"
- + " created_on TIMESTAMP,"
- + " description CLOB,"
- + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
- + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
- + ")");
- }
- }
-
- @Test
- public void migrationIsSkipped() throws Exception {
- // Create a group in NoteDb (doesn't create the group in ReviewDb since
- // disableReviewDb == true)
- InternalGroup internalGroup =
- groupsUpdate.createGroup(
- InternalGroupCreation.builder()
- .setNameKey(new AccountGroup.NameKey("users"))
- .setGroupUUID(new AccountGroup.UUID("users"))
- .setId(new AccountGroup.Id(seq.nextGroupId()))
- .build(),
- InternalGroupUpdate.builder().setDescription("description").build());
-
- // Insert the group into ReviewDb
- AccountGroup group1 =
- newGroup()
- .setName(internalGroup.getName())
- .setGroupUuid(internalGroup.getGroupUUID())
- .setId(internalGroup.getId())
- .setCreatedOn(internalGroup.getCreatedOn())
- .setDescription(internalGroup.getDescription())
- .setGroupUuid(internalGroup.getGroupUUID())
- .setVisibleToAll(internalGroup.isVisibleToAll())
- .build();
- storeInReviewDb(group1);
-
- // Update the group description in ReviewDb so that the group state differs between ReviewDb and
- // NoteDb
- group1.setDescription("outdated");
- updateInReviewDb(group1);
-
- // Create a group that only exists in ReviewDb
- AccountGroup group2 = newGroup().setName("reviewDbOnlyGroup").build();
- storeInReviewDb(group2);
-
- // Remember the SHA1 of the group ref in NoteDb
- ObjectId groupSha1 = getGroupSha1(group1.getGroupUUID());
-
- executeSchemaMigration(schema167);
-
- // Verify the groups in NoteDb: "users" should still exist, "reviewDbOnlyGroup" should not have
- // been created
- ImmutableList<GroupReference> groupReferences = getAllGroupsFromNoteDb();
- ImmutableList<String> groupNames =
- groupReferences.stream().map(GroupReference::getName).collect(toImmutableList());
- assertThat(groupNames).contains("users");
- assertThat(groupNames).doesNotContain("reviewDbOnlyGroup");
-
- // Verify that the group refs in NoteDb were not touched.
- assertThat(getGroupSha1(group1.getGroupUUID())).isEqualTo(groupSha1);
- assertThat(getGroupSha1(group2.getGroupUUID())).isNull();
- }
-
- private static TestGroup.Builder newGroup() {
- return TestGroup.builder();
- }
-
- private void storeInReviewDb(AccountGroup... groups) throws Exception {
- try (PreparedStatement stmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_groups"
- + " (group_uuid,"
- + " group_id,"
- + " name,"
- + " description,"
- + " created_on,"
- + " owner_group_uuid,"
- + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
- for (AccountGroup group : groups) {
- stmt.setString(1, group.getGroupUUID().get());
- stmt.setInt(2, group.getId().get());
- stmt.setString(3, group.getName());
- stmt.setString(4, group.getDescription());
- stmt.setTimestamp(5, group.getCreatedOn());
- stmt.setString(6, group.getOwnerGroupUUID().get());
- stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
- stmt.addBatch();
- }
- stmt.executeBatch();
- }
- }
-
- private void updateInReviewDb(AccountGroup... groups) throws Exception {
- try (PreparedStatement stmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "UPDATE account_groups SET"
- + " group_uuid = ?,"
- + " name = ?,"
- + " description = ?,"
- + " created_on = ?,"
- + " owner_group_uuid = ?,"
- + " visible_to_all = ?"
- + " WHERE group_id = ?")) {
- for (AccountGroup group : groups) {
- stmt.setString(1, group.getGroupUUID().get());
- stmt.setString(2, group.getName());
- stmt.setString(3, group.getDescription());
- stmt.setTimestamp(4, group.getCreatedOn());
- stmt.setString(5, group.getOwnerGroupUUID().get());
- stmt.setString(6, group.isVisibleToAll() ? "Y" : "N");
- stmt.setInt(7, group.getId().get());
- stmt.addBatch();
- }
- stmt.executeBatch();
- }
- }
-
- private void executeSchemaMigration(SchemaVersion schema) throws Exception {
- schema.migrateData(db, new TestUpdateUI());
- }
-
- private ImmutableList<GroupReference> getAllGroupsFromNoteDb()
- throws IOException, ConfigInvalidException {
- try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
- return GroupNameNotes.loadAllGroups(allUsersRepo);
- }
- }
-
- @Nullable
- private ObjectId getGroupSha1(AccountGroup.UUID groupUuid) throws IOException {
- try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
- Ref ref = allUsersRepo.exactRef(RefNames.refsGroups(groupUuid));
- return ref != null ? ref.getObjectId() : null;
- }
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
deleted file mode 100644
index 78fef3932c..0000000000
--- a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
+++ /dev/null
@@ -1,1125 +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.schema;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.truth.OptionalSubject.assertThat;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.api.accounts.AccountInput;
-import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
-import com.google.gerrit.extensions.api.groups.GroupInput;
-import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.extensions.common.CommitInfo;
-import com.google.gerrit.extensions.common.GroupAuditEventInfo;
-import com.google.gerrit.extensions.common.GroupAuditEventInfo.GroupMemberAuditEventInfo;
-import com.google.gerrit.extensions.common.GroupAuditEventInfo.Type;
-import com.google.gerrit.extensions.common.GroupAuditEventInfo.UserMemberAuditEventInfo;
-import com.google.gerrit.extensions.common.GroupInfo;
-import com.google.gerrit.extensions.common.GroupOptionsInfo;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.extensions.restapi.IdString;
-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.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.account.GroupUUID;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.git.CommitUtil;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.group.db.GroupsConsistencyChecker;
-import com.google.gerrit.server.group.testing.InternalGroupSubject;
-import com.google.gerrit.server.group.testing.TestGroupBackend;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gerrit.testing.TestTimeUtil.TempClockStep;
-import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gerrit.truth.OptionalSubject;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import java.io.IOException;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.time.LocalDate;
-import java.time.Month;
-import java.time.ZoneOffset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class Schema_166_to_167_WithGroupsInReviewDbTest {
- private static Config createConfig() {
- Config config = new Config();
- config.setString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY, "1234567");
-
- // Enable groups in ReviewDb. This means the primary storage for groups is ReviewDb.
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false);
-
- return config;
- }
-
- @Rule
- public InMemoryTestEnvironment testEnv =
- new InMemoryTestEnvironment(Schema_166_to_167_WithGroupsInReviewDbTest::createConfig);
-
- @Inject private GerritApi gApi;
- @Inject private Schema_167 schema167;
- @Inject private ReviewDb db;
- @Inject private GitRepositoryManager gitRepoManager;
- @Inject private AllUsersName allUsersName;
- @Inject private GroupsConsistencyChecker consistencyChecker;
- @Inject private IdentifiedUser currentUser;
- @Inject private @GerritServerId String serverId;
- @Inject private @GerritPersonIdent PersonIdent serverIdent;
- @Inject private GroupBundle.Factory groupBundleFactory;
- @Inject private GroupBackend groupBackend;
- @Inject private DynamicSet<GroupBackend> backends;
- @Inject private Sequences seq;
-
- private JdbcSchema jdbcSchema;
-
- @Before
- public void initDb() throws Exception {
- jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
-
- try (Statement stmt = jdbcSchema.getConnection().createStatement()) {
- stmt.execute(
- "CREATE TABLE account_groups ("
- + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " name varchar(255) DEFAULT '' NOT NULL,"
- + " created_on TIMESTAMP,"
- + " description CLOB,"
- + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
- + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
- + ")");
-
- stmt.execute(
- "CREATE TABLE account_group_members ("
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " account_id INTEGER DEFAULT 0 NOT NULL"
- + ")");
-
- stmt.execute(
- "CREATE TABLE account_group_members_audit ("
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " account_id INTEGER DEFAULT 0 NOT NULL,"
- + " added_by INTEGER DEFAULT 0 NOT NULL,"
- + " added_on TIMESTAMP,"
- + " removed_by INTEGER,"
- + " removed_on TIMESTAMP"
- + ")");
-
- stmt.execute(
- "CREATE TABLE account_group_by_id ("
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " include_uuid VARCHAR(255) DEFAULT '' NOT NULL"
- + ")");
-
- stmt.execute(
- "CREATE TABLE account_group_by_id_aud ("
- + " group_id INTEGER DEFAULT 0 NOT NULL,"
- + " include_uuid VARCHAR(255) DEFAULT '' NOT NULL,"
- + " added_by INTEGER DEFAULT 0 NOT NULL,"
- + " added_on TIMESTAMP,"
- + " removed_by INTEGER,"
- + " removed_on TIMESTAMP"
- + ")");
- }
- }
-
- @Before
- public void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- }
-
- @Test
- public void reviewDbOnlyGroupsAreMigratedToNoteDb() throws Exception {
- // Create groups only in ReviewDb
- AccountGroup group1 = newGroup().setName("verifiers").build();
- AccountGroup group2 = newGroup().setName("contributors").build();
- storeInReviewDb(group1, group2);
-
- executeSchemaMigration(schema167, group1, group2);
-
- ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
- ImmutableList<String> groupNames =
- groups.stream().map(GroupReference::getName).collect(toImmutableList());
- assertThat(groupNames).containsAllOf("verifiers", "contributors");
- }
-
- @Test
- public void alreadyExistingGroupsAreMigratedToNoteDb() throws Exception {
- // Create group in NoteDb and ReviewDb
- GroupInput groupInput = new GroupInput();
- groupInput.name = "verifiers";
- groupInput.description = "old";
- GroupInfo group1 = gApi.groups().create(groupInput).get();
- storeInReviewDb(group1);
-
- // Update group only in ReviewDb
- AccountGroup group1InReviewDb = getFromReviewDb(new AccountGroup.Id(group1.groupId));
- group1InReviewDb.setDescription("new");
- updateInReviewDb(group1InReviewDb);
-
- // Create a second group in NoteDb and ReviewDb
- GroupInfo group2 = gApi.groups().create("contributors").get();
- storeInReviewDb(group2);
-
- executeSchemaMigration(schema167, group1, group2);
-
- // Verify that both groups are present in NoteDb
- ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
- ImmutableList<String> groupNames =
- groups.stream().map(GroupReference::getName).collect(toImmutableList());
- assertThat(groupNames).containsAllOf("verifiers", "contributors");
-
- // Verify that group1 has the description from ReviewDb
- Optional<InternalGroup> group1InNoteDb = getGroupFromNoteDb(new AccountGroup.UUID(group1.id));
- assertThatGroup(group1InNoteDb).value().description().isEqualTo("new");
- }
-
- @Test
- public void adminGroupIsMigratedToNoteDb() throws Exception {
- // Administrators group is automatically created for all Gerrit servers (NoteDb only).
- GroupInfo adminGroup = gApi.groups().id("Administrators").get();
- storeInReviewDb(adminGroup);
-
- executeSchemaMigration(schema167, adminGroup);
-
- ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
- ImmutableList<String> groupNames =
- groups.stream().map(GroupReference::getName).collect(toImmutableList());
- assertThat(groupNames).contains("Administrators");
- }
-
- @Test
- public void nonInteractiveUsersGroupIsMigratedToNoteDb() throws Exception {
- // 'Non-Interactive Users' group is automatically created for all Gerrit servers (NoteDb only).
- GroupInfo nonInteractiveUsersGroup = gApi.groups().id("Non-Interactive Users").get();
- storeInReviewDb(nonInteractiveUsersGroup);
-
- executeSchemaMigration(schema167, nonInteractiveUsersGroup);
-
- ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
- ImmutableList<String> groupNames =
- groups.stream().map(GroupReference::getName).collect(toImmutableList());
- assertThat(groupNames).contains("Non-Interactive Users");
- }
-
- @Test
- public void groupsAreConsistentAfterMigrationToNoteDb() throws Exception {
- // Administrators group are automatically created for all Gerrit servers (NoteDb only).
- GroupInfo adminGroup = gApi.groups().id("Administrators").get();
- GroupInfo nonInteractiveUsersGroup = gApi.groups().id("Non-Interactive Users").get();
- storeInReviewDb(adminGroup, nonInteractiveUsersGroup);
-
- AccountGroup group1 = newGroup().setName("verifiers").build();
- AccountGroup group2 = newGroup().setName("contributors").build();
- storeInReviewDb(group1, group2);
-
- executeSchemaMigration(schema167, group1, group2);
-
- List<ConsistencyCheckInfo.ConsistencyProblemInfo> consistencyProblems =
- consistencyChecker.check();
- assertThat(consistencyProblems).isEmpty();
- }
-
- @Test
- public void nameIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().setName("verifiers").build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().name().isEqualTo("verifiers");
- }
-
- @Test
- public void emptyNameIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().setName("").build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().name().isEqualTo("");
- }
-
- @Test
- public void uuidIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup.UUID groupUuid = new AccountGroup.UUID("ABCDEF");
- AccountGroup group = newGroup().setGroupUuid(groupUuid).build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(groupUuid);
- assertThatGroup(groupInNoteDb).value().groupUuid().isEqualTo(groupUuid);
- }
-
- @Test
- public void idIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup.Id id = new AccountGroup.Id(12345);
- AccountGroup group = newGroup().setId(id).build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().id().isEqualTo(id);
- }
-
- @Test
- public void createdOnIsKeptDuringMigrationToNoteDb() throws Exception {
- Timestamp createdOn =
- Timestamp.from(
- LocalDate.of(2018, Month.FEBRUARY, 20)
- .atTime(18, 2, 56)
- .atZone(ZoneOffset.UTC)
- .toInstant());
- AccountGroup group = newGroup().setCreatedOn(createdOn).build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().createdOn().isEqualTo(createdOn);
- }
-
- @Test
- public void ownerUuidIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID("UVWXYZ");
- AccountGroup group = newGroup().setOwnerGroupUuid(ownerGroupUuid).build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().ownerGroupUuid().isEqualTo(ownerGroupUuid);
- }
-
- @Test
- public void descriptionIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().setDescription("A test group").build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().description().isEqualTo("A test group");
- }
-
- @Test
- public void absentDescriptionIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().description().isNull();
- }
-
- @Test
- public void visibleToAllIsKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().setVisibleToAll(true).build();
- storeInReviewDb(group);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().visibleToAll().isTrue();
- }
-
- @Test
- public void membersAreKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().build();
- storeInReviewDb(group);
- Account.Id member1 = new Account.Id(23456);
- Account.Id member2 = new Account.Id(93483);
- addMembersInReviewDb(group.getId(), member1, member2);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().members().containsExactly(member1, member2);
- }
-
- @Test
- public void subgroupsAreKeptDuringMigrationToNoteDb() throws Exception {
- AccountGroup group = newGroup().build();
- storeInReviewDb(group);
- AccountGroup.UUID subgroup1 = new AccountGroup.UUID("FGHIKL");
- AccountGroup.UUID subgroup2 = new AccountGroup.UUID("MNOPQR");
- addSubgroupsInReviewDb(group.getId(), subgroup1, subgroup2);
-
- executeSchemaMigration(schema167, group);
-
- Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
- assertThatGroup(groupInNoteDb).value().subgroups().containsExactly(subgroup1, subgroup2);
- }
-
- @Test
- public void logFormatWithAccountsAndGerritGroups() throws Exception {
- AccountInfo user1 = createAccount("user1");
- AccountInfo user2 = createAccount("user2");
-
- AccountGroup group1 = createInReviewDb("group1");
- AccountGroup group2 = createInReviewDb("group2");
- AccountGroup group3 = createInReviewDb("group3");
-
- // Add some accounts
- try (TempClockStep step = TestTimeUtil.freezeClock()) {
- addMembersInReviewDb(
- group1.getId(), new Account.Id(user1._accountId), new Account.Id(user2._accountId));
- }
- TimeUtil.nowTs();
-
- // Add some Gerrit groups
- try (TempClockStep step = TestTimeUtil.freezeClock()) {
- addSubgroupsInReviewDb(group1.getId(), group2.getGroupUUID(), group3.getGroupUUID());
- }
-
- executeSchemaMigration(schema167, group1, group2, group3);
-
- GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group1.getGroupUUID());
-
- ImmutableList<CommitInfo> log = log(group1);
- assertThat(log).hasSize(4);
-
- // Verify commit that created the group
- assertThat(log.get(0)).message().isEqualTo("Create group");
- assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
- assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
- assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
- assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
- assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
-
- // Verify commit that the group creator as member
- assertThat(log.get(1))
- .message()
- .isEqualTo(
- "Update group\n\nAdd: "
- + currentUser.getName()
- + " <"
- + currentUser.getAccountId()
- + "@"
- + serverId
- + ">");
- assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
-
- // Verify commit that added members
- assertThat(log.get(2))
- .message()
- .isEqualTo(
- "Update group\n"
- + "\n"
- + ("Add: user1 <" + user1._accountId + "@" + serverId + ">\n")
- + ("Add: user2 <" + user2._accountId + "@" + serverId + ">"));
- assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
-
- // Verify commit that added Gerrit groups
- assertThat(log.get(3))
- .message()
- .isEqualTo(
- "Update group\n"
- + "\n"
- + ("Add-group: " + group2.getName() + " <" + group2.getGroupUUID().get() + ">\n")
- + ("Add-group: " + group3.getName() + " <" + group3.getGroupUUID().get() + ">"));
- assertThat(log.get(3)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(3)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(3)).committer().hasSameDateAs(log.get(3).author);
-
- // Verify that audit log is correctly read by Gerrit
- List<? extends GroupAuditEventInfo> auditEvents =
- gApi.groups().id(group1.getGroupUUID().get()).auditLog();
- assertThat(auditEvents).hasSize(5);
- AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
- assertMemberAuditEvent(
- auditEvents.get(4), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
- assertMemberAuditEvents(
- auditEvents.get(3),
- auditEvents.get(2),
- Type.ADD_USER,
- currentUser.getAccountId(),
- user1,
- user2);
- assertSubgroupAuditEvents(
- auditEvents.get(1),
- auditEvents.get(0),
- Type.ADD_GROUP,
- currentUser.getAccountId(),
- toGroupInfo(group2),
- toGroupInfo(group3));
- }
-
- @Test
- public void logFormatWithSystemGroups() throws Exception {
- AccountGroup group = createInReviewDb("group");
-
- try (TempClockStep step = TestTimeUtil.freezeClock()) {
- addSubgroupsInReviewDb(
- group.getId(), SystemGroupBackend.ANONYMOUS_USERS, SystemGroupBackend.REGISTERED_USERS);
- }
-
- executeSchemaMigration(schema167, group);
-
- GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group.getGroupUUID());
-
- ImmutableList<CommitInfo> log = log(group);
- assertThat(log).hasSize(3);
-
- // Verify commit that created the group
- assertThat(log.get(0)).message().isEqualTo("Create group");
- assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
- assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
- assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
- assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
- assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
-
- // Verify commit that the group creator as member
- assertThat(log.get(1))
- .message()
- .isEqualTo(
- "Update group\n\nAdd: "
- + currentUser.getName()
- + " <"
- + currentUser.getAccountId()
- + "@"
- + serverId
- + ">");
- assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
-
- // Verify commit that added system groups
- assertThat(log.get(2))
- .message()
- .isEqualTo(
- "Update group\n"
- + "\n"
- + "Add-group: Anonymous Users <global:Anonymous-Users>\n"
- + "Add-group: Registered Users <global:Registered-Users>");
- assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
-
- // Verify that audit log is correctly read by Gerrit
- List<? extends GroupAuditEventInfo> auditEvents =
- gApi.groups().id(group.getGroupUUID().get()).auditLog();
- assertThat(auditEvents).hasSize(3);
- AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
- assertMemberAuditEvent(
- auditEvents.get(2), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
- assertSubgroupAuditEvents(
- auditEvents.get(1),
- auditEvents.get(0),
- Type.ADD_GROUP,
- currentUser.getAccountId(),
- groupInfoForExternalGroup(SystemGroupBackend.ANONYMOUS_USERS),
- groupInfoForExternalGroup(SystemGroupBackend.REGISTERED_USERS));
- }
-
- @Test
- public void logFormatWithExternalGroup() throws Exception {
- AccountGroup group = createInReviewDb("group");
-
- TestGroupBackend testGroupBackend = new TestGroupBackend();
- backends.add("gerrit", testGroupBackend);
- AccountGroup.UUID subgroupUuid = testGroupBackend.create("test").getGroupUUID();
- assertThat(groupBackend.handles(subgroupUuid)).isTrue();
- addSubgroupsInReviewDb(group.getId(), subgroupUuid);
-
- executeSchemaMigration(schema167, group);
-
- GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group.getGroupUUID());
-
- ImmutableList<CommitInfo> log = log(group);
- assertThat(log).hasSize(3);
-
- // Verify commit that created the group
- assertThat(log.get(0)).message().isEqualTo("Create group");
- assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
- assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
- assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
- assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
- assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
-
- // Verify commit that the group creator as member
- assertThat(log.get(1))
- .message()
- .isEqualTo(
- "Update group\n\nAdd: "
- + currentUser.getName()
- + " <"
- + currentUser.getAccountId()
- + "@"
- + serverId
- + ">");
- assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
-
- // Verify commit that added system groups
- // Note: The schema migration can only resolve names of Gerrit groups, not of external groups
- // and system groups, hence the UUID shows up in commit messages where we would otherwise
- // expect the group name.
- assertThat(log.get(2))
- .message()
- .isEqualTo(
- "Update group\n"
- + "\n"
- + "Add-group: "
- + subgroupUuid.get()
- + " <"
- + subgroupUuid.get()
- + ">");
- assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
-
- // Verify that audit log is correctly read by Gerrit
- List<? extends GroupAuditEventInfo> auditEvents =
- gApi.groups().id(group.getGroupUUID().get()).auditLog();
- assertThat(auditEvents).hasSize(2);
- AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
- assertMemberAuditEvent(
- auditEvents.get(1), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
- assertSubgroupAuditEvent(
- auditEvents.get(0),
- Type.ADD_GROUP,
- currentUser.getAccountId(),
- groupInfoForExternalGroup(subgroupUuid));
- }
-
- @Test
- public void logFormatWithNonExistingExternalGroup() throws Exception {
- AccountGroup group = createInReviewDb("group");
-
- AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("notExisting:foo");
-
- assertThat(groupBackend.handles(subgroupUuid)).isFalse();
- addSubgroupsInReviewDb(group.getId(), subgroupUuid);
-
- executeSchemaMigration(schema167, group);
-
- GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group.getGroupUUID());
-
- ImmutableList<CommitInfo> log = log(group);
- assertThat(log).hasSize(3);
-
- // Verify commit that created the group
- assertThat(log.get(0)).message().isEqualTo("Create group");
- assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
- assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
- assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
- assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
- assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
-
- // Verify commit that the group creator as member
- assertThat(log.get(1))
- .message()
- .isEqualTo(
- "Update group\n\nAdd: "
- + currentUser.getName()
- + " <"
- + currentUser.getAccountId()
- + "@"
- + serverId
- + ">");
- assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
-
- // Verify commit that added system groups
- // Note: The schema migration can only resolve names of Gerrit groups, not of external groups
- // and system groups, hence the UUID shows up in commit messages where we would otherwise
- // expect the group name.
- assertThat(log.get(2))
- .message()
- .isEqualTo("Update group\n" + "\n" + "Add-group: notExisting:foo <notExisting:foo>");
- assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
-
- // Verify that audit log is correctly read by Gerrit
- List<? extends GroupAuditEventInfo> auditEvents =
- gApi.groups().id(group.getGroupUUID().get()).auditLog();
- assertThat(auditEvents).hasSize(2);
- AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
- assertMemberAuditEvent(
- auditEvents.get(1), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
- assertSubgroupAuditEvent(
- auditEvents.get(0),
- Type.ADD_GROUP,
- currentUser.getAccountId(),
- groupInfoForExternalGroup(subgroupUuid));
- }
-
- private static TestGroup.Builder newGroup() {
- return TestGroup.builder();
- }
-
- private AccountGroup createInReviewDb(String groupName) throws Exception {
- AccountGroup group =
- new AccountGroup(
- new AccountGroup.NameKey(groupName),
- new AccountGroup.Id(seq.nextGroupId()),
- GroupUUID.make(groupName, serverIdent),
- TimeUtil.nowTs());
- storeInReviewDb(group);
- addMembersInReviewDb(group.getId(), currentUser.getAccountId());
- return group;
- }
-
- private void storeInReviewDb(GroupInfo... groups) throws Exception {
- storeInReviewDb(
- Arrays.stream(groups)
- .map(Schema_166_to_167_WithGroupsInReviewDbTest::toAccountGroup)
- .toArray(AccountGroup[]::new));
- }
-
- private void storeInReviewDb(AccountGroup... groups) throws Exception {
- try (PreparedStatement stmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_groups"
- + " (group_uuid,"
- + " group_id,"
- + " name,"
- + " description,"
- + " created_on,"
- + " owner_group_uuid,"
- + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
- for (AccountGroup group : groups) {
- stmt.setString(1, group.getGroupUUID().get());
- stmt.setInt(2, group.getId().get());
- stmt.setString(3, group.getName());
- stmt.setString(4, group.getDescription());
- stmt.setTimestamp(5, group.getCreatedOn());
- stmt.setString(6, group.getOwnerGroupUUID().get());
- stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
- stmt.addBatch();
- }
- stmt.executeBatch();
- }
- }
-
- private void updateInReviewDb(AccountGroup... groups) throws Exception {
- try (PreparedStatement stmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "UPDATE account_groups SET"
- + " group_uuid = ?,"
- + " name = ?,"
- + " description = ?,"
- + " created_on = ?,"
- + " owner_group_uuid = ?,"
- + " visible_to_all = ?"
- + " WHERE group_id = ?")) {
- for (AccountGroup group : groups) {
- stmt.setString(1, group.getGroupUUID().get());
- stmt.setString(2, group.getName());
- stmt.setString(3, group.getDescription());
- stmt.setTimestamp(4, group.getCreatedOn());
- stmt.setString(5, group.getOwnerGroupUUID().get());
- stmt.setString(6, group.isVisibleToAll() ? "Y" : "N");
- stmt.setInt(7, group.getId().get());
- stmt.addBatch();
- }
- stmt.executeBatch();
- }
- }
-
- private AccountGroup getFromReviewDb(AccountGroup.Id groupId) throws Exception {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT group_uuid,"
- + " name,"
- + " description,"
- + " created_on,"
- + " owner_group_uuid,"
- + " visible_to_all"
- + " FROM account_groups"
- + " WHERE group_id = "
- + groupId.get())) {
- if (!rs.next()) {
- throw new OrmException(String.format("Group %s not found", groupId.get()));
- }
-
- AccountGroup.UUID groupUuid = new AccountGroup.UUID(rs.getString(1));
- AccountGroup.NameKey groupName = new AccountGroup.NameKey(rs.getString(2));
- String description = rs.getString(3);
- Timestamp createdOn = rs.getTimestamp(4);
- AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID(rs.getString(5));
- boolean visibleToAll = "Y".equals(rs.getString(6));
-
- AccountGroup group = new AccountGroup(groupName, groupId, groupUuid, createdOn);
- group.setDescription(description);
- group.setOwnerGroupUUID(ownerGroupUuid);
- group.setVisibleToAll(visibleToAll);
-
- if (rs.next()) {
- throw new OrmException(String.format("Group ID %s is ambiguous", groupId.get()));
- }
-
- return group;
- }
- }
-
- private void addMembersInReviewDb(AccountGroup.Id groupId, Account.Id... memberIds)
- throws Exception {
- try (PreparedStatement addMemberStmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_group_members"
- + " (group_id,"
- + " account_id) VALUES ("
- + groupId.get()
- + ", ?)");
- PreparedStatement addMemberAuditStmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_group_members_audit"
- + " (group_id,"
- + " account_id,"
- + " added_by,"
- + " added_on) VALUES ("
- + groupId.get()
- + ", ?, "
- + currentUser.getAccountId().get()
- + ", ?)")) {
- Timestamp addedOn = TimeUtil.nowTs();
- for (Account.Id memberId : memberIds) {
- addMemberStmt.setInt(1, memberId.get());
- addMemberStmt.addBatch();
-
- addMemberAuditStmt.setInt(1, memberId.get());
- addMemberAuditStmt.setTimestamp(2, addedOn);
- addMemberAuditStmt.addBatch();
- }
- addMemberStmt.executeBatch();
- addMemberAuditStmt.executeBatch();
- }
- }
-
- private void addSubgroupsInReviewDb(AccountGroup.Id groupId, AccountGroup.UUID... subgroupUuids)
- throws Exception {
- try (PreparedStatement addSubGroupStmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_group_by_id"
- + " (group_id,"
- + " include_uuid) VALUES ("
- + groupId.get()
- + ", ?)");
- PreparedStatement addSubGroupAuditStmt =
- jdbcSchema
- .getConnection()
- .prepareStatement(
- "INSERT INTO account_group_by_id_aud"
- + " (group_id,"
- + " include_uuid,"
- + " added_by,"
- + " added_on) VALUES ("
- + groupId.get()
- + ", ?, "
- + currentUser.getAccountId().get()
- + ", ?)")) {
- Timestamp addedOn = TimeUtil.nowTs();
- for (AccountGroup.UUID subgroupUuid : subgroupUuids) {
- addSubGroupStmt.setString(1, subgroupUuid.get());
- addSubGroupStmt.addBatch();
-
- addSubGroupAuditStmt.setString(1, subgroupUuid.get());
- addSubGroupAuditStmt.setTimestamp(2, addedOn);
- addSubGroupAuditStmt.addBatch();
- }
- addSubGroupStmt.executeBatch();
- addSubGroupAuditStmt.executeBatch();
- }
- }
-
- private AccountInfo createAccount(String name) throws RestApiException {
- AccountInput accountInput = new AccountInput();
- accountInput.username = name;
- accountInput.name = name;
- return gApi.accounts().create(accountInput).get();
- }
-
- private GroupBundle readGroupBundleFromNoteDb(AccountGroup.UUID groupUuid) throws Exception {
- try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
- return groupBundleFactory.fromNoteDb(allUsersName, allUsersRepo, groupUuid);
- }
- }
-
- private void executeSchemaMigration(SchemaVersion schema, AccountGroup... groupsToVerify)
- throws Exception {
- executeSchemaMigration(
- schema,
- Arrays.stream(groupsToVerify)
- .map(AccountGroup::getGroupUUID)
- .toArray(AccountGroup.UUID[]::new));
- }
-
- private void executeSchemaMigration(SchemaVersion schema, GroupInfo... groupsToVerify)
- throws Exception {
- executeSchemaMigration(
- schema,
- Arrays.stream(groupsToVerify)
- .map(i -> new AccountGroup.UUID(i.id))
- .toArray(AccountGroup.UUID[]::new));
- }
-
- private void executeSchemaMigration(SchemaVersion schema, AccountGroup.UUID... groupsToVerify)
- throws Exception {
- List<GroupBundle> reviewDbBundles = new ArrayList<>();
- for (AccountGroup.UUID groupUuid : groupsToVerify) {
- reviewDbBundles.add(GroupBundle.Factory.fromReviewDb(db, groupUuid));
- }
-
- schema.migrateData(db, new TestUpdateUI());
-
- for (GroupBundle reviewDbBundle : reviewDbBundles) {
- assertMigratedCleanly(readGroupBundleFromNoteDb(reviewDbBundle.uuid()), reviewDbBundle);
- }
- }
-
- private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
- assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- private ImmutableList<CommitInfo> log(AccountGroup group) throws Exception {
- ImmutableList.Builder<CommitInfo> result = ImmutableList.builder();
- List<Date> commitDates = new ArrayList<>();
- try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(allUsersRepo)) {
- Ref ref = allUsersRepo.exactRef(RefNames.refsGroups(group.getGroupUUID()));
- if (ref != null) {
- rw.sort(RevSort.REVERSE);
- rw.setRetainBody(true);
- rw.markStart(rw.parseCommit(ref.getObjectId()));
- for (RevCommit c : rw) {
- result.add(CommitUtil.toCommitInfo(c));
- commitDates.add(c.getCommitterIdent().getWhen());
- }
- }
- }
- assertThat(commitDates).named("commit timestamps for %s", result).isOrdered();
- return result.build();
- }
-
- private ImmutableList<GroupReference> getAllGroupsFromNoteDb()
- throws IOException, ConfigInvalidException {
- try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
- return GroupNameNotes.loadAllGroups(allUsersRepo);
- }
- }
-
- private Optional<InternalGroup> getGroupFromNoteDb(AccountGroup.UUID groupUuid) throws Exception {
- try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
- return GroupConfig.loadForGroup(allUsersName, allUsersRepo, groupUuid).getLoadedGroup();
- }
- }
-
- private static OptionalSubject<InternalGroupSubject, InternalGroup> assertThatGroup(
- Optional<InternalGroup> group) {
- return assertThat(group, InternalGroupSubject::assertThat).named("group");
- }
-
- private void assertMemberAuditEvent(
- GroupAuditEventInfo info,
- Type expectedType,
- Account.Id expectedUser,
- AccountInfo expectedMember) {
- assertThat(info.user._accountId).isEqualTo(expectedUser.get());
- assertThat(info.type).isEqualTo(expectedType);
- assertThat(info).isInstanceOf(UserMemberAuditEventInfo.class);
- assertAccount(((UserMemberAuditEventInfo) info).member, expectedMember);
- }
-
- private void assertMemberAuditEvents(
- GroupAuditEventInfo info1,
- GroupAuditEventInfo info2,
- Type expectedType,
- Account.Id expectedUser,
- AccountInfo expectedMember1,
- AccountInfo expectedMember2) {
- assertThat(info1).isInstanceOf(UserMemberAuditEventInfo.class);
- assertThat(info2).isInstanceOf(UserMemberAuditEventInfo.class);
-
- UserMemberAuditEventInfo event1 = (UserMemberAuditEventInfo) info1;
- UserMemberAuditEventInfo event2 = (UserMemberAuditEventInfo) info2;
-
- assertThat(event1.member._accountId)
- .isAnyOf(expectedMember1._accountId, expectedMember2._accountId);
- assertThat(event2.member._accountId)
- .isAnyOf(expectedMember1._accountId, expectedMember2._accountId);
- assertThat(event1.member._accountId).isNotEqualTo(event2.member._accountId);
-
- if (event1.member._accountId == expectedMember1._accountId) {
- assertMemberAuditEvent(info1, expectedType, expectedUser, expectedMember1);
- assertMemberAuditEvent(info2, expectedType, expectedUser, expectedMember2);
- } else {
- assertMemberAuditEvent(info1, expectedType, expectedUser, expectedMember2);
- assertMemberAuditEvent(info2, expectedType, expectedUser, expectedMember1);
- }
- }
-
- private void assertSubgroupAuditEvent(
- GroupAuditEventInfo info,
- Type expectedType,
- Account.Id expectedUser,
- GroupInfo expectedSubGroup) {
- assertThat(info.user._accountId).isEqualTo(expectedUser.get());
- assertThat(info.type).isEqualTo(expectedType);
- assertThat(info).isInstanceOf(GroupMemberAuditEventInfo.class);
- assertGroup(((GroupMemberAuditEventInfo) info).member, expectedSubGroup);
- }
-
- private void assertSubgroupAuditEvents(
- GroupAuditEventInfo info1,
- GroupAuditEventInfo info2,
- Type expectedType,
- Account.Id expectedUser,
- GroupInfo expectedSubGroup1,
- GroupInfo expectedSubGroup2) {
- assertThat(info1).isInstanceOf(GroupMemberAuditEventInfo.class);
- assertThat(info2).isInstanceOf(GroupMemberAuditEventInfo.class);
-
- GroupMemberAuditEventInfo event1 = (GroupMemberAuditEventInfo) info1;
- GroupMemberAuditEventInfo event2 = (GroupMemberAuditEventInfo) info2;
-
- assertThat(event1.member.id).isAnyOf(expectedSubGroup1.id, expectedSubGroup2.id);
- assertThat(event2.member.id).isAnyOf(expectedSubGroup1.id, expectedSubGroup2.id);
- assertThat(event1.member.id).isNotEqualTo(event2.member.id);
-
- if (event1.member.id.equals(expectedSubGroup1.id)) {
- assertSubgroupAuditEvent(info1, expectedType, expectedUser, expectedSubGroup1);
- assertSubgroupAuditEvent(info2, expectedType, expectedUser, expectedSubGroup2);
- } else {
- assertSubgroupAuditEvent(info1, expectedType, expectedUser, expectedSubGroup2);
- assertSubgroupAuditEvent(info2, expectedType, expectedUser, expectedSubGroup1);
- }
- }
-
- private void assertAccount(AccountInfo actual, AccountInfo expected) {
- assertThat(actual._accountId).isEqualTo(expected._accountId);
- assertThat(actual.name).isEqualTo(expected.name);
- assertThat(actual.email).isEqualTo(expected.email);
- assertThat(actual.username).isEqualTo(expected.username);
- }
-
- private void assertGroup(GroupInfo actual, GroupInfo expected) {
- assertThat(actual.id).isEqualTo(expected.id);
- assertThat(actual.name).isEqualTo(expected.name);
- assertThat(actual.groupId).isEqualTo(expected.groupId);
- }
-
- private GroupInfo groupInfoForExternalGroup(AccountGroup.UUID groupUuid) {
- GroupInfo groupInfo = new GroupInfo();
- groupInfo.id = IdString.fromDecoded(groupUuid.get()).encoded();
-
- if (groupBackend.handles(groupUuid)) {
- groupInfo.name = groupBackend.get(groupUuid).getName();
- }
-
- return groupInfo;
- }
-
- private static AccountGroup toAccountGroup(GroupInfo info) {
- AccountGroup group =
- new AccountGroup(
- new AccountGroup.NameKey(info.name),
- new AccountGroup.Id(info.groupId),
- new AccountGroup.UUID(info.id),
- info.createdOn);
- group.setDescription(info.description);
- if (info.ownerId != null) {
- group.setOwnerGroupUUID(new AccountGroup.UUID(info.ownerId));
- }
- group.setVisibleToAll(
- info.options != null && info.options.visibleToAll != null && info.options.visibleToAll);
- return group;
- }
-
- private static GroupInfo toGroupInfo(AccountGroup group) {
- GroupInfo groupInfo = new GroupInfo();
- groupInfo.id = group.getGroupUUID().get();
- groupInfo.groupId = group.getId().get();
- groupInfo.name = group.getName();
- groupInfo.createdOn = group.getCreatedOn();
- groupInfo.description = group.getDescription();
- groupInfo.owner = group.getOwnerGroupUUID().get();
- groupInfo.options = new GroupOptionsInfo();
- groupInfo.options.visibleToAll = group.isVisibleToAll() ? true : null;
- return groupInfo;
- }
-}
diff --git a/javatests/com/google/gerrit/server/update/BUILD b/javatests/com/google/gerrit/server/update/BUILD
index dd96967679..c98b35fc3a 100644
--- a/javatests/com/google/gerrit/server/update/BUILD
+++ b/javatests/com/google/gerrit/server/update/BUILD
@@ -1,37 +1,15 @@
load("//tools/bzl:junit.bzl", "junit_tests")
-MEDIUM_TESTS = ["RefUpdateUtilRepoTest.java"]
-
-junit_tests(
- name = "medium_tests",
- size = "medium",
- timeout = "short",
- srcs = MEDIUM_TESTS,
- tags = ["no_windows"],
- deps = [
- "//java/com/google/gerrit/server",
- "//lib:guava",
- "//lib:junit",
- "//lib/jgit/org.eclipse.jgit:jgit",
- "//lib/jgit/org.eclipse.jgit.junit:junit",
- "//lib/truth",
- ],
-)
-
junit_tests(
name = "small_tests",
size = "small",
- srcs = glob(
- ["*.java"],
- exclude = MEDIUM_TESTS,
- ),
+ srcs = glob(["*.java"]),
deps = [
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/util/time",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//lib:guava",
- "//lib:gwtorm",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/jgit/org.eclipse.jgit.junit:junit",
diff --git a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
index 7f8e1f444a..cca844ebdd 100644
--- a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -14,10 +14,9 @@
package com.google.gerrit.server.update;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.util.time.TimeUtil;
@@ -37,7 +36,6 @@ public class BatchUpdateTest extends GerritBaseTests {
@Inject private GitRepositoryManager repoManager;
@Inject private BatchUpdate.Factory batchUpdateFactory;
- @Inject private ReviewDb db;
@Inject private Provider<CurrentUser> user;
private Project.NameKey project;
@@ -53,10 +51,10 @@ public class BatchUpdateTest extends GerritBaseTests {
@Test
public void addRefUpdateFromFastForwardCommit() throws Exception {
- final RevCommit masterCommit = repo.branch("master").commit().create();
- final RevCommit branchCommit = repo.branch("branch").commit().parent(masterCommit).create();
+ RevCommit masterCommit = repo.branch("master").commit().create();
+ RevCommit branchCommit = repo.branch("branch").commit().parent(masterCommit).create();
- try (BatchUpdate bu = batchUpdateFactory.create(db, project, user.get(), TimeUtil.nowTs())) {
+ try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
bu.addRepoOnlyOp(
new RepoOnlyOp() {
@Override
@@ -67,7 +65,7 @@ public class BatchUpdateTest extends GerritBaseTests {
bu.execute();
}
- assertEquals(
- repo.getRepository().exactRef("refs/heads/master").getObjectId(), branchCommit.getId());
+ assertThat(repo.getRepository().exactRef("refs/heads/master").getObjectId())
+ .isEqualTo(branchCommit.getId());
}
}
diff --git a/javatests/com/google/gerrit/server/update/RefUpdateUtilRepoTest.java b/javatests/com/google/gerrit/server/update/RefUpdateUtilRepoTest.java
deleted file mode 100644
index fe9d522ed2..0000000000
--- a/javatests/com/google/gerrit/server/update/RefUpdateUtilRepoTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// 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.update;
-
-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 java.nio.file.Files;
-import java.nio.file.Path;
-import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.junit.TestRepository;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class RefUpdateUtilRepoTest {
- public enum RepoSetup {
- LOCAL_DISK {
- @Override
- Repository setUpRepo() throws Exception {
- Path p = Files.createTempDirectory("gerrit_repo_");
- try {
- Repository repo = new FileRepository(p.toFile());
- repo.create(true);
- return repo;
- } catch (Exception e) {
- delete(p);
- throw e;
- }
- }
-
- @Override
- void tearDownRepo(Repository repo) throws Exception {
- delete(repo.getDirectory().toPath());
- }
-
- private void delete(Path p) throws Exception {
- MoreFiles.deleteRecursively(p, RecursiveDeleteOption.ALLOW_INSECURE);
- }
- },
-
- IN_MEMORY {
- @Override
- Repository setUpRepo() {
- return new InMemoryRepository(new DfsRepositoryDescription("repo"));
- }
-
- @Override
- void tearDownRepo(Repository repo) {}
- };
-
- abstract Repository setUpRepo() throws Exception;
-
- abstract void tearDownRepo(Repository repo) throws Exception;
- }
-
- @Parameters(name = "{0}")
- public static ImmutableList<RepoSetup[]> data() {
- return ImmutableList.copyOf(new RepoSetup[][] {{RepoSetup.LOCAL_DISK}, {RepoSetup.IN_MEMORY}});
- }
-
- @Parameter public RepoSetup repoSetup;
-
- private Repository repo;
-
- @Before
- public void setUp() throws Exception {
- repo = repoSetup.setUpRepo();
- }
-
- @After
- public void tearDown() throws Exception {
- if (repo != null) {
- repoSetup.tearDownRepo(repo);
- repo = null;
- }
- }
-
- @Test
- public void deleteRefNoOp() throws Exception {
- String ref = "refs/heads/foo";
- assertThat(repo.exactRef(ref)).isNull();
- RefUpdateUtil.deleteChecked(repo, "refs/heads/foo");
- assertThat(repo.exactRef(ref)).isNull();
- }
-
- @Test
- public void deleteRef() throws Exception {
- String ref = "refs/heads/foo";
- new TestRepository<>(repo).branch(ref).commit().create();
- assertThat(repo.exactRef(ref)).isNotNull();
- RefUpdateUtil.deleteChecked(repo, "refs/heads/foo");
- assertThat(repo.exactRef(ref)).isNull();
- }
-}
diff --git a/javatests/com/google/gerrit/server/update/RefUpdateUtilTest.java b/javatests/com/google/gerrit/server/update/RefUpdateUtilTest.java
deleted file mode 100644
index fc8696aed4..0000000000
--- a/javatests/com/google/gerrit/server/update/RefUpdateUtilTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.update;
-
-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 com.google.common.collect.ImmutableList;
-import com.google.gerrit.server.git.LockFailureException;
-import java.io.IOException;
-import java.util.function.Consumer;
-import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
-import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-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);
- private static final Consumer<ReceiveCommand> REJECTED =
- c -> c.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON);
- private static final Consumer<ReceiveCommand> ABORTED =
- c -> {
- c.setResult(ReceiveCommand.Result.NOT_ATTEMPTED);
- ReceiveCommand.abort(ImmutableList.of(c));
- checkState(
- c.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED
- && c.getResult() != ReceiveCommand.Result.LOCK_FAILURE
- && c.getResult() != ReceiveCommand.Result.OK,
- "unexpected state after abort: %s",
- c);
- };
-
- @Test
- public void checkBatchRefUpdateResults() throws Exception {
- checkResults();
- checkResults(OK);
- checkResults(OK, OK);
-
- assertIoException(REJECTED);
- assertIoException(OK, REJECTED);
- assertIoException(LOCK_FAILURE, REJECTED);
- assertIoException(LOCK_FAILURE, OK);
- assertIoException(LOCK_FAILURE, REJECTED, OK);
- assertIoException(LOCK_FAILURE, LOCK_FAILURE, REJECTED);
- assertIoException(LOCK_FAILURE, ABORTED, REJECTED);
- assertIoException(LOCK_FAILURE, ABORTED, OK);
-
- assertLockFailureException(LOCK_FAILURE);
- assertLockFailureException(LOCK_FAILURE, LOCK_FAILURE);
- assertLockFailureException(LOCK_FAILURE, LOCK_FAILURE, ABORTED);
- assertLockFailureException(LOCK_FAILURE, LOCK_FAILURE, ABORTED, ABORTED);
- assertLockFailureException(ABORTED);
- assertLockFailureException(ABORTED, ABORTED);
- }
-
- @SafeVarargs
- private static void checkResults(Consumer<ReceiveCommand>... resultSetters) throws Exception {
- RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters));
- }
-
- @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);
- }
- }
-
- @SafeVarargs
- private static void assertLockFailureException(Consumer<ReceiveCommand>... resultSetters)
- throws Exception {
- try {
- RefUpdateUtil.checkResults(newBatchRefUpdate(resultSetters));
- assert_().fail("expected LockFailureException");
- } catch (LockFailureException e) {
- // Expected.
- }
- }
-
- @SafeVarargs
- private static BatchRefUpdate newBatchRefUpdate(Consumer<ReceiveCommand>... resultSetters) {
- try (Repository repo = new InMemoryRepository(new DfsRepositoryDescription("repo"))) {
- BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
- for (int i = 0; i < resultSetters.length; i++) {
- ReceiveCommand cmd =
- new ReceiveCommand(
- ObjectId.fromString(String.format("%039x1", i)),
- ObjectId.fromString(String.format("%039x2", i)),
- "refs/heads/branch" + i);
- bru.addCommand(cmd);
- resultSetters[i].accept(cmd);
- }
- return bru;
- }
- }
-}
diff --git a/javatests/com/google/gerrit/server/update/RepoViewTest.java b/javatests/com/google/gerrit/server/update/RepoViewTest.java
index 9f7deee6cb..b41c66c280 100644
--- a/javatests/com/google/gerrit/server/update/RepoViewTest.java
+++ b/javatests/com/google/gerrit/server/update/RepoViewTest.java
@@ -19,6 +19,7 @@ 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.testing.InMemoryRepositoryManager;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -30,7 +31,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-public class RepoViewTest {
+public class RepoViewTest extends GerritBaseTests {
private static final String MASTER = "refs/heads/master";
private static final String BRANCH = "refs/heads/branch";
diff --git a/javatests/com/google/gerrit/server/util/IdGeneratorTest.java b/javatests/com/google/gerrit/server/util/IdGeneratorTest.java
index 808eca8691..e702656bd1 100644
--- a/javatests/com/google/gerrit/server/util/IdGeneratorTest.java
+++ b/javatests/com/google/gerrit/server/util/IdGeneratorTest.java
@@ -17,10 +17,11 @@ 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 {
+public class IdGeneratorTest extends GerritBaseTests {
@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 9069928836..3048b75deb 100644
--- a/javatests/com/google/gerrit/server/util/LabelVoteTest.java
+++ b/javatests/com/google/gerrit/server/util/LabelVoteTest.java
@@ -19,9 +19,10 @@ 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 com.google.gerrit.testing.GerritBaseTests;
import org.junit.Test;
-public class LabelVoteTest {
+public class LabelVoteTest extends GerritBaseTests {
@Test
public void labelVoteParse() {
assertLabelVoteEquals(parse("Code-Review-2"), "Code-Review", -2);
diff --git a/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java b/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java
index 025bf847eb..9ea17f394f 100644
--- a/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java
+++ b/javatests/com/google/gerrit/server/util/MostSpecificComparatorTest.java
@@ -16,9 +16,10 @@ 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 {
+public class MostSpecificComparatorTest extends GerritBaseTests {
private MostSpecificComparator cmp;
diff --git a/javatests/com/google/gerrit/server/util/git/BUILD b/javatests/com/google/gerrit/server/util/git/BUILD
index a816d80fed..0cb7b8aee9 100644
--- a/javatests/com/google/gerrit/server/util/git/BUILD
+++ b/javatests/com/google/gerrit/server/util/git/BUILD
@@ -10,10 +10,11 @@ junit_tests(
deps = [
"//java/com/google/gerrit/reviewdb:server",
"//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:gwtorm",
"//lib:protobuf",
"//lib/auto:auto-value",
"//lib/auto:auto-value-annotations",
diff --git a/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java b/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java
index 0ec9b3844e..c5e683fefe 100644
--- a/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java
+++ b/javatests/com/google/gerrit/server/util/git/SubmoduleSectionParserTest.java
@@ -20,11 +20,12 @@ 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 java.util.Set;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
-public class SubmoduleSectionParserTest {
+public class SubmoduleSectionParserTest extends GerritBaseTests {
private static final String THIS_SERVER = "http://localhost/";
@Test
diff --git a/javatests/com/google/gerrit/sshd/BUILD b/javatests/com/google/gerrit/sshd/BUILD
index 3e11ff22e2..c010d7c4d3 100644
--- a/javatests/com/google/gerrit/sshd/BUILD
+++ b/javatests/com/google/gerrit/sshd/BUILD
@@ -6,6 +6,7 @@ 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 777cb4fb1e..b663849419 100644
--- a/javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
+++ b/javatests/com/google/gerrit/sshd/commands/ProjectConfigParamParserTest.java
@@ -17,12 +17,13 @@ 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 {
+public class ProjectConfigParamParserTest extends GerritBaseTests {
private CreateProjectCommand cmd;
diff --git a/java/com/google/gerrit/testing/GerritJUnitTest.java b/javatests/com/google/gerrit/testing/GerritJUnitTest.java
index 430f48f832..430f48f832 100644
--- a/java/com/google/gerrit/testing/GerritJUnitTest.java
+++ b/javatests/com/google/gerrit/testing/GerritJUnitTest.java
diff --git a/javatests/com/google/gerrit/util/http/BUILD b/javatests/com/google/gerrit/util/http/BUILD
index 48b4339ad8..8f6afbb070 100644
--- a/javatests/com/google/gerrit/util/http/BUILD
+++ b/javatests/com/google/gerrit/util/http/BUILD
@@ -4,10 +4,11 @@ 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-3_1-without-neverlink",
+ "//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 48b7b9c95b..adda5e7092 100644
--- a/javatests/com/google/gerrit/util/http/RequestUtilTest.java
+++ b/javatests/com/google/gerrit/util/http/RequestUtilTest.java
@@ -15,42 +15,54 @@
package com.google.gerrit.util.http;
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 {
+public class RequestUtilTest extends GerritBaseTests {
@Test
- public void emptyContextPath() {
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("", "/s", "/foo/bar")))
- .isEqualTo("/foo/bar");
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("", "/s", "/foo%2Fbar")))
- .isEqualTo("/foo%2Fbar");
+ public void getEncodedPathInfo_emptyContextPath() {
+ assertThat(getEncodedPathInfo(fakeRequest("", "/s", "/foo/bar"))).isEqualTo("/foo/bar");
+ assertThat(getEncodedPathInfo(fakeRequest("", "/s", "/foo%2Fbar"))).isEqualTo("/foo%2Fbar");
}
@Test
- public void emptyServletPath() {
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("", "/c", "/foo/bar")))
- .isEqualTo("/foo/bar");
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("", "/c", "/foo%2Fbar")))
- .isEqualTo("/foo%2Fbar");
+ public void getEncodedPathInfo_emptyServletPath() {
+ assertThat(getEncodedPathInfo(fakeRequest("", "/c", "/foo/bar"))).isEqualTo("/foo/bar");
+ assertThat(getEncodedPathInfo(fakeRequest("", "/c", "/foo%2Fbar"))).isEqualTo("/foo%2Fbar");
}
@Test
- public void trailingSlashes() {
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("/c", "/s", "/foo/bar/")))
- .isEqualTo("/foo/bar/");
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("/c", "/s", "/foo/bar///")))
- .isEqualTo("/foo/bar/");
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("/c", "/s", "/foo%2Fbar/")))
- .isEqualTo("/foo%2Fbar/");
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("/c", "/s", "/foo%2Fbar///")))
+ public void getEncodedPathInfo_trailingSlashes() {
+ assertThat(getEncodedPathInfo(fakeRequest("/c", "/s", "/foo/bar/"))).isEqualTo("/foo/bar/");
+ assertThat(getEncodedPathInfo(fakeRequest("/c", "/s", "/foo/bar///"))).isEqualTo("/foo/bar/");
+ assertThat(getEncodedPathInfo(fakeRequest("/c", "/s", "/foo%2Fbar/"))).isEqualTo("/foo%2Fbar/");
+ assertThat(getEncodedPathInfo(fakeRequest("/c", "/s", "/foo%2Fbar///")))
.isEqualTo("/foo%2Fbar/");
}
@Test
public void emptyPathInfo() {
- assertThat(RequestUtil.getEncodedPathInfo(fakeRequest("/c", "/s", ""))).isNull();
+ assertThat(getEncodedPathInfo(fakeRequest("/c", "/s", ""))).isNull();
+ }
+
+ @Test
+ public void getRestPathWithoutIds_emptyContextPath() {
+ assertThat(getRestPathWithoutIds(fakeRequest("", "/a/accounts", "/123/test")))
+ .isEqualTo("/accounts/test");
+ assertThat(getRestPathWithoutIds(fakeRequest("", "/accounts", "/123/test")))
+ .isEqualTo("/accounts/test");
+ }
+
+ @Test
+ public void getRestPathWithoutIds_nonEmptyContextPath() {
+ assertThat(getRestPathWithoutIds(fakeRequest("/c", "/a/accounts", "/123/test")))
+ .isEqualTo("/accounts/test");
+ assertThat(getRestPathWithoutIds(fakeRequest("/c", "/accounts", "/123/test")))
+ .isEqualTo("/accounts/test");
}
private FakeHttpServletRequest fakeRequest(
diff --git a/javatests/com/google/gerrit/util/http/testutil/BUILD b/javatests/com/google/gerrit/util/http/testutil/BUILD
index 6acc7caf03..d7ac5bdf4e 100644
--- a/javatests/com/google/gerrit/util/http/testutil/BUILD
+++ b/javatests/com/google/gerrit/util/http/testutil/BUILD
@@ -7,7 +7,7 @@ java_library(
visibility = ["//visibility:public"],
deps = [
"//lib:guava",
- "//lib:servlet-api-3_1",
+ "//lib:servlet-api",
"//lib/httpcomponents:httpclient",
"//lib/jgit/org.eclipse.jgit:jgit",
],
diff --git a/javatests/com/google/gwtexpui/safehtml/BUILD b/javatests/com/google/gwtexpui/safehtml/BUILD
deleted file mode 100644
index 694f4220e7..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/BUILD
+++ /dev/null
@@ -1,13 +0,0 @@
-load("//tools/bzl:junit.bzl", "junit_tests")
-
-junit_tests(
- name = "safehtml_tests",
- srcs = glob(["client/**/*.java"]),
- deps = [
- "//java/com/google/gwtexpui/safehtml",
- "//lib:guava",
- "//lib/gwt:dev",
- "//lib/gwt:user",
- "//lib/truth",
- ],
-)
diff --git a/javatests/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java b/javatests/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java
deleted file mode 100644
index a77c5b4fed..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/LinkFindReplaceTest.java
+++ /dev/null
@@ -1,80 +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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class LinkFindReplaceTest {
- @Rule public ExpectedException exception = ExpectedException.none();
-
- @Test
- public void noEscaping() {
- String find = "find";
- String link = "link";
- LinkFindReplace a = new LinkFindReplace(find, link);
- assertThat(a.pattern().getSource()).isEqualTo(find);
- assertThat(a.replace(find)).isEqualTo("<a href=\"link\">find</a>");
- assertThat(a.toString()).isEqualTo("find = " + find + ", link = " + link);
- }
-
- @Test
- public void backreference() {
- LinkFindReplace l = new LinkFindReplace("(bug|issue)\\s*([0-9]+)", "/bug?id=$2");
- assertThat(l.replace("issue 123")).isEqualTo("<a href=\"/bug?id=123\">issue 123</a>");
- }
-
- @Test
- public void hasValidScheme() {
- assertThat(LinkFindReplace.hasValidScheme("/absolute/path")).isTrue();
- assertThat(LinkFindReplace.hasValidScheme("relative/path")).isTrue();
- assertThat(LinkFindReplace.hasValidScheme("http://url/")).isTrue();
- assertThat(LinkFindReplace.hasValidScheme("HTTP://url/")).isTrue();
- assertThat(LinkFindReplace.hasValidScheme("https://url/")).isTrue();
- assertThat(LinkFindReplace.hasValidScheme("mailto://url/")).isTrue();
- assertThat(LinkFindReplace.hasValidScheme("ftp://url/")).isFalse();
- assertThat(LinkFindReplace.hasValidScheme("data:evil")).isFalse();
- assertThat(LinkFindReplace.hasValidScheme("javascript:alert(1)")).isFalse();
- }
-
- @Test
- public void invalidSchemeInReplace() {
- exception.expect(IllegalArgumentException.class);
- new LinkFindReplace("find", "javascript:alert(1)").replace("find");
- }
-
- @Test
- public void invalidSchemeWithBackreference() {
- exception.expect(IllegalArgumentException.class);
- new LinkFindReplace(".*(script:[^;]*)", "java$1").replace("Look at this script: alert(1);");
- }
-
- @Test
- public void replaceEscaping() {
- assertThat(new LinkFindReplace("find", "a\"&'<>b").replace("find"))
- .isEqualTo("<a href=\"a&quot;&amp;&#39;&lt;&gt;b\">find</a>");
- }
-
- @Test
- public void htmlInFind() {
- String rawFind = "<b>&quot;bold&quot;</b>";
- LinkFindReplace a = new LinkFindReplace(rawFind, "/bold");
- assertThat(a.pattern().getSource()).isEqualTo(rawFind);
- assertThat(a.replace(rawFind)).isEqualTo("<a href=\"/bold\">" + rawFind + "</a>");
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java b/javatests/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java
deleted file mode 100644
index 3b5e769040..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/RawFindReplaceTest.java
+++ /dev/null
@@ -1,31 +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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-public class RawFindReplaceTest {
- @Test
- public void findReplace() {
- final String find = "find";
- final String replace = "replace";
- final RawFindReplace a = new RawFindReplace(find, replace);
- assertThat(a.pattern().getSource()).isEqualTo(find);
- assertThat(a.replace(find)).isEqualTo(replace);
- assertThat(a.toString()).isEqualTo("find = " + find + ", replace = " + replace);
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java
deleted file mode 100644
index 9a2dbe3889..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtmlBuilderTest.java
+++ /dev/null
@@ -1,290 +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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class SafeHtmlBuilderTest {
- @Rule public ExpectedException exception = ExpectedException.none();
-
- @Test
- public void empty() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b.isEmpty()).isTrue();
- assertThat(b.hasContent()).isFalse();
- assertThat(b.asString()).isEmpty();
-
- b.append("a");
- assertThat(b.hasContent()).isTrue();
- assertThat(b.asString()).isEqualTo("a");
- }
-
- @Test
- public void toSafeHtml() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.append(1);
-
- final SafeHtml h = b.toSafeHtml();
- assertThat(h).isNotNull();
- assertThat(h).isNotSameAs(b);
- assertThat(h).isNotInstanceOf(SafeHtmlBuilder.class);
- assertThat(h.asString()).isEqualTo("1");
- }
-
- @Test
- public void append_boolean() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append(true));
- assertThat(b).isSameAs(b.append(false));
- assertThat(b.asString()).isEqualTo("truefalse");
- }
-
- @Test
- public void append_char() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append('a'));
- assertThat(b).isSameAs(b.append('b'));
- assertThat(b.asString()).isEqualTo("ab");
- }
-
- @Test
- public void append_int() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append(4));
- assertThat(b).isSameAs(b.append(2));
- assertThat(b).isSameAs(b.append(-100));
- assertThat(b.asString()).isEqualTo("42-100");
- }
-
- @Test
- public void append_long() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append(4L));
- assertThat(b).isSameAs(b.append(2L));
- assertThat(b.asString()).isEqualTo("42");
- }
-
- @Test
- public void append_float() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append(0.0f));
- assertThat(b.asString()).isEqualTo("0.0");
- }
-
- @Test
- public void append_double() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append(0.0));
- assertThat(b.asString()).isEqualTo("0.0");
- }
-
- @Test
- public void append_String() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append((String) null));
- assertThat(b.asString()).isEmpty();
- assertThat(b).isSameAs(b.append("foo"));
- assertThat(b).isSameAs(b.append("bar"));
- assertThat(b.asString()).isEqualTo("foobar");
- }
-
- @Test
- public void append_StringBuilder() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append((StringBuilder) null));
- assertThat(b.asString()).isEmpty();
- assertThat(b).isSameAs(b.append(new StringBuilder("foo")));
- assertThat(b).isSameAs(b.append(new StringBuilder("bar")));
- assertThat(b.asString()).isEqualTo("foobar");
- }
-
- @Test
- public void append_StringBuffer() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append((StringBuffer) null));
- assertThat(b.asString()).isEmpty();
- assertThat(b).isSameAs(b.append(new StringBuffer("foo")));
- assertThat(b).isSameAs(b.append(new StringBuffer("bar")));
- assertThat(b.asString()).isEqualTo("foobar");
- }
-
- @Test
- public void append_Object() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append((Object) null));
- assertThat(b.asString()).isEmpty();
- assertThat(b)
- .isSameAs(
- b.append(
- new Object() {
- @Override
- public String toString() {
- return "foobar";
- }
- }));
- assertThat(b.asString()).isEqualTo("foobar");
- }
-
- @Test
- public void append_CharSequence() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append((CharSequence) null));
- assertThat(b.asString()).isEmpty();
- assertThat(b).isSameAs(b.append((CharSequence) "foo"));
- assertThat(b).isSameAs(b.append((CharSequence) "bar"));
- assertThat(b.asString()).isEqualTo("foobar");
- }
-
- @Test
- public void append_SafeHtml() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.append((SafeHtml) null));
- assertThat(b.asString()).isEmpty();
- assertThat(b).isSameAs(b.append(new SafeHtmlString("foo")));
- assertThat(b).isSameAs(b.append(new SafeHtmlBuilder().append("bar")));
- assertThat(b.asString()).isEqualTo("foobar");
- }
-
- @Test
- public void htmlSpecialCharacters() {
- assertThat(escape("&")).isEqualTo("&amp;");
- assertThat(escape("<")).isEqualTo("&lt;");
- assertThat(escape(">")).isEqualTo("&gt;");
- assertThat(escape("\"")).isEqualTo("&quot;");
- assertThat(escape("'")).isEqualTo("&#39;");
-
- assertThat(escape('&')).isEqualTo("&amp;");
- assertThat(escape('<')).isEqualTo("&lt;");
- assertThat(escape('>')).isEqualTo("&gt;");
- assertThat(escape('"')).isEqualTo("&quot;");
- assertThat(escape('\'')).isEqualTo("&#39;");
-
- assertThat(escape("<b>")).isEqualTo("&lt;b&gt;");
- assertThat(escape("&lt;b&gt;")).isEqualTo("&amp;lt;b&amp;gt;");
- }
-
- @Test
- public void entityNbsp() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.nbsp());
- assertThat(b.asString()).isEqualTo("&nbsp;");
- }
-
- @Test
- public void tagBr() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.br());
- assertThat(b.asString()).isEqualTo("<br />");
- }
-
- @Test
- public void tagTableTrTd() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.openElement("table"));
- assertThat(b).isSameAs(b.openTr());
- assertThat(b).isSameAs(b.openTd());
- assertThat(b).isSameAs(b.append("d<a>ta"));
- assertThat(b).isSameAs(b.closeTd());
- assertThat(b).isSameAs(b.closeTr());
- assertThat(b).isSameAs(b.closeElement("table"));
- assertThat(b.asString()).isEqualTo("<table><tr><td>d&lt;a&gt;ta</td></tr></table>");
- }
-
- @Test
- public void tagDiv() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.openDiv());
- assertThat(b).isSameAs(b.append("d<a>ta"));
- assertThat(b).isSameAs(b.closeDiv());
- assertThat(b.asString()).isEqualTo("<div>d&lt;a&gt;ta</div>");
- }
-
- @Test
- public void tagAnchor() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.openAnchor());
-
- assertThat(b.getAttribute("href")).isEmpty();
- assertThat(b).isSameAs(b.setAttribute("href", "http://here"));
- assertThat(b.getAttribute("href")).isEqualTo("http://here");
- assertThat(b).isSameAs(b.setAttribute("href", "d<a>ta"));
- assertThat(b.getAttribute("href")).isEqualTo("d<a>ta");
-
- assertThat(b.getAttribute("target")).isEmpty();
- assertThat(b).isSameAs(b.setAttribute("target", null));
- assertThat(b.getAttribute("target")).isEmpty();
-
- assertThat(b).isSameAs(b.append("go"));
- assertThat(b).isSameAs(b.closeAnchor());
- assertThat(b.asString()).isEqualTo("<a href=\"d&lt;a&gt;ta\">go</a>");
- }
-
- @Test
- public void tagHeightWidth() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.openElement("img"));
- assertThat(b).isSameAs(b.setHeight(100));
- assertThat(b).isSameAs(b.setWidth(42));
- assertThat(b).isSameAs(b.closeSelf());
- assertThat(b.asString()).isEqualTo("<img height=\"100\" width=\"42\" />");
- }
-
- @Test
- public void styleName() {
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- assertThat(b).isSameAs(b.openSpan());
- assertThat(b).isSameAs(b.setStyleName("foo"));
- assertThat(b).isSameAs(b.addStyleName("bar"));
- assertThat(b).isSameAs(b.append("d<a>ta"));
- assertThat(b).isSameAs(b.closeSpan());
- assertThat(b.asString()).isEqualTo("<span class=\"foo bar\">d&lt;a&gt;ta</span>");
- }
-
- @Test
- public void rejectJavaScript_AnchorHref() {
- final String href = "javascript:window.close();";
- exception.expect(RuntimeException.class);
- exception.expectMessage("javascript unsafe in href: " + href);
- new SafeHtmlBuilder().openAnchor().setAttribute("href", href);
- }
-
- @Test
- public void rejectJavaScript_ImgSrc() {
- final String href = "javascript:window.close();";
- exception.expect(RuntimeException.class);
- exception.expectMessage("javascript unsafe in href: " + href);
- new SafeHtmlBuilder().openElement("img").setAttribute("src", href);
- }
-
- @Test
- public void rejectJavaScript_FormAction() {
- final String href = "javascript:window.close();";
- exception.expect(RuntimeException.class);
- exception.expectMessage("javascript unsafe in href: " + href);
- new SafeHtmlBuilder().openElement("form").setAttribute("action", href);
- }
-
- private static String escape(char c) {
- return new SafeHtmlBuilder().append(c).asString();
- }
-
- private static String escape(String c) {
- return new SafeHtmlBuilder().append(c).asString();
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java
deleted file mode 100644
index b42878beed..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_LinkifyTest.java
+++ /dev/null
@@ -1,124 +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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-public class SafeHtml_LinkifyTest {
- @Test
- public void linkify_SimpleHttp1() {
- final SafeHtml o = html("A http://go.here/ B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a> B");
- }
-
- @Test
- public void linkify_SimpleHttps2() {
- final SafeHtml o = html("A https://go.here/ B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"https://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">https://go.here/</a> B");
- }
-
- @Test
- public void linkify_Parens1() {
- final SafeHtml o = html("A (http://go.here/) B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A (<a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a>) B");
- }
-
- @Test
- public void linkify_Parens() {
- final SafeHtml o = html("A http://go.here/#m() B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"http://go.here/#m()\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/#m()</a> B");
- }
-
- @Test
- public void linkify_AngleBrackets1() {
- final SafeHtml o = html("A <http://go.here/> B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A &lt;<a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a>&gt; B");
- }
-
- @Test
- public void linkify_TrailingPlainLetter() {
- final SafeHtml o = html("A http://go.here/foo B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"http://go.here/foo\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/foo</a> B");
- }
-
- @Test
- public void linkify_TrailingDot() {
- final SafeHtml o = html("A http://go.here/. B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a>. B");
- }
-
- @Test
- public void linkify_TrailingComma() {
- final SafeHtml o = html("A http://go.here/, B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a>, B");
- }
-
- @Test
- public void linkify_TrailingDotDot() {
- final SafeHtml o = html("A http://go.here/.. B");
- final SafeHtml n = o.linkify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A <a href=\"http://go.here/.\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/.</a>. B");
- }
-
- private static SafeHtml html(String text) {
- return new SafeHtmlBuilder().append(text).toSafeHtml();
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java
deleted file mode 100644
index ac0f6fd69a..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_ReplaceTest.java
+++ /dev/null
@@ -1,127 +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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import org.junit.Test;
-
-public class SafeHtml_ReplaceTest {
- @Test
- public void replaceEmpty() {
- SafeHtml o = html("A\nissue42\nB");
- assertThat(o.replaceAll(null)).isSameAs(o);
- assertThat(o.replaceAll(Collections.<FindReplace>emptyList())).isSameAs(o);
- }
-
- @Test
- public void replaceOneLink() {
- SafeHtml o = html("A\nissue 42\nB");
- SafeHtml n =
- o.replaceAll(repls(new RawFindReplace("(issue\\s(\\d+))", "<a href=\"?$2\">$1</a>")));
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("A\n<a href=\"?42\">issue 42</a>\nB");
- }
-
- @Test
- public void replaceNoLeadingOrTrailingText() {
- SafeHtml o = html("issue 42");
- SafeHtml n =
- o.replaceAll(repls(new RawFindReplace("(issue\\s(\\d+))", "<a href=\"?$2\">$1</a>")));
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("<a href=\"?42\">issue 42</a>");
- }
-
- @Test
- public void replaceTwoLinks() {
- SafeHtml o = html("A\nissue 42\nissue 9918\nB");
- SafeHtml n =
- o.replaceAll(repls(new RawFindReplace("(issue\\s(\\d+))", "<a href=\"?$2\">$1</a>")));
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo("A\n<a href=\"?42\">issue 42</a>\n<a href=\"?9918\">issue 9918</a>\nB");
- }
-
- @Test
- public void replaceInOrder() {
- SafeHtml o = html("A\nissue 42\nReally GWTEXPUI-9918 is better\nB");
- SafeHtml n =
- o.replaceAll(
- repls(
- new RawFindReplace("(GWTEXPUI-(\\d+))", "<a href=\"gwtexpui-bug?$2\">$1</a>"),
- new RawFindReplace("(issue\\s+(\\d+))", "<a href=\"generic-bug?$2\">$1</a>")));
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "A\n"
- + "<a href=\"generic-bug?42\">issue 42</a>\n"
- + "Really <a href=\"gwtexpui-bug?9918\">GWTEXPUI-9918</a> is better\n"
- + "B");
- }
-
- @Test
- public void replaceOverlappingAfterFirstChar() {
- SafeHtml o = html("abcd");
- RawFindReplace ab = new RawFindReplace("ab", "AB");
- RawFindReplace bc = new RawFindReplace("bc", "23");
- RawFindReplace cd = new RawFindReplace("cd", "YZ");
-
- assertThat(o.replaceAll(repls(ab, bc)).asString()).isEqualTo("ABcd");
- assertThat(o.replaceAll(repls(bc, ab)).asString()).isEqualTo("ABcd");
- assertThat(o.replaceAll(repls(ab, bc, cd)).asString()).isEqualTo("ABYZ");
- }
-
- @Test
- public void replaceOverlappingAtFirstCharLongestMatch() {
- SafeHtml o = html("abcd");
- RawFindReplace ab = new RawFindReplace("ab", "AB");
- RawFindReplace abc = new RawFindReplace("[^d][^d][^d]", "234");
-
- assertThat(o.replaceAll(repls(ab, abc)).asString()).isEqualTo("ABcd");
- assertThat(o.replaceAll(repls(abc, ab)).asString()).isEqualTo("234d");
- }
-
- @Test
- public void replaceOverlappingAtFirstCharFirstMatch() {
- SafeHtml o = html("abcd");
- RawFindReplace ab1 = new RawFindReplace("ab", "AB");
- RawFindReplace ab2 = new RawFindReplace("[^cd][^cd]", "12");
-
- assertThat(o.replaceAll(repls(ab1, ab2)).asString()).isEqualTo("ABcd");
- assertThat(o.replaceAll(repls(ab2, ab1)).asString()).isEqualTo("12cd");
- }
-
- @Test
- public void failedSanitization() {
- SafeHtml o = html("abcd");
- LinkFindReplace evil = new LinkFindReplace("(b)", "javascript:alert('$1')");
- LinkFindReplace ok = new LinkFindReplace("(b)", "/$1");
- assertThat(o.replaceAll(repls(evil)).asString()).isEqualTo("abcd");
- String linked = "a<a href=\"/b\">b</a>cd";
- assertThat(o.replaceAll(repls(ok)).asString()).isEqualTo(linked);
- assertThat(o.replaceAll(repls(evil, ok)).asString()).isEqualTo(linked);
- }
-
- private static SafeHtml html(String text) {
- return new SafeHtmlBuilder().append(text).toSafeHtml();
- }
-
- private static List<FindReplace> repls(FindReplace... repls) {
- return Arrays.asList(repls);
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java
deleted file mode 100644
index d69b36cacf..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyListTest.java
+++ /dev/null
@@ -1,125 +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 "<p>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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-public class SafeHtml_WikifyListTest {
- private static final String BEGIN_LIST = "<ul class=\"wikiList\">";
- private static final String END_LIST = "</ul>";
-
- private static String item(String raw) {
- return "<li>" + raw + "</li>";
- }
-
- @Test
- public void bulletList1() {
- final SafeHtml o = html("A\n\n* line 1\n* 2nd line");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo("<p>A</p>" + BEGIN_LIST + item("line 1") + item("2nd line") + END_LIST);
- }
-
- @Test
- public void bulletList2() {
- final SafeHtml o = html("A\n\n* line 1\n* 2nd line\n\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A</p>" + BEGIN_LIST + item("line 1") + item("2nd line") + END_LIST + "<p>B</p>");
- }
-
- @Test
- public void bulletList3() {
- final SafeHtml o = html("* line 1\n* 2nd line\n\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(BEGIN_LIST + item("line 1") + item("2nd line") + END_LIST + "<p>B</p>");
- }
-
- @Test
- public void bulletList4() {
- final SafeHtml o =
- html(
- "To see this bug, you have to:\n" //
- + "* Be on IMAP or EAS (not on POP)\n" //
- + "* Be very unlucky\n");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>To see this bug, you have to:</p>"
- + BEGIN_LIST
- + item("Be on IMAP or EAS (not on POP)")
- + item("Be very unlucky")
- + END_LIST);
- }
-
- @Test
- public void bulletList5() {
- final SafeHtml o =
- html(
- "To see this bug,\n" //
- + "you have to:\n" //
- + "* Be on IMAP or EAS (not on POP)\n" //
- + "* Be very unlucky\n");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>To see this bug, you have to:</p>"
- + BEGIN_LIST
- + item("Be on IMAP or EAS (not on POP)")
- + item("Be very unlucky")
- + END_LIST);
- }
-
- @Test
- public void dashList1() {
- final SafeHtml o = html("A\n\n- line 1\n- 2nd line");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo("<p>A</p>" + BEGIN_LIST + item("line 1") + item("2nd line") + END_LIST);
- }
-
- @Test
- public void dashList2() {
- final SafeHtml o = html("A\n\n- line 1\n- 2nd line\n\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A</p>" + BEGIN_LIST + item("line 1") + item("2nd line") + END_LIST + "<p>B</p>");
- }
-
- @Test
- public void dashList3() {
- final SafeHtml o = html("- line 1\n- 2nd line\n\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(BEGIN_LIST + item("line 1") + item("2nd line") + END_LIST + "<p>B</p>");
- }
-
- private static SafeHtml html(String text) {
- return new SafeHtmlBuilder().append(text).toSafeHtml();
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java
deleted file mode 100644
index 1346cda72b..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyPreformatTest.java
+++ /dev/null
@@ -1,81 +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 "<p>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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-public class SafeHtml_WikifyPreformatTest {
- private static final String B = "<span class=\"wikiPreFormat\">";
- private static final String E = "</span><br />";
-
- private static String pre(String raw) {
- return B + raw + E;
- }
-
- @Test
- public void preformat1() {
- final SafeHtml o = html("A\n\n This is pre\n formatted");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo("<p>A</p><p>" + pre(" This is pre") + pre(" formatted") + "</p>");
- }
-
- @Test
- public void preformat2() {
- final SafeHtml o = html("A\n\n This is pre\n formatted\n\nbut this is not");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A</p>"
- + "<p>"
- + pre(" This is pre")
- + pre(" formatted")
- + "</p>"
- + "<p>but this is not</p>");
- }
-
- @Test
- public void preformat3() {
- final SafeHtml o = html("A\n\n Q\n <R>\n S\n\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A</p>"
- + "<p>"
- + pre(" Q")
- + pre(" &lt;R&gt;")
- + pre(" S")
- + "</p>"
- + "<p>B</p>");
- }
-
- @Test
- public void preformat4() {
- final SafeHtml o = html(" Q\n <R>\n S\n\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo("<p>" + pre(" Q") + pre(" &lt;R&gt;") + pre(" S") + "</p><p>B</p>");
- }
-
- private static SafeHtml html(String text) {
- return new SafeHtmlBuilder().append(text).toSafeHtml();
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyQuoteTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyQuoteTest.java
deleted file mode 100644
index 20084475bd..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyQuoteTest.java
+++ /dev/null
@@ -1,56 +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 "<p>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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-public class SafeHtml_WikifyQuoteTest {
- private static final String B = "<blockquote class=\"wikiQuote\">";
- private static final String E = "</blockquote>";
-
- private static String quote(String raw) {
- return B + raw + E;
- }
-
- @Test
- public void quote1() {
- final SafeHtml o = html("> I'm happy\n > with quotes!\n\nSee above.");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo(quote("I&#39;m happy\nwith quotes!") + "<p>See above.</p>");
- }
-
- @Test
- public void quote2() {
- final SafeHtml o = html("See this said:\n\n > a quoted\n > string block\n\nOK?");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo("<p>See this said:</p>" + quote("a quoted\nstring block") + "<p>OK?</p>");
- }
-
- @Test
- public void nestedQuotes1() {
- final SafeHtml o = html(" > > prior\n > \n > next\n");
- final SafeHtml n = o.wikify();
- assertThat(n.asString()).isEqualTo(quote(quote("prior") + "next\n"));
- }
-
- private static SafeHtml html(String text) {
- return new SafeHtmlBuilder().append(text).toSafeHtml();
- }
-}
diff --git a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java b/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java
deleted file mode 100644
index 166af97ffc..0000000000
--- a/javatests/com/google/gwtexpui/safehtml/client/SafeHtml_WikifyTest.java
+++ /dev/null
@@ -1,120 +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 "<p>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.gwtexpui.safehtml.client;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-public class SafeHtml_WikifyTest {
- @Test
- public void wikify_OneLine1() {
- final SafeHtml o = html("A B");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("<p>A B</p>");
- }
-
- @Test
- public void wikify_OneLine2() {
- final SafeHtml o = html("A B\n");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("<p>A B\n</p>");
- }
-
- @Test
- public void wikify_OneParagraph1() {
- final SafeHtml o = html("A\nB");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("<p>A\nB</p>");
- }
-
- @Test
- public void wikify_OneParagraph2() {
- final SafeHtml o = html("A\nB\n");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("<p>A\nB\n</p>");
- }
-
- @Test
- public void wikify_TwoParagraphs() {
- final SafeHtml o = html("A\nB\n\nC\nD");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString()).isEqualTo("<p>A\nB</p><p>C\nD</p>");
- }
-
- @Test
- public void linkify_SimpleHttp1() {
- final SafeHtml o = html("A http://go.here/ B");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A <a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a> B</p>");
- }
-
- @Test
- public void linkify_SimpleHttps2() {
- final SafeHtml o = html("A https://go.here/ B");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A <a href=\"https://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">https://go.here/</a> B</p>");
- }
-
- @Test
- public void linkify_Parens1() {
- final SafeHtml o = html("A (http://go.here/) B");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A (<a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a>) B</p>");
- }
-
- @Test
- public void linkify_Parens() {
- final SafeHtml o = html("A http://go.here/#m() B");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A <a href=\"http://go.here/#m()\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/#m()</a> B</p>");
- }
-
- @Test
- public void linkify_AngleBrackets1() {
- final SafeHtml o = html("A <http://go.here/> B");
- final SafeHtml n = o.wikify();
- assertThat(o).isNotSameAs(n);
- assertThat(n.asString())
- .isEqualTo(
- "<p>A &lt;<a href=\"http://go.here/\" target=\"_blank\" rel=\"nofollow\""
- + ">http://go.here/</a>&gt; B</p>");
- }
-
- private static SafeHtml html(String text) {
- return new SafeHtmlBuilder().append(text).toSafeHtml();
- }
-}
diff --git a/javatests/org/eclipse/jgit/BUILD b/javatests/org/eclipse/jgit/BUILD
deleted file mode 100644
index 49ae496ac8..0000000000
--- a/javatests/org/eclipse/jgit/BUILD
+++ /dev/null
@@ -1,13 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_test")
-
-java_test(
- name = "jgit_patch_tests",
- srcs = glob(["**/*.java"]),
- test_class = "org.eclipse.jgit.diff.EditDeserializerTest",
- visibility = ["//visibility:public"],
- deps = [
- "//java/org/eclipse/jgit:server",
- "//lib:junit",
- "//lib/jgit/org.eclipse.jgit:jgit",
- ],
-)
diff --git a/javatests/org/eclipse/jgit/diff/EditDeserializerTest.java b/javatests/org/eclipse/jgit/diff/EditDeserializerTest.java
deleted file mode 100644
index c431715d48..0000000000
--- a/javatests/org/eclipse/jgit/diff/EditDeserializerTest.java
+++ /dev/null
@@ -1,26 +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 org.eclipse.jgit.diff;
-
-import static org.junit.Assert.assertNotNull;
-
-import org.junit.Test;
-
-public class EditDeserializerTest {
- @Test
- public void diffDeserializer() {
- assertNotNull("edit deserializer", new EditDeserializer());
- }
-}
diff --git a/lib/BUILD b/lib/BUILD
index 77b2fcdc3d..2ea8f54214 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -14,32 +14,18 @@ filegroup(
)
java_library(
- name = "servlet-api-3_1",
+ name = "servlet-api",
data = ["//lib:LICENSE-Apache2.0"],
neverlink = 1,
visibility = ["//visibility:public"],
- exports = ["@servlet-api-3_1//jar"],
+ exports = ["@servlet-api//jar"],
)
java_library(
- name = "servlet-api-3_1-without-neverlink",
+ name = "servlet-api-without-neverlink",
data = ["//lib:LICENSE-Apache2.0"],
visibility = ["//visibility:public"],
- exports = ["@servlet-api-3_1//jar"],
-)
-
-java_library(
- name = "gwtjsonrpc",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@gwtjsonrpc//jar"],
-)
-
-java_library(
- name = "gwtjsonrpc_src",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@gwtjsonrpc//jar:src"],
+ exports = ["@servlet-api//jar"],
)
java_library(
@@ -50,20 +36,6 @@ java_library(
)
java_library(
- name = "gwtorm-client",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@gwtorm-client//jar"],
-)
-
-java_library(
- name = "gwtorm-client_src",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@gwtorm-client//jar:src"],
-)
-
-java_library(
name = "protobuf",
data = ["//lib:LICENSE-protobuf"],
visibility = ["//visibility:public"],
@@ -71,14 +43,10 @@ java_library(
)
java_library(
- name = "gwtorm",
+ name = "guava-failureaccess",
+ data = ["//lib:LICENSE-Apache2.0"],
visibility = ["//visibility:public"],
- exports = [":gwtorm-client"],
- runtime_deps = [
- ":protobuf",
- "//lib/antlr:java-runtime",
- "//lib/ow2:ow2-asm",
- ],
+ exports = ["@guava-failureaccess//jar"],
)
java_library(
@@ -93,6 +61,7 @@ java_library(
data = ["//lib:LICENSE-Apache2.0"],
visibility = ["//visibility:public"],
exports = [
+ ":guava-failureaccess",
":j2objc",
"@guava//jar",
],
@@ -480,13 +449,6 @@ java_library(
)
java_library(
- name = "derby",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@derby//jar"],
-)
-
-java_library(
name = "soy",
data = ["//lib:LICENSE-Apache2.0"],
visibility = ["//visibility:public"],
@@ -523,13 +485,6 @@ java_library(
exports = ["@icu4j//jar"],
)
-java_library(
- name = "postgresql",
- data = ["//lib:LICENSE-postgresql"],
- visibility = ["//visibility:public"],
- exports = ["@postgresql//jar"],
-)
-
sh_test(
name = "nongoogle_test",
srcs = ["nongoogle_test.sh"],
diff --git a/lib/LICENSE-clippy b/lib/LICENSE-clippy
deleted file mode 100644
index b0feeaeb65..0000000000
--- a/lib/LICENSE-clippy
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2008 Tom Preston-Werner
-
-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.
diff --git a/lib/LICENSE-codemirror-original b/lib/LICENSE-codemirror-original
deleted file mode 100644
index 766132177a..0000000000
--- a/lib/LICENSE-codemirror-original
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (C) 2016 by Marijn Haverbeke <marijnh@gmail.com> and others
-
-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.
diff --git a/lib/LICENSE-commonmark b/lib/LICENSE-commonmark
new file mode 100644
index 0000000000..3fecb98dda
--- /dev/null
+++ b/lib/LICENSE-commonmark
@@ -0,0 +1,23 @@
+Copyright (c) 2015-2016, Atlassian Pty Ltd
+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.
+
+ 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 HOLDER 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/LICENSE-diffy b/lib/LICENSE-diffy
deleted file mode 100644
index e837a1a418..0000000000
--- a/lib/LICENSE-diffy
+++ /dev/null
@@ -1 +0,0 @@
-link:http://creativecommons.org/licenses/by/3.0/[CC-BY 3.0] (c) Sara Owens, inkylabs.com
diff --git a/lib/LICENSE-mockito b/lib/LICENSE-mockito
new file mode 100644
index 0000000000..5a311f7c5c
--- /dev/null
+++ b/lib/LICENSE-mockito
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2007 Mockito contributors
+
+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.
diff --git a/lib/LICENSE-postgresql b/lib/LICENSE-postgresql
deleted file mode 100644
index fd416d2ec4..0000000000
--- a/lib/LICENSE-postgresql
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright (c) 1997-2011, PostgreSQL Global Development Group
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-2. 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.
-3. Neither the name of the PostgreSQL Global Development Group 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/LICENSE-silk_icons b/lib/LICENSE-silk_icons
deleted file mode 100644
index b7417f6521..0000000000
--- a/lib/LICENSE-silk_icons
+++ /dev/null
@@ -1,338 +0,0 @@
-link:http://creativecommons.org/licenses/by/3.0/[CC-BY 3.0] (c) Mark James, link:http://famfamfam.com/lab/icons/silk/[SILK ICONS]
-
-As an author, I would appreciate a reference to my authorship of the Silk
-icon set contents within a readme file or equivalent documentation for
-the software which includes the set or a subset of the icons contained
-within.
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
-CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
-PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
-WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
-PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
-AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS
-LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU
-THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH
-TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work
- and other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and
- includes cinematographic adaptations or any other form in which
- the Work may be recast, transformed, or adapted including in any
- form recognizably derived from the original, except that a work
- that constitutes a Collection will not be considered an
- Adaptation for the purpose of this License. For the avoidance
- of doubt, where the Work is a musical work, performance or
- phonogram, the synchronization of the Work in timed-relation
- with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
-
- b. "Collection" means a collection of literary or artistic works,
- such as encyclopedias and anthologies, or performances,
- phonograms or broadcasts, or other works or subject matter other
- than works listed in Section 1(f) below, which, by reason of the
- selection and arrangement of their contents, constitute
- intellectual creations, in which the Work is included in its
- entirety in unmodified form along with one or more other
- contributions, each constituting separate and independent works
- in themselves, which together are assembled into a collective
- whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of
- this License.
-
- c. "Distribute" means to make available to the public the original
- and copies of the Work or Adaptation, as appropriate, through
- sale or other transfer of ownership.
-
- d. "Licensor" means the individual, individuals, entity or entities
- that offer(s) the Work under the terms of this License.
-
- e. "Original Author" means, in the case of a literary or artistic
- work, the individual, individuals, entity or entities who
- created the Work or if no individual or entity can be
- identified, the publisher; and in addition (i) in the case of a
- performance the actors, singers, musicians, dancers, and other
- persons who act, sing, deliver, declaim, play in, interpret or
- otherwise perform literary or artistic works or expressions of
- folklore; (ii) in the case of a phonogram the producer being the
- person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of
- broadcasts, the organization that transmits the broadcast.
-
- f. "Work" means the literary and/or artistic work offered under the
- terms of this License including without limitation any
- production in the literary, scientific and artistic domain,
- whatever may be the mode or form of its expression including
- digital form, such as a book, pamphlet and other writing; a
- lecture, address, sermon or other work of the same nature; a
- dramatic or dramatico-musical work; a choreographic work or
- entertainment in dumb show; a musical composition with or
- without words; a cinematographic work to which are assimilated
- works expressed by a process analogous to cinematography; a work
- of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of
- applied art; an illustration, map, plan, sketch or
- three-dimensional work relative to geography, topography,
- architecture or science; a performance; a broadcast; a
- phonogram; a compilation of data to the extent it is protected
- as a copyrightable work; or a work performed by a variety or
- circus performer to the extent it is not otherwise considered a
- literary or artistic work.
-
- g. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this
- License with respect to the Work, or who has received express
- permission from the Licensor to exercise rights under this
- License despite a previous violation.
-
- h. "Publicly Perform" means to perform public recitations of the
- Work and to communicate to the public those public recitations,
- by any means or process, including by wire or wireless means or
- public digital performances; to make available to the public
- Works in such a way that members of the public may access these
- Works from a place and at a place individually chosen by them;
- to perform the Work to the public by any means or process and
- the communication to the public of the performances of the Work,
- including by public digital performance; to broadcast and
- rebroadcast the Work by any means including signs, sounds or
- images.
-
- i. "Reproduce" means to make copies of the Work by any means
- including without limitation by sound or visual recordings and
- the right of fixation and reproducing fixations of the Work,
- including storage of a protected performance or phonogram in
- digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to
- reduce, limit, or restrict any uses free from copyright or rights
- arising from limitations or exceptions that are provided for in
- connection with the copyright protection under copyright law or
- other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this
- License, Licensor hereby grants You a worldwide, royalty-free,
- non-exclusive, perpetual (for the duration of the applicable
- copyright) license to exercise the rights in the Work as stated
- below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections;
-
- b. to create and Reproduce Adaptations provided that any such
- Adaptation, including any translation in any medium, takes
- reasonable steps to clearly label, demarcate or otherwise
- identify that changes were made to the original Work. For
- example, a translation could be marked "The original work was
- translated from English to Spanish," or a modification could
- indicate "The original work has been modified.";
-
- c. to Distribute and Publicly Perform the Work including as
- incorporated in Collections; and,
-
- d. to Distribute and Publicly Perform Adaptations.
-
- e. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those
- jurisdictions in which the right to collect royalties
- through any statutory or compulsory licensing scheme
- cannot be waived, the Licensor reserves the exclusive
- right to collect such royalties for any exercise by You
- of the rights granted under this License;
-
- ii. Waivable Compulsory License Schemes. In those jurisdictions
- in which the right to collect royalties through any
- statutory or compulsory licensing scheme can be waived,
- the Licensor waives the exclusive right to collect such
- royalties for any exercise by You of the rights granted
- under this License; and,
-
- iii. Voluntary License Schemes. The Licensor waives the right to
- collect royalties, whether individually or, in the event
- that the Licensor is a member of a collecting society
- that administers voluntary licensing schemes, via that
- society, from any exercise by You of the rights granted
- under this License.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to
-make such modifications as are technically necessary to exercise the
-rights in other media and formats. Subject to Section 8(f), all
-rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly
- made subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the
- terms of this License. You must include a copy of, or the
- Uniform Resource Identifier (URI) for, this License with every
- copy of the Work You Distribute or Publicly Perform. You may
- not offer or impose any terms on the Work that restrict the
- terms of this License or the ability of the recipient of the
- Work to exercise the rights granted to that recipient under the
- terms of the License. You may not sublicense the Work. You
- must keep intact all notices that refer to this License and to
- the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of
- the Work from You to exercise the rights granted to that
- recipient under the terms of the License. This Section 4(a)
- applies to the Work as incorporated in a Collection, but this
- does not require the Collection apart from the Work itself to be
- made subject to the terms of this License. If You create a
- Collection, upon notice from any Licensor You must, to the
- extent practicable, remove from the Collection any credit as
- required by Section 4(b), as requested. If You create an
- Adaptation, upon notice from any Licensor You must, to the
- extent practicable, remove from the Adaptation any credit as
- required by Section 4(b), as requested.
-
- b. If You Distribute, or Publicly Perform the Work or any
- Adaptations or Collections, You must, unless a request has been
- made pursuant to Section 4(a), keep intact all copyright notices
- for the Work and provide, reasonable to the medium or means You
- are utilizing: (i) the name of the Original Author (or
- pseudonym, if applicable) if supplied, and/or if the Original
- Author and/or Licensor designate another party or parties (e.g.,
- a sponsor institute, publishing entity, journal) for attribution
- ("Attribution Parties") in Licensor's copyright notice, terms of
- service or by other reasonable means, the name of such party or
- parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does
- not refer to the copyright notice or licensing information for
- the Work; and (iv) , consistent with Section 3(b), in the case
- of an Adaptation, a credit identifying the use of the Work in
- the Adaptation (e.g., "French translation of the Work by
- Original Author," or "Screenplay based on original Work by
- Original Author"). The credit required by this Section 4 (b)
- may be implemented in any reasonable manner; provided, however,
- that in the case of a Adaptation or Collection, at a minimum
- such credit will appear, if a credit for all contributing
- authors of the Adaptation or Collection appears, then as part of
- these credits and in a manner at least as prominent as the
- credits for the other contributing authors. For the avoidance
- of doubt, You may only use the credit required by this Section
- for the purpose of attribution in the manner set out above and,
- by exercising Your rights under this License, You may not
- implicitly or explicitly assert or imply any connection with,
- sponsorship or endorsement by the Original Author, Licensor
- and/or Attribution Parties, as appropriate, of You or Your use
- of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
-
- c. Except as otherwise agreed in writing by the Licensor or as may
- be otherwise permitted by applicable law, if You Reproduce,
- Distribute or Publicly Perform the Work either by itself or as
- part of any Adaptations or Collections, You must not distort,
- mutilate, modify or take other derogatory action in relation to
- the Work which would be prejudicial to the Original Author's
- honor or reputation. Licensor agrees that in those
- jurisdictions (e.g. Japan), in which any exercise of the right
- granted in Section 3(b) of this License (the right to make
- Adaptations) would be deemed to be a distortion, mutilation,
- modification or other derogatory action prejudicial to the
- Original Author's honor and reputation, the Licensor will waive
- or not assert, as appropriate, this Section, to the fullest
- extent permitted by the applicable national law, to enable You
- to reasonably exercise Your right under Section 3(b) of this
- License (right to make Adaptations) but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
-LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR
-WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
-STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF
-TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
-NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY,
-OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES,
-SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
- APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
- LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
- OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
- WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this
- License. Individuals or entities who have received Adaptations
- or Collections from You under this License, however, will not
- have their licenses terminated provided such individuals or
- entities remain in full compliance with those licenses.
- Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
- this License.
-
- b. Subject to the above terms and conditions, the license granted
- here is perpetual (for the duration of the applicable copyright
- in the Work). Notwithstanding the above, Licensor reserves the
- right to release the Work under different license terms or to
- stop distributing the Work at any time; provided, however that
- any such election will not serve to withdraw this License (or
- any other license that has been, or is required to be, granted
- under the terms of this License), and this License will continue
- in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a
- Collection, the Licensor offers to the recipient a license to
- the Work on the same terms and conditions as the license granted
- to You under this License.
-
- b. Each time You Distribute or Publicly Perform an Adaptation,
- Licensor offers to the recipient a license to the original Work
- on the same terms and conditions as the license granted to You
- under this License.
-
- c. If any provision of this License is invalid or unenforceable
- under applicable law, it shall not affect the validity or
- enforceability of the remainder of the terms of this License,
- and without further action by the parties to this agreement,
- such provision shall be reformed to the minimum extent necessary
- to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and
- no breach consented to unless such waiver or consent shall be in
- writing and signed by the party to be charged with such waiver
- or consent.
-
- e. This License constitutes the entire agreement between the
- parties with respect to the Work licensed here. There are no
- understandings, agreements or representations with respect to
- the Work not specified here. Licensor shall not be bound by any
- additional provisions that may appear in any communication from
- You. This License may not be modified without the mutual
- written agreement of the Licensor and You.
-
- f. The rights granted under, and the subject matter referenced, in
- this License were drafted utilizing the terminology of the Berne
- Convention for the Protection of Literary and Artistic Works (as
- amended on September 28, 1979), the Rome Convention of 1961, the
- WIPO Copyright Treaty of 1996, the WIPO Performances and
- Phonograms Treaty of 1996 and the Universal Copyright Convention
- (as revised on July 24, 1971). These rights and subject matter
- take effect in the relevant jurisdiction in which the License
- terms are sought to be enforced according to the corresponding
- provisions of the implementation of those treaty provisions in
- the applicable national law. If the standard suite of rights
- granted under applicable copyright law includes additional
- rights not granted under this License, such additional rights
- are deemed to be included in the License; this License is not
- intended to restrict the license of any rights under applicable
- law.
diff --git a/lib/codemirror/BUILD b/lib/codemirror/BUILD
deleted file mode 100644
index 05ca920eb6..0000000000
--- a/lib/codemirror/BUILD
+++ /dev/null
@@ -1,12 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-load("//lib/codemirror:cm.bzl", "pkg_cm")
-
-# This library is only used to insert a license statement into
-# js_licenses.txt.
-java_library(
- name = "diff-match-patch",
- data = ["//lib:LICENSE-Apache2.0"],
- runtime_deps = ["@diff-match-patch//jar"],
-)
-
-pkg_cm()
diff --git a/lib/codemirror/cm.bzl b/lib/codemirror/cm.bzl
deleted file mode 100644
index 3e1589a9a3..0000000000
--- a/lib/codemirror/cm.bzl
+++ /dev/null
@@ -1,376 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_import")
-load("//tools/bzl:genrule2.bzl", "genrule2")
-
-CM_CSS = [
- "lib/codemirror.css",
- "addon/dialog/dialog.css",
- "addon/merge/merge.css",
- "addon/scroll/simplescrollbars.css",
- "addon/search/matchesonscrollbar.css",
- "addon/lint/lint.css",
-]
-
-CM_JS = [
- "lib/codemirror.js",
- "mode/meta.js",
- "keymap/emacs.js",
- "keymap/sublime.js",
- "keymap/vim.js",
-]
-
-CM_ADDONS = [
- "dialog/dialog.js",
- "edit/closebrackets.js",
- "edit/matchbrackets.js",
- "edit/trailingspace.js",
- "scroll/annotatescrollbar.js",
- "scroll/simplescrollbars.js",
- "search/jump-to-line.js",
- "search/matchesonscrollbar.js",
- "search/searchcursor.js",
- "search/search.js",
- "selection/mark-selection.js",
- "mode/multiplex.js",
- "mode/overlay.js",
- "mode/simple.js",
- "lint/lint.js",
-]
-
-# Available themes must be enumerated here,
-# in gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/Theme.java,
-# in gerrit-gwtui/src/main/java/net/codemirror/theme/Themes.java
-CM_THEMES = [
- "3024-day",
- "3024-night",
- "abcdef",
- "ambiance",
- "base16-dark",
- "base16-light",
- "bespin",
- "blackboard",
- "cobalt",
- "colorforth",
- "dracula",
- "duotone-dark",
- "duotone-light",
- "eclipse",
- "elegant",
- "erlang-dark",
- "gruvbox-dark",
- "hopscotch",
- "icecoder",
- "idea",
- "isotope",
- "lesser-dark",
- "liquibyte",
- "lucario",
- "material",
- "mbo",
- "mdn-like",
- "midnight",
- "monokai",
- "neat",
- "neo",
- "night",
- "paraiso-dark",
- "paraiso-light",
- "pastel-on-dark",
- "railscasts",
- "rubyblue",
- "seti",
- "solarized",
- "ssms",
- "the-matrix",
- "tomorrow-night-bright",
- "tomorrow-night-eighties",
- "ttcn",
- "twilight",
- "vibrant-ink",
- "xq-dark",
- "xq-light",
- "yeti",
- "zenburn",
-]
-
-# Available modes must be enumerated here,
-# in gerrit-gwtui/src/main/java/net/codemirror/mode/Modes.java,
-# gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java,
-# and in CodeMirror's own mode/meta.js script.
-CM_MODES = [
- "apl",
- "asciiarmor",
- "asn.1",
- "asterisk",
- "brainfuck",
- "clike",
- "clojure",
- "cmake",
- "cobol",
- "coffeescript",
- "commonlisp",
- "crystal",
- "css",
- "cypher",
- "d",
- "dart",
- "diff",
- "django",
- "dockerfile",
- "dtd",
- "dylan",
- "ebnf",
- "ecl",
- "eiffel",
- "elm",
- "erlang",
- "factor",
- "fcl",
- "forth",
- "fortran",
- "gas",
- "gfm",
- "gherkin",
- "go",
- "groovy",
- "haml",
- "handlebars",
- "haskell-literate",
- "haskell",
- "haxe",
- "htmlembedded",
- "htmlmixed",
- "http",
- "idl",
- "javascript",
- "jinja2",
- "jsx",
- "julia",
- "livescript",
- "lua",
- "markdown",
- "mathematica",
- "mbox",
- "mirc",
- "mllike",
- "modelica",
- "mscgen",
- "mumps",
- "nginx",
- "nsis",
- "ntriples",
- "octave",
- "oz",
- "pascal",
- "pegjs",
- "perl",
- "php",
- "pig",
- "powershell",
- "properties",
- "protobuf",
- "pug",
- "puppet",
- "python",
- "q",
- "r",
- "rpm",
- "rst",
- "ruby",
- "rust",
- "sas",
- "sass",
- "scheme",
- "shell",
- "sieve",
- "slim",
- "smalltalk",
- "smarty",
- "solr",
- "soy",
- "sparql",
- "spreadsheet",
- "sql",
- "stex",
- "stylus",
- "swift",
- "tcl",
- "textile",
- "tiddlywiki",
- "tiki",
- "toml",
- "tornado",
- "troff",
- "ttcn-cfg",
- "ttcn",
- "turtle",
- "twig",
- "vb",
- "vbscript",
- "velocity",
- "verilog",
- "vhdl",
- "vue",
- "webidl",
- "xml",
- "xquery",
- "yacas",
- "yaml-frontmatter",
- "yaml",
- "z80",
-]
-
-CM_VERSION = "5.37.0"
-
-TOP = "META-INF/resources/webjars/codemirror/%s" % CM_VERSION
-
-TOP_MINIFIED = "META-INF/resources/webjars/codemirror-minified/%s" % CM_VERSION
-
-LICENSE = "//lib:LICENSE-codemirror-original"
-
-LICENSE_MINIFIED = "//lib:LICENSE-codemirror-minified"
-
-DIFF_MATCH_PATCH_VERSION = "20121119-1"
-
-DIFF_MATCH_PATCH_TOP = ("META-INF/resources/webjars/google-diff-match-patch/%s" %
- DIFF_MATCH_PATCH_VERSION)
-
-def pkg_cm():
- for archive, suffix, top, license in [
- ("@codemirror-original-gwt//jar", "", TOP, LICENSE),
- ("@codemirror-minified-gwt//jar", "_r", TOP_MINIFIED, LICENSE_MINIFIED),
- ]:
- # Main JavaScript and addons
- genrule2(
- name = "cm" + suffix,
- cmd = " && ".join(
- [
- "echo '/** @license' >$@",
- "unzip -p $(location %s) %s/LICENSE >>$@" % (archive, top),
- "echo '*/' >>$@",
- ] +
- ["unzip -p $(location %s) %s/%s >>$@" % (archive, top, n) for n in CM_JS] +
- [
- "unzip -p $(location %s) %s/addon/%s >>$@" % (archive, top, n)
- for n in CM_ADDONS
- ],
- ),
- tools = [archive],
- outs = ["cm%s.js" % suffix],
- )
-
- # Main CSS
- genrule2(
- name = "css" + suffix,
- cmd = " && ".join(
- [
- "echo '/** @license' >$@",
- "unzip -p $(location %s) %s/LICENSE >>$@" % (archive, top),
- "echo '*/' >>$@",
- ] +
- [
- "unzip -p $(location %s) %s/%s >>$@" % (archive, top, n)
- for n in CM_CSS
- ],
- ),
- tools = [archive],
- outs = ["cm%s.css" % suffix],
- )
-
- # Modes
- for n in CM_MODES:
- genrule2(
- name = "mode_%s%s" % (n, suffix),
- cmd = " && ".join(
- [
- "echo '/** @license' >$@",
- "unzip -p $(location %s) %s/LICENSE >>$@" % (archive, top),
- "echo '*/' >>$@",
- "unzip -p $(location %s) %s/mode/%s/%s.js >>$@" % (archive, top, n, n),
- ],
- ),
- tools = [archive],
- outs = ["mode_%s%s.js" % (n, suffix)],
- )
-
- # Themes
- for n in CM_THEMES:
- genrule2(
- name = "theme_%s%s" % (n, suffix),
- cmd = " && ".join(
- [
- "echo '/** @license' >$@",
- "unzip -p $(location %s) %s/LICENSE >>$@" % (archive, top),
- "echo '*/' >>$@",
- "unzip -p $(location %s) %s/theme/%s.css >>$@" % (archive, top, n),
- ],
- ),
- tools = [archive],
- outs = ["theme_%s%s.css" % (n, suffix)],
- )
-
- # Merge Addon bundled with diff-match-patch
- genrule2(
- name = "addon_merge_with_diff_match_patch%s" % suffix,
- cmd = " && ".join(
- [
- "echo '/** @license' >$@",
- "unzip -p $(location %s) %s/LICENSE >>$@" % (archive, top),
- "echo '*/\n' >>$@",
- "echo '// The google-diff-match-patch library is from https://repo1.maven.org/maven2/org/webjars/google-diff-match-patch/%s/google-diff-match-patch-%s.jar\n' >> $@" % (DIFF_MATCH_PATCH_VERSION, DIFF_MATCH_PATCH_VERSION),
- "echo '/** @license' >>$@",
- "echo 'LICENSE-Apache2.0' >>$@",
- "echo '*/' >>$@",
- "unzip -p $(location @diff-match-patch//jar) %s/diff_match_patch.js >>$@" % DIFF_MATCH_PATCH_TOP,
- "echo ';' >> $@",
- "unzip -p $(location %s) %s/addon/merge/merge.js >>$@" % (archive, top),
- ],
- ),
- tools = [
- "@diff-match-patch//jar",
- # dependency just for license tracking.
- ":diff-match-patch",
- archive,
- "//lib:LICENSE-Apache2.0",
- ],
- outs = ["addon_merge_with_diff_match_patch%s.js" % suffix],
- )
-
- # Jar packaging
- genrule2(
- name = "jar" + suffix,
- cmd = " && ".join([
- "cd $$TMP",
- "mkdir -p net/codemirror/{addon,lib,mode,theme}",
- "cp $$ROOT/$(location :css%s) net/codemirror/lib/cm.css" % suffix,
- "cp $$ROOT/$(location :cm%s) net/codemirror/lib/cm.js" % suffix,
- ] +
- [
- "cp $$ROOT/$(location :mode_%s%s) net/codemirror/mode/%s.js" % (n, suffix, n)
- for n in CM_MODES
- ] +
- [
- "cp $$ROOT/$(location :theme_%s%s) net/codemirror/theme/%s.css" % (n, suffix, n)
- for n in CM_THEMES
- ] +
- ["cp $$ROOT/$(location :addon_merge_with_diff_match_patch%s) net/codemirror/addon/merge_bundled.js" % suffix] +
- ["zip -qr $$ROOT/$@ net/codemirror/{addon,lib,mode,theme}"]),
- tools = [
- ":addon_merge_with_diff_match_patch%s" % suffix,
- ":cm%s" % suffix,
- ":css%s" % suffix,
- ] + [
- ":mode_%s%s" % (n, suffix)
- for n in CM_MODES
- ] + [
- ":theme_%s%s" % (n, suffix)
- for n in CM_THEMES
- ],
- outs = ["codemirror%s.jar" % suffix],
- )
-
- java_import(
- name = "codemirror" + suffix,
- jars = [":jar%s" % suffix],
- visibility = ["//visibility:public"],
- data = [license],
- )
diff --git a/lib/commons/BUILD b/lib/commons/BUILD
index 7cf2f1f99a..38b1b6de84 100644
--- a/lib/commons/BUILD
+++ b/lib/commons/BUILD
@@ -46,6 +46,13 @@ java_library(
)
java_library(
+ name = "text",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@commons-text//jar"],
+)
+
+java_library(
name = "validator",
data = ["//lib:LICENSE-Apache2.0"],
exports = ["@commons-validator//jar"],
diff --git a/lib/gitiles/BUILD b/lib/gitiles/BUILD
new file mode 100644
index 0000000000..6e0380170a
--- /dev/null
+++ b/lib/gitiles/BUILD
@@ -0,0 +1,58 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+ name = "gitiles",
+ visibility = ["//visibility:public"],
+ exports = [
+ ":cm-autolink",
+ ":commonmark",
+ ":gfm-strikethrough",
+ ":gfm-tables",
+ ":gitiles-servlet",
+ ":prettify",
+ "//lib/commons:lang3",
+ "//lib/commons:text",
+ ],
+)
+
+java_library(
+ name = "cm-autolink",
+ data = ["//lib:LICENSE-commonmark"],
+ visibility = ["//visibility:public"],
+ exports = ["@cm-autolink//jar"],
+)
+
+java_library(
+ name = "commonmark",
+ data = ["//lib:LICENSE-commonmark"],
+ visibility = ["//visibility:public"],
+ exports = ["@commonmark//jar"],
+)
+
+java_library(
+ name = "gfm-strikethrough",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@gfm-strikethrough//jar"],
+)
+
+java_library(
+ name = "gfm-tables",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@gfm-tables//jar"],
+)
+
+java_library(
+ name = "gitiles-servlet",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@gitiles-servlet//jar"],
+)
+
+java_library(
+ name = "prettify",
+ data = ["//lib:LICENSE-Apache2.0"],
+ visibility = ["//visibility:public"],
+ exports = ["@prettify//jar"],
+)
diff --git a/lib/greenmail/BUILD b/lib/greenmail/BUILD
index 0587287ced..e8845e2628 100644
--- a/lib/greenmail/BUILD
+++ b/lib/greenmail/BUILD
@@ -6,7 +6,7 @@ POST_JDK8_DEPS = [":javax-activation"]
java_library(
name = "javax-activation",
- testonly = 1,
+ testonly = True,
data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
exports = ["@javax-activation//jar"],
)
diff --git a/lib/guava.bzl b/lib/guava.bzl
index 1add718bff..c36bf14c2f 100644
--- a/lib/guava.bzl
+++ b/lib/guava.bzl
@@ -1,5 +1,5 @@
-GUAVA_VERSION = "26.0-jre"
+GUAVA_VERSION = "27.1-jre"
-GUAVA_BIN_SHA1 = "6a806eff209f36f635f943e16d97491f00f6bfab"
+GUAVA_BIN_SHA1 = "e47b59c893079b87743cdcfb6f17ca95c08c592c"
GUAVA_DOC_URL = "https://google.github.io/guava/releases/" + GUAVA_VERSION + "/api/docs/"
diff --git a/lib/gwt/BUILD b/lib/gwt/BUILD
deleted file mode 100644
index 5606647a84..0000000000
--- a/lib/gwt/BUILD
+++ /dev/null
@@ -1,47 +0,0 @@
-load("@rules_java//java:defs.bzl", "java_library")
-
-[java_library(
- name = n,
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@%s//jar" % n],
-) for n in [
- "ant",
- "colt",
- "dev",
- "javax-validation",
- "jsinterop-annotations",
- "tapestry",
- "user",
- "w3c-css-sac",
-]]
-
-java_library(
- name = "user-neverlink",
- data = ["//lib:LICENSE-Apache2.0"],
- neverlink = 1,
- visibility = ["//visibility:public"],
- exports = ["@user//jar"],
-)
-
-java_library(
- name = "dev-neverlink",
- data = ["//lib:LICENSE-Apache2.0"],
- neverlink = 1,
- visibility = ["//visibility:public"],
- exports = ["@dev//jar"],
-)
-
-java_library(
- name = "javax-validation_src",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@javax-validation//jar:src"],
-)
-
-java_library(
- name = "jsinterop-annotations_src",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@jsinterop-annotations//jar:src"],
-)
diff --git a/lib/jetty/BUILD b/lib/jetty/BUILD
index 1664a5c0f3..641738597d 100644
--- a/lib/jetty/BUILD
+++ b/lib/jetty/BUILD
@@ -17,13 +17,6 @@ java_library(
)
java_library(
- name = "servlets",
- data = ["//lib:LICENSE-Apache2.0"],
- visibility = ["//visibility:public"],
- exports = ["@jetty-servlets//jar"],
-)
-
-java_library(
name = "server",
data = ["//lib:LICENSE-Apache2.0"],
visibility = ["//visibility:public"],
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index 997879edf1..db0e1e153f 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,6 +1,6 @@
load("//tools/bzl:maven_jar.bzl", "MAVEN_CENTRAL", "maven_jar")
-_JGIT_VERS = "5.1.13.202002110435-r"
+_JGIT_VERS = "5.3.7.202002110540-r"
_DOC_VERS = _JGIT_VERS # Set to _JGIT_VERS unless using a snapshot
@@ -40,29 +40,25 @@ def jgit_maven_repos():
name = "jgit-lib",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "dc99e6ef37038090561bd5253c71b150791bea58",
- src_sha1 = "882b62cac802a26e5fe383cd1a16f8b2d557e2fe",
- unsign = True,
+ sha1 = "b1714d4917750d6fad0d19d3b0e258b373db819a",
)
maven_jar(
name = "jgit-servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "5860d7828839db9a41b664488540704f783d0280",
- unsign = True,
+ sha1 = "cf61e6e00a758a6f33995e53883aede76d3b2400",
)
maven_jar(
name = "jgit-archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "fb8538a090daa5dfb6674cdf860dd34d6a4078ed",
+ sha1 = "3c0b259040d3bc3a9e884a301055cf4f2e1bb1e2",
)
maven_jar(
name = "jgit-junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "1e45351d6c19f65135ee4b693c0346772307671d",
- unsign = True,
+ sha1 = "f78409fb808c5a108c629ec3cba74cc6c14ebff2",
)
def jgit_dep(name):
@@ -70,7 +66,6 @@ def jgit_dep(name):
"@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-lib//jar:src": "@jgit//org.eclipse.jgit:libjgit-src.jar",
"@jgit-servlet//jar": "@jgit//org.eclipse.jgit.http.server:jgit-servlet",
}
diff --git a/lib/jgit/org.eclipse.jgit/BUILD b/lib/jgit/org.eclipse.jgit/BUILD
index d6e0c5db38..c1f260740f 100644
--- a/lib/jgit/org.eclipse.jgit/BUILD
+++ b/lib/jgit/org.eclipse.jgit/BUILD
@@ -12,12 +12,6 @@ java_library(
],
)
-alias(
- name = "jgit-source",
- actual = jgit_dep("@jgit-lib//jar:src"),
- visibility = ["//visibility:public"],
-)
-
java_library(
name = "javaewah",
data = ["//lib:LICENSE-Apache2.0"],
diff --git a/lib/js/bower_archives.bzl b/lib/js/bower_archives.bzl
index a7ce585537..1597c02848 100644
--- a/lib/js/bower_archives.bzl
+++ b/lib/js/bower_archives.bzl
@@ -124,8 +124,8 @@ def load_bower_archives():
bower_archive(
name = "paper-icon-button",
package = "PolymerElements/paper-icon-button",
- version = "2.2.0",
- sha1 = "9525e76ef433428bb9d6ec4fa65c4ef83156a803",
+ version = "2.2.1",
+ sha1 = "68f76af3a9379f256a3900a4b68d871898f1fe57",
)
bower_archive(
name = "paper-ripple",
diff --git a/lib/mockito/BUILD b/lib/mockito/BUILD
new file mode 100644
index 0000000000..cff36cd019
--- /dev/null
+++ b/lib/mockito/BUILD
@@ -0,0 +1,37 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+ default_testonly = True,
+ default_visibility = ["//visibility:private"],
+)
+
+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__"],
+ exports = ["@mockito//jar"],
+ runtime_deps = [
+ ":byte-buddy",
+ ":byte-buddy-agent",
+ ":objenesis",
+ ],
+)
+
+java_library(
+ name = "byte-buddy",
+ data = ["//lib:LICENSE-Apache2.0"],
+ exports = ["@byte-buddy//jar"],
+)
+
+java_library(
+ name = "byte-buddy-agent",
+ data = ["//lib:LICENSE-Apache2.0"],
+ exports = ["@byte-buddy-agent//jar"],
+)
+
+java_library(
+ name = "objenesis",
+ data = ["//lib:LICENSE-Apache2.0"],
+ exports = ["@objenesis//jar"],
+)
diff --git a/lib/nongoogle_test.sh b/lib/nongoogle_test.sh
index 492c603a29..b04a454154 100755
--- a/lib/nongoogle_test.sh
+++ b/lib/nongoogle_test.sh
@@ -16,6 +16,9 @@ dropwizard-core
duct-tape
eddsa
elasticsearch-rest-client
+flogger
+flogger-log4j-backend
+flogger-system-backend
httpasyncclient
httpcore-nio
j2objc
diff --git a/package.json b/package.json
index 740eb4edee..db09e9c4c2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gerrit",
- "version": "2.16.16-SNAPSHOT",
+ "version": "3.0.7-SNAPSHOT",
"description": "Gerrit Code Review",
"dependencies": {},
"devDependencies": {
diff --git a/plugins/BUILD b/plugins/BUILD
index a33ce56b56..b9776ead8e 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -32,16 +32,20 @@ EXPORTS = [
"//antlr3:query_parser",
"//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/git",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index/project",
"//java/com/google/gerrit/index:query_exception",
+ "//java/com/google/gerrit/json",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/lucene",
"//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/server/api",
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/cache/mem",
"//java/com/google/gerrit/server/cache/serialize",
@@ -51,6 +55,8 @@ EXPORTS = [
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/http",
"//java/com/google/gerrit/util/logging",
+ "//lib/antlr:java-runtime",
+ "//lib/auto:auto-value-annotations",
"//lib/commons:compress",
"//lib/commons:dbcp",
"//lib/commons:lang",
@@ -78,12 +84,11 @@ EXPORTS = [
"//lib:guava",
"//lib:guava-retrying",
"//lib:gson",
- "//lib:gwtorm",
"//lib:icu4j",
"//lib:jsch",
"//lib:mime-util",
"//lib:protobuf",
- "//lib:servlet-api-3_1-without-neverlink",
+ "//lib:servlet-api-without-neverlink",
"//lib:soy",
"//prolog:gerrit-prolog-common",
]
diff --git a/plugins/delete-project b/plugins/delete-project
new file mode 160000
+Subproject 8b22484caeaf118bce493a46130f6a23f779cac
diff --git a/plugins/gitiles b/plugins/gitiles
new file mode 160000
+Subproject 0f6ffbecc51a0571c8a77248b2655a834700d6a
diff --git a/plugins/hooks b/plugins/hooks
-Subproject e7769bd52c2b709d7712a386aa6cb96aec2c11a
+Subproject 089687bdcc64b003d09a77f00eaa77bb79b15b9
diff --git a/plugins/plugin-manager b/plugins/plugin-manager
new file mode 160000
+Subproject d3b2a6eabcb641e952f253e61b927cd1f7f6e30
diff --git a/plugins/replication b/plugins/replication
-Subproject 53e083fd0f17d1403b4d150e66655907c1ea139
+Subproject 8650a78b2682c90247435cfd807f0d0bacc84f9
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
-Subproject ba3a422f1909f4970c563212d45cce70d494f60
+Subproject be5037839d987a319dac2236e9c1221d4d31848
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
-Subproject 2a38a8413c25fec910fb843932aa869647bdc1e
+Subproject 8a3b6faeaebc0f9b7d1af19eb0022b32994e476
diff --git a/plugins/webhooks b/plugins/webhooks
new file mode 160000
+Subproject 8f7a232ef94f60b20c059fff7066d0d1864bec8
diff --git a/polygerrit-ui/BUILD b/polygerrit-ui/BUILD
index 3988f956be..0e9b4bb3f5 100644
--- a/polygerrit-ui/BUILD
+++ b/polygerrit-ui/BUILD
@@ -57,11 +57,8 @@ go_binary(
data = [
":fonts.zip",
"//polygerrit-ui/app:test_components.zip",
- "//resources/com/google/gerrit/httpd/raw",
],
deps = [
- "@com_github_robfig_soy//:go_default_library",
- "@com_github_robfig_soy//soyhtml:go_default_library",
"@org_golang_x_tools//godoc/vfs/httpfs:go_default_library",
"@org_golang_x_tools//godoc/vfs/zipfs:go_default_library",
],
diff --git a/polygerrit-ui/README.md b/polygerrit-ui/README.md
index 04a13e57bf..4dbe14678d 100644
--- a/polygerrit-ui/README.md
+++ b/polygerrit-ui/README.md
@@ -62,9 +62,9 @@ When your project is set up and works using the classic UI, run a test server
that serves PolyGerrit:
```sh
-bazel build polygerrit &&
+bazel build gerrit &&
$(bazel info output_base)/external/local_jdk/bin/java -DsourceRoot=/path/to/my/checkout \
- -jar bazel-bin/polygerrit.war daemon --polygerrit-dev \
+ -jar bazel-bin/gerrit.war daemon --polygerrit-dev \
-d ../gerrit_testsite --console-log --show-stack-trace
```
diff --git a/polygerrit-ui/app/.eslintrc.json b/polygerrit-ui/app/.eslintrc.json
index c481fd01ba..fb1a5b383e 100644
--- a/polygerrit-ui/app/.eslintrc.json
+++ b/polygerrit-ui/app/.eslintrc.json
@@ -85,7 +85,6 @@
],
"prefer-arrow-callback": "error",
"prefer-const": "error",
- "prefer-promise-reject-errors": "off",
"prefer-spread": "error",
"quote-props": ["error", "consistent-as-needed"],
"semi": [2, "always"],
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html
index 48bd10e392..174864709e 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior.html
@@ -21,27 +21,12 @@ limitations under the License.
window.Gerrit = window.Gerrit || {};
- const PROJECT_DASHBOARD_PATTERN = /\/p\/(.+)\/\+\/dashboard\/(.*)/;
- const REPO_URL_PATTERN = /^\/admin\/repos/;
- const PROJECT_URL = '/admin/projects';
/** @polymerBehavior Gerrit.BaseUrlBehavior */
Gerrit.BaseUrlBehavior = {
/** @return {string} */
getBaseUrl() {
return window.CANONICAL_PATH || '';
},
-
- computeGwtUrl(path) {
- const base = this.getBaseUrl();
- let clientPath = path.substring(base.length);
- const match = clientPath.match(PROJECT_DASHBOARD_PATTERN);
- if (match) {
- clientPath = `/projects/${match[1]},dashboards/${match[2]}`;
- }
- // Replace any '/admin/project' links to '/admin/repo'
- clientPath = clientPath.replace(REPO_URL_PATTERN, PROJECT_URL);
- return base + '/?polygerrit=0#' + clientPath;
- },
};
})(window);
</script>
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 429abe1d1c..c21e96f357 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
@@ -52,6 +52,7 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
],
@@ -66,24 +67,5 @@ limitations under the License.
test('getBaseUrl', () => {
assert.deepEqual(element.getBaseUrl(), '/r');
});
-
- test('computeGwtUrl', () => {
- assert.deepEqual(
- element.computeGwtUrl('/r/c/1/'),
- '/r/?polygerrit=0#/c/1/'
- );
- });
-
- test('computeGwtUrl for project dashboard', () => {
- assert.deepEqual(
- element.computeGwtUrl('/r/p/gerrit/proj/+/dashboard/main:default'),
- '/r/?polygerrit=0#/projects/gerrit/proj,dashboards/main:default');
- });
-
- test('computeGwtUrl for project access', () => {
- assert.deepEqual(
- element.computeGwtUrl('/r/admin/repos/demo-project,access'),
- '/r/?polygerrit=0#/admin/projects/demo-project,access');
- });
});
</script>
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 e92eb497e3..96d4a08538 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
@@ -38,6 +38,7 @@ 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 640d902069..e445a7848a 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
@@ -46,6 +46,7 @@ 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/gr-access-behavior/gr-access-behavior.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior.html
index 111662b0ba..530c76c120 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
@@ -120,6 +120,10 @@ limitations under the License.
id: 'submitAs',
name: 'Submit (On Behalf Of)',
},
+ toggleWipState: {
+ id: 'toggleWipState',
+ name: 'Toggle Work In Progress State',
+ },
viewPrivateChanges: {
id: 'viewPrivateChanges',
name: 'View Private Changes',
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 929834fdc7..0b37a0d8f6 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
@@ -38,6 +38,7 @@ 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.html b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html
index db11937b73..182d242f98 100644
--- a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html
+++ b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html
@@ -18,8 +18,6 @@ limitations under the License.
(function(window) {
'use strict';
- const ACCOUNT_CAPABILITIES = ['createProject', 'createGroup', 'viewPlugins'];
-
const ADMIN_LINKS = [{
name: 'Repositories',
noBaseUrl: true,
@@ -65,7 +63,7 @@ limitations under the License.
return Promise.resolve(this._filterLinks(link => link.viewableToAll,
getAdminMenuLinks, opt_options));
}
- return getAccountCapabilities(ACCOUNT_CAPABILITIES)
+ return getAccountCapabilities()
.then(capabilities => {
return this._filterLinks(link => {
return !link.capability ||
@@ -97,9 +95,10 @@ limitations under the License.
links.push(...getAdminMenuLinks().map(link => ({
url: link.url,
name: link.text,
+ capability: link.capability || null,
noBaseUrl: !isExernalLink(link),
view: null,
- viewableToAll: true,
+ viewableToAll: !link.capability,
target: isExernalLink(link) ? '_blank' : null,
})));
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 a1902cda5f..d2175779e1 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
@@ -41,6 +41,7 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.AdminNavBehavior,
],
@@ -307,5 +308,61 @@ limitations under the License.
testAdminLinks(account, options, expected, done);
});
});
+
+
+ suite('view plugin screen with plugin capability', () => {
+ const account = {
+ name: 'test-user',
+ };
+ let expected;
+
+ setup(() => {
+ capabilityStub.returns(Promise.resolve({pluginCapability: true}));
+ expected = {};
+ });
+
+ test('with plugin with capabilities', done => {
+ let options;
+ const generatedLinks = [
+ {text: 'without capability', url: '/without'},
+ {text: 'with capability', url: '/with', capability: 'pluginCapability'},
+ ];
+ menuLinkStub.returns(generatedLinks);
+ expected = Object.assign(expected, {
+ totalLength: 4,
+ pluginGeneratedLinks: generatedLinks,
+ });
+ testAdminLinks(account, options, expected, done);
+ });
+ });
+
+
+ suite('view plugin screen without plugin capability', () => {
+ const account = {
+ name: 'test-user',
+ };
+ let expected;
+
+ setup(() => {
+ capabilityStub.returns(Promise.resolve({}));
+ expected = {};
+ });
+
+ test('with plugin with capabilities', done => {
+ let options;
+ const generatedLinks = [
+ {text: 'without capability', url: '/without'},
+ {text: 'with capability',
+ url: '/with',
+ capability: 'pluginCapability'},
+ ];
+ menuLinkStub.returns(generatedLinks);
+ expected = Object.assign(expected, {
+ totalLength: 3,
+ pluginGeneratedLinks: [generatedLinks[0]],
+ });
+ testAdminLinks(account, options, expected, done);
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html b/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
index 3ac94fea46..820d6bc602 100644
--- a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
@@ -44,6 +44,7 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element-anon',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.AnonymousNameBehavior,
],
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 e8cbb090d0..b052d06737 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
@@ -48,6 +48,7 @@ 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-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 54b979fed2..f6c765f8f7 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
@@ -40,6 +40,7 @@ limitations under the License.
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
+ _legacyUndefinedCheck: true,
behaviors: [Gerrit.ListViewBehavior],
});
});
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 31a51a9966..a4c1e86ae1 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
@@ -238,7 +238,8 @@ limitations under the License.
return restAPI.getChangeDetail(change._number)
.then(detail => {
if (!detail) {
- return Promise.reject('Unable to check for latest patchset.');
+ const error = new Error('Unable to check for latest patchset.');
+ return Promise.reject(error);
}
const actualLatest = Gerrit.PatchSetBehavior.computeLatestPatchNum(
Gerrit.PatchSetBehavior.computeAllPatchSets(detail));
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
new file mode 100644
index 0000000000..2dc070d2b1
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/gr-repo-plugin-config-behavior/gr-repo-plugin-config-behavior.html
@@ -0,0 +1,38 @@
+<!--
+@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 this */
+ Gerrit.RepoPluginConfig = {
+ // Should be kept in sync with
+ // gerrit/java/com/google/gerrit/extensions/api/projects/ProjectConfigEntryType.java.
+ ENTRY_TYPES: {
+ ARRAY: 'ARRAY',
+ BOOLEAN: 'BOOLEAN',
+ INT: 'INT',
+ LIST: 'LIST',
+ LONG: 'LONG',
+ STRING: 'STRING',
+ },
+ PLUGIN_CONFIG_CHANGED: 'plugin-config-changed',
+ };
+})(window);
+</script>
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 8bca33935c..943e0001d5 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
@@ -51,6 +51,7 @@ 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 73dda1b461..d909e86019 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
@@ -40,6 +40,7 @@ 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 3e99d44108..df7f3cff7d 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
@@ -91,7 +91,7 @@ iron-a11y-keys-behavior's keyBindings attribute to implement the binding in the
element. An example of this is in comment threads. A diff view supports actions
on comment threads, but there may be zero or many comment threads attached at
any given point. So the shortcut is declared as doc-only by the diff view and
-by gr-app, and actually implemented by gr-diff-comment-thread.
+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.
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 15d31b7659..b4451fecd2 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
@@ -50,6 +50,7 @@ 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_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
index 49d90f03f8..6af43dc449 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
@@ -54,6 +54,7 @@ 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 bc16b39e09..6e040a3b05 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
@@ -39,6 +39,7 @@ limitations under the License.
suiteSetup(() => {
Polymer({
is: 'safe-types-element',
+ _legacyUndefinedCheck: true,
behaviors: [Gerrit.SafeTypes],
});
});
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 409fab43b1..71f8d26ecf 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,6 +39,7 @@
Polymer({
is: 'gr-access-section',
+ _legacyUndefinedCheck: true,
properties: {
capabilities: Object,
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 f1c27af6a5..9db2e34471 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,6 +19,7 @@
Polymer({
is: 'gr-admin-group-list',
+ _legacyUndefinedCheck: true,
properties: {
/**
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 fc04c34536..4d964d7bb5 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,6 +22,7 @@
Polymer({
is: 'gr-admin-view',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
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 56079e3a42..ff25377277 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
@@ -158,6 +158,7 @@ limitations under the License.
return element.reload().then(() => {
assert.equal(element._filteredLinks.length, 3);
assert.deepEqual(element._filteredLinks[1], {
+ capability: null,
url: '/internal/link/url',
name: 'internal link text',
noBaseUrl: true,
@@ -166,6 +167,7 @@ limitations under the License.
target: null,
});
assert.deepEqual(element._filteredLinks[2], {
+ capability: null,
url: 'http://external/link/url',
name: 'external link text',
noBaseUrl: false,
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 b5dcf6372f..9c0e405292 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,6 +25,7 @@
Polymer({
is: 'gr-confirm-delete-item-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 c6687ddbcd..5a4c0646ad 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,6 +22,7 @@
Polymer({
is: 'gr-create-change-dialog',
+ _legacyUndefinedCheck: true,
properties: {
repoName: String,
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 01aeb4382f..ec667ee8a9 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,6 +19,7 @@
Polymer({
is: 'gr-create-group-dialog',
+ _legacyUndefinedCheck: true,
properties: {
params: Object,
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 0557021150..aa9639b98d 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
@@ -36,7 +36,10 @@ limitations under the License.
input {
width: 20em;
}
- .hideItem {
+ /* Add css selector with #id to increase priority
+ (otherwise ".gr-form-styles section" rule wins) */
+ .hideItem,
+ #itemAnnotationSection.hideItem {
display: none;
}
</style>
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 4e9da900ba..65bb46d7d7 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,6 +24,7 @@
Polymer({
is: 'gr-create-pointer-dialog',
+ _legacyUndefinedCheck: true,
properties: {
detailType: String,
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 bb2b5f22a6..ef7edd4d94 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,6 +19,7 @@
Polymer({
is: 'gr-create-repo-dialog',
+ _legacyUndefinedCheck: true,
properties: {
params: Object,
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 bc6a5d03b4..966f3c9eca 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,6 +21,7 @@
Polymer({
is: 'gr-group-audit-log',
+ _legacyUndefinedCheck: true,
properties: {
groupId: String,
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 b7917bebcb..b2784e5a5a 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,6 +25,7 @@
Polymer({
is: 'gr-group-members',
+ _legacyUndefinedCheck: true,
properties: {
groupId: Number,
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 ef22daf8d1..45d5497c3d 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
@@ -233,9 +233,10 @@ limitations under the License.
const memberName = 'bad-name';
const alertStub = sandbox.stub();
element.addEventListener('show-alert', alertStub);
-
+ const error = new Error('error');
+ error.status = 404;
sandbox.stub(element.$.restAPI, 'saveGroupMembers',
- () => Promise.reject({status: 404}));
+ () => Promise.reject(error));
element.$.groupMemberSearchInput.text = memberName;
element.$.groupMemberSearchInput.value = 1234;
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 de16a7c4cf..2d7f9cbf18 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
@@ -32,6 +32,7 @@
Polymer({
is: 'gr-group',
+ _legacyUndefinedCheck: true,
/**
* Fired when the group name changes.
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 e33c1ec478..58a006da20 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -38,6 +38,7 @@
Polymer({
is: 'gr-permission',
+ _legacyUndefinedCheck: true,
properties: {
labels: Object,
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
new file mode 100644
index 0000000000..ca98c500de
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.html
@@ -0,0 +1,100 @@
+<!--
+@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.
+-->
+
+<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="../../../styles/gr-form-styles.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
+
+<dom-module id="gr-plugin-config-array-editor">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-form-styles">
+ .wrapper {
+ width: 30em;
+ }
+ .existingItems {
+ background: var(--table-header-background-color);
+ border: 1px solid var(--border-color);
+ border-radius: 2px;
+ }
+ gr-button {
+ float: right;
+ margin-left: .5em;
+ width: 4.5em;
+ }
+ .row {
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+ padding: .5em 0;
+ width: 100%;
+ }
+ .existingItems .row {
+ padding: .5em;
+ }
+ .existingItems .row:not(:first-of-type) {
+ border-top: 1px solid var(--border-color);
+ }
+ input {
+ flex-grow: 1;
+ }
+ .hide {
+ display: none;
+ }
+ .placeholder {
+ color: var(--deemphasized-text-color);
+ padding-top: .75em;
+ }
+ </style>
+ <div class="wrapper gr-form-styles">
+ <template is="dom-if" if="[[pluginOption.info.values.length]]">
+ <div class="existingItems">
+ <template is="dom-repeat" items="[[pluginOption.info.values]]">
+ <div class="row">
+ <span>[[item]]</span>
+ <gr-button
+ link
+ disabled$="[[disabled]]"
+ data-item="[[item]]"
+ on-tap="_handleDelete">Delete</gr-button>
+ </div>
+ </template>
+ </div>
+ </template>
+ <template is="dom-if" if="[[!pluginOption.info.values.length]]">
+ <div class="row placeholder">None configured.</div>
+ </template>
+ <div class$="row [[_computeShowInputRow(disabled)]]">
+ <input
+ is="iron-input"
+ id="input"
+ on-keydown="_handleInputKeydown"
+ bind-value="{{_newValue}}"/>
+ <gr-button
+ id="addButton"
+ disabled$="[[!_newValue.length]]"
+ link
+ on-tap="_handleAddTap">Add</gr-button>
+ </div>
+ </div>
+ </template>
+ <script src="gr-plugin-config-array-editor.js"></script>
+</dom-module>
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
new file mode 100644
index 0000000000..ab4d286cd7
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor.js
@@ -0,0 +1,90 @@
+/**
+ * @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.
+ */
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-plugin-config-array-editor',
+
+ /**
+ * Fired when the plugin config option changes.
+ *
+ * @event plugin-config-option-changed
+ */
+
+ properties: {
+ /** @type {?} */
+ pluginOption: Object,
+ /** @type {Boolean} */
+ disabled: {
+ type: Boolean,
+ computed: '_computeDisabled(pluginOption.*)',
+ },
+ /** @type {?} */
+ _newValue: {
+ type: String,
+ value: '',
+ },
+ },
+
+ _computeDisabled(record) {
+ return !(record && record.base && record.base.info &&
+ record.base.info.editable);
+ },
+
+ _handleAddTap(e) {
+ e.preventDefault();
+ this._handleAdd();
+ },
+
+ _handleInputKeydown(e) {
+ // Enter.
+ if (e.keyCode === 13) {
+ e.preventDefault();
+ this._handleAdd();
+ }
+ },
+
+ _handleAdd() {
+ if (!this._newValue.length) { return; }
+ this._dispatchChanged(
+ this.pluginOption.info.values.concat([this._newValue]));
+ this._newValue = '';
+ },
+
+ _handleDelete(e) {
+ const value = Polymer.dom(e).localTarget.dataItem;
+ this._dispatchChanged(
+ this.pluginOption.info.values.filter(str => str !== value));
+ },
+
+ _dispatchChanged(values) {
+ const {_key, info} = this.pluginOption;
+ const detail = {
+ _key,
+ info: Object.assign(info, {values}, {}),
+ notifyPath: `${_key}.values`,
+ };
+ this.dispatchEvent(
+ new CustomEvent('plugin-config-option-changed', {detail}));
+ },
+
+ _computeShowInputRow(disabled) {
+ return disabled ? 'hide' : '';
+ },
+ });
+})();
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
new file mode 100644
index 0000000000..dc3f67ecbf
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html
@@ -0,0 +1,142 @@
+<!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-plugin-config-array-editor</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"/>
+<link rel="import" href="gr-plugin-config-array-editor.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-plugin-config-array-editor></gr-plugin-config-array-editor>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-plugin-config-array-editor tests', () => {
+ let element;
+ let sandbox;
+ let dispatchStub;
+
+ const getAll = str => Polymer.dom(element.root).querySelectorAll(str);
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ element.pluginOption = {
+ _key: 'test-key',
+ info: {
+ values: [],
+ },
+ };
+ });
+
+ teardown(() => sandbox.restore());
+
+ test('_computeShowInputRow', () => {
+ assert.equal(element._computeShowInputRow(true), 'hide');
+ assert.equal(element._computeShowInputRow(false), '');
+ });
+
+ test('_computeDisabled', () => {
+ assert.isTrue(element._computeDisabled({}));
+ assert.isTrue(element._computeDisabled({base: {}}));
+ assert.isTrue(element._computeDisabled({base: {info: {}}}));
+ assert.isTrue(
+ element._computeDisabled({base: {info: {editable: false}}}));
+ assert.isFalse(
+ element._computeDisabled({base: {info: {editable: true}}}));
+ });
+
+ suite('adding', () => {
+ setup(() => {
+ dispatchStub = sandbox.stub(element, '_dispatchChanged');
+ });
+
+ test('with enter', () => {
+ element._newValue = '';
+ MockInteractions.pressAndReleaseKeyOn(element.$.input, 13); // Enter
+ flushAsynchronousOperations();
+
+ assert.isFalse(dispatchStub.called);
+ element._newValue = 'test';
+ MockInteractions.pressAndReleaseKeyOn(element.$.input, 13); // Enter
+ flushAsynchronousOperations();
+
+ assert.isTrue(dispatchStub.called);
+ assert.equal(dispatchStub.lastCall.args[0], 'test');
+ assert.equal(element._newValue, '');
+ });
+
+ test('with add btn', () => {
+ element._newValue = '';
+ MockInteractions.tap(element.$.addButton);
+ flushAsynchronousOperations();
+
+ assert.isFalse(dispatchStub.called);
+ element._newValue = 'test';
+ MockInteractions.tap(element.$.addButton);
+ flushAsynchronousOperations();
+
+ assert.isTrue(dispatchStub.called);
+ assert.equal(dispatchStub.lastCall.args[0], 'test');
+ assert.equal(element._newValue, '');
+ });
+ });
+
+ test('deleting', () => {
+ dispatchStub = sandbox.stub(element, '_dispatchChanged');
+ element.pluginOption = {info: {values: ['test', 'test2']}};
+ flushAsynchronousOperations();
+
+ const rows = getAll('.existingItems .row');
+ assert.equal(rows.length, 2);
+ const button = rows[0].querySelector('gr-button');
+
+ MockInteractions.tap(button);
+ flushAsynchronousOperations();
+
+ assert.isFalse(dispatchStub.called);
+ element.pluginOption.info.editable = true;
+ element.notifyPath('pluginOption.info.editable');
+ flushAsynchronousOperations();
+
+ MockInteractions.tap(button);
+ flushAsynchronousOperations();
+
+ assert.isTrue(dispatchStub.called);
+ assert.deepEqual(dispatchStub.lastCall.args[0], ['test2']);
+ });
+
+ test('_dispatchChanged', () => {
+ const eventStub = sandbox.stub(element, 'dispatchEvent');
+ element._dispatchChanged(['new-test-value']);
+
+ assert.isTrue(eventStub.called);
+ const {detail} = eventStub.lastCall.args[0];
+ assert.equal(detail._key, 'test-key');
+ assert.deepEqual(detail.info, {values: ['new-test-value']});
+ assert.equal(detail.notifyPath, 'test-key.values');
+ });
+ });
+</script>
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 9e4396afa3..9be526efb2 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
@@ -25,7 +25,11 @@ limitations under the License.
<dom-module id="gr-plugin-list">
<template>
<style include="shared-styles"></style>
- <style include="gr-table-styles"></style>
+ <style include="gr-table-styles">
+ .placeholder {
+ color: var(--deemphasized-text-color);
+ }
+ </style>
<gr-list-view
filter="[[_filter]]"
items-per-page="[[_pluginsPerPage]]"
@@ -37,6 +41,7 @@ limitations under the License.
<tr class="headerRow">
<th class="name topHeader">Plugin Name</th>
<th class="version topHeader">Version</th>
+ <th class="apiVersion topHeader">API Version</th>
<th class="status topHeader">Status</th>
</tr>
<tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
@@ -53,7 +58,22 @@ limitations under the License.
[[item.id]]
</template>
</td>
- <td class="version">[[item.version]]</td>
+ <td class="version">
+ <template is="dom-if" if="[[item.version]]">
+ [[item.version]]
+ </template>
+ <template is="dom-if" if="[[!item.version]]">
+ <span class="placeholder">--</span>
+ </template>
+ </td>
+ <td class="apiVersion">
+ <template is="dom-if" if="[[item.api_version]]">
+ [[item.api_version]]
+ </template>
+ <template is="dom-if" if="[[!item.api_version]]">
+ <span class="placeholder">--</span>
+ </template>
+ </td>
<td class="status">[[_status(item)]]</td>
</tr>
</template>
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 7fd3e05ea0..d6484d8d0f 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,6 +19,7 @@
Polymer({
is: 'gr-plugin-list',
+ _legacyUndefinedCheck: true,
properties: {
/**
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 9781cf74ec..e42e37476b 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
@@ -38,13 +38,18 @@ limitations under the License.
const pluginGenerator = () => {
const plugin = {
id: `test${++counter}`,
- version: '3.0-SNAPSHOT',
disabled: false,
};
if (counter !== 2) {
plugin.index_url = `plugins/test${counter}/`;
}
+ if (counter !== 3) {
+ plugin.version = `version-${counter}`;
+ }
+ if (counter !== 4) {
+ plugin.api_version = `api-version-${counter}`;
+ }
return plugin;
};
@@ -79,10 +84,11 @@ limitations under the License.
test('plugin in the list is formatted correctly', done => {
flush(() => {
- assert.equal(element._plugins[2].id, 'test3');
- assert.equal(element._plugins[2].index_url, 'plugins/test3/');
- assert.equal(element._plugins[2].version, '3.0-SNAPSHOT');
- assert.equal(element._plugins[2].disabled, false);
+ assert.equal(element._plugins[4].id, 'test5');
+ assert.equal(element._plugins[4].index_url, 'plugins/test5/');
+ assert.equal(element._plugins[4].version, 'version-5');
+ assert.equal(element._plugins[4].api_version, 'api-version-5');
+ assert.equal(element._plugins[4].disabled, false);
done();
});
});
@@ -98,6 +104,25 @@ limitations under the License.
});
});
+ test('versions', done => {
+ flush(() => {
+ const versions = Polymer.dom(element.root).querySelectorAll('.version');
+ assert.equal(versions[2].innerText, 'version-2');
+ assert.equal(versions[3].innerText, '--');
+ done();
+ });
+ });
+
+ test('api versions', done => {
+ flush(() => {
+ const apiVersions = Polymer.dom(element.root).querySelectorAll(
+ '.apiVersion');
+ assert.equal(apiVersions[3].innerText, 'api-version-3');
+ assert.equal(apiVersions[4].innerText, '--');
+ done();
+ });
+ });
+
test('_shownPlugins', () => {
assert.equal(element._shownPlugins.length, 25);
});
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 f7ddb23289..1b8b322c14 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,6 +70,7 @@
Polymer({
is: 'gr-repo-access',
+ _legacyUndefinedCheck: true,
properties: {
repo: {
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 bcdb7f6ac5..e0becaf68c 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,6 +19,7 @@
Polymer({
is: 'gr-repo-command',
+ _legacyUndefinedCheck: true,
properties: {
title: String,
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 17d89a840a..09c902621d 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,6 +28,7 @@
Polymer({
is: 'gr-repo-commands',
+ _legacyUndefinedCheck: true,
properties: {
params: Object,
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 a620ba1df6..f72e986384 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,6 +19,7 @@
Polymer({
is: 'gr-repo-dashboards',
+ _legacyUndefinedCheck: true,
properties: {
repo: {
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 c576d7e1d8..e4000e064c 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,6 +26,7 @@
Polymer({
is: 'gr-repo-detail-list',
+ _legacyUndefinedCheck: true,
properties: {
/**
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 0eaa496c08..d4eb29bf15 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,6 +19,7 @@
Polymer({
is: 'gr-repo-list',
+ _legacyUndefinedCheck: true,
properties: {
/**
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
new file mode 100644
index 0000000000..7f2cbe7a25
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.html
@@ -0,0 +1,109 @@
+<!--
+@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.
+-->
+
+<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">
+<link rel="import" href="../../../styles/gr-subpage-styles.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
+<link rel="import" href="../../shared/gr-select/gr-select.html">
+<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
+<link rel="import" href="../gr-plugin-config-array-editor/gr-plugin-config-array-editor.html">
+
+<dom-module id="gr-repo-plugin-config">
+ <template>
+ <style include="shared-styles"></style>
+ <style include="gr-form-styles"></style>
+ <style include="gr-subpage-styles">
+ .inherited {
+ color: var(--deemphasized-text-color);
+ margin-left: .5em;
+ }
+ section.section:not(.ARRAY) .title {
+ align-items: center;
+ display: flex;
+ }
+ section.section.ARRAY .title {
+ padding-top: .75em;
+ }
+ </style>
+ <div class="gr-form-styles">
+ <fieldset>
+ <h4>[[pluginData.name]]</h4>
+ <template is="dom-repeat" items="[[_pluginConfigOptions]]" as="option">
+ <section class$="section [[option.info.type]]">
+ <span class="title">
+ <gr-tooltip-content
+ has-tooltip="[[option.info.description]]"
+ show-icon="[[option.info.description]]"
+ title="[[option.info.description]]">
+ <span>[[option.info.display_name]]</span>
+ </gr-tooltip-content>
+ </span>
+ <span class="value">
+ <template is="dom-if" if="[[_isArray(option.info.type)]]">
+ <gr-plugin-config-array-editor
+ on-plugin-config-option-changed="_handleArrayChange"
+ plugin-option="[[option]]"></gr-plugin-config-array-editor>
+ </template>
+ <template is="dom-if" if="[[_isBoolean(option.info.type)]]">
+ <paper-toggle-button
+ checked="[[_computeChecked(option.info.value)]]"
+ on-change="_handleBooleanChange"
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]"></paper-toggle-button>
+ </template>
+ <template is="dom-if" if="[[_isList(option.info.type)]]">
+ <gr-select
+ bind-value$="[[option.info.value]]"
+ on-change="_handleListChange">
+ <select
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]">
+ <template is="dom-repeat"
+ items="[[option.info.permitted_values]]"
+ as="value">
+ <option value$="[[value]]">[[value]]</option>
+ </template>
+ </select>
+ </gr-select>
+ </template>
+ <template is="dom-if" if="[[_isString(option.info.type)]]">
+ <input
+ is="iron-input"
+ value="[[option.info.value]]"
+ on-input="_handleStringChange"
+ data-option-key$="[[option._key]]"
+ disabled$="[[_computeDisabled(option.info.editable)]]"></input>
+ </template>
+ <template is="dom-if" if="[[option.info.inherited_value]]">
+ <span class="inherited">
+ (Inherited: [[option.info.inherited_value]])
+ </span>
+ </template>
+ </span>
+ </section>
+ </template>
+ </fieldset>
+ </div>
+ </template>
+ <script src="gr-repo-plugin-config.js"></script>
+</dom-module>
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
new file mode 100644
index 0000000000..6d7677e4bf
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.js
@@ -0,0 +1,130 @@
+/**
+ * @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.
+ */
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-repo-plugin-config',
+
+ /**
+ * Fired when the plugin config changes.
+ *
+ * @event plugin-config-changed
+ */
+
+ properties: {
+ /** @type {?} */
+ pluginData: Object,
+ /** @type {Array} */
+ _pluginConfigOptions: {
+ type: Array,
+ computed: '_computePluginConfigOptions(pluginData.*)',
+ },
+ },
+
+ behaviors: [
+ Gerrit.RepoPluginConfig,
+ ],
+
+ _computePluginConfigOptions(dataRecord) {
+ if (!dataRecord || !dataRecord.base || !dataRecord.base.config) {
+ return [];
+ }
+ const {config} = dataRecord.base;
+ return Object.keys(config).map(_key => ({_key, info: config[_key]}));
+ },
+
+ _isArray(type) {
+ return type === this.ENTRY_TYPES.ARRAY;
+ },
+
+ _isBoolean(type) {
+ return type === this.ENTRY_TYPES.BOOLEAN;
+ },
+
+ _isList(type) {
+ return type === this.ENTRY_TYPES.LIST;
+ },
+
+ _isString(type) {
+ // Treat numbers like strings for simplicity.
+ return type === this.ENTRY_TYPES.STRING ||
+ type === this.ENTRY_TYPES.INT ||
+ type === this.ENTRY_TYPES.LONG;
+ },
+
+ _computeDisabled(editable) {
+ return editable === 'false';
+ },
+
+ _computeChecked(value) {
+ return JSON.parse(value);
+ },
+
+ _handleStringChange(e) {
+ const el = Polymer.dom(e).localTarget;
+ const _key = el.getAttribute('data-option-key');
+ const configChangeInfo =
+ this._buildConfigChangeInfo(el.value, _key);
+ this._handleChange(configChangeInfo);
+ },
+
+ _handleListChange(e) {
+ const el = Polymer.dom(e).localTarget;
+ const _key = el.getAttribute('data-option-key');
+ const configChangeInfo =
+ this._buildConfigChangeInfo(el.value, _key);
+ this._handleChange(configChangeInfo);
+ },
+
+ _handleBooleanChange(e) {
+ const el = Polymer.dom(e).localTarget;
+ const _key = el.getAttribute('data-option-key');
+ const configChangeInfo =
+ this._buildConfigChangeInfo(JSON.stringify(el.checked), _key);
+ this._handleChange(configChangeInfo);
+ },
+
+ _buildConfigChangeInfo(value, _key) {
+ const info = this.pluginData.config[_key];
+ info.value = value;
+ return {
+ _key,
+ info,
+ notifyPath: `${_key}.value`,
+ };
+ },
+
+ _handleArrayChange({detail}) {
+ this._handleChange(detail);
+ },
+
+ _handleChange({_key, info, notifyPath}) {
+ const {name, config} = this.pluginData;
+
+ /** @type {Object} */
+ const detail = {
+ name,
+ config: Object.assign(config, {[_key]: info}, {}),
+ notifyPath: `${name}.${notifyPath}`,
+ };
+
+ this.dispatchEvent(new CustomEvent(this.PLUGIN_CONFIG_CHANGED,
+ {detail, bubbles: 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
new file mode 100644
index 0000000000..ba0c8769ba
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html
@@ -0,0 +1,175 @@
+<!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-repo-plugin-config</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"/>
+<link rel="import" href="gr-repo-plugin-config.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-repo-plugin-config></gr-repo-plugin-config>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-repo-plugin-config tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ });
+
+ teardown(() => sandbox.restore());
+
+ test('_computePluginConfigOptions', () => {
+ assert.deepEqual(element._computePluginConfigOptions(), []);
+ assert.deepEqual(element._computePluginConfigOptions({}), []);
+ assert.deepEqual(element._computePluginConfigOptions({base: {}}), []);
+ assert.deepEqual(element._computePluginConfigOptions(
+ {base: {config: {}}}), []);
+ assert.deepEqual(element._computePluginConfigOptions(
+ {base: {config: {testKey: 'testInfo'}}}),
+ [{_key: 'testKey', info: 'testInfo'}]);
+ });
+
+ test('_computeDisabled', () => {
+ assert.isFalse(element._computeDisabled('true'));
+ assert.isTrue(element._computeDisabled('false'));
+ });
+
+ test('_handleChange', () => {
+ const eventStub = sandbox.stub(element, 'dispatchEvent');
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test'}},
+ };
+ element._handleChange({
+ _key: 'plugin',
+ info: {value: 'newTest'},
+ notifyPath: 'plugin.value',
+ });
+
+ assert.isTrue(eventStub.called);
+
+ const {detail} = eventStub.lastCall.args[0];
+ assert.equal(detail.name, 'testName');
+ assert.deepEqual(detail.config, {plugin: {value: 'newTest'}});
+ assert.equal(detail.notifyPath, 'testName.plugin.value');
+ });
+
+ suite('option types', () => {
+ let changeStub;
+ let buildStub;
+
+ setup(() => {
+ changeStub = sandbox.stub(element, '_handleChange');
+ buildStub = sandbox.stub(element, '_buildConfigChangeInfo');
+ });
+
+ test('ARRAY type option', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test', type: 'ARRAY'}},
+ };
+ flushAsynchronousOperations();
+
+ const editor = element.$$('gr-plugin-config-array-editor');
+ assert.ok(editor);
+ element._handleArrayChange({detail: 'test'});
+ assert.isTrue(changeStub.called);
+ assert.equal(changeStub.lastCall.args[0], 'test');
+ });
+
+ test('BOOLEAN type option', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'true', type: 'BOOLEAN'}},
+ };
+ flushAsynchronousOperations();
+
+ const toggle = element.$$('paper-toggle-button');
+ assert.ok(toggle);
+ toggle.click();
+ flushAsynchronousOperations();
+
+ assert.isTrue(buildStub.called);
+ assert.deepEqual(buildStub.lastCall.args, ['false', 'plugin']);
+
+ assert.isTrue(changeStub.called);
+ });
+
+ test('INT/LONG/STRING type option', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test', type: 'STRING'}},
+ };
+ flushAsynchronousOperations();
+
+ const input = element.$$('input');
+ assert.ok(input);
+ input.value = 'newTest';
+ input.dispatchEvent(new Event('input'));
+ flushAsynchronousOperations();
+
+ assert.isTrue(buildStub.called);
+ assert.deepEqual(buildStub.lastCall.args, ['newTest', 'plugin']);
+
+ assert.isTrue(changeStub.called);
+ });
+
+ test('LIST type option', () => {
+ const permitted_values = ['test', 'newTest'];
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test', type: 'LIST', permitted_values}},
+ };
+ flushAsynchronousOperations();
+
+ const select = element.$$('select');
+ assert.ok(select);
+ select.value = 'newTest';
+ select.dispatchEvent(new Event('change', {bubbles: true}));
+ flushAsynchronousOperations();
+
+ assert.isTrue(buildStub.called);
+ assert.deepEqual(buildStub.lastCall.args, ['newTest', 'plugin']);
+
+ assert.isTrue(changeStub.called);
+ });
+ });
+
+ test('_buildConfigChangeInfo', () => {
+ element.pluginData = {
+ name: 'testName',
+ config: {plugin: {value: 'test'}},
+ };
+ const detail = element._buildConfigChangeInfo('newTest', 'plugin');
+ assert.equal(detail._key, 'plugin');
+ assert.deepEqual(detail.info, {value: 'newTest'});
+ assert.equal(detail.notifyPath, 'plugin.value');
+ });
+ });
+</script>
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 186740eb14..8604ad8743 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.html
@@ -26,7 +26,7 @@ limitations under the License.
<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">
-
+<link rel="import" href="../gr-repo-plugin-config/gr-repo-plugin-config.html">
<dom-module id="gr-repo">
<template>
@@ -37,7 +37,7 @@ limitations under the License.
content: ' *';
}
.loading,
- .hideDownload {
+ .hide {
display: none;
}
#loading.loading {
@@ -46,10 +46,10 @@ limitations under the License.
#loading:not(.loading) {
display: none;
}
- .repositorySettings {
+ #options .repositorySettings {
display: none;
}
- .repositorySettings.showConfig {
+ #options .repositorySettings.showConfig {
display: block;
}
</style>
@@ -67,7 +67,7 @@ limitations under the License.
</div>
<div id="loading" class$="[[_computeLoadingClass(_loading)]]">Loading...</div>
<div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
- <div id="downloadContent" class$="[[_computeDownloadClass(_schemes)]]">
+ <div id="downloadContent" class$="[[_computeHideClass(_schemes)]]">
<h2 id="download">Download</h2>
<fieldset>
<gr-download-commands
@@ -219,7 +219,7 @@ limitations under the License.
</gr-select>
</span>
</section>
- <section id="noteDbSettings" class$="repositorySettings [[_computeRepositoriesClass(_noteDbEnabled)]]">
+ <section>
<span class="title">
Enable adding unregistered users as reviewers and CCs on changes</span>
<span class="value">
@@ -346,7 +346,15 @@ limitations under the License.
</span>
</section>
</fieldset>
- <!-- TODO @beckysiegel add plugin config widgets -->
+ <div
+ class$="pluginConfig [[_computeHideClass(_pluginData)]]"
+ on-plugin-config-changed="_handlePluginConfigChanged">
+ <h3>Plugins</h3>
+ <template is="dom-repeat" items="[[_pluginData]]" as="data">
+ <gr-repo-plugin-config
+ plugin-data="[[data]]"></gr-repo-plugin-config>
+ </template>
+ </div>
<gr-button
on-tap="_handleSaveRepoConfig"
disabled$="[[_computeButtonDisabled(_readOnly, _configChanged)]]">Save changes</gr-button>
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 91a0ffbbca..ff630c49eb 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
@@ -53,6 +53,7 @@
Polymer({
is: 'gr-repo',
+ _legacyUndefinedCheck: true,
properties: {
params: Object,
@@ -73,6 +74,11 @@
},
/** @type {?} */
_repoConfig: Object,
+ /** @type {?} */
+ _pluginData: {
+ type: Array,
+ computed: '_computePluginData(_repoConfig.plugin_config.*)',
+ },
_readOnly: {
type: Boolean,
value: true,
@@ -101,10 +107,6 @@
},
_selectedScheme: String,
_schemesObj: Object,
- _noteDbEnabled: {
- type: Boolean,
- value: false,
- },
},
observers: [
@@ -117,6 +119,15 @@
this.fire('title-change', {title: this.repo});
},
+ _computePluginData(configRecord) {
+ if (!configRecord ||
+ !configRecord.base) { return []; }
+
+ const pluginConfig = configRecord.base;
+ return Object.keys(pluginConfig)
+ .map(name => ({name, config: pluginConfig[name]}));
+ },
+
_loadRepo() {
if (!this.repo) { return Promise.resolve(); }
@@ -162,7 +173,6 @@
if (!config) { return Promise.resolve(); }
this._schemesObj = config.download.schemes;
- this._noteDbEnabled = !!config.note_db_enabled;
}));
return Promise.all(promises);
@@ -172,8 +182,8 @@
return loading ? 'loading' : '';
},
- _computeDownloadClass(schemes) {
- return !schemes || !schemes.length ? 'hideDownload' : '';
+ _computeHideClass(arr) {
+ return !arr || !arr.length ? 'hide' : '';
},
_loggedInChanged(_loggedIn) {
@@ -245,20 +255,22 @@
return this.$.restAPI.getLoggedIn();
},
- _formatRepoConfigForSave(p) {
+ _formatRepoConfigForSave(repoConfig) {
const configInputObj = {};
- for (const key in p) {
- if (p.hasOwnProperty(key)) {
+ for (const key in repoConfig) {
+ if (repoConfig.hasOwnProperty(key)) {
if (key === 'default_submit_type') {
// default_submit_type is not in the input type, and the
// configured value was already copied to submit_type by
// _loadProject. Omit this property when saving.
continue;
}
- if (typeof p[key] === 'object') {
- configInputObj[key] = p[key].configured_value;
+ if (key === 'plugin_config') {
+ configInputObj.plugin_config_values = repoConfig[key];
+ } else if (typeof repoConfig[key] === 'object') {
+ configInputObj[key] = repoConfig[key].configured_value;
} else {
- configInputObj[key] = p[key];
+ configInputObj[key] = repoConfig[key];
}
}
}
@@ -322,5 +334,10 @@
_computeChangesUrl(name) {
return Gerrit.Nav.getUrlForProjectChanges(name);
},
+
+ _handlePluginConfigChanged({detail: {name, config, notifyPath}}) {
+ this._repoConfig.plugin_config[name] = config;
+ this.notifyPath('_repoConfig.plugin_config.' + notifyPath);
+ },
});
})();
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 d6d4366ae1..60645bb667 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
@@ -125,6 +125,29 @@ limitations under the License.
sandbox.restore();
});
+ test('_computePluginData', () => {
+ assert.deepEqual(element._computePluginData(), []);
+ assert.deepEqual(element._computePluginData({}), []);
+ assert.deepEqual(element._computePluginData({base: {}}), []);
+ assert.deepEqual(element._computePluginData({base: {plugin: 'data'}}),
+ [{name: 'plugin', config: 'data'}]);
+ });
+
+ test('_handlePluginConfigChanged', () => {
+ const notifyStub = sandbox.stub(element, 'notifyPath');
+ element._repoConfig = {plugin_config: {}};
+ element._handlePluginConfigChanged({detail: {
+ name: 'test',
+ config: 'data',
+ notifyPath: 'path',
+ }});
+ flushAsynchronousOperations();
+
+ assert.equal(element._repoConfig.plugin_config.test, 'data');
+ assert.equal(notifyStub.lastCall.args[0],
+ '_repoConfig.plugin_config.path');
+ });
+
test('loading displays before repo config is loaded', () => {
assert.isTrue(element.$.loading.classList.contains('loading'));
assert.isFalse(getComputedStyle(element.$.loading).display === 'none');
@@ -136,14 +159,12 @@ limitations under the License.
test('download commands visibility', () => {
element._loading = false;
flushAsynchronousOperations();
- assert.isTrue(element.$.downloadContent.classList
- .contains('hideDownload'));
+ assert.isTrue(element.$.downloadContent.classList.contains('hide'));
assert.isTrue(getComputedStyle(element.$.downloadContent)
.display == 'none');
element._schemesObj = SCHEMES;
flushAsynchronousOperations();
- assert.isFalse(element.$.downloadContent.classList
- .contains('hideDownload'));
+ assert.isFalse(element.$.downloadContent.classList.contains('hide'));
assert.isFalse(getComputedStyle(element.$.downloadContent)
.display == 'none');
});
@@ -293,17 +314,6 @@ limitations under the License.
});
test('fields update and save correctly', () => {
- // test notedb
- element._noteDbEnabled = false;
-
- assert.equal(
- element._computeRepositoriesClass(element._noteDbEnabled), '');
-
- element._noteDbEnabled = true;
-
- assert.equal(element._computeRepositoriesClass(
- element._noteDbEnabled), 'showConfig');
-
const configInputObj = {
description: 'new description',
use_contributor_agreements: 'TRUE',
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 a5dc04e49c..84c61016d6 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,6 +66,7 @@
Polymer({
is: 'gr-rule-editor',
+ _legacyUndefinedCheck: true,
properties: {
hasRange: Boolean,
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 23746770ea..a6dde7a3d4 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
@@ -223,6 +223,15 @@ limitations under the License.
[[_computeLabelValue(change, labelName)]]
</td>
</template>
+ <template is="dom-repeat" items="[[_dynamicCellEndpoints]]"
+ as="pluginEndpointName">
+ <td class="cell endpoint">
+ <gr-endpoint-decorator name$="[[pluginEndpointName]]">
+ <gr-endpoint-param name="change" value="[[change]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </td>
+ </template>
</template>
<script src="gr-change-list-item.js"></script>
</dom-module>
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 65fe9ea556..ecc7532a89 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,6 +26,7 @@
Polymer({
is: 'gr-change-list-item',
+ _legacyUndefinedCheck: true,
properties: {
visibleChangeTableColumns: Array,
@@ -57,6 +58,9 @@
type: String,
computed: '_computeChangeSize(change)',
},
+ _dynamicCellEndpoints: {
+ type: Array,
+ },
},
behaviors: [
@@ -67,6 +71,13 @@
Gerrit.URLEncodingBehavior,
],
+ attached() {
+ Gerrit.awaitPluginsLoaded().then(() => {
+ this._dynamicCellEndpoints = Gerrit._endpoints.getDynamicEndpoints(
+ 'change-list-item-cell');
+ });
+ },
+
_computeItemNeedsReview(reviewed) {
return !reviewed;
},
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 64cedf308b..6d051329db 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
@@ -31,6 +31,7 @@
Polymer({
is: 'gr-change-list-view',
+ _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -244,9 +245,13 @@
if (!changes || !changes.length) {
return;
}
- if (USER_QUERY_PATTERN.test(this._query) && changes[0].owner.email) {
- this._userId = changes[0].owner.email;
- return;
+ if (USER_QUERY_PATTERN.test(this._query)) {
+ const owner = changes[0].owner;
+ const userId = owner._account_id ? owner._account_id : owner.email;
+ if (userId) {
+ this._userId = userId;
+ return;
+ }
}
if (REPO_QUERY_PATTERN.test(this._query)) {
this._repo = changes[0].project;
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 372e6beff2..ef17baad32 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
@@ -52,17 +52,24 @@ limitations under the License.
[[_computeLabelShortcut(labelName)]]
</th>
</template>
+ <template is="dom-repeat" items="[[_dynamicHeaderEndpoints]]"
+ as="pluginHeader">
+ <th class="endpoint">
+ <gr-endpoint-decorator name$="[[pluginHeader]]">
+ </gr-endpoint-decorator>
+ </th>
+ </template>
</tr>
<template is="dom-repeat" items="[[sections]]" as="changeSection"
index-as="sectionIndex">
- <template is="dom-if" if="[[changeSection.sectionName]]">
+ <template is="dom-if" if="[[changeSection.name]]">
<tr class="groupHeader">
<td class="leftPadding"></td>
<td class="star" hidden$="[[!showStar]]" hidden></td>
<td class="cell"
colspan$="[[_computeColspan(changeTableColumns, labelNames)]]">
<a href$="[[_sectionHref(changeSection.query)]]">
- [[changeSection.sectionName]]
+ [[changeSection.name]]
</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 e57a389ba2..587719df55 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,6 +24,7 @@
Polymer({
is: 'gr-change-list',
+ _legacyUndefinedCheck: true,
/**
* Fired when next page key shortcut was pressed.
@@ -63,7 +64,7 @@
* properties should not be used together.
*
* @type {!Array<{
- * sectionName: string,
+ * name: string,
* query: string,
* results: !Array<!Object>
* }>}
@@ -76,6 +77,9 @@
type: Array,
computed: '_computeLabelNames(sections)',
},
+ _dynamicHeaderEndpoints: {
+ type: Array,
+ },
selectedIndex: {
type: Number,
notify: true,
@@ -128,6 +132,13 @@
};
},
+ attached() {
+ Gerrit.awaitPluginsLoaded().then(() => {
+ this._dynamicHeaderEndpoints = Gerrit._endpoints.getDynamicEndpoints(
+ 'change-list-header');
+ });
+ },
+
/**
* Iron-a11y-keys-behavior catches keyboard events globally. Some keyboard
* events must be scoped to a component level (e.g. `enter`) in order to not
@@ -341,7 +352,9 @@
},
_getListItems() {
- return Polymer.dom(this.root).querySelectorAll('gr-change-list-item');
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ return Array.from(
+ Polymer.dom(this.root).querySelectorAll('gr-change-list-item'));
},
_sectionsChanged() {
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 e793f42dcb..ecbd67e543 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
@@ -58,6 +58,7 @@ limitations under the License.
font-size: var(--font-size-large);
}
#help p {
+ margin-bottom: .6em;
max-width: 35em;
}
@media only screen and (max-width: 50em) {
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 b5b02a78e1..42d7bd768d 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,6 +19,7 @@
Polymer({
is: 'gr-create-change-help',
+ _legacyUndefinedCheck: true,
/**
* Fired when the "Create change" button is tapped.
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 bf17b9117d..e6d123c7cd 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
@@ -74,8 +74,8 @@ limitations under the License.
<li>
<p>
Close this dialog and you should be able to see your recently
- created change in ``Outgoing changes'' section on
- ``Your changes'' page.
+ created change in the 'Outgoing changes' section on the
+ 'Your changes' page.
</p>
</li>
</ol>
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 0e71f1c3f7..e4958c52c6 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,6 +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-destination-dialog/gr-create-destination-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
index 4d2802eed1..ed87e9ea7e 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,6 +26,7 @@
Polymer({
is: 'gr-create-destination-dialog',
+ _legacyUndefinedCheck: true,
properties: {
_repo: String,
_branch: String,
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 43fe0c2a94..5faef085d5 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,6 +21,7 @@
Polymer({
is: 'gr-dashboard-view',
+ _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -206,7 +207,7 @@
this._showNewUserHelp = lastResultSet.length == 0;
}
this._results = changes.map((results, i) => ({
- sectionName: res.sections[i].name,
+ name: res.sections[i].name,
query: res.sections[i].query,
results,
isOutgoing: res.sections[i].isOutgoing,
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 6da56e6bcb..54c8ea988d 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
@@ -278,7 +278,7 @@ limitations under the License.
return element._fetchDashboardChanges({sections}, false).then(() => {
assert.equal(element._results.length, 1);
- assert.equal(element._results[0].sectionName, 'test2');
+ assert.equal(element._results[0].name, 'test2');
});
});
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
new file mode 100644
index 0000000000..2394e24b7b
--- /dev/null
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html
@@ -0,0 +1,41 @@
+<!--
+@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.
+-->
+
+<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">
+
+<dom-module id="gr-embed-dashboard">
+ <template>
+ <gr-change-list
+ show-star
+ account="[[account]]"
+ preferences="[[preferences]]"
+ sections="[[sections]]">
+ <div id="emptyOutgoing" slot="empty-outgoing">
+ <template is="dom-if" if="[[showNewUserHelp]]">
+ <gr-create-change-help></gr-create-change-help>
+ </template>
+ <template is="dom-if" if="[[!showNewUserHelp]]">
+ No changes
+ </template>
+ </div>
+ </gr-change-list>
+ </template>
+ <script src="gr-embed-dashboard.js"></script>
+</dom-module>
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
new file mode 100644
index 0000000000..14d0cb01e9
--- /dev/null
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
@@ -0,0 +1,30 @@
+/**
+ * @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.
+ */
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-embed-dashboard',
+ _legacyUndefinedCheck: true,
+ properties: {
+ account: Object,
+ sections: Array,
+ preferences: Object,
+ showNewUserHelp: Boolean,
+ },
+ });
+})();
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 cd6eb77a7c..67fbd979d4 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,6 +19,7 @@
Polymer({
is: 'gr-repo-header',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {?String} */
repo: {
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 d3d54f56d8..c7fda2ce62 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,6 +19,7 @@
Polymer({
is: 'gr-user-header',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {?String} */
userId: {
@@ -79,8 +80,11 @@
},
_computeDashboardUrl(accountDetails) {
- if (!accountDetails || !accountDetails.email) { return null; }
- return Gerrit.Nav.getUrlForUserDashboard(accountDetails.email);
+ if (!accountDetails) { return null; }
+ const id = accountDetails._account_id;
+ const email = accountDetails.email;
+ if (!id && !email ) { return null; }
+ return Gerrit.Nav.getUrlForUserDashboard(id ? id : email);
},
_computeDashboardLinkClass(showDashboardLink, loggedIn) {
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
index 88b2858a3d..c53c111624 100644
--- 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
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-account-entry',
+ _legacyUndefinedCheck: true,
/**
* Fired when an account is entered.
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
index 458719a881..7cdffc8846 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
@@ -21,6 +21,7 @@
Polymer({
is: 'gr-account-list',
+ _legacyUndefinedCheck: true,
/**
* Fired when user inputs an invalid email address.
@@ -89,7 +90,9 @@
},
get accountChips() {
- return Polymer.dom(this.root).querySelectorAll('gr-account-chip');
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ return Array.from(
+ Polymer.dom(this.root).querySelectorAll('gr-account-chip'));
},
get focusStart() {
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 60177d1588..9182bbceb6 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
@@ -193,6 +193,7 @@
Polymer({
is: 'gr-change-actions',
+ _legacyUndefinedCheck: true,
/**
* Fired when the change should be reloaded.
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 670de5da68..9803fabcf3 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
@@ -64,7 +64,9 @@ limitations under the License.
});
},
send(method, url, payload) {
- if (method !== 'POST') { return Promise.reject('bad method'); }
+ if (method !== 'POST') {
+ return Promise.reject(new Error('bad method'));
+ }
if (url === '/changes/test~42/revisions/2/submit') {
return Promise.resolve({
@@ -78,7 +80,7 @@ limitations under the License.
});
}
- return Promise.reject('bad url');
+ return Promise.reject(new Error('bad url'));
},
getProjectConfig() { return Promise.resolve({}); },
});
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 d62aaeeccb..f1d3cd3faf 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
@@ -83,9 +83,6 @@ limitations under the License.
getLoggedIn() { return Promise.resolve(false); },
deleteVote() { return Promise.resolve({ok: true}); },
});
- stub('gr-change-metadata', {
- _computeShowReviewersByState() { return true; },
- });
});
teardown(() => {
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 c07a775590..a09b730157 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
@@ -200,38 +200,26 @@ limitations under the License.
allow-any-user></gr-account-list>
</span>
</section>
- <template is="dom-if" if="[[_showReviewersByState]]">
- <section>
- <span class="title">Reviewers</span>
- <span class="value">
- <gr-reviewer-list
- change="{{change}}"
- mutable="[[_mutable]]"
- reviewers-only
- max-reviewers-displayed="3"></gr-reviewer-list>
- </span>
- </section>
- <section>
- <span class="title">CC</span>
- <span class="value">
- <gr-reviewer-list
- change="{{change}}"
- mutable="[[_mutable]]"
- ccs-only
- max-reviewers-displayed="3"></gr-reviewer-list>
- </span>
- </section>
- </template>
- <template is="dom-if" if="[[!_showReviewersByState]]">
- <section>
- <span class="title">Reviewers</span>
- <span class="value">
- <gr-reviewer-list
- change="{{change}}"
- mutable="[[_mutable]]"></gr-reviewer-list>
- </span>
- </section>
- </template>
+ <section>
+ <span class="title">Reviewers</span>
+ <span class="value">
+ <gr-reviewer-list
+ change="{{change}}"
+ mutable="[[_mutable]]"
+ reviewers-only
+ max-reviewers-displayed="3"></gr-reviewer-list>
+ </span>
+ </section>
+ <section>
+ <span class="title">CC</span>
+ <span class="value">
+ <gr-reviewer-list
+ change="{{change}}"
+ mutable="[[_mutable]]"
+ ccs-only
+ max-reviewers-displayed="3"></gr-reviewer-list>
+ </span>
+ </section>
<section>
<span class="title">Repo</span>
<span class="value">
@@ -299,31 +287,29 @@ limitations under the License.
<span class="title">Strategy</span>
<span class="value">[[_computeStrategy(change)]]</span>
</section>
- <template is="dom-if" if="[[serverConfig.note_db_enabled]]">
- <section class="hashtag">
- <span class="title">Hashtags</span>
- <span class="value">
- <template is="dom-repeat" items="[[change.hashtags]]">
- <gr-linked-chip
- class="hashtagChip"
- text="[[item]]"
- href="[[_computeHashtagURL(item)]]"
- removable="[[!_hashtagReadOnly]]"
- on-remove="_handleHashtagRemoved">
- </gr-linked-chip>
- </template>
- <template is="dom-if" if="[[!_hashtagReadOnly]]">
- <gr-editable-label
- uppercase
- label-text="Add a hashtag"
- value="{{_newHashtag}}"
- placeholder="[[_computeHashtagPlaceholder(_hashtagReadOnly)]]"
- read-only="[[_hashtagReadOnly]]"
- on-changed="_handleHashtagChanged"></gr-editable-label>
- </template>
- </span>
- </section>
- </template>
+ <section class="hashtag">
+ <span class="title">Hashtags</span>
+ <span class="value">
+ <template is="dom-repeat" items="[[change.hashtags]]">
+ <gr-linked-chip
+ class="hashtagChip"
+ text="[[item]]"
+ href="[[_computeHashtagURL(item)]]"
+ removable="[[!_hashtagReadOnly]]"
+ on-remove="_handleHashtagRemoved">
+ </gr-linked-chip>
+ </template>
+ <template is="dom-if" if="[[!_hashtagReadOnly]]">
+ <gr-editable-label
+ uppercase
+ label-text="Add a hashtag"
+ value="{{_newHashtag}}"
+ placeholder="[[_computeHashtagPlaceholder(_hashtagReadOnly)]]"
+ read-only="[[_hashtagReadOnly]]"
+ on-changed="_handleHashtagChanged"></gr-editable-label>
+ </template>
+ </span>
+ </section>
<div class="separatedSection">
<gr-change-requirements
change="{{change}}"
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 e107e150af..94b85fdf59 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
@@ -61,6 +61,7 @@
Polymer({
is: 'gr-change-metadata',
+ _legacyUndefinedCheck: true,
/**
* Fired when the change topic is changed.
@@ -83,9 +84,7 @@
type: Boolean,
computed: '_computeIsMutable(account)',
},
- /**
- * @type {{ note_db_enabled: string }}
- */
+ /** @type {?} */
serverConfig: Object,
parentIsCurrent: Boolean,
_notCurrentMessage: {
@@ -101,10 +100,6 @@
type: Boolean,
computed: '_computeHashtagReadOnly(_mutable, change)',
},
- _showReviewersByState: {
- type: Boolean,
- computed: '_computeShowReviewersByState(serverConfig)',
- },
/**
* @type {Defs.PushCertificateValidation}
*/
@@ -282,10 +277,6 @@
return _hashtagReadOnly ? '' : HASHTAG_ADD_MESSAGE;
},
- _computeShowReviewersByState(serverConfig) {
- return !!serverConfig.note_db_enabled;
- },
-
_computeShowRequirements(change) {
if (change.status !== this.ChangeStatus.NEW) {
// TODO(maximeg) change this to display the stored
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 f3ef41e98d..ce69d45eb6 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
@@ -117,18 +117,6 @@ limitations under the License.
assert.isTrue(element.$$('.strategy').hasAttribute('hidden'));
});
- test('show CC section when NoteDb enabled', () => {
- function hasCc() {
- return element._showReviewersByState;
- }
-
- element.serverConfig = {};
- assert.isFalse(hasCc());
-
- element.serverConfig = {note_db_enabled: true};
- assert.isTrue(hasCc());
- });
-
test('weblinks use Gerrit.Nav interface', () => {
const weblinksStub = sandbox.stub(Gerrit.Nav, '_generateWeblinks')
.returns([{name: 'stubb', url: '#s'}]);
@@ -574,9 +562,6 @@ limitations under the License.
});
test('_computeHashtagReadOnly', () => {
- element.serverConfig = {
- note_db_enabled: true,
- };
flushAsynchronousOperations();
let mutable = false;
assert.isTrue(element._computeHashtagReadOnly(mutable, change));
@@ -589,9 +574,6 @@ limitations under the License.
});
test('hashtag read only hides delete button', () => {
- element.serverConfig = {
- note_db_enabled: true,
- };
flushAsynchronousOperations();
element.account = {};
element.change = change;
@@ -601,9 +583,6 @@ limitations under the License.
});
test('hashtag not read only does not hide delete button', () => {
- element.serverConfig = {
- note_db_enabled: true,
- };
flushAsynchronousOperations();
element.account = {test: true};
change.actions.hashtags.enabled = true;
@@ -725,9 +704,6 @@ limitations under the License.
});
test('changing hashtag', () => {
- element.serverConfig = {
- note_db_enabled: true,
- };
flushAsynchronousOperations();
element._newHashtag = 'new hashtag';
const newHashtag = ['new hashtag'];
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 dfdcd59ea5..4717ff9624 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,6 +19,7 @@
Polymer({
is: 'gr-change-requirements',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
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 07573d3742..857976223e 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
@@ -200,6 +200,7 @@ limitations under the License.
}
.collapseToggleContainer {
display: flex;
+ margin-bottom: 8px;
}
#relatedChangesToggle {
display: none;
@@ -234,6 +235,7 @@ limitations under the License.
--paper-tabs-selection-bar-color: var(--link-color);
}
paper-tab {
+ box-sizing: border-box;
max-width: 15rem;
--paper-tab-ink: var(--link-color);
}
@@ -355,6 +357,9 @@ limitations under the License.
min-width: initial;
width: 100vw;
}
+ #replyOverlay {
+ z-index: var(--reply-overlay-z-index);
+ }
}
</style>
<div class="container loading" hidden$="[[!_loading]]">Loading...</div>
@@ -518,8 +523,27 @@ limitations under the License.
</div>
</div>
</section>
+
<section class="patchInfo">
- <gr-file-list-header
+ <template is="dom-if" if="[[_showPrimaryTabs]]">
+ <paper-tabs id="primaryTabs" on-selected-changed="_handleFileTabChange">
+ <paper-tab>Files</paper-tab>
+ <template is="dom-repeat" items="[[_dynamicTabHeaderEndpoints]]"
+ as="tabHeader">
+ <paper-tab>
+ <gr-endpoint-decorator name$="[[tabHeader]]">
+ <gr-endpoint-param name="change" value="[[_change]]">
+ </gr-endpoint-param>
+ <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </paper-tab>
+ </template>
+ </paper-tabs>
+ </template>
+
+ <div hidden$="[[!_showFileTabContent]]">
+ <gr-file-list-header
id="fileListHeader"
account="[[_account]]"
all-patch-sets="[[_allPatchSets]]"
@@ -538,6 +562,8 @@ limitations under the License.
patch-num="{{_patchRange.patchNum}}"
base-patch-num="{{_patchRange.basePatchNum}}"
files-expanded="[[_filesExpanded]]"
+ diff-prefs-disabled="[[_diffPrefsDisabled]]"
+ show-title="[[!_showPrimaryTabs]]"
on-open-diff-prefs="_handleOpenDiffPrefs"
on-open-download-dialog="_handleOpenDownloadDialog"
on-open-upload-help-dialog="_handleOpenUploadHelpDialog"
@@ -565,7 +591,18 @@ limitations under the License.
on-files-shown-changed="_setShownFiles"
on-file-action-tap="_handleFileActionTap"
on-reload-drafts="_reloadDraftsWithCallback"></gr-file-list>
+ </div>
+
+ <template is="dom-if" if="[[!_showFileTabContent]]">
+ <gr-endpoint-decorator name$="[[_selectedFilesTabPluginEndpoint]]">
+ <gr-endpoint-param name="change" value="[[_change]]">
+ </gr-endpoint-param>
+ <gr-endpoint-param name="revision" value="[[_selectedRevision]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </template>
</section>
+
<gr-endpoint-decorator name="change-view-integration">
<gr-endpoint-param name="change" value="[[_change]]">
</gr-endpoint-param>
@@ -574,7 +611,7 @@ limitations under the License.
</gr-endpoint-decorator>
<paper-tabs
id="commentTabs"
- on-selected-changed="_handleTabChange">
+ on-selected-changed="_handleCommentTabChange">
<paper-tab class="changeLog">Change Log</paper-tab>
<paper-tab
class="commentThreads">
@@ -594,6 +631,7 @@ limitations under the License.
change-comments="[[_changeComments]]"
project-name="[[_change.project]]"
show-reply-buttons="[[_loggedIn]]"
+ on-message-anchor-tap="_handleMessageAnchorTap"
on-reply="_handleMessageReply"></gr-messages-list>
</template>
<template is="dom-if" if="[[!_showMessagesView]]">
@@ -635,7 +673,6 @@ limitations under the License.
patch-num="[[computeLatestPatchNum(_allPatchSets)]]"
permitted-labels="[[_change.permitted_labels]]"
diff-drafts="[[_diffDrafts]]"
- server-config="[[_serverConfig]]"
project-config="[[_projectConfig]]"
can-be-started="[[_canStartReview]]"
on-send="_handleReplySent"
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 ea54239714..4788999235 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
@@ -44,6 +44,8 @@
const TRAILING_WHITESPACE_REGEX = /[ \t]+$/gm;
+ const MSG_PREFIX = '#message-';
+
const ReloadToastMessage = {
NEWER_REVISION: 'A newer patch set has been uploaded',
RESTORED: 'This change has been restored',
@@ -62,6 +64,7 @@
Polymer({
is: 'gr-change-view',
+ _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -106,6 +109,14 @@
type: Boolean,
value: false,
},
+ disableDiffPrefs: {
+ type: Boolean,
+ value: false,
+ },
+ _diffPrefsDisabled: {
+ type: Boolean,
+ computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
+ },
_commentThreads: Array,
/** @type {?} */
_serverConfig: {
@@ -248,6 +259,25 @@
type: Boolean,
value: true,
},
+ _showFileTabContent: {
+ type: Boolean,
+ value: true,
+ },
+ /** @type {Array<string>} */
+ _dynamicTabHeaderEndpoints: {
+ type: Array,
+ },
+ _showPrimaryTabs: {
+ type: Boolean,
+ computed: '_computeShowPrimaryTabs(_dynamicTabHeaderEndpoints)',
+ },
+ /** @type {Array<string>} */
+ _dynamicTabContentEndpoints: {
+ type: Array,
+ },
+ _selectedFilesTabPluginEndpoint: {
+ type: String,
+ },
},
behaviors: [
@@ -286,6 +316,7 @@
[this.Shortcut.UP_TO_DASHBOARD]: '_handleUpToDashboard',
[this.Shortcut.EXPAND_ALL_MESSAGES]: '_handleExpandAllMessages',
[this.Shortcut.COLLAPSE_ALL_MESSAGES]: '_handleCollapseAllMessages',
+ [this.Shortcut.EXPAND_ALL_DIFF_CONTEXT]: '_expandAllDiffs',
[this.Shortcut.OPEN_DIFF_PREFS]: '_handleOpenDiffPrefsShortcut',
[this.Shortcut.EDIT_TOPIC]: '_handleEditTopic',
};
@@ -306,6 +337,17 @@
this._setDiffViewMode();
});
+ Gerrit.awaitPluginsLoaded().then(() => {
+ this._dynamicTabHeaderEndpoints =
+ Gerrit._endpoints.getDynamicEndpoints('change-view-tab-header');
+ this._dynamicTabContentEndpoints =
+ Gerrit._endpoints.getDynamicEndpoints('change-view-tab-content');
+ if (this._dynamicTabContentEndpoints.length
+ !== this._dynamicTabHeaderEndpoints.length) {
+ console.warn('Different number of tab headers and tab content.');
+ }
+ });
+
this.addEventListener('comment-save', this._handleCommentSave.bind(this));
this.addEventListener('comment-refresh', this._reloadDrafts.bind(this));
this.addEventListener('comment-discard',
@@ -364,10 +406,18 @@
}
},
- _handleTabChange() {
+ _handleCommentTabChange() {
this._showMessagesView = this.$.commentTabs.selected === 0;
},
+ _handleFileTabChange() {
+ const selectedIndex = this.$$('#primaryTabs').selected;
+ this._showFileTabContent = selectedIndex === 0;
+ // Initial tab is the static files list.
+ this._selectedFilesTabPluginEndpoint =
+ this._dynamicTabContentEndpoints[selectedIndex - 1];
+ },
+
_handleEditCommitMessage(e) {
this._editingCommitMessage = true;
this.$.commitMessageEditor.focusTextarea();
@@ -458,9 +508,9 @@
},
_handleCommentSave(e) {
- if (!e.target.comment.__draft) { return; }
+ const draft = e.detail.comment;
+ if (!draft.__draft) { return; }
- const draft = e.target.comment;
draft.patch_set = draft.patch_set || this._patchRange.patchNum;
// The use of path-based notification helpers (set, push) can’t be used
@@ -490,9 +540,9 @@
},
_handleCommentDiscard(e) {
- if (!e.target.comment.__draft) { return; }
+ const draft = e.detail.comment;
+ if (!draft.__draft) { return; }
- const draft = e.target.comment;
if (!this._diffDrafts[draft.path]) {
return;
}
@@ -695,6 +745,8 @@
// 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.async(() => {
if (this.viewState.scrollTop) {
@@ -727,10 +779,17 @@
this.viewState.numFilesShown = numFilesShown;
},
+ _handleMessageAnchorTap(e) {
+ const hash = MSG_PREFIX + e.detail.id;
+ const url = Gerrit.Nav.getUrlForChange(this._change,
+ this._patchRange.patchNum, this._patchRange.basePatchNum,
+ this._editMode, hash);
+ history.replaceState(null, '', url);
+ },
+
_maybeScrollToMessage(hash) {
- const msgPrefix = '#message-';
- if (hash.startsWith(msgPrefix)) {
- this.messagesList.scrollToMessage(hash.substr(msgPrefix.length));
+ if (hash.startsWith(MSG_PREFIX)) {
+ this.messagesList.scrollToMessage(hash.substr(MSG_PREFIX.length));
}
},
@@ -840,6 +899,10 @@
return 'PARENT';
},
+ _computeShowPrimaryTabs(dynamicTabContentEndpoints) {
+ return dynamicTabContentEndpoints.length > 0;
+ },
+
_computeChangeUrl(change) {
return Gerrit.Nav.getUrlForChange(change);
},
@@ -1014,6 +1077,8 @@
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
+ if (this._diffPrefsDisabled) { return; }
+
e.preventDefault();
this.$.fileList.openDiffPrefs();
},
@@ -1712,5 +1777,9 @@
_computeCurrentRevision(currentRevision, revisions) {
return revisions && revisions[currentRevision];
},
+
+ _computeDiffPrefsDisabled(disableDiffPrefs, loggedIn) {
+ return 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 71ca338b95..f74160b503 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
@@ -95,6 +95,20 @@ limitations under the License.
return element.getComputedStyleValue(cssParam);
};
+ test('_handleMessageAnchorTap', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ basePatchNum: 'PARENT',
+ patchNum: 1,
+ };
+ const getUrlStub = sandbox.stub(Gerrit.Nav, 'getUrlForChange');
+ const replaceStateStub = sandbox.stub(history, 'replaceState');
+ element._handleMessageAnchorTap({detail: {id: 'a12345'}});
+
+ assert.equal(getUrlStub.lastCall.args[4], '#message-a12345');
+ assert.isTrue(replaceStateStub.called);
+ });
+
suite('keyboard shortcuts', () => {
test('t to add topic', () => {
const editStub = sandbox.stub(element.$.metadata, 'editTopic');
@@ -285,6 +299,16 @@ limitations under the License.
test(', should open diff preferences', () => {
const stub = sandbox.stub(
element.$.fileList.$.diffPreferencesDialog, 'open');
+ element._loggedIn = false;
+ element.disableDiffPrefs = true;
+ MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
+ assert.isFalse(stub.called);
+
+ element._loggedIn = true;
+ MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
+ assert.isFalse(stub.called);
+
+ element.disableDiffPrefs = false;
MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
assert.isTrue(stub.called);
});
@@ -643,12 +667,12 @@ limitations under the License.
path: '/foo/bar.txt',
text: 'hello',
};
- element._handleCommentSave({target: {comment: draft}});
+ element._handleCommentSave({detail: {comment: draft}});
draft.patch_set = 2;
assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft]});
draft.patch_set = null;
draft.text = 'hello, there';
- element._handleCommentSave({target: {comment: draft}});
+ element._handleCommentSave({detail: {comment: draft}});
draft.patch_set = 2;
assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft]});
const draft2 = {
@@ -657,14 +681,14 @@ limitations under the License.
path: '/foo/bar.txt',
text: 'hola',
};
- element._handleCommentSave({target: {comment: draft2}});
+ element._handleCommentSave({detail: {comment: draft2}});
draft2.patch_set = 2;
assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft, draft2]});
draft.patch_set = null;
- element._handleCommentDiscard({target: {comment: draft}});
+ element._handleCommentDiscard({detail: {comment: draft}});
draft.patch_set = 2;
assert.deepEqual(element._diffDrafts, {'/foo/bar.txt': [draft2]});
- element._handleCommentDiscard({target: {comment: draft2}});
+ element._handleCommentDiscard({detail: {comment: draft2}});
assert.deepEqual(element._diffDrafts, {});
});
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 42cb976ff3..b19ca62dce 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,6 +18,7 @@
'use strict';
Polymer({
is: 'gr-comment-list',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
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 837de59742..ddab319bfe 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,6 +19,7 @@
Polymer({
is: 'gr-commit-info',
+ _legacyUndefinedCheck: true,
properties: {
change: Object,
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 03509ce5f9..a371e13eb9 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,6 +19,7 @@
Polymer({
is: 'gr-confirm-abandon-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 355aa78c63..2e8af0e140 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,6 +19,7 @@
Polymer({
is: 'gr-confirm-cherrypick-conflict-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 a63ef3c0ee..bdc0bb2722 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,6 +21,7 @@
Polymer({
is: 'gr-confirm-cherrypick-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 f198483a4b..91600ff541 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,6 +21,7 @@
Polymer({
is: 'gr-confirm-move-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 461f0308fa..77bc798f7b 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,6 +19,7 @@
Polymer({
is: 'gr-confirm-rebase-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 1cae866e80..93e21c77c8 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,6 +22,7 @@
Polymer({
is: 'gr-confirm-revert-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 346bdd0d8c..1036b7fbcc 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
@@ -49,9 +49,10 @@ limitations under the License.
</div>
<div class="main" slot="main">
<gr-endpoint-decorator name="confirm-submit-change">
- <p>
- Ready to submit &ldquo;<strong>[[change.subject]]</strong>&rdquo;?
- </p>
+ <p>Ready to submit &ldquo;<strong>[[change.subject]]</strong>&rdquo;?</p>
+ <template is="dom-if" if="[[change.is_private]]">
+ <p><strong>Heads Up!</strong> Submitting this private change will also make it public.</p>
+ </template>
<gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
<gr-endpoint-param name="action" value="[[action]]"></gr-endpoint-param>
</gr-endpoint-decorator>
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 bda79a11b1..93d38df33d 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,6 +19,7 @@
Polymer({
is: 'gr-confirm-submit-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
@@ -35,6 +36,7 @@
properties: {
/**
* @type {{
+ * is_private: boolean,
* subject: string,
* }}
*/
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 0e6315a67d..b6c155e2ad 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,6 +19,7 @@
Polymer({
is: 'gr-download-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
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 22c476cc42..f7d90eb472 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
@@ -153,7 +153,10 @@ limitations under the License.
</style>
<div class$="patchInfo-header [[_computeEditModeClass(editMode)]] [[_computePatchInfoClass(patchNum, allPatchSets)]]">
<div class="patchInfo-left">
- <h3 class="label">Files</h3>
+ <template is="dom-if"
+ if="[[showTitle]]">
+ <h3 class="label">Files</h3>
+ </template>
<div class="patchInfoContent">
<gr-patch-range-select
id="rangeSelect"
@@ -248,10 +251,10 @@ limitations under the License.
<gr-diff-mode-selector
id="modeSelect"
mode="{{diffViewMode}}"
- save-on-change="[[loggedIn]]"></gr-diff-mode-selector>
+ save-on-change="[[!diffPrefsDisabled]]"></gr-diff-mode-selector>
<span id="diffPrefsContainer"
class="hideOnEdit"
- hidden$="[[_computePrefsButtonHidden(diffPrefs, loggedIn)]]"
+ hidden$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]"
hidden>
<gr-button
link
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 da8384ffb8..8321879ee0 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,6 +23,7 @@
Polymer({
is: 'gr-file-list-header',
+ _legacyUndefinedCheck: true,
/**
* @event expand-diffs
@@ -62,6 +63,7 @@
serverConfig: Object,
shownFileCount: Number,
diffPrefs: Object,
+ diffPrefsDisabled: Boolean,
diffViewMode: {
type: String,
notify: true,
@@ -80,6 +82,10 @@
type: String,
value: '',
},
+ showTitle: {
+ type: Boolean,
+ value: true,
+ },
_descriptionReadOnly: {
type: Boolean,
computed: '_computeDescriptionReadOnly(loggedIn, change, account)',
@@ -184,11 +190,10 @@
});
},
- _computePrefsButtonHidden(prefs, loggedIn) {
- return !loggedIn || !prefs;
+ _computePrefsButtonHidden(prefs, diffPrefsDisabled) {
+ return diffPrefsDisabled || !prefs;
},
-
_fileListActionsVisible(shownFileCount, maxFilesForBulkActions) {
return shownFileCount <= maxFilesForBulkActions;
},
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 e2685e1c88..adfeeb4c30 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
@@ -62,21 +62,21 @@ limitations under the License.
});
});
- test('Diff preferences hidden when no prefs or logged out', () => {
- element.loggedIn = false;
+ test('Diff preferences hidden when no prefs or diffPrefsDisabled', () => {
+ element.diffPrefsDisabled = true;
flushAsynchronousOperations();
assert.isTrue(element.$.diffPrefsContainer.hidden);
- element.loggedIn = true;
+ element.diffPrefsDisabled = false;
flushAsynchronousOperations();
assert.isTrue(element.$.diffPrefsContainer.hidden);
- element.loggedIn = false;
+ element.diffPrefsDisabled = true;
element.diffPrefs = {font_size: '12'};
flushAsynchronousOperations();
assert.isTrue(element.$.diffPrefsContainer.hidden);
- element.loggedIn = true;
+ element.diffPrefsDisabled = false;
flushAsynchronousOperations();
assert.isFalse(element.$.diffPrefsContainer.hidden);
});
@@ -265,7 +265,7 @@ limitations under the License.
suite('editMode behavior', () => {
setup(() => {
- element.loggedIn = true;
+ element.diffPrefsDisabled = false;
element.diffPrefs = {};
});
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 84de48f413..f9ee54fca9 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
@@ -65,6 +65,9 @@ limitations under the License.
.invisible {
visibility: hidden;
}
+ .header-row {
+ font-weight: var(--font-weight-bold);
+ }
.controlRow {
align-items: center;
display: flex;
@@ -125,22 +128,27 @@ limitations under the License.
.oldPath {
color: var(--deemphasized-text-color);
}
- .comments,
+ .header-stats {
+ text-align: center;
+ min-width: 7.5em;
+ }
.stats {
text-align: right;
+ min-width: 7.5em;
}
.comments {
padding-left: 2em;
+ min-width: 20em;
}
- .stats {
- min-width: 7em;
- }
- .row:not(.header) .stats,
+ .row:not(.header-row) .stats,
.total-stats {
font-family: var(--monospace-font-family);
+ display: flex;
}
.sizeBars {
margin-left: .5em;
+ min-width: 7em;
+ text-align: center;
}
.sizeBars.hide {
display: none;
@@ -156,6 +164,8 @@ limitations under the License.
.removed {
color: var(--vote-text-color-disliked);
text-align: left;
+ min-width: 4em;
+ padding-left: 0.5em;
}
.drafts {
color: #C62828;
@@ -276,6 +286,24 @@ limitations under the License.
<div
id="container"
on-tap="_handleFileListTap">
+ <div class="header-row row">
+ <div class="status"></div>
+ <div class="path">File</div>
+ <div class="comments">Comments</div>
+ <div class="sizeBars">Size</div>
+ <div class="header-stats">Delta</div>
+ <template is="dom-if" if="[[_showDynamicColumns]]">
+ <template is="dom-repeat" items="[[_dynamicHeaderEndpoints]]" as="headerEndpoint">
+ <gr-endpoint-decorator name$="[[headerEndpoint]]">
+ </gr-endpoint-decorator>
+ </template>
+ </template>
+ <!-- Empty div here exists to keep spacing in sync with file rows. -->
+ <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]"></div>
+ <div class="editFileControls showOnEdit"></div>
+ <div class="show-hide"></div>
+ </div>
+
<template is="dom-repeat"
items="[[_shownFiles]]"
id="files"
@@ -361,6 +389,20 @@ limitations under the License.
[[_formatPercentage(file.size, file.size_delta)]]
</span>
</div>
+ <template is="dom-if" if="[[_showDynamicColumns]]">
+ <template is="dom-repeat" items="[[_dynamicContentEndpoints]]" as="contentEndpoint">
+ <div class$="[[_computeClass('', file.__path)]]">
+ <gr-endpoint-decorator name="[[contentEndpoint]]">
+ <gr-endpoint-param name="changeNum" value="[[changeNum]]">
+ </gr-endpoint-param>
+ <gr-endpoint-param name="patchRange" value="[[patchRange]]">
+ </gr-endpoint-param>
+ <gr-endpoint-param name="path" value="[[file.__path]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </div>
+ </template>
+ </template>
<div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden>
<span class$="reviewedLabel [[_computeReviewedClass(file.isReviewed)]]">Reviewed</span>
<label>
@@ -401,7 +443,6 @@ limitations under the License.
path="[[file.__path]]"
prefs="[[diffPrefs]]"
project-name="[[change.project]]"
- project-config="[[projectConfig]]"
on-line-selected="_onLineSelected"
no-render-on-prefs-change
view-mode="[[diffViewMode]]"></gr-diff-host>
@@ -426,8 +467,14 @@ limitations under the License.
-[[_patchChange.deleted]]
</span>
</div>
+ <template is="dom-if" if="[[_showDynamicColumns]]">
+ <template is="dom-repeat" items="[[_dynamicSummaryEndpoints]]" as="summaryEndpoint">
+ <gr-endpoint-decorator name="[[summaryEndpoint]]">
+ </gr-endpoint-decorator>
+ </template>
+ </template>
<!-- Empty div here exists to keep spacing in sync with file rows. -->
- <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]" hidden></div>
+ <div class="reviewed hideOnEdit" hidden$="[[!_loggedIn]]"></div>
<div class="editFileControls showOnEdit"></div>
<div class="show-hide"></div>
</div>
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 0f2b37ec8a..8dba99d8fb 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
@@ -63,6 +63,7 @@
Polymer({
is: 'gr-file-list',
+ _legacyUndefinedCheck: true,
/**
* Fired when a draft refresh should get triggered
@@ -179,6 +180,24 @@
/** @type {Function} */
_cancelForEachDiff: Function,
+
+ _showDynamicColumns: {
+ type: Boolean,
+ computed: '_computeShowDynamicColumns(_dynamicHeaderEndpoints, ' +
+ '_dynamicContentEndpoints, _dynamicSummaryEndpoints)',
+ },
+ /** @type {Array<string>} */
+ _dynamicHeaderEndpoints: {
+ type: Array,
+ },
+ /** @type {Array<string>} */
+ _dynamicContentEndpoints: {
+ type: Array,
+ },
+ /** @type {Array<string>} */
+ _dynamicSummaryEndpoints: {
+ type: Array,
+ },
},
behaviors: [
@@ -218,7 +237,7 @@
[this.Shortcut.TOGGLE_FILE_REVIEWED]: '_handleToggleFileReviewed',
[this.Shortcut.TOGGLE_LEFT_PANE]: '_handleToggleLeftPane',
- // Final two are actually handled by gr-diff-comment-thread.
+ // Final two are actually handled by gr-comment-thread.
[this.Shortcut.EXPAND_ALL_COMMENT_THREADS]: null,
[this.Shortcut.COLLAPSE_ALL_COMMENT_THREADS]: null,
};
@@ -227,6 +246,28 @@
keydown: '_scopedKeydownHandler',
},
+ attached() {
+ Gerrit.awaitPluginsLoaded().then(() => {
+ this._dynamicHeaderEndpoints = Gerrit._endpoints.getDynamicEndpoints(
+ 'change-view-file-list-header');
+ this._dynamicContentEndpoints = Gerrit._endpoints.getDynamicEndpoints(
+ 'change-view-file-list-content');
+ this._dynamicSummaryEndpoints = Gerrit._endpoints.getDynamicEndpoints(
+ 'change-view-file-list-summary');
+
+ if (this._dynamicHeaderEndpoints.length !==
+ this._dynamicContentEndpoints.length) {
+ console.warn(
+ 'Different number of dynamic file-list header and content.');
+ }
+ if (this._dynamicHeaderEndpoints.length !==
+ this._dynamicSummaryEndpoints.length) {
+ console.warn(
+ 'Different number of dynamic file-list headers and summary.');
+ }
+ });
+ },
+
detached() {
this._cancelDiffs();
},
@@ -291,7 +332,9 @@
},
get diffs() {
- return Polymer.dom(this.root).querySelectorAll('gr-diff-host');
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ return Array.from(
+ Polymer.dom(this.root).querySelectorAll('gr-diff-host'));
},
openDiffPrefs() {
@@ -797,7 +840,10 @@
* @param {string} path
*/
_computeClass(baseClass, path) {
- const classes = [baseClass];
+ const classes = [];
+ if (baseClass) {
+ classes.push(baseClass);
+ }
if (path === this.COMMIT_MESSAGE_PATH || path === this.MERGE_LIST_PATH) {
classes.push('invisible');
}
@@ -859,7 +905,9 @@
_filesChanged() {
Polymer.dom.flush();
- const files = Polymer.dom(this.root).querySelectorAll('.file-row');
+ // 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);
},
@@ -1235,6 +1283,19 @@
},
/**
+ * Shows registered dynamic columns iff the 'header', 'content' and
+ * 'summary' endpoints are regiestered the exact same number of times.
+ * Ideally, there should be a better way to enforce the expectation of the
+ * dependencies between dynamic endpoints.
+ */
+ _computeShowDynamicColumns(
+ headerEndpoints, contentEndpoints, summaryEndpoints) {
+ return headerEndpoints && contentEndpoints && summaryEndpoints &&
+ headerEndpoints.length === contentEndpoints.length &&
+ headerEndpoints.length === summaryEndpoints.length;
+ },
+
+ /**
* Returns true if none of the inline diffs have been expanded.
*
* @return {boolean}
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 3e4e763d6c..fffac8e2e0 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
@@ -800,7 +800,7 @@ limitations under the License.
flushAsynchronousOperations();
const fileRows =
- Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
+ Polymer.dom(element.root).querySelectorAll('.row:not(.header-row)');
const checkSelector = 'input.reviewed[type="checkbox"]';
const commitMsg = fileRows[0].querySelector(checkSelector);
const fileAdded = fileRows[1].querySelector(checkSelector);
@@ -931,7 +931,7 @@ limitations under the License.
sandbox.stub(element, '_expandedPathsChanged');
flushAsynchronousOperations();
const fileRows =
- Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
+ Polymer.dom(element.root).querySelectorAll('.row:not(.header-row)');
// Because the label surrounds the input, the tap event is triggered
// there first.
const showHideLabel = fileRows[0].querySelector('label.show-hide');
@@ -958,7 +958,7 @@ limitations under the License.
// Tap on a file to generate the diff.
const row = Polymer.dom(element.root)
- .querySelectorAll('.row:not(.header) label.show-hide')[0];
+ .querySelectorAll('.row:not(.header-row) label.show-hide')[0];
MockInteractions.tap(row);
flushAsynchronousOperations();
@@ -988,7 +988,7 @@ limitations under the License.
sandbox.stub(element, '_expandedPathsChanged');
flushAsynchronousOperations();
const commitMsgFile = Polymer.dom(element.root)
- .querySelectorAll('.row:not(.header) a.pathLink')[0];
+ .querySelectorAll('.row:not(.header-row) a.pathLink')[0];
// Remove href attribute so the app doesn't route to a diff view
commitMsgFile.removeAttribute('href');
@@ -1372,7 +1372,7 @@ limitations under the License.
id: '503008e2_0ab203ee',
line: 10,
updated: '2018-02-14 22:07:43.000000000',
- message: 'response',
+ message: 'a comment',
unresolved: true,
},
{
@@ -1381,7 +1381,7 @@ limitations under the License.
line: 20,
in_reply_to: 'ecf0b9fa_fe1a5f62',
updated: '2018-02-13 22:07:43.000000000',
- message: 'a comments',
+ message: 'response',
unresolved: true,
},
];
@@ -1416,6 +1416,7 @@ limitations under the License.
ignore_whitespace: 'IGNORE_NONE',
};
diff.diff = mock.diffResponse;
+ diff.$.diff.flushDebouncer('renderDiffTable');
};
const renderAndGetNewDiffs = function(index) {
@@ -1583,7 +1584,7 @@ limitations under the License.
nextChunkStub = sandbox.stub(element.$.diffCursor,
'moveToNextChunk');
fileRows =
- Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
+ Polymer.dom(element.root).querySelectorAll('.row:not(.header-row)');
});
test('n key with some files expanded and no shift key', () => {
@@ -1712,17 +1713,49 @@ limitations under the License.
// Commit message should not have edit controls.
const editControls =
- Polymer.dom(element.root).querySelectorAll('.row:not(.header)')
+ Polymer.dom(element.root).querySelectorAll('.row:not(.header-row)')
.map(row => row.querySelector('gr-edit-file-controls'));
assert.isTrue(editControls[0].classList.contains('invisible'));
});
test('reloadCommentsForThreadWithRootId', () => {
+ // Expand the commit message diff
+ MockInteractions.keyUpOn(element, 73, 'shift', 'i');
+ const diffs = renderAndGetNewDiffs(0);
+ flushAsynchronousOperations();
+
+ // Two comment threads should be generated by renderAndGetNewDiffs
+ const threadEls = diffs[0].getThreadEls();
+ assert.equal(threadEls.length, 2);
+ const threadElsByRootId = new Map(
+ threadEls.map(threadEl => [threadEl.rootId, threadEl]));
+
+ const thread1 = threadElsByRootId.get('503008e2_0ab203ee');
+ assert.equal(thread1.comments.length, 1);
+ assert.equal(thread1.comments[0].message, 'a comment');
+ assert.equal(thread1.comments[0].line, 10);
+
+ const thread2 = threadElsByRootId.get('ecf0b9fa_fe1a5f62');
+ assert.equal(thread2.comments.length, 2);
+ assert.isTrue(thread2.comments[0].unresolved);
+ assert.equal(thread2.comments[0].message, 'another comment');
+ assert.equal(thread2.comments[0].line, 20);
+
const commentStub =
sandbox.stub(element.changeComments, 'getCommentsForThread');
const commentStubRes1 = [
{
patch_set: 2,
+ id: '503008e2_0ab203ee',
+ line: 20,
+ updated: '2018-02-08 18:49:18.000000000',
+ message: 'edited text',
+ unresolved: false,
+ },
+ ];
+ const commentStubRes2 = [
+ {
+ patch_set: 2,
id: 'ecf0b9fa_fe1a5f62',
line: 20,
updated: '2018-02-08 18:49:18.000000000',
@@ -1733,6 +1766,7 @@ limitations under the License.
patch_set: 2,
id: '503008e2_0ab203ee',
line: 10,
+ in_reply_to: 'ecf0b9fa_fe1a5f62',
updated: '2018-02-14 22:07:43.000000000',
message: 'response',
unresolved: true,
@@ -1741,57 +1775,35 @@ limitations under the License.
patch_set: 2,
id: '503008e2_0ab203ef',
line: 20,
- in_reply_to: 'ecf0b9fa_fe1a5f62',
+ in_reply_to: '503008e2_0ab203ee',
updated: '2018-02-15 22:07:43.000000000',
message: 'a third comment in the thread',
unresolved: true,
},
];
- const commentStubRes2 = [
- {
- patch_set: 2,
- id: 'ecf0b9fa_fe1a5f62',
- line: 20,
- updated: '2018-02-08 18:49:18.000000000',
- message: 'edited text',
- unresolved: false,
- },
- ];
- commentStub.withArgs('cc788d2c_cb1d728c').returns(
+ commentStub.withArgs('503008e2_0ab203ee').returns(
commentStubRes1);
commentStub.withArgs('ecf0b9fa_fe1a5f62').returns(
commentStubRes2);
- // Expand the commit message diff
- MockInteractions.keyUpOn(element, 73, 'shift', 'i');
- const diffs = renderAndGetNewDiffs(0);
- flushAsynchronousOperations();
-
- // Two comment threads sould be generated
- const commentThreadEls = diffs[0].getThreadEls();
- assert(commentThreadEls[0].comments.length, 2);
- assert(commentThreadEls[1].comments.length, 1);
- assert.isTrue(commentThreadEls[1].comments[0].unresolved);
- assert.equal(commentThreadEls[1].comments[0].message, 'another comment');
-
- // Reload comments from the first comment thread, which should have a new
- // reply.
- element.reloadCommentsForThreadWithRootId('cc788d2c_cb1d728c',
- '/COMMIT_MSG');
- assert(commentThreadEls[0].comments.length, 3);
-
// Reload comments from the first comment thread, which should have a
// an updated message and a toggled resolve state.
+ element.reloadCommentsForThreadWithRootId('503008e2_0ab203ee',
+ '/COMMIT_MSG');
+ assert.equal(thread1.comments.length, 1);
+ assert.isFalse(thread1.comments[0].unresolved);
+ assert.equal(thread1.comments[0].message, 'edited text');
+
+ // Reload comments from the second comment thread, which should have a new
+ // reply.
element.reloadCommentsForThreadWithRootId('ecf0b9fa_fe1a5f62',
'/COMMIT_MSG');
- assert(commentThreadEls[1].comments.length, 1);
- assert.isFalse(commentThreadEls[1].comments[0].unresolved);
- assert.equal(commentThreadEls[1].comments[0].message, 'edited text');
+ assert.equal(thread2.comments.length, 3);
const commentStubCount = commentStub.callCount;
const getThreadsSpy = sandbox.spy(diffs[0], 'getThreadEls');
- // Should not be getting threadss when the file is not expanded.
+ // Should not be getting threads when the file is not expanded.
element.reloadCommentsForThreadWithRootId('ecf0b9fa_fe1a5f62',
'other/file');
assert.isFalse(getThreadsSpy.called);
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 d2ff035f50..7755a60099 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,6 +19,7 @@
Polymer({
is: 'gr-included-in-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
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 0ac5019b71..27c5baa969 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
@@ -61,7 +61,7 @@ limitations under the License.
background-color: var(--button-background-color, var(--table-header-background-color));
color: var(--primary-text-color);
padding: .2em .85em;
- @apply(--vote-chip-styles);
+ @apply --vote-chip-styles;
}
}
gr-button.iron-selected.max {
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 aa5b61a971..9b5847d7f1 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,6 +19,7 @@
Polymer({
is: 'gr-label-score-row',
+ _legacyUndefinedCheck: true,
/**
* Fired when any label is changed.
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 8734da208f..eaf39bc4a7 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,6 +19,7 @@
Polymer({
is: 'gr-label-scores',
+ _legacyUndefinedCheck: true,
properties: {
_labels: {
type: Array,
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 be03b23e03..d3a46feec2 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -133,9 +133,12 @@ limitations under the License.
right: var(--default-horizontal-margin);
top: 10px;
}
- .date {
+ span.date {
color: var(--deemphasized-text-color);
}
+ span.date:hover {
+ text-decoration: underline;
+ }
.dateContainer iron-icon {
cursor: pointer;
}
@@ -193,15 +196,17 @@ limitations under the License.
class="message hideOnCollapsed"
content="[[message.message]]"
config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
- <div class="replyContainer" hidden$="[[!showReplyButton]]" hidden>
- <gr-button link small on-tap="_handleReplyTap">Reply</gr-button>
- </div>
- <gr-comment-list
- comments="[[comments]]"
- change-num="[[changeNum]]"
- patch-num="[[message._revision_number]]"
- project-name="[[projectName]]"
- project-config="[[_projectConfig]]"></gr-comment-list>
+ <template is="dom-if" if="[[_expanded]]">
+ <div class="replyContainer" hidden$="[[!showReplyButton]]" hidden>
+ <gr-button link small on-tap="_handleReplyTap">Reply</gr-button>
+ </div>
+ <gr-comment-list
+ comments="[[comments]]"
+ change-num="[[changeNum]]"
+ patch-num="[[message._revision_number]]"
+ project-name="[[projectName]]"
+ project-config="[[_projectConfig]]"></gr-comment-list>
+ </template>
</div>
</template>
<template is="dom-if" if="[[_computeIsReviewerUpdate(message)]]">
@@ -228,12 +233,12 @@ limitations under the License.
</span>
</template>
<template is="dom-if" if="[[message.id]]">
- <a class="date" href$="[[_computeMessageHash(message)]]" on-tap="_handleLinkTap">
+ <span class="date" on-tap="_handleAnchorTap">
<gr-date-formatter
has-tooltip
show-date-and-time
date-str="[[message.date]]"></gr-date-formatter>
- </a>
+ </span>
</template>
<iron-icon
id="expandToggle"
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 0590c73eb4..7be1c5d5f6 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -22,17 +22,18 @@
Polymer({
is: 'gr-message',
+ _legacyUndefinedCheck: true,
/**
- * Fired when this message's permalink is tapped.
+ * Fired when this message's reply link is tapped.
*
- * @event scroll-to
+ * @event reply
*/
/**
- * Fired when this message's reply link is tapped.
+ * Fired when the message's timestamp is tapped.
*
- * @event reply
+ * @event message-anchor-tap
*/
listeners: {
@@ -49,7 +50,6 @@
},
comments: {
type: Object,
- observer: '_commentsChanged',
},
config: Object,
hideAutomated: {
@@ -147,17 +147,6 @@
return expanded;
},
- /**
- * If there is no value set on the message object as to whether _expanded
- * should be true or not, then _expanded is set to true if there are
- * inline comments (otherwise false).
- */
- _commentsChanged(value) {
- if (this.message && this.message.expanded === undefined) {
- this.set('message.expanded', Object.keys(value || {}).length > 0);
- }
- },
-
_handleTap(e) {
if (this.message.expanded) { return; }
e.stopPropagation();
@@ -223,22 +212,12 @@
return classes.join(' ');
},
- _computeMessageHash(message) {
- return '#message-' + message.id;
- },
-
- _handleLinkTap(e) {
+ _handleAnchorTap(e) {
e.preventDefault();
-
- this.fire('scroll-to', {message: this.message}, {bubbles: false});
-
- const hash = this._computeMessageHash(this.message);
- // Don't add the hash to the window history if it's already there.
- // Otherwise you mess up expected back button behavior.
- if (window.location.hash == hash) { return; }
- // Change the URL but don’t trigger a nav event. Otherwise it will
- // reload the page.
- page.show(window.location.pathname + hash, null, false);
+ this.dispatchEvent(new CustomEvent('message-anchor-tap', {
+ bubbles: true,
+ detail: {id: this.message.id},
+ }));
},
_handleReplyTap(e) {
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 870f366fa6..566442963e 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
@@ -56,6 +56,7 @@ limitations under the License.
date: '2016-01-12 20:24:49.448000000',
message: 'Uploaded patch set 1.',
_revision_number: 1,
+ expanded: true,
};
element.addEventListener('reply', e => {
@@ -70,6 +71,7 @@ limitations under the License.
element.message = {
tag: 'autogenerated:gerrit:test',
updated: '2016-01-12 20:24:49.448000000',
+ expanded: false,
};
assert.isTrue(element.isAutomated);
@@ -85,6 +87,7 @@ limitations under the License.
tag: 'autogenerated:gerrit:test',
updated: '2016-01-12 20:24:49.448000000',
reviewer: {},
+ expanded: false,
};
assert.isTrue(element.isAutomated);
@@ -100,6 +103,7 @@ limitations under the License.
type: 'REVIEWER_UPDATE',
updated: '2016-01-12 20:24:49.448000000',
reviewer: {},
+ expanded: false,
};
assert.isTrue(element.isAutomated);
@@ -114,6 +118,7 @@ limitations under the License.
element.message = {
tag: 'something',
updated: '2016-01-12 20:24:49.448000000',
+ expanded: false,
};
assert.isFalse(element.isAutomated);
@@ -127,6 +132,7 @@ limitations under the License.
test('reply button hidden unless logged in', () => {
const message = {
message: 'Uploaded patch set 1.',
+ expanded: false,
};
assert.isFalse(element._computeShowReplyButton(message, false));
assert.isTrue(element._computeShowReplyButton(message, true));
@@ -135,6 +141,7 @@ limitations under the License.
test('_computeShowOnBehalfOf', () => {
const message = {
message: '...',
+ expanded: false,
};
assert.isNotOk(element._computeShowOnBehalfOf(message));
message.author = {_account_id: 1115495};
@@ -164,6 +171,25 @@ limitations under the License.
});
});
+ test('clicking on date link fires event', () => {
+ element.message = {
+ type: 'REVIEWER_UPDATE',
+ updated: '2016-01-12 20:24:49.448000000',
+ reviewer: {},
+ id: '47c43261_55aa2c41',
+ expanded: false,
+ };
+ flushAsynchronousOperations();
+ const stub = sinon.stub();
+ element.addEventListener('message-anchor-tap', stub);
+ const dateEl = element.$$('.date');
+ assert.ok(dateEl);
+ MockInteractions.tap(dateEl);
+
+ assert.isTrue(stub.called);
+ assert.deepEqual(stub.lastCall.args[0].detail, {id: element.message.id});
+ });
+
test('votes', () => {
element.message = {
author: {},
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 0a7dacc759..80708a1d33 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
@@ -109,7 +109,7 @@ limitations under the License.
hide-automated="[[_hideAutomated]]"
project-name="[[projectName]]"
show-reply-button="[[showReplyButtons]]"
- on-scroll-to="_handleScrollTo"
+ on-message-anchor-tap="_handleAnchorTap"
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 6d8bb453db..d1cd08d428 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,6 +27,7 @@
Polymer({
is: 'gr-messages-list',
+ _legacyUndefinedCheck: true,
properties: {
changeNum: Number,
@@ -146,6 +147,11 @@
mDate = null;
}
}
+ result.forEach(m => {
+ if (m.expanded === undefined) {
+ m.expanded = false;
+ }
+ });
return result;
},
@@ -184,8 +190,8 @@
this.handleExpandCollapse(!this._expanded);
},
- _handleScrollTo(e) {
- this.scrollToMessage(e.detail.message.id);
+ _handleAnchorTap(e) {
+ this.scrollToMessage(e.detail.id);
},
_hasAutomatedMessages(messages) {
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 7f4d782147..07f6e20d0d 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,6 +19,7 @@
Polymer({
is: 'gr-related-changes-list',
+ _legacyUndefinedCheck: true,
/**
* Fired when a new section is loaded so that the change view can determine
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 eb06b0f681..692cb93866 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
@@ -86,7 +86,6 @@ limitations under the License.
'+1',
],
};
- element.serverConfig = {note_db_enabled: true};
sandbox.stub(element, 'fetchChangeUpdates')
.returns(Promise.resolve({isLatest: true}));
};
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 45b9c15e8e..f8d43cb3ea 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
@@ -171,21 +171,19 @@ limitations under the License.
on-account-text-changed="_handleAccountTextEntry">
</gr-account-list>
</div>
- <template is="dom-if" if="[[serverConfig.note_db_enabled]]">
- <div class="peopleList">
- <div class="peopleListLabel">CC</div>
- <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">
- </gr-account-list>
- </div>
- </template>
+ <div class="peopleList">
+ <div class="peopleListLabel">CC</div>
+ <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">
+ </gr-account-list>
+ </div>
<gr-overlay
id="reviewerConfirmationOverlay"
on-iron-overlay-canceled="_cancelPendingReviewer">
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 b8eeb9084d..43f5e8fbc6 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
@@ -58,6 +58,7 @@
Polymer({
is: 'gr-reply-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when a reply is successfully sent.
@@ -141,10 +142,6 @@
},
permittedLabels: Object,
/**
- * @type {{ note_db_enabled: boolean }}
- */
- serverConfig: Object,
- /**
* @type {{ commentlinks: Array }}
*/
projectConfig: Object,
@@ -194,10 +191,6 @@
type: String,
computed: '_computeSendButtonLabel(canBeStarted)',
},
- _ccsEnabled: {
- type: Boolean,
- computed: '_computeCCsEnabled(serverConfig)',
- },
_savingComments: Boolean,
_reviewersMutated: {
type: Boolean,
@@ -244,7 +237,7 @@
},
observers: [
- '_changeUpdated(change.reviewers.*, change.owner, serverConfig)',
+ '_changeUpdated(change.reviewers.*, change.owner)',
'_ccsChanged(_ccs.splices)',
'_reviewersChanged(_reviewers.splices)',
],
@@ -628,14 +621,14 @@
'Say something nice...';
},
- _changeUpdated(changeRecord, owner, serverConfig) {
- this._rebuildReviewerArrays(changeRecord.base, owner, serverConfig);
+ _changeUpdated(changeRecord, owner) {
+ this._rebuildReviewerArrays(changeRecord.base, owner);
},
- _rebuildReviewerArrays(change, owner, serverConfig) {
+ _rebuildReviewerArrays(change, owner) {
this._owner = owner;
- let reviewers = [];
+ const reviewers = [];
const ccs = [];
for (const key in change) {
@@ -660,12 +653,7 @@
}
}
- if (this._ccsEnabled) {
- this._ccs = ccs;
- } else {
- this._ccs = [];
- reviewers = reviewers.concat(ccs);
- }
+ this._ccs = ccs;
this._reviewers = reviewers;
},
@@ -719,13 +707,12 @@
this.fire('cancel', null, {bubbles: false});
this.$.textarea.closeDropdown();
this._purgeReviewersPendingRemove(true);
- this._rebuildReviewerArrays(this.change.reviewers, this._owner,
- this.serverConfig);
+ this._rebuildReviewerArrays(this.change.reviewers, this._owner);
},
_saveTapHandler(e) {
e.preventDefault();
- if (this._ccsEnabled && !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;
@@ -741,7 +728,7 @@
},
_submit() {
- if (this._ccsEnabled && !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;
@@ -855,10 +842,6 @@
return canBeStarted ? ButtonTooltips.START_REVIEW : ButtonTooltips.SEND;
},
- _computeCCsEnabled(serverConfig) {
- return serverConfig && serverConfig.note_db_enabled;
- },
-
_computeSavingLabelClass(savingComments) {
return savingComments ? 'saving' : '';
},
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 733a49037c..5577c1b586 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
@@ -97,7 +97,6 @@ limitations under the License.
'+1',
],
};
- element.serverConfig = {};
getDraftCommentStub = sandbox.stub(element.$.storage, 'getDraftComment');
setDraftCommentStub = sandbox.stub(element.$.storage, 'setDraftComment');
@@ -296,7 +295,6 @@ limitations under the License.
const noButton =
element.$$('.reviewerConfirmationButtons gr-button:last-child');
- element.serverConfig = {note_db_enabled: true};
element._ccPendingConfirmation = null;
element._reviewerPendingConfirmation = null;
flushAsynchronousOperations();
@@ -409,7 +407,6 @@ limitations under the License.
});
test('_reviewersMutated when account-text-change is fired from ccs', () => {
- element.serverConfig = {note_db_enabled: true};
flushAsynchronousOperations();
assert.isFalse(element._reviewersMutated);
assert.isTrue(element.$$('#ccs').allowAnyInput);
@@ -496,19 +493,6 @@ limitations under the License.
flush(() => { element.send(); });
});
- test('ccs are displayed if NoteDb is enabled', () => {
- function hasCc() {
- flushAsynchronousOperations();
- return !!element.$$('#ccs');
- }
-
- element.serverConfig = {};
- assert.isFalse(hasCc());
-
- element.serverConfig = {note_db_enabled: true};
- assert.isTrue(hasCc());
- });
-
test('filterReviewerSuggestion', () => {
const owner = makeAccount();
const reviewer1 = makeAccount();
@@ -540,7 +524,6 @@ limitations under the License.
test('_focusOn', () => {
sandbox.spy(element, '_chooseFocusTarget');
- element.serverConfig = {note_db_enabled: true};
flushAsynchronousOperations();
const textareaStub = sandbox.stub(element.$.textarea, 'async');
const reviewerEntryStub = sandbox.stub(element.$.reviewers.focusStart,
@@ -680,7 +663,6 @@ limitations under the License.
});
test('moving from cc to reviewer', () => {
- element.serverConfig = {note_db_enabled: true};
element._reviewersPendingRemove = {
CC: [],
REVIEWER: [],
@@ -714,7 +696,6 @@ limitations under the License.
});
test('migrate reviewers between states', done => {
- element.serverConfig = {note_db_enabled: true};
element._reviewersPendingRemove = {
CC: [],
REVIEWER: [],
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 ab677041d7..4f1b05155c 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,6 +19,7 @@
Polymer({
is: 'gr-reviewer-list',
+ _legacyUndefinedCheck: true,
/**
* Fired when the "Add reviewer..." button is tapped.
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 2201a9a10b..4d8e5aea52 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
@@ -18,7 +18,7 @@ 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="../../../styles/shared-styles.html">
-<link rel="import" href="../../diff/gr-diff-comment-thread/gr-diff-comment-thread.html">
+<link rel="import" href="../../shared/gr-comment-thread/gr-comment-thread.html">
<dom-module id="gr-thread-list">
<template>
@@ -28,7 +28,7 @@ limitations under the License.
min-height: 20rem;
padding: 1rem;
}
- gr-diff-comment-thread {
+ gr-comment-thread {
display: block;
margin-bottom: .5rem;
max-width: 80ch;
@@ -54,9 +54,9 @@ limitations under the License.
display: flex;
margin-right: 1rem;
}
- .draftsOnly:not(.unresolvedOnly) gr-diff-comment-thread[has-draft],
- .unresolvedOnly:not(.draftsOnly) gr-diff-comment-thread[unresolved],
- .draftsOnly.unresolvedOnly gr-diff-comment-thread[has-draft][unresolved] {
+ .draftsOnly:not(.unresolvedOnly) gr-comment-thread[has-draft],
+ .unresolvedOnly:not(.draftsOnly) gr-comment-thread[unresolved],
+ .draftsOnly.unresolvedOnly gr-comment-thread[has-draft][unresolved] {
display: block
}
</style>
@@ -82,7 +82,7 @@ limitations under the License.
as="thread"
initial-count="5"
target-framerate="60">
- <gr-diff-comment-thread
+ <gr-comment-thread
show-file-path
change-num="[[changeNum]]"
comments="[[thread.comments]]"
@@ -94,7 +94,7 @@ limitations under the License.
path="[[thread.path]]"
root-id="{{thread.rootId}}"
on-thread-changed="_handleCommentsChanged"
- on-thread-discard="_handleThreadDiscard"></gr-diff-comment-thread>
+ on-thread-discard="_handleThreadDiscard"></gr-comment-thread>
</template>
</div>
</template>
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 dd6db8cb9b..317685b207 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,6 +25,7 @@
Polymer({
is: 'gr-thread-list',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
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 804446aadb..792644e7b0 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
@@ -171,7 +171,7 @@ limitations under the License.
];
flushAsynchronousOperations();
threadElements = Polymer.dom(element.root)
- .querySelectorAll('gr-diff-comment-thread');
+ .querySelectorAll('gr-comment-thread');
});
teardown(() => {
@@ -188,7 +188,7 @@ limitations under the License.
test('there are five threads by default', () => {
assert.equal(Polymer.dom(element.root)
- .querySelectorAll('gr-diff-comment-thread').length, 5);
+ .querySelectorAll('gr-comment-thread').length, 5);
});
test('_computeSortedThreads', () => {
@@ -231,14 +231,14 @@ limitations under the License.
MockInteractions.tap(element.$.unresolvedToggle);
flushAsynchronousOperations();
assert.equal(Polymer.dom(element.root)
- .querySelectorAll('gr-diff-comment-thread').length, 3);
+ .querySelectorAll('gr-comment-thread').length, 3);
});
test('toggle drafts only shows threads with draft comments', () => {
MockInteractions.tap(element.$.draftToggle);
flushAsynchronousOperations();
assert.equal(Polymer.dom(element.root)
- .querySelectorAll('gr-diff-comment-thread').length, 2);
+ .querySelectorAll('gr-comment-thread').length, 2);
});
test('toggle drafts and unresolved only shows threads with drafts and ' +
@@ -247,7 +247,7 @@ limitations under the License.
MockInteractions.tap(element.$.unresolvedToggle);
flushAsynchronousOperations();
assert.equal(Polymer.dom(element.root)
- .querySelectorAll('gr-diff-comment-thread').length, 2);
+ .querySelectorAll('gr-comment-thread').length, 2);
});
test('modification events are consumed and displatched', () => {
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 02d00cf714..df96be2d8f 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,6 +29,7 @@
Polymer({
is: 'gr-upload-help-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
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 72ec7faf3c..af4510d360 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,6 +21,7 @@
Polymer({
is: 'gr-account-dropdown',
+ _legacyUndefinedCheck: true,
properties: {
account: Object,
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 8d3b58e363..5679408286 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,6 +19,7 @@
Polymer({
is: 'gr-error-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the dismiss button is pressed.
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 3ec4bb5818..4ca106eeb6 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
@@ -18,6 +18,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="../../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">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -32,6 +33,7 @@ limitations under the License.
confirm-on-enter></gr-error-dialog>
</gr-overlay>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ <gr-reporting id="reporting"></gr-reporting>
</template>
<script src="gr-error-manager.js"></script>
</dom-module>
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 b254182672..38dfecb5a3 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,6 +27,7 @@
Polymer({
is: 'gr-error-manager',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.BaseUrlBehavior,
@@ -283,6 +284,7 @@
},
_showErrorDialog(message) {
+ this.$.reporting.reportErrorDialog(message);
this.$.errorDialog.text = message;
this.$.errorOverlay.open();
},
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 f92feae404..88e3efd9dc 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
@@ -304,11 +304,14 @@ limitations under the License.
test('show-error', () => {
const openStub = sandbox.stub(element.$.errorOverlay, 'open');
const closeStub = sandbox.stub(element.$.errorOverlay, 'close');
+ const reportStub = sandbox.stub(element.$.reporting, 'reportErrorDialog');
+
const message = 'test message';
element.fire('show-error', {message});
flushAsynchronousOperations();
assert.isTrue(openStub.called);
+ assert.isTrue(reportStub.called);
assert.equal(element.$.errorDialog.text, message);
element.$.errorDialog.fire('dismiss');
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 89d1091086..e8c6479947 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,6 +19,7 @@
Polymer({
is: 'gr-key-binding-display',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {Array<string>} */
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 5b29972e40..f206db1496 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,6 +21,7 @@
Polymer({
is: 'gr-keyboard-shortcuts-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the user presses the close button.
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 e38b16e6a8..e552cdad36 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
@@ -155,6 +155,9 @@ limitations under the License.
gr-account-dropdown {
color: var(--header-text-color);
}
+ #mobileSearch {
+ display: none;
+ }
@media screen and (max-width: 50em) {
.bigTitle {
font-size: var(--font-size-large);
@@ -166,6 +169,9 @@ limitations under the License.
.links > li.hideOnMobile {
display: none;
}
+ #mobileSearch {
+ display: inline-flex;
+ }
.accountContainer {
margin-left: .5em !important;
}
@@ -206,6 +212,7 @@ limitations under the License.
class="hideOnMobile"
name="header-browse-source"></gr-endpoint-decorator>
<div class="accountContainer" id="accountContainer">
+ <iron-icon id="mobileSearch" icon="gr-icons:search" on-tap='_onMobileSearchTap'></iron-icon>
<div class$="[[_computeIsInvisible(_registerURL)]]">
<a
class="registerButton"
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 65757c754e..69fc89ff0b 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,6 +71,7 @@
Polymer({
is: 'gr-main-header',
+ _legacyUndefinedCheck: true,
hostAttributes: {
role: 'banner',
@@ -276,8 +277,7 @@
if (!account) { return; }
this.$.restAPI.getPreferences().then(prefs => {
- this._userLinks =
- prefs.my.map(this._fixCustomMenuItem).filter(this._isSupportedLink);
+ this._userLinks = prefs.my.map(this._fixCustomMenuItem);
});
},
@@ -312,13 +312,13 @@
return linkObj;
},
- _isSupportedLink(linkObj) {
- // Groups are not yet supported.
- return !linkObj.url.startsWith('/groups');
- },
-
_generateSettingsLink() {
return this.getBaseUrl() + '/settings/';
},
+
+ _onMobileSearchTap(e) {
+ e.preventDefault();
+ this.fire('mobile-search', null, {bubbles: false});
+ },
});
})();
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 77328704e3..89b3908754 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
@@ -91,17 +91,6 @@ limitations under the License.
]);
});
- test('filter unsupported urls', () => {
- assert.deepEqual([
- {url: '/c/331788/'},
- {url: '/groups/self'},
- {url: 'https://awesometown.com/#hashyhash'},
- ].filter(element._isSupportedLink), [
- {url: '/c/331788/'},
- {url: 'https://awesometown.com/#hashyhash'},
- ]);
- });
-
test('user links', () => {
const defaultLinks = [{
title: 'Faves',
diff --git a/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.html b/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.html
deleted file mode 100644
index b2b382ca50..0000000000
--- a/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!--
-@license
-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.
--->
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-
-<dom-module id="gr-message-header">
- <template>
- <style include="shared-styles">
- #container {
- background-color: lightyellow;
- display: flex;
- height: fit-content;
- justify-content: space-between;
- padding: 1em;
- }
- </style>
- <div id="container" hidden$="[[_hidden]]">
- <div id="message"></div>
- <gr-button id="dismissMessageBtn"
- link
- on-tap="_handleDismissMessage">Dismiss</gr-button>
- </div>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- </template>
- <script src="../../../scripts/util.js"></script>
- <script src="gr-message-header.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.js b/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.js
deleted file mode 100644
index fac4a6a399..0000000000
--- a/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @license
- * 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.
- */
-(function() {
- 'use strict';
-
- Polymer({
- is: 'gr-message-header',
-
- properties: {
- message: {
- type: Object,
- reflectToAttribute: true,
- },
- _hidden: {
- type: Boolean,
- value: true,
- },
- },
-
- attached() {
- if (!this.message || !this.message.html) {
- return;
- }
- this._isHidden();
- this.$.message.innerHTML = this.message.html;
- },
-
- _handleDismissMessage() {
- document.cookie =
- `msg-${this.message.id}=1; expires=${this.message.redisplay}`;
- this._hidden = true;
- },
-
- _isHidden() {
- this._hidden = window.util.getCookie(`msg-${this.message.id}`) === '1';
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header_test.html b/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header_test.html
deleted file mode 100644
index c275e55345..0000000000
--- a/polygerrit-ui/app/elements/core/gr-message-header/gr-message-header_test.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-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.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-message-header</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"/>
-<link rel="import" href="gr-message-header.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-message-header></gr-message-header>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-message-header tests', () => {
- let element;
-
- setup(() => {
- element = fixture('basic');
- });
-
- test('show message', () => {
- element.message = {html: 'This is a test message.'};
- element.attached();
- assert.equal(element.$.message.innerHTML, element.message.html);
- });
-
- test('hide message on dismiss', () => {
- element.message = {html: 'This is a test message.', id: 'test'};
- element.attached();
- MockInteractions.tap(element.$.dismissMessageBtn);
- assert.isTrue(element.$.container.hidden);
- assert.isTrue(document.cookie.includes('msg-test=1'));
-
- element.attached();
- assert.isTrue(element.$.container.hidden);
- });
- });
-</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 3d0f6f21ca..ce2012770e 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -33,6 +33,8 @@ limitations under the License.
// also be provided.
// - `edit`, optional, Boolean: whether or not to load the file list with
// edit controls.
+ // - `messageHash`, optional, String: the hash of the change message to
+ // scroll to.
//
// - Gerrit.Nav.View.SEARCH:
// - `query`, optional, String: the literal search query. If provided,
@@ -138,10 +140,10 @@ limitations under the License.
'(reviewer:${user} OR assignee:${user})',
},
{
- // Open changes the viewed user is CCed on. Changes ignored by the viewing
- // user are filtered out.
+ // 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:ignored cc:${user}',
+ query: 'is:open -is:wip -is:ignored cc:${user}',
},
{
name: 'Recently closed',
@@ -364,9 +366,11 @@ limitations under the License.
* @param {number|string=} opt_basePatchNum The string 'PARENT' can be
* used for none.
* @param {boolean=} opt_isEdit
+ * @param {string=} opt_messageHash
* @return {string}
*/
- getUrlForChange(change, opt_patchNum, opt_basePatchNum, opt_isEdit) {
+ getUrlForChange(change, opt_patchNum, opt_basePatchNum, opt_isEdit,
+ opt_messageHash) {
if (opt_basePatchNum === PARENT_PATCHNUM) {
opt_basePatchNum = undefined;
}
@@ -380,6 +384,7 @@ limitations under the License.
basePatchNum: opt_basePatchNum,
edit: opt_isEdit,
host: change.internalHost || undefined,
+ messageHash: opt_messageHash,
});
},
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 9b4c6114f7..0947d77d09 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -69,6 +69,11 @@
CATEGORY: 'exception',
};
+ const ERROR_DIALOG = {
+ TYPE: 'error',
+ CATEGORY: 'Error Dialog',
+ };
+
const TIMER = {
CHANGE_DISPLAYED: 'ChangeDisplayed',
CHANGE_LOAD_FULL: 'ChangeFullyLoaded',
@@ -139,6 +144,7 @@
// eslint-disable-next-line prefer-const
let GrReporting = Polymer({
is: 'gr-reporting',
+ _legacyUndefinedCheck: true,
properties: {
category: String,
@@ -192,7 +198,7 @@
};
document.dispatchEvent(new CustomEvent(type, {detail}));
if (opt_noLog) { return; }
- if (type === ERROR.TYPE) {
+ if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
console.error(eventValue.error || eventName);
} else {
console.log(eventName + (eventValue !== undefined ?
@@ -212,7 +218,7 @@
* logged to the JS console.
*/
cachingReporter(type, category, eventName, eventValue, opt_noLog) {
- if (type === ERROR.TYPE) {
+ if (type === ERROR.TYPE && category === ERROR.CATEGORY) {
console.error(eventValue.error || eventName);
}
if (this._arePluginsLoaded()) {
@@ -231,10 +237,8 @@
* User-perceived app start time, should be reported when the app is ready.
*/
appStarted(hidden) {
- const startTime =
- new Date().getTime() - this.performanceTiming.navigationStart;
this.reporter(TIMING.TYPE, TIMING.CATEGORY_UI_LATENCY,
- TIMING.APP_STARTED, startTime);
+ TIMING.APP_STARTED, this.now());
if (hidden) {
this.reporter(PAGE_VISIBILITY.TYPE, PAGE_VISIBILITY.CATEGORY,
PAGE_VISIBILITY.STARTED_HIDDEN);
@@ -458,6 +462,11 @@
// Mark the time and reinitialize the timer.
timer.end().reset();
},
+
+ reportErrorDialog(message) {
+ this.reporter(ERROR_DIALOG.TYPE, ERROR_DIALOG.CATEGORY,
+ 'ErrorDialog: ' + message);
+ },
});
window.GrReporting = GrReporting;
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 cd5e2e5134..29e70ea74e 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
@@ -61,11 +61,11 @@ limitations under the License.
});
test('appStarted', () => {
+ sandbox.stub(element, 'now').returns(42);
element.appStarted(true);
assert.isTrue(
element.reporter.calledWithExactly(
- 'timing-report', 'UI Latency', 'App Started',
- NOW_TIME - fakePerformance.navigationStart
+ 'timing-report', 'UI Latency', 'App Started', 42
));
assert.isTrue(
element.reporter.calledWithExactly(
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 efc95ce2b4..2e1f5b8369 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -39,6 +39,9 @@
// Matches /admin/groups/[uuid-]<group>
GROUP: /^\/admin\/groups\/(?:uuid-)?([^,]+)$/,
+ // Redirects /groups/self to /settings/#Groups for GWT compatibility
+ GROUP_SELF: /^\/groups\/self/,
+
// Matches /admin/groups/[uuid-]<group>,info (backwords compat with gwtui)
// Redirects to /admin/groups/[uuid-]<group>
GROUP_INFO: /^\/admin\/groups\/(?:uuid-)?(.+),info$/,
@@ -207,6 +210,7 @@
Polymer({
is: 'gr-router',
+ _legacyUndefinedCheck: true,
properties: {
_app: {
@@ -402,6 +406,9 @@
} else if (params.edit) {
suffix += ',edit';
}
+ if (params.messageHash) {
+ suffix += params.messageHash;
+ }
if (params.project) {
const encodedProject = this.encodeURL(params.project, true);
return `/c/${encodedProject}/+/${params.changeNum}${suffix}`;
@@ -585,16 +592,6 @@
params.patchNum = params.basePatchNum;
params.basePatchNum = null;
}
- // In GWTUI, edits are represented in URLs with either 0 or 'edit'.
- // TODO(kaspern): Remove this normalization when GWT UI is gone.
- if (this.patchNumEquals(params.basePatchNum, 0)) {
- params.basePatchNum = this.EDIT_NAME;
- needsRedirect = true;
- }
- if (this.patchNumEquals(params.patchNum, 0)) {
- params.patchNum = this.EDIT_NAME;
- needsRedirect = true;
- }
return needsRedirect;
},
@@ -646,7 +643,7 @@
return Promise.resolve();
} else {
this._redirectToLogin(data.canonicalPath);
- return Promise.reject();
+ return Promise.reject(new Error());
}
});
},
@@ -758,6 +755,9 @@
this._mapRoute(RoutePattern.GROUP_LIST_FILTER,
'_handleGroupListFilterRoute', true);
+ this._mapRoute(RoutePattern.GROUP_SELF, '_handleGroupSelfRedirectRoute',
+ true);
+
this._mapRoute(RoutePattern.GROUP, '_handleGroupRoute', true);
this._mapRoute(RoutePattern.PROJECT_OLD,
@@ -1055,6 +1055,10 @@
this._redirect('/admin/groups/' + encodeURIComponent(data.params[0]));
},
+ _handleGroupSelfRedirectRoute(data) {
+ this._redirect('/settings/#Groups');
+ },
+
_handleGroupRoute(data) {
this._setParams({
view: Gerrit.Nav.View.GROUP,
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 84b211be7c..168dc289db 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
@@ -179,6 +179,7 @@ limitations under the License.
'_handleGroupListOffsetRoute',
'_handleGroupMembersRoute',
'_handleGroupRoute',
+ '_handleGroupSelfRedirectRoute',
'_handleNewAgreementsRoute',
'_handlePluginListFilterOffsetRoute',
'_handlePluginListFilterRoute',
@@ -328,6 +329,9 @@ limitations under the License.
paramsWithQuery.basePatchNum = 5;
assert.equal(element._generateUrl(paramsWithQuery),
'/c/test/+/1234/5..10?revert&foo=bar');
+
+ params.messageHash = '#123';
+ assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10#123');
});
test('change with repo name encoding', () => {
@@ -599,30 +603,6 @@ limitations under the License.
assert.isNotOk(params.basePatchNum);
assert.equal(params.patchNum, 4);
});
-
- test('range 0..n normalizes to edit..n', () => {
- const params = {basePatchNum: 0, patchNum: 4};
- const needsRedirect = element._normalizePatchRangeParams(params);
- assert.isTrue(needsRedirect);
- assert.equal(params.basePatchNum, 'edit');
- assert.equal(params.patchNum, 4);
- });
-
- test('range n..0 normalizes to n..edit', () => {
- const params = {basePatchNum: 4, patchNum: 0};
- const needsRedirect = element._normalizePatchRangeParams(params);
- assert.isTrue(needsRedirect);
- assert.equal(params.basePatchNum, 4);
- assert.equal(params.patchNum, 'edit');
- });
-
- test('range 0..0 normalizes to edit', () => {
- const params = {basePatchNum: 0, patchNum: 0};
- const needsRedirect = element._normalizePatchRangeParams(params);
- assert.isTrue(needsRedirect);
- assert.isNotOk(params.basePatchNum);
- assert.equal(params.patchNum, 'edit');
- });
});
});
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 9a2be9649b..c877ac412d 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
@@ -36,7 +36,12 @@
'conflicts:',
'deleted:',
'delta:',
+ 'dir:',
+ 'directory:',
+ 'ext:',
+ 'extension:',
'file:',
+ 'footer:',
'from:',
'has:',
'has:draft',
@@ -65,6 +70,8 @@
'is:wip',
'label:',
'message:',
+ 'onlyexts:',
+ 'onlyextensions:',
'owner:',
'ownerin:',
'parentproject:',
@@ -99,6 +106,7 @@
Polymer({
is: 'gr-search-bar',
+ _legacyUndefinedCheck: true,
/**
* Fired when a search is committed
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 f2f9a4c6a5..65141aa4b4 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,6 +23,7 @@
Polymer({
is: 'gr-smart-search',
+ _legacyUndefinedCheck: true,
properties: {
searchQuery: String,
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 b7994e6078..7bf71f5c66 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,6 +19,7 @@
Polymer({
is: 'comment-api-mock',
+ _legacyUndefinedCheck: true,
properties: {
_changeComments: Object,
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 6c0d19a746..37491d265a 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
@@ -485,6 +485,7 @@
Polymer({
is: 'gr-comment-api',
+ _legacyUndefinedCheck: true,
properties: {
_changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js b/polygerrit-ui/app/elements/diff/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
deleted file mode 100644
index b86b72a8bc..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * @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.
- */
-(function() {
- 'use strict';
-
- Polymer({
- is: 'gr-confirm-delete-comment-dialog',
-
- /**
- * Fired when the confirm button is pressed.
- *
- * @event confirm
- */
-
- /**
- * Fired when the cancel button is pressed.
- *
- * @event cancel
- */
-
- properties: {
- message: String,
- },
-
- resetFocus() {
- this.$.messageInput.textarea.focus();
- },
-
- _handleConfirmTap(e) {
- e.preventDefault();
- this.fire('confirm', {reason: this.message}, {bubbles: false});
- },
-
- _handleCancelTap(e) {
- e.preventDefault();
- this.fire('cancel', null, {bubbles: false});
- },
- });
-})();
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
new file mode 100644
index 0000000000..56a6fb9b1a
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.html
@@ -0,0 +1,24 @@
+<!--
+@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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<dom-module id="gr-coverage-layer">
+ <template>
+ </template>
+ <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
new file mode 100644
index 0000000000..e8d6900c7d
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer.js
@@ -0,0 +1,137 @@
+/**
+ * @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';
+
+ /** @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.'],
+ [Gerrit.CoverageType.PARTIALLY_COVERED, 'Partially covered by tests.'],
+ [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',
+
+ properties: {
+ /**
+ * Must be sorted by code_range.start_line.
+ * Must only contain ranges that match the side.
+ *
+ * @type {!Array<!Gerrit.CoverageRange>}
+ */
+ coverageRanges: Array,
+ side: String,
+
+ /**
+ * We keep track of the line number from the previous annotate() call,
+ * and also of the index of the coverage range that had matched.
+ * annotate() calls are coming in with increasing line numbers and
+ * coverage ranges are sorted by line number. So this is a very simple
+ * and efficient way for finding the coverage range that matches a given
+ * line number.
+ */
+ _lineNumber: {
+ type: Number,
+ value: 0,
+ },
+ _index: {
+ type: Number,
+ value: 0,
+ },
+ },
+
+ /**
+ * Layer method to add annotations to a line.
+ *
+ * @param {!HTMLElement} el Not used for this layer.
+ * @param {!HTMLElement} lineNumberEl The <td> element with the line number.
+ * @param {!Object} line Not used for this layer.
+ */
+ annotate(el, lineNumberEl, line) {
+ if (!lineNumberEl || !lineNumberEl.classList.contains(this.side)) {
+ return;
+ }
+ const elementLineNumber = parseInt(
+ lineNumberEl.getAttribute('data-value'), 10);
+ if (!elementLineNumber || elementLineNumber < 1) return;
+
+ // If the line number is smaller than before, then we have to reset our
+ // algorithm and start searching the coverage ranges from the beginning.
+ // That happens for example when you expand diff sections.
+ if (elementLineNumber < this._lineNumber) {
+ this._index = 0;
+ }
+ this._lineNumber = elementLineNumber;
+
+ // We simply loop through all the coverage ranges until we find one that
+ // matches the line number.
+ while (this._index < this.coverageRanges.length) {
+ const coverageRange = this.coverageRanges[this._index];
+
+ // If the line number has moved past the current coverage range, then
+ // try the next coverage range.
+ if (this._lineNumber > coverageRange.code_range.end_line) {
+ this._index++;
+ continue;
+ }
+
+ // If the line number has not reached the next coverage range (and the
+ // range before also did not match), then this line has not been
+ // instrumented. Nothing to do for this line.
+ if (this._lineNumber < coverageRange.code_range.start_line) {
+ return;
+ }
+
+ // The line number is within the current coverage range. Style it!
+ lineNumberEl.classList.add(coverageRange.type);
+ lineNumberEl.title = TOOLTIP_MAP.get(coverageRange.type);
+ return;
+ }
+ },
+ });
+})();
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
new file mode 100644
index 0000000000..edd88a24e1
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-coverage-layer/gr-coverage-layer_test.html
@@ -0,0 +1,138 @@
+<!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-coverage-layer</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.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"/>
+
+<link rel="import" href="gr-coverage-layer.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-coverage-layer></gr-coverage-layer>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-coverage-layer', () => {
+ let element;
+
+ setup(() => {
+ const initialCoverageRanges = [
+ {
+ 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,
+ },
+ },
+ {
+ type: 'PARTIALLY_COVERED',
+ side: 'right',
+ code_range: {
+ start_line: 5,
+ end_line: 6,
+ },
+ },
+ {
+ type: 'NOT_INSTRUMENTED',
+ side: 'right',
+ code_range: {
+ start_line: 8,
+ end_line: 9,
+ },
+ },
+ ];
+
+ element = fixture('basic');
+ element.coverageRanges = initialCoverageRanges;
+ element.side = 'right';
+ });
+
+ suite('annotate', () => {
+ function createLine(lineNumber) {
+ lineEl = document.createElement('div');
+ lineEl.setAttribute('data-side', 'right');
+ lineEl.setAttribute('data-value', lineNumber);
+ lineEl.className = 'right';
+ return lineEl;
+ }
+
+ function checkLine(lineNumber, className, opt_negated) {
+ const line = createLine(lineNumber);
+ element.annotate(undefined, line, undefined);
+ let contains = line.classList.contains(className);
+ if (opt_negated) contains = !contains;
+ assert.isTrue(contains);
+ }
+
+ test('line 1-2 are covered', () => {
+ checkLine(1, 'COVERED');
+ checkLine(2, 'COVERED');
+ });
+
+ test('line 3-4 are not covered', () => {
+ checkLine(3, 'NOT_COVERED');
+ checkLine(4, 'NOT_COVERED');
+ });
+
+ test('line 5-6 are partially covered', () => {
+ checkLine(5, 'PARTIALLY_COVERED');
+ checkLine(6, 'PARTIALLY_COVERED');
+ });
+
+ test('line 7 is implicitly not instrumented', () => {
+ checkLine(7, 'COVERED', true);
+ checkLine(7, 'NOT_COVERED', true);
+ checkLine(7, 'PARTIALLY_COVERED', true);
+ checkLine(7, 'NOT_INSTRUMENTED', true);
+ });
+
+ test('line 8-9 are not instrumented', () => {
+ checkLine(8, 'NOT_INSTRUMENTED');
+ checkLine(9, 'NOT_INSTRUMENTED');
+ });
+
+ test('coverage correct, if annotate is called out of order', () => {
+ checkLine(8, 'NOT_INSTRUMENTED');
+ checkLine(1, 'COVERED');
+ checkLine(5, 'PARTIALLY_COVERED');
+ checkLine(3, 'NOT_COVERED');
+ checkLine(6, 'PARTIALLY_COVERED');
+ checkLine(4, 'NOT_COVERED');
+ checkLine(9, 'NOT_INSTRUMENTED');
+ checkLine(2, 'COVERED');
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-binary.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-binary.js
index b2fc64cd6f..6f5a8d356e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-binary.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-binary.js
@@ -14,14 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-(function(window, GrDiffBuilderSideBySide) {
+(function(window, GrDiffBuilder) {
'use strict';
// Prevent redefinition.
if (window.GrDiffBuilderBinary) { return; }
- function GrDiffBuilderBinary(diff, comments, prefs, outputEl) {
- GrDiffBuilder.call(this, diff, comments, null, prefs, outputEl);
+ function GrDiffBuilderBinary(diff, prefs, outputEl) {
+ GrDiffBuilder.call(this, diff, prefs, outputEl);
}
GrDiffBuilderBinary.prototype = Object.create(GrDiffBuilder.prototype);
@@ -43,4 +43,4 @@
};
window.GrDiffBuilderBinary = GrDiffBuilderBinary;
-})(window, GrDiffBuilderSideBySide);
+})(window, GrDiffBuilder);
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 7eaca0de64..11bea8c820 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
@@ -24,10 +24,8 @@
// arbitrary JavaScript.
const IMAGE_MIME_PATTERN = /^image\/(bmp|gif|x-icon|jpeg|jpg|png|tiff|webp)$/;
- function GrDiffBuilderImage(diff, comments, createThreadGroupFn, prefs,
- outputEl, baseImage, revisionImage) {
- GrDiffBuilderSideBySide.call(this, diff, comments, createThreadGroupFn,
- prefs, outputEl, []);
+ function GrDiffBuilderImage(diff, prefs, outputEl, baseImage, revisionImage) {
+ GrDiffBuilderSideBySide.call(this, diff, prefs, outputEl, []);
this._baseImage = baseImage;
this._revisionImage = revisionImage;
}
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 fafae6324a..1ef278f99d 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
@@ -20,10 +20,8 @@
// Prevent redefinition.
if (window.GrDiffBuilderSideBySide) { return; }
- function GrDiffBuilderSideBySide(diff, comments, createThreadGroupFn, prefs,
- outputEl, layers) {
- GrDiffBuilder.call(this, diff, comments, createThreadGroupFn, prefs,
- outputEl, layers);
+ function GrDiffBuilderSideBySide(diff, prefs, outputEl, layers) {
+ GrDiffBuilder.call(this, diff, prefs, outputEl, layers);
}
GrDiffBuilderSideBySide.prototype = Object.create(GrDiffBuilder.prototype);
GrDiffBuilderSideBySide.prototype.constructor = GrDiffBuilderSideBySide;
@@ -91,18 +89,14 @@
GrDiffBuilderSideBySide.prototype._appendPair = function(section, row, line,
lineNumber, side) {
- const lineEl = this._createLineEl(line, lineNumber, line.type, side);
- lineEl.classList.add(side);
- row.appendChild(lineEl);
+ const lineNumberEl = this._createLineEl(line, lineNumber, line.type, side);
+ lineNumberEl.classList.add(side);
+ row.appendChild(lineNumberEl);
const action = this._createContextControl(section, line);
if (action) {
row.appendChild(action);
} else {
- const textEl = this._createTextEl(line, side);
- const threadGroupEl = this._commentThreadGroupForLine(line, side);
- if (threadGroupEl) {
- textEl.appendChild(threadGroupEl);
- }
+ const textEl = this._createTextEl(lineNumberEl, line, side);
row.appendChild(textEl);
}
};
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 247ccf7a45..6be209799e 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
@@ -20,10 +20,8 @@
// Prevent redefinition.
if (window.GrDiffBuilderUnified) { return; }
- function GrDiffBuilderUnified(diff, comments, createThreadGroupFn, prefs,
- outputEl, layers) {
- GrDiffBuilder.call(this, diff, comments, createThreadGroupFn, prefs,
- outputEl, layers);
+ function GrDiffBuilderUnified(diff, prefs, outputEl, layers) {
+ GrDiffBuilder.call(this, diff, prefs, outputEl, layers);
}
GrDiffBuilderUnified.prototype = Object.create(GrDiffBuilder.prototype);
GrDiffBuilderUnified.prototype.constructor = GrDiffBuilderUnified;
@@ -70,28 +68,24 @@
GrDiffBuilderUnified.prototype._createRow = function(section, line) {
const row = this._createElement('tr', line.type);
+ row.classList.add('diff-row', 'unified');
+ row.tabIndex = -1;
row.appendChild(this._createBlameCell(line));
- let lineEl = this._createLineEl(line, line.beforeNumber,
+ let lineNumberEl = this._createLineEl(line, line.beforeNumber,
GrDiffLine.Type.REMOVE);
- lineEl.classList.add('left');
- row.appendChild(lineEl);
- lineEl = this._createLineEl(line, line.afterNumber,
+ lineNumberEl.classList.add('left');
+ row.appendChild(lineNumberEl);
+ lineNumberEl = this._createLineEl(line, line.afterNumber,
GrDiffLine.Type.ADD);
- lineEl.classList.add('right');
- row.appendChild(lineEl);
- row.classList.add('diff-row', 'unified');
- row.tabIndex = -1;
+ lineNumberEl.classList.add('right');
+ row.appendChild(lineNumberEl);
const action = this._createContextControl(section, line);
if (action) {
row.appendChild(action);
} else {
- const textEl = this._createTextEl(line);
- const threadGroupEl = this._commentThreadGroupForLine(line);
- if (threadGroupEl) {
- textEl.appendChild(threadGroupEl);
- }
+ const textEl = this._createTextEl(lineNumberEl, line);
row.appendChild(textEl);
}
return row;
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 2a2b75316b..d42d8c1fd6 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
@@ -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="../../core/gr-reporting/gr-reporting.html">
-<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
-<link rel="import" href="../gr-diff-comment-thread-group/gr-diff-comment-thread-group.html">
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.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">
@@ -29,16 +28,24 @@ limitations under the License.
</div>
<gr-ranged-comment-layer
id="rangeLayer"
- comments="[[comments]]"></gr-ranged-comment-layer>
+ 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]]"
+ side="left"></gr-coverage-layer>
+ <gr-coverage-layer
+ id="coverageLayerRight"
+ coverage-ranges="[[_rightCoverageRanges]]"
+ side="right"></gr-coverage-layer>
<gr-diff-processor
id="processor"
groups="{{_groups}}"></gr-diff-processor>
- <gr-reporting id="reporting"></gr-reporting>
<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>
<script src="../gr-diff/gr-diff-group.js"></script>
<script src="../gr-diff-highlight/gr-annotation.js"></script>
@@ -51,27 +58,11 @@ limitations under the License.
(function() {
'use strict';
- const Defs = {};
-
- /**
- * @typedef {{
- * number: number,
- * leftSide: {boolean}
- * }}
- */
- Defs.LineOfInterest;
-
const DiffViewMode = {
SIDE_BY_SIDE: 'SIDE_BY_SIDE',
UNIFIED: 'UNIFIED_DIFF',
};
- const TimingLabel = {
- TOTAL: 'Diff Total Render',
- CONTENT: 'Diff Content Render',
- SYNTAX: 'Diff Syntax Render',
- };
-
// 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;
@@ -83,6 +74,7 @@ limitations under the License.
Polymer({
is: 'gr-diff-builder',
+ _legacyUndefinedCheck: true,
/**
* Fired when the diff begins rendering.
@@ -91,16 +83,16 @@ limitations under the License.
*/
/**
- * Fired when the diff is rendered.
+ * Fired when the diff finishes rendering text content and starts
+ * syntax highlighting.
*
- * @event render
+ * @event render-content
*/
/**
- * Fired when the diff finishes rendering text content, but not
- * necessarily syntax highlights.
+ * Fired when the diff finishes syntax highlighting.
*
- * @event render-content
+ * @event render-syntax
*/
properties: {
@@ -109,26 +101,43 @@ limitations under the License.
changeNum: String,
patchNum: String,
viewMode: String,
- comments: Object,
isImageDiff: Boolean,
baseImage: Object,
revisionImage: Object,
- projectName: String,
parentIndex: Number,
- /**
- * @type {Defs.LineOfInterest|null}
- */
- lineOfInterest: Object,
-
- /**
- * @type {function(number, booleam, !string)}
- */
- createCommentFn: Function,
+ path: String,
+ projectName: String,
_builder: Object,
_groups: Array,
_layers: Array,
_showTabs: Boolean,
+ /** @type {!Array<!Gerrit.HoveredRange>} */
+ commentRanges: {
+ type: Array,
+ value: () => [],
+ },
+ /** @type {!Array<!Gerrit.CoverageRange>} */
+ coverageRanges: {
+ type: Array,
+ value: () => [],
+ },
+ _leftCoverageRanges: {
+ type: Array,
+ computed: '_computeLeftCoverageRanges(coverageRanges)',
+ },
+ _rightCoverageRanges: {
+ type: Array,
+ computed: '_computeRightCoverageRanges(coverageRanges)',
+ },
+ /**
+ * The promise last returned from `render()` while the asynchronous
+ * rendering is running - `null` otherwise. Provides a `cancel()`
+ * method that rejects it with `{isCancelled: true}`.
+ *
+ * @type {?Object}
+ */
+ _cancelableRenderPromise: Object,
},
get diffElement() {
@@ -139,30 +148,21 @@ limitations under the License.
'_groupsChanged(_groups.splices)',
],
- attached() {
- // Setup annotation layers.
- const layers = [
- this._createTrailingWhitespaceLayer(),
- this.$.syntaxLayer,
- this._createIntralineLayer(),
- this._createTabIndicatorLayer(),
- this.$.rangeLayer,
- ];
-
- // Get layers from plugins (if any).
- for (const pluginLayer of this.$.jsAPI.getDiffLayers(
- this.diffPath, this.changeNum, this.patchNum)) {
- layers.push(pluginLayer);
- }
-
- this._layers = layers;
+ _computeLeftCoverageRanges(coverageRanges) {
+ return coverageRanges.filter(range => range && range.side === 'left');
+ },
- this.async(() => {
- this._preRenderThread();
- });
+ _computeRightCoverageRanges(coverageRanges) {
+ return coverageRanges.filter(range => range && range.side === 'right');
},
- render(comments, prefs) {
+ render(keyLocations, prefs) {
+ // Setting up annotation layers must happen after plugins are
+ // installed, and |render| satisfies the requirement, however,
+ // |attached| doesn't because in the diff view page, the element is
+ // attached before plugins are installed.
+ this._setupAnnotationLayers();
+
this.$.syntaxLayer.enabled = prefs.syntax_highlighting;
this._showTabs = !!prefs.show_tabs;
this._showTrailingWhitespace = !!prefs.show_whitespace_errors;
@@ -170,42 +170,62 @@ limitations under the License.
// Stop the processor and syntax layer (if they're running).
this.cancel();
- this._builder = this._getDiffBuilder(this.diff, comments, prefs);
+ this._builder = this._getDiffBuilder(this.diff, prefs);
this.$.processor.context = prefs.context;
- this.$.processor.keyLocations = this._getKeyLocations(comments,
- this.lineOfInterest);
+ this.$.processor.keyLocations = keyLocations;
this._clearDiffContent();
this._builder.addColumns(this.diffElement, prefs.font_size);
- const reporting = this.$.reporting;
const isBinary = !!(this.isImageDiff || this.diff.binary);
- reporting.time(TimingLabel.TOTAL);
- reporting.time(TimingLabel.CONTENT);
this.dispatchEvent(new CustomEvent('render-start', {bubbles: true}));
- return this.$.processor.process(this.diff.content, isBinary)
- .then(() => {
- if (this.isImageDiff) {
- this._builder.renderDiff();
- }
- this.dispatchEvent(new CustomEvent('render-content',
- {bubbles: true}));
-
- if (this._diffTooLargeForSyntax()) {
- this.$.syntaxLayer.enabled = false;
- }
-
- reporting.timeEnd(TimingLabel.CONTENT);
- reporting.time(TimingLabel.SYNTAX);
- return this.$.syntaxLayer.process().then(() => {
- reporting.timeEnd(TimingLabel.SYNTAX);
- reporting.timeEnd(TimingLabel.TOTAL);
- this.dispatchEvent(
- new CustomEvent('render', {bubbles: true}));
- });
- });
+ this._cancelableRenderPromise = util.makeCancelable(
+ this.$.processor.process(this.diff.content, isBinary)
+ .then(() => {
+ if (this.isImageDiff) {
+ 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}));
+ }));
+ return this._cancelableRenderPromise
+ .finally(() => { this._cancelableRenderPromise = null; })
+ // Mocca testing does not like uncaught rejections, so we catch
+ // the cancels which are expected and should not throw errors in
+ // tests.
+ .catch(e => { if (!e.isCanceled) return Promise.reject(e); });
+ },
+
+ _setupAnnotationLayers() {
+ const layers = [
+ this._createTrailingWhitespaceLayer(),
+ this.$.syntaxLayer,
+ this._createIntralineLayer(),
+ this._createTabIndicatorLayer(),
+ this.$.rangeLayer,
+ this.$.coverageLayerLeft,
+ this.$.coverageLayerRight,
+ ];
+
+ // Get layers from plugins (if any).
+ for (const pluginLayer of this.$.jsAPI.getDiffLayers(
+ this.diffPath, this.changeNum, this.patchNum)) {
+ layers.push(pluginLayer);
+ }
+
+ this._layers = layers;
},
getLineElByChild(node) {
@@ -282,6 +302,10 @@ limitations under the License.
cancel() {
this.$.processor.cancel();
this.$.syntaxLayer.cancel();
+ if (this._cancelableRenderPromise) {
+ this._cancelableRenderPromise.cancel();
+ this._cancelableRenderPromise = null;
+ }
},
_handlePreferenceError(pref) {
@@ -294,7 +318,7 @@ limitations under the License.
throw Error(`Invalid preference value: ${pref}`);
},
- _getDiffBuilder(diff, comments, prefs) {
+ _getDiffBuilder(diff, prefs) {
if (isNaN(prefs.tab_size) || prefs.tab_size <= 0) {
this._handlePreferenceError('tab size');
return;
@@ -306,20 +330,18 @@ limitations under the License.
}
let builder = null;
- const createFn = this.createCommentFn;
if (this.isImageDiff) {
- builder = new GrDiffBuilderImage(diff, comments, createFn, prefs,
- this.diffElement, this.baseImage, this.revisionImage);
+ builder = new GrDiffBuilderImage(diff, prefs, this.diffElement,
+ this.baseImage, this.revisionImage);
} else if (diff.binary) {
// If the diff is binary, but not an image.
- return new GrDiffBuilderBinary(diff, comments, prefs,
- this.diffElement);
+ return new GrDiffBuilderBinary(diff, prefs, this.diffElement);
} else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
- builder = new GrDiffBuilderSideBySide(diff, comments, createFn,
- prefs, this.diffElement, this._layers);
+ builder = new GrDiffBuilderSideBySide(diff, prefs, this.diffElement,
+ this._layers);
} else if (this.viewMode === DiffViewMode.UNIFIED) {
- builder = new GrDiffBuilderUnified(diff, comments, createFn, prefs,
- this.diffElement, this._layers);
+ builder = new GrDiffBuilderUnified(diff, prefs, this.diffElement,
+ this._layers);
}
if (!builder) {
throw Error('Unsupported diff view mode: ' + this.viewMode);
@@ -331,33 +353,6 @@ limitations under the License.
this.diffElement.innerHTML = null;
},
- /**
- * @param {!Object} comments
- * @param {Defs.LineOfInterest|null} lineOfInterest
- */
- _getKeyLocations(comments, lineOfInterest) {
- const result = {
- left: {},
- right: {},
- };
- for (const side in comments) {
- if (side !== GrDiffBuilder.Side.LEFT &&
- side !== GrDiffBuilder.Side.RIGHT) {
- continue;
- }
- for (const c of comments[side]) {
- result[side][c.line || GrDiffLine.FILE] = true;
- }
- }
-
- if (lineOfInterest) {
- const side = lineOfInterest.leftSide ? 'left' : 'right';
- result[side][lineOfInterest.number] = true;
- }
-
- return result;
- },
-
_groupsChanged(changeRecord) {
if (!changeRecord) { return; }
for (const splice of changeRecord.indexSplices) {
@@ -375,7 +370,7 @@ limitations under the License.
// Take a DIV.contentText element and a line object with intraline
// differences to highlight and apply them to the element as
// annotations.
- annotate(el, line) {
+ annotate(contentEl, lineNumberEl, line) {
const HL_CLASS = 'style-scope gr-diff intraline';
for (const highlight of line.highlights) {
// The start and end indices could be the same if a highlight is
@@ -389,7 +384,7 @@ limitations under the License.
highlight.endIndex;
GrAnnotation.annotateElement(
- el,
+ contentEl,
highlight.startIndex,
endIndex - highlight.startIndex,
HL_CLASS);
@@ -401,7 +396,7 @@ limitations under the License.
_createTabIndicatorLayer() {
const show = () => this._showTabs;
return {
- annotate(el, line) {
+ annotate(contentEl, lineNumberEl, line) {
// If visible tabs are disabled, do nothing.
if (!show()) { return; }
@@ -412,7 +407,7 @@ limitations under the License.
// Skip forward by the length of the content
pos += split[i].length;
- GrAnnotation.annotateElement(el, pos, 1,
+ GrAnnotation.annotateElement(contentEl, pos, 1,
'style-scope gr-diff tab-indicator');
// Skip forward by one tab character.
@@ -428,7 +423,7 @@ limitations under the License.
}.bind(this);
return {
- annotate(el, line) {
+ annotate(contentEl, lineNumberEl, line) {
if (!show()) { return; }
const match = line.text.match(TRAILING_WHITESPACE_PATTERN);
@@ -438,7 +433,7 @@ limitations under the License.
const index = GrAnnotation.getStringLength(
line.text.substr(0, match.index));
const length = GrAnnotation.getStringLength(match[0]);
- GrAnnotation.annotateElement(el, index, length,
+ GrAnnotation.annotateElement(contentEl, index, length,
'style-scope gr-diff trailing-whitespace');
}
},
@@ -446,26 +441,6 @@ limitations under the License.
},
/**
- * In pages with large diffs, creating the first comment thread can be
- * slow because nested Polymer elements (particularly
- * iron-autogrow-textarea) add style elements to the document head,
- * which, in turn, triggers a reflow on the page. Create a hidden
- * thread, attach it to the page, and remove it so the stylesheet will
- * already exist and the user's comment will be quick to load.
- *
- * @see https://gerrit-review.googlesource.com/c/82213/
- */
- _preRenderThread() {
- const thread = document.createElement('gr-diff-comment-thread');
- thread.setAttribute('hidden', true);
- thread.addDraft();
- const parent = Polymer.dom(this.root);
- parent.appendChild(thread);
- Polymer.dom.flush();
- parent.removeChild(thread);
- },
-
- /**
* @return {boolean} whether any of the lines in _groups are longer
* than SYNTAX_MAX_LINE_LENGTH.
*/
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 f37b5d8ddf..65a56f04fe 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
@@ -42,11 +42,8 @@
*/
const REGEX_TAB_OR_SURROGATE_PAIR = /\t|[\uD800-\uDBFF][\uDC00-\uDFFF]/;
- function GrDiffBuilder(diff, comments, createThreadGroupFn, prefs, outputEl,
- layers) {
+ function GrDiffBuilder(diff, prefs, outputEl, layers) {
this._diff = diff;
- this._comments = comments;
- this._createThreadGroupFn = createThreadGroupFn;
this._prefs = prefs;
this._outputEl = outputEl;
this.groups = [];
@@ -223,7 +220,9 @@
// if lines are collapsed and not visible on the page yet.
continue;
}
- el.parentElement.replaceChild(this._createTextEl(line, side).firstChild,
+ const lineNumberEl = this._getLineNumberEl(el, side);
+ el.parentElement.replaceChild(
+ this._createTextEl(lineNumberEl, line, side).firstChild,
el);
}
};
@@ -321,152 +320,6 @@
return button;
};
- GrDiffBuilder.prototype._getCommentsForLine = function(comments, line,
- opt_side) {
- function byLineNum(lineNum) {
- return function(c) {
- return (c.line === lineNum) ||
- (c.line === undefined && lineNum === GrDiffLine.FILE);
- };
- }
- const leftComments =
- comments[GrDiffBuilder.Side.LEFT].filter(byLineNum(line.beforeNumber));
- const rightComments =
- comments[GrDiffBuilder.Side.RIGHT].filter(byLineNum(line.afterNumber));
-
- leftComments.forEach(c => { c.__commentSide = 'left'; });
- rightComments.forEach(c => { c.__commentSide = 'right'; });
-
- let result;
-
- switch (opt_side) {
- case GrDiffBuilder.Side.LEFT:
- result = leftComments;
- break;
- case GrDiffBuilder.Side.RIGHT:
- result = rightComments;
- break;
- default:
- result = leftComments.concat(rightComments);
- break;
- }
-
- return result;
- };
-
- /**
- * @param {Array<Object>} comments
- * @param {string} patchForNewThreads
- */
- GrDiffBuilder.prototype._getThreads = function(comments, patchForNewThreads) {
- const sortedComments = comments.slice(0).sort((a, b) => {
- if (b.__draft && !a.__draft ) { return 0; }
- if (a.__draft && !b.__draft ) { return 1; }
- return util.parseDate(a.updated) - util.parseDate(b.updated);
- });
-
- const threads = [];
- for (const comment of sortedComments) {
- // If the comment is in reply to another comment, find that comment's
- // thread and append to it.
- if (comment.in_reply_to) {
- const thread = threads.find(thread =>
- thread.comments.some(c => c.id === comment.in_reply_to));
- if (thread) {
- thread.comments.push(comment);
- continue;
- }
- }
-
- // Otherwise, this comment starts its own thread.
- const newThread = {
- start_datetime: comment.updated,
- comments: [comment],
- commentSide: comment.__commentSide,
- /**
- * Determines what the patchNum of a thread should be. Use patchNum from
- * comment if it exists, otherwise the property of the thread group.
- * This is needed for switching between side-by-side and unified views
- * when there are unsaved drafts.
- */
- patchNum: comment.patch_set || patchForNewThreads,
- rootId: comment.id || comment.__draftID,
- };
- if (comment.range) {
- newThread.range = Object.assign({}, comment.range);
- }
- threads.push(newThread);
- }
- return threads;
- };
-
- /**
- * Returns the patch number that new comment threads should be attached to.
- *
- * @param {GrDiffLine} line The line new thread will be attached to.
- * @param {string=} opt_side Set to LEFT to force adding it to the LEFT side -
- * will be ignored if the left is a parent or a merge parent
- * @return {number} Patch set to attach the new thread to
- */
- GrDiffBuilder.prototype._determinePatchNumForNewThreads = function(
- patchRange, line, opt_side) {
- if ((line.type === GrDiffLine.Type.REMOVE ||
- opt_side === GrDiffBuilder.Side.LEFT) &&
- patchRange.basePatchNum !== 'PARENT' &&
- !Gerrit.PatchSetBehavior.isMergeParent(patchRange.basePatchNum)) {
- return patchRange.basePatchNum;
- } else {
- return patchRange.patchNum;
- }
- };
-
- /**
- * Returns whether the comments on the given line are on a (merge) parent.
- *
- * @param {string} firstCommentSide
- * @param {{basePatchNum: number, patchNum: number}} patchRange
- * @param {GrDiffLine} line The line the comments are on.
- * @param {string=} opt_side
- * @return {boolean} True iff the comments on the given line are on a (merge)
- * parent.
- */
- GrDiffBuilder.prototype._determineIsOnParent = function(
- firstCommentSide, patchRange, line, opt_side) {
- return ((line.type === GrDiffLine.Type.REMOVE ||
- opt_side === GrDiffBuilder.Side.LEFT) &&
- (patchRange.basePatchNum === 'PARENT' ||
- Gerrit.PatchSetBehavior.isMergeParent(
- patchRange.basePatchNum))) ||
- firstCommentSide === 'PARENT';
- };
-
- /**
- * @param {GrDiffLine} line
- * @param {string=} opt_side
- * @return {!Object}
- */
- GrDiffBuilder.prototype._commentThreadGroupForLine = function(
- line, opt_side) {
- const comments =
- this._getCommentsForLine(this._comments, line, opt_side);
- if (!comments || comments.length === 0) {
- return null;
- }
-
- const patchNum = this._determinePatchNumForNewThreads(
- this._comments.meta.patchRange, line, opt_side);
- const isOnParent = this._determineIsOnParent(
- comments[0].side, this._comments.meta.patchRange, line, opt_side);
-
- const threadGroupEl = this._createThreadGroupFn(patchNum, isOnParent,
- opt_side);
- threadGroupEl.threads = this._getThreads(comments, patchNum);
- if (opt_side) {
- threadGroupEl.setAttribute('data-side', opt_side);
- }
- return threadGroupEl;
- };
-
GrDiffBuilder.prototype._createLineEl = function(
line, number, type, opt_class) {
const td = this._createElement('td');
@@ -494,7 +347,8 @@
return td;
};
- GrDiffBuilder.prototype._createTextEl = function(line, opt_side) {
+ GrDiffBuilder.prototype._createTextEl = function(
+ lineNumberEl, line, opt_side) {
const td = this._createElement('td');
if (line.type !== GrDiffLine.Type.BLANK) {
td.classList.add('content');
@@ -511,7 +365,7 @@
}
for (const layer of this.layers) {
- layer.annotate(contentText, line);
+ layer.annotate(contentText, lineNumberEl, line);
}
td.appendChild(contentText);
@@ -752,5 +606,18 @@
return blameTd;
};
+ /**
+ * Finds the line number element given the content element by walking up the
+ * DOM tree to the diff row and then querying for a .lineNum element on the
+ * requested side.
+ *
+ * TODO(brohlfs): Consolidate this with getLineEl... methods in html file.
+ */
+ GrDiffBuilder.prototype._getLineNumberEl = function(content, side) {
+ let row = content;
+ while (row && !row.classList.contains('diff-row')) row = row.parentElement;
+ return row ? row.querySelector('.lineNum.' + side) : null;
+ };
+
window.GrDiffBuilder = GrDiffBuilder;
})(window, GrDiffGroup, GrDiffLine);
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 9a88ac9a02..a8db47a7c2 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
@@ -57,9 +57,9 @@ limitations under the License.
<script>
suite('gr-diff-builder tests', () => {
+ let prefs;
let element;
let builder;
- let createThreadGroupFn;
let sandbox;
const LINE_FEED_HTML = '<span class="style-scope gr-diff br"></span>';
@@ -70,180 +70,16 @@ limitations under the License.
getLoggedIn() { return Promise.resolve(false); },
getProjectConfig() { return Promise.resolve({}); },
});
- const prefs = {
+ prefs = {
line_length: 10,
show_tabs: true,
tab_size: 4,
};
- createThreadGroupFn = sinon.spy(() => ({
- setAttribute: sinon.spy(),
- }));
- builder = new GrDiffBuilder(
- {content: []}, {left: [], right: []}, createThreadGroupFn, prefs);
+ builder = new GrDiffBuilder({content: []}, prefs);
});
teardown(() => { sandbox.restore(); });
- test('_getThreads', () => {
- const patchForNewThreads = 3;
- const comments = [
- {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-23 15:00:20.396000000',
- __commentSide: 'left',
- }, {
- id: 'jacks_reply',
- message: 'i like you, too',
- updated: '2015-12-24 15:01:20.396000000',
- __commentSide: 'left',
- in_reply_to: 'sallys_confession',
- },
- {
- id: 'new_draft',
- message: 'i do not like either of you',
- __commentSide: 'left',
- __draft: true,
- updated: '2015-12-20 15:01:20.396000000',
- },
- ];
-
- let expectedThreadGroups = [
- {
- start_datetime: '2015-12-23 15:00:20.396000000',
- commentSide: 'left',
- comments: [{
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-23 15:00:20.396000000',
- __commentSide: 'left',
- }, {
- id: 'jacks_reply',
- message: 'i like you, too',
- updated: '2015-12-24 15:01:20.396000000',
- __commentSide: 'left',
- in_reply_to: 'sallys_confession',
- }],
- rootId: 'sallys_confession',
- patchNum: 3,
- },
- {
- start_datetime: '2015-12-20 15:01:20.396000000',
- commentSide: 'left',
- comments: [
- {
- id: 'new_draft',
- message: 'i do not like either of you',
- __commentSide: 'left',
- __draft: true,
- updated: '2015-12-20 15:01:20.396000000',
- },
- ],
- rootId: 'new_draft',
- patchNum: 3,
- },
- ];
-
- assert.deepEqual(
- builder._getThreads(comments, patchForNewThreads),
- expectedThreadGroups);
-
- // Patch num should get inherited from comment rather
- comments.push({
- id: 'betsys_confession',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:10.396000000',
- range: {
- start_line: 1,
- start_character: 1,
- end_line: 1,
- end_character: 2,
- },
- __commentSide: 'left',
- });
-
- expectedThreadGroups = [
- {
- start_datetime: '2015-12-23 15:00:20.396000000',
- commentSide: 'left',
- comments: [{
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-23 15:00:20.396000000',
- __commentSide: 'left',
- }, {
- id: 'jacks_reply',
- in_reply_to: 'sallys_confession',
- message: 'i like you, too',
- updated: '2015-12-24 15:01:20.396000000',
- __commentSide: 'left',
- }],
- patchNum: 3,
- rootId: 'sallys_confession',
- },
- {
- start_datetime: '2015-12-24 15:00:10.396000000',
- commentSide: 'left',
- comments: [{
- id: 'betsys_confession',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:10.396000000',
- range: {
- start_line: 1,
- start_character: 1,
- end_line: 1,
- end_character: 2,
- },
- __commentSide: 'left',
- }],
- patchNum: 3,
- rootId: 'betsys_confession',
- range: {
- start_line: 1,
- start_character: 1,
- end_line: 1,
- end_character: 2,
- },
- },
- {
- start_datetime: '2015-12-20 15:01:20.396000000',
- commentSide: 'left',
- comments: [
- {
- id: 'new_draft',
- message: 'i do not like either of you',
- __commentSide: 'left',
- __draft: true,
- updated: '2015-12-20 15:01:20.396000000',
- },
- ],
- rootId: 'new_draft',
- patchNum: 3,
- },
- ];
-
- assert.deepEqual(
- builder._getThreads(comments, patchForNewThreads),
- expectedThreadGroups);
- });
-
- test('multiple comments at same location but not threaded', () => {
- const comments = [
- {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-23 15:00:20.396000000',
- __commentSide: 'left',
- }, {
- id: 'jacks_reply',
- message: 'i like you, too',
- updated: '2015-12-24 15:01:20.396000000',
- __commentSide: 'left',
- },
- ];
- assert.equal(builder._getThreads(comments, '3').length, 2);
- });
-
test('_createElement classStr applies all classes', () => {
const node = builder._createElement('div', 'test classes');
assert.isTrue(node.classList.contains('gr-diff'));
@@ -327,7 +163,7 @@ limitations under the License.
const text = 'a'.repeat(51);
const line = {text, highlights: []};
- const result = builder._createTextEl(line).firstChild.innerHTML;
+ const result = builder._createTextEl(undefined, line).firstChild.innerHTML;
assert.equal(result, text);
});
@@ -337,14 +173,14 @@ limitations under the License.
const line = {text, highlights: []};
const expected = 'a'.repeat(50) + LINE_FEED_HTML + 'a';
- const result = builder._createTextEl(line).firstChild.innerHTML;
+ const result = builder._createTextEl(undefined, line).firstChild.innerHTML;
assert.equal(result, expected);
});
test('_createTextEl linewrap with tabs', () => {
const text = '\t'.repeat(7) + '!';
const line = {text, highlights: []};
- const el = builder._createTextEl(line);
+ const el = builder._createTextEl(undefined, line);
assert.equal(el.innerText, text);
// With line length 10 and tab size 2, there should be a line break
// after every two tabs.
@@ -417,135 +253,10 @@ limitations under the License.
}
});
- test('comments', () => {
- const line = new GrDiffLine(GrDiffLine.Type.BOTH);
- line.beforeNumber = 3;
- line.afterNumber = 5;
-
- let comments = {left: [], right: []};
- assert.deepEqual(builder._getCommentsForLine(comments, line), []);
- assert.deepEqual(builder._getCommentsForLine(comments, line,
- GrDiffBuilder.Side.LEFT), []);
- assert.deepEqual(builder._getCommentsForLine(comments, line,
- GrDiffBuilder.Side.RIGHT), []);
-
- comments = {
- left: [
- {id: 'l3', line: 3},
- {id: 'l5', line: 5},
- ],
- right: [
- {id: 'r3', line: 3},
- {id: 'r5', line: 5},
- ],
- };
- assert.deepEqual(builder._getCommentsForLine(comments, line),
- [{id: 'l3', line: 3, __commentSide: 'left'},
- {id: 'r5', line: 5, __commentSide: 'right'}]);
- assert.deepEqual(builder._getCommentsForLine(comments, line,
- GrDiffBuilder.Side.LEFT), [{id: 'l3', line: 3,
- __commentSide: 'left'}]);
- assert.deepEqual(builder._getCommentsForLine(comments, line,
- GrDiffBuilder.Side.RIGHT), [{id: 'r5', line: 5,
- __commentSide: 'right'}]);
- });
-
- test('comment thread group creation', () => {
- const l3 = {id: 'l3', line: 3, updated: '2016-08-09 00:42:32.000000000',
- __commentSide: 'left'};
- const l5 = {id: 'l5', line: 5, updated: '2016-08-09 00:42:32.000000000',
- __commentSide: 'left'};
- const r5 = {id: 'r5', line: 5, updated: '2016-08-09 00:42:32.000000000',
- __commentSide: 'right'};
-
- builder._comments = {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: '3',
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [l3, l5],
- right: [r5],
- };
-
- function threadForComment(c, patchNum) {
- return {
- commentSide: c.__commentSide,
- comments: [c],
- patchNum,
- rootId: c.id,
- start_datetime: c.updated,
- };
- }
-
- function checkThreadGroupProps(threadGroupEl, patchNum, isOnParent,
- comments) {
- assert.equal(createThreadGroupFn.lastCall.args[0], patchNum);
- assert.equal(createThreadGroupFn.lastCall.args[1], isOnParent);
- assert.deepEqual(
- threadGroupEl.threads,
- comments.map(c => threadForComment(c, patchNum)));
- }
-
- let line = new GrDiffLine(GrDiffLine.Type.BOTH);
- line.beforeNumber = 5;
- line.afterNumber = 5;
- let threadGroupEl = builder._commentThreadGroupForLine(line);
- assert.isTrue(createThreadGroupFn.calledOnce);
- checkThreadGroupProps(threadGroupEl, '3', false, [l5, r5]);
-
- threadGroupEl =
- builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.RIGHT);
- assert.isTrue(createThreadGroupFn.calledTwice);
- checkThreadGroupProps(threadGroupEl, '3', false, [r5]);
-
- threadGroupEl =
- builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.LEFT);
- assert.isTrue(createThreadGroupFn.calledThrice);
- checkThreadGroupProps(threadGroupEl, '3', true, [l5]);
-
- builder._comments.meta.patchRange.basePatchNum = '1';
-
- threadGroupEl = builder._commentThreadGroupForLine(line);
- assert.equal(createThreadGroupFn.callCount, 4);
- checkThreadGroupProps(threadGroupEl, '3', false, [l5, r5]);
-
- threadEl =
- builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.LEFT);
- assert.equal(createThreadGroupFn.callCount, 5);
- checkThreadGroupProps(threadEl, '1', false, [l5]);
-
- threadGroupEl =
- builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.RIGHT);
- assert.equal(createThreadGroupFn.callCount, 6);
- checkThreadGroupProps(threadGroupEl, '3', false, [r5]);
-
- builder._comments.meta.patchRange.basePatchNum = 'PARENT';
-
- line = new GrDiffLine(GrDiffLine.Type.REMOVE);
- line.beforeNumber = 5;
- line.afterNumber = 5;
- threadGroupEl = builder._commentThreadGroupForLine(line);
- assert.equal(createThreadGroupFn.callCount, 7);
- checkThreadGroupProps(threadGroupEl, '3', true, [l5, r5]);
-
- line = new GrDiffLine(GrDiffLine.Type.ADD);
- line.beforeNumber = 3;
- line.afterNumber = 5;
- threadGroupEl = builder._commentThreadGroupForLine(line);
- assert.equal(createThreadGroupFn.callCount, 8);
- checkThreadGroupProps(threadGroupEl, '3', false, [l3, r5]);
- });
-
-
test('_handlePreferenceError called with invalid preference', () => {
sandbox.stub(element, '_handlePreferenceError');
const prefs = {tab_size: 0};
- element._getDiffBuilder(element.diff, element.comments, prefs);
+ element._getDiffBuilder(element.diff, prefs);
assert.isTrue(element._handlePreferenceError.lastCall
.calledWithExactly('tab size'));
});
@@ -559,31 +270,6 @@ limitations under the License.
`Fix in diff preferences`);
});
- test('_getKeyLocations', () => {
- assert.deepEqual(element._getKeyLocations({left: [], right: []}, null),
- {left: {}, right: {}});
- const comments = {
- left: [{line: 123}, {}],
- right: [{line: 456}],
- };
- assert.deepEqual(element._getKeyLocations(comments, null), {
- left: {FILE: true, 123: true},
- right: {456: true},
- });
-
- const lineOfInterest = {number: 789, leftSide: true};
- assert.deepEqual(element._getKeyLocations(comments, lineOfInterest), {
- left: {FILE: true, 123: true, 789: true},
- right: {456: true},
- });
-
- delete lineOfInterest.leftSide;
- assert.deepEqual(element._getKeyLocations(comments, lineOfInterest), {
- left: {FILE: true, 123: true},
- right: {456: true, 789: true},
- });
- });
-
suite('_isTotal', () => {
test('is total for add', () => {
const group = new GrDiffGroup(GrDiffGroup.Type.DELTA);
@@ -620,6 +306,7 @@ limitations under the License.
let str;
let annotateElementSpy;
let layer;
+ const lineNumberEl = document.createElement('td');
function slice(str, start, end) {
return Array.from(str).slice(start, end).join('');
@@ -639,7 +326,7 @@ limitations under the License.
highlights: [],
};
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
// The content is unchanged.
assert.isFalse(annotateElementSpy.called);
@@ -662,7 +349,7 @@ limitations under the License.
const str3 = slice(str, 18, 22);
const str4 = slice(str, 22);
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementSpy.called);
assert.equal(el.childNodes.length, 5);
@@ -694,7 +381,7 @@ limitations under the License.
const str0 = slice(str, 0, 28);
const str1 = slice(str, 28);
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementSpy.called);
assert.equal(el.childNodes.length, 2);
@@ -714,7 +401,7 @@ limitations under the License.
],
};
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementSpy.called);
assert.equal(el.childNodes.length, 1);
@@ -735,7 +422,7 @@ limitations under the License.
const str1 = slice(str, 6, 12);
const str2 = slice(str, 12);
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementSpy.called);
assert.equal(el.childNodes.length, 3);
@@ -765,7 +452,7 @@ limitations under the License.
const str0 = slice(str, 0, 6);
const str1 = slice(str, 6);
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementSpy.called);
assert.equal(el.childNodes.length, 2);
@@ -781,6 +468,7 @@ limitations under the License.
suite('tab indicators', () => {
let element;
let layer;
+ const lineNumberEl = document.createElement('td');
setup(() => {
element = fixture('basic');
@@ -794,7 +482,7 @@ limitations under the License.
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -807,7 +495,7 @@ limitations under the License.
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -820,7 +508,7 @@ limitations under the License.
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.equal(annotateElementStub.callCount, 1);
const args = annotateElementStub.getCalls()[0].args;
@@ -840,7 +528,7 @@ limitations under the License.
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -853,7 +541,7 @@ limitations under the License.
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.equal(annotateElementStub.callCount, 2);
@@ -878,7 +566,7 @@ limitations under the License.
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.equal(annotateElementStub.callCount, 1);
const args = annotateElementStub.getCalls()[0].args;
@@ -896,13 +584,14 @@ limitations under the License.
setup(() => {
element = fixture('basic');
element._showTrailingWhitespace = true;
+ element._setupAnnotationLayers();
initialLayersCount = element._layers.length;
});
test('no plugin layers', () => {
const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers')
.returns([]);
- element.attached();
+ element._setupAnnotationLayers();
assert.isTrue(getDiffLayersStub.called);
assert.equal(element._layers.length, initialLayersCount);
});
@@ -910,15 +599,16 @@ limitations under the License.
test('with plugin layers', () => {
const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers')
.returns([{}, {}]);
- element.attached();
+ element._setupAnnotationLayers();
assert.isTrue(getDiffLayersStub.called);
- assert.equal(element._layers.length, initialLayersCount+2);
+ assert.equal(element._layers.length, initialLayersCount + 2);
});
});
suite('trailing whitespace', () => {
let element;
let layer;
+ const lineNumberEl = document.createElement('td');
setup(() => {
element = fixture('basic');
@@ -931,7 +621,7 @@ limitations under the License.
const el = document.createElement('div');
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -942,7 +632,7 @@ limitations under the License.
el.textContent = str;
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -953,7 +643,7 @@ limitations under the License.
el.textContent = str;
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
assert.equal(annotateElementStub.lastCall.args[1], 11);
assert.equal(annotateElementStub.lastCall.args[2], 3);
@@ -966,7 +656,7 @@ limitations under the License.
el.textContent = str;
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
assert.equal(annotateElementStub.lastCall.args[1], 11);
assert.equal(annotateElementStub.lastCall.args[2], 3);
@@ -979,7 +669,7 @@ limitations under the License.
el.textContent = str;
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
assert.equal(annotateElementStub.lastCall.args[1], 11);
assert.equal(annotateElementStub.lastCall.args[2], 3);
@@ -992,7 +682,7 @@ limitations under the License.
el.textContent = str;
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
assert.equal(annotateElementStub.lastCall.args[1], 1);
assert.equal(annotateElementStub.lastCall.args[2], 1);
@@ -1006,14 +696,14 @@ limitations under the License.
el.textContent = str;
const annotateElementStub =
sandbox.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, line);
+ layer.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
});
suite('rendering text, images and binary files', () => {
let processStub;
- let comments;
+ let keyLocations;
let prefs;
let content;
@@ -1023,7 +713,7 @@ limitations under the License.
processStub = sandbox.stub(element.$.processor, 'process')
.returns(Promise.resolve());
sandbox.stub(element, '_anyLineTooLong').returns(true);
- comments = {left: [], right: []};
+ keyLocations = {left: {}, right: {}};
prefs = {
line_length: 10,
show_tabs: true,
@@ -1044,7 +734,7 @@ limitations under the License.
test('text', () => {
element.diff = {content};
- return element.render(comments, prefs).then(() => {
+ return element.render(keyLocations, prefs).then(() => {
assert.isTrue(processStub.calledOnce);
assert.isFalse(processStub.lastCall.args[1]);
});
@@ -1053,7 +743,7 @@ limitations under the License.
test('image', () => {
element.diff = {content, binary: true};
element.isImageDiff = true;
- return element.render(comments, prefs).then(() => {
+ return element.render(keyLocations, prefs).then(() => {
assert.isTrue(processStub.calledOnce);
assert.isTrue(processStub.lastCall.args[1]);
});
@@ -1061,7 +751,7 @@ limitations under the License.
test('binary', () => {
element.diff = {content, binary: true};
- return element.render(comments, prefs).then(() => {
+ return element.render(keyLocations, prefs).then(() => {
assert.isTrue(processStub.calledOnce);
assert.isTrue(processStub.lastCall.args[1]);
});
@@ -1071,6 +761,7 @@ limitations under the License.
suite('rendering', () => {
let content;
let outputEl;
+ let keyLocations;
setup(done => {
const prefs = {
@@ -1092,15 +783,11 @@ limitations under the License.
],
},
];
- stub('gr-reporting', {
- time: sandbox.stub(),
- timeEnd: sandbox.stub(),
- });
element = fixture('basic');
outputEl = element.queryEffectiveChildren('#diffTable');
+ keyLocations = {left: {}, right: {}};
sandbox.stub(element, '_getDiffBuilder', () => {
- const builder = new GrDiffBuilder(
- {content}, {left: [], right: []}, null, prefs, outputEl);
+ const builder = new GrDiffBuilder({content}, prefs, outputEl);
sandbox.stub(builder, 'addColumns');
builder.buildSectionElement = function(group) {
const section = document.createElement('stub');
@@ -1112,19 +799,7 @@ limitations under the License.
return builder;
});
element.diff = {content};
- element.render({left: [], right: []}, prefs).then(done);
- });
-
- test('reporting', done => {
- const timeStub = element.$.reporting.time;
- const timeEndStub = element.$.reporting.timeEnd;
- assert.isTrue(timeStub.calledWithExactly('Diff Total Render'));
- assert.isTrue(timeStub.calledWithExactly('Diff Content Render'));
- assert.isTrue(timeStub.calledWithExactly('Diff Syntax Render'));
- assert.isTrue(timeEndStub.calledWithExactly('Diff Total Render'));
- assert.isTrue(timeEndStub.calledWithExactly('Diff Content Render'));
- assert.isTrue(timeEndStub.calledWithExactly('Diff Syntax Render'));
- done();
+ element.render(keyLocations, prefs).then(done);
});
test('renderSection', () => {
@@ -1137,7 +812,7 @@ limitations under the License.
});
test('addColumns is called', done => {
- element.render({left: [], right: []}, {}).then(done);
+ element.render(keyLocations, {}).then(done);
assert.isTrue(element._builder.addColumns.called);
});
@@ -1159,14 +834,14 @@ limitations under the License.
assert.strictEqual(sections[1], section[1]);
});
- test('render-start and render are fired', done => {
+ test('render-start and render-content are fired', done => {
const dispatchEventStub = sandbox.stub(element, 'dispatchEvent');
- element.render({left: [], right: []}, {}).then(() => {
+ element.render(keyLocations, {}).then(() => {
const firedEventTypes = dispatchEventStub.getCalls()
.map(c => { return c.args[0].type; });
assert.include(firedEventTypes, 'render-start');
assert.include(firedEventTypes, 'render-content');
- assert.include(firedEventTypes, 'render');
+ assert.include(firedEventTypes, 'render-syntax');
done();
});
});
@@ -1178,10 +853,6 @@ limitations under the License.
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('*')];
- element.addEventListener('render', () => {
- assert.isFalse(element.$.syntaxLayer.enabled);
- done();
- });
const prefs = {
line_length: 10,
show_tabs: true,
@@ -1189,7 +860,10 @@ limitations under the License.
context: -1,
syntax_highlighting: true,
};
- element.render({left: [], right: []}, prefs);
+ element.render(keyLocations, prefs).then(() => {
+ assert.isFalse(element.$.syntaxLayer.enabled);
+ done();
+ });
});
test('cancel', () => {
@@ -1206,6 +880,7 @@ limitations under the License.
let builder;
let diff;
let prefs;
+ let keyLocations;
setup(done => {
element = fixture('mock-diff');
@@ -1217,8 +892,9 @@ limitations under the License.
show_tabs: true,
tab_size: 4,
};
+ keyLocations = {left: {}, right: {}};
- element.render({left: [], right: []}, prefs).then(() => {
+ element.render(keyLocations, prefs).then(() => {
builder = element._builder;
done();
});
@@ -1274,7 +950,7 @@ limitations under the License.
assert.equal(spy.callCount, count);
spy.getCalls().forEach((call, i) => {
- assert.equal(call.args[0].beforeNumber, start + i);
+ assert.equal(call.args[1].beforeNumber, start + i);
});
});
@@ -1285,9 +961,11 @@ limitations under the License.
(s, e, d, lines, elements) => {
// Add a line and a corresponding element.
lines.push(new GrDiffLine(GrDiffLine.Type.BOTH));
- const parEl = document.createElement('div');
+ const tr = document.createElement('tr');
+ const td = document.createElement('td');
const el = document.createElement('div');
- parEl.appendChild(el);
+ tr.appendChild(td);
+ td.appendChild(el);
elements.push(el);
// Add 2 lines without corresponding elements.
@@ -1301,6 +979,52 @@ limitations under the License.
assert.equal(spy.callCount, 1);
});
+ test('_getLineNumberEl side-by-side left', () => {
+ const contentEl = builder.getContentByLine(5, 'left',
+ element.$.diffTable);
+ const lineNumberEl = builder._getLineNumberEl(contentEl, 'left');
+ assert.isTrue(lineNumberEl.classList.contains('lineNum'));
+ assert.isTrue(lineNumberEl.classList.contains('left'));
+ });
+
+ test('_getLineNumberEl side-by-side right', () => {
+ const contentEl = builder.getContentByLine(5, 'right',
+ element.$.diffTable);
+ const lineNumberEl = builder._getLineNumberEl(contentEl, 'right');
+ assert.isTrue(lineNumberEl.classList.contains('lineNum'));
+ assert.isTrue(lineNumberEl.classList.contains('right'));
+ });
+
+ test('_getLineNumberEl unified left', done => {
+ // Re-render as unified:
+ element.viewMode = 'UNIFIED_DIFF';
+ element.render(keyLocations, prefs).then(() => {
+ builder = element._builder;
+
+ const contentEl = builder.getContentByLine(5, 'left',
+ element.$.diffTable);
+ const lineNumberEl = builder._getLineNumberEl(contentEl, 'left');
+ assert.isTrue(lineNumberEl.classList.contains('lineNum'));
+ assert.isTrue(lineNumberEl.classList.contains('left'));
+ done();
+ });
+ });
+
+ test('_getLineNumberEl unified right', done => {
+ // Re-render as unified:
+ element.viewMode = 'UNIFIED_DIFF';
+ element.render(keyLocations, prefs).then(() => {
+ builder = element._builder;
+
+ const contentEl = builder.getContentByLine(5, 'right',
+ element.$.diffTable);
+ const lineNumberEl = builder._getLineNumberEl(contentEl, 'right');
+ assert.isTrue(lineNumberEl.classList.contains('lineNum'));
+ assert.isTrue(lineNumberEl.classList.contains('right'));
+ done();
+ });
+ });
+
test('_getNextContentOnSide side-by-side left', () => {
const startElem = builder.getContentByLine(5, 'left',
element.$.diffTable);
@@ -1328,7 +1052,7 @@ limitations under the License.
test('_getNextContentOnSide unified left', done => {
// Re-render as unified:
element.viewMode = 'UNIFIED_DIFF';
- element.render({left: [], right: []}, prefs).then(() => {
+ element.render(keyLocations, prefs).then(() => {
builder = element._builder;
const startElem = builder.getContentByLine(5, 'left',
@@ -1348,7 +1072,7 @@ limitations under the License.
test('_getNextContentOnSide unified right', done => {
// Re-render as unified:
element.viewMode = 'UNIFIED_DIFF';
- element.render({left: [], right: []}, prefs).then(() => {
+ element.render(keyLocations, prefs).then(() => {
builder = element._builder;
const startElem = builder.getContentByLine(5, 'right',
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.html
deleted file mode 100644
index 58b7c32534..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!--
-@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.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
-<link rel="import" href="../../../styles/shared-styles.html">
-
-<dom-module id="gr-diff-comment-thread-group">
- <template>
- <style include="shared-styles">
- :host {
- display: block;
- max-width: var(--content-width, 80ch);
- white-space: normal;
- }
- gr-diff-comment-thread + gr-diff-comment-thread {
- margin-top: .2em;
- }
- </style>
- <template is="dom-repeat" items="[[threads]]" as="thread">
- <gr-diff-comment-thread
- comments="[[thread.comments]]"
- comment-side="[[thread.commentSide]]"
- is-on-parent="[[isOnParent]]"
- parent-index="[[parentIndex]]"
- change-num="[[changeNum]]"
- patch-num="[[thread.patchNum]]"
- root-id="{{thread.rootId}}"
- path="[[path]]"
- project-name="[[projectName]]"
- range="[[thread.range]]"
- on-thread-discard="_handleThreadDiscard"></gr-diff-comment-thread>
- </template>
- </template>
- <script src="gr-diff-comment-thread-group.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.js b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.js
deleted file mode 100644
index ae45c93a73..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * @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.
- */
-(function() {
- 'use strict';
-
- Polymer({
- is: 'gr-diff-comment-thread-group',
-
- properties: {
- changeNum: String,
- projectName: String,
- patchForNewThreads: String,
- range: Object,
- isOnParent: {
- type: Boolean,
- value: false,
- },
- parentIndex: {
- type: Number,
- value: null,
- },
- threads: {
- type: Array,
- value() { return []; },
- },
- },
-
- get threadEls() {
- return Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread');
- },
-
- /**
- * Adds a new thread. Range is optional because a comment can be
- * added to a line without a range selected.
- *
- * @param {!Object} opt_range
- */
- addNewThread(commentSide, opt_range) {
- this.push('threads', {
- comments: [],
- commentSide,
- patchNum: this.patchForNewThreads,
- range: opt_range,
- });
- },
-
- removeThread(rootId) {
- for (let i = 0; i < this.threads.length; i++) {
- if (this.threads[i].rootId === rootId) {
- this.splice('threads', i, 1);
- return;
- }
- }
- },
-
- /**
- * Fetch the thread group at the given range, or the range-less thread
- * on the line if no range is provided, lineNum, and side.
- *
- * @param {string} side
- * @param {!Object=} opt_range
- * @return {!Object|undefined}
- */
- getThread(side, opt_range) {
- const threads = [].filter.call(this.threadEls,
- thread => this._rangesEqual(thread.range, opt_range))
- .filter(thread => thread.commentSide === side);
- if (threads.length === 1) {
- return threads[0];
- }
- },
-
- _handleThreadDiscard(e) {
- this.removeThread(e.detail.rootId);
- },
-
- /**
- * 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
- * values.
- *
- * @param {Object=} a range 1
- * @param {Object=} b range 2
- * @return {boolean}
- */
- _rangesEqual(a, b) {
- if (!a && !b) { return true; }
- if (!a || !b) { return false; }
- return a.startLine === b.startLine &&
- a.startChar === b.startChar &&
- a.endLine === b.endLine &&
- a.endChar === b.endChar;
- },
-
- _sortByDate(threadGroups) {
- if (!threadGroups.length) { return; }
- return threadGroups.sort((a, b) => {
- // If a comment is a draft, it doesn't have a start_datetime yet.
- // Assume it is newer than the comment it is being compared to.
- if (!a.start_datetime) {
- return 1;
- }
- if (!b.start_datetime) {
- return -1;
- }
- return util.parseDate(a.start_datetime) -
- util.parseDate(b.start_datetime);
- });
- },
-
- _calculateLocationRange(range, comment) {
- return 'range-' + range.start_line + '-' +
- range.start_character + '-' +
- range.end_line + '-' +
- range.end_character + '-' +
- comment.__commentSide;
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html
deleted file mode 100644
index 1fb8136268..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html
+++ /dev/null
@@ -1,232 +0,0 @@
-<!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-diff-comment-thread-group</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-diff-comment-thread-group.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-diff-comment-thread-group></gr-diff-comment-thread-group>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-diff-comment-thread-group tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- stub('gr-rest-api-interface', {
- getLoggedIn() { return Promise.resolve(false); },
- });
- element = fixture('basic');
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('getThread', () => {
- const range = {
- start_line: 1,
- start_character: 1,
- end_line: 1,
- end_character: 2,
- };
- element.threads = [
- {
- rootId: 'sallys_confession',
- commentSide: 'left',
- comments: [
- {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-23 15:00:20.396000000',
- __commentSide: 'left',
- }, {
- id: 'jacks_reply',
- message: 'i like you, too',
- updated: '2015-12-24 15:01:20.396000000',
- __commentSide: 'left',
- in_reply_to: 'sallys_confession',
- }, {
- id: 'new_draft',
- message: 'i do not like either of you',
- __commentSide: 'left',
- __draft: true,
- in_reply_to: 'sallys_confession',
- updated: '2015-12-20 15:01:20.396000000',
- },
- ],
- },
- {
- rootId: 'right_side_comment',
- commentSide: 'right',
- comments: [
- {
- id: 'right_side_comment',
- message: 'right side comment',
- __commentSide: 'right',
- __draft: true,
- updated: '2015-12-20 15:01:20.396000000',
- },
- ],
- }, {
- rootId: 'betsys_confession',
- commentSide: 'left',
- range,
- comments: [
- {
- id: 'betsys_confession',
- message: 'i like you more, jack',
- updated: '2015-12-24 15:00:10.396000000',
- range,
- __commentSide: 'left',
- },
- ],
- },
- ];
-
- flushAsynchronousOperations();
- assert.deepEqual(element.getThread('right').rootId, 'right_side_comment');
- assert.deepEqual(element.getThread('right').comments.length, 1);
- assert.deepEqual(element.getThread('left').rootId, 'sallys_confession');
- assert.deepEqual(element.getThread('left').comments.length, 3);
- assert.deepEqual(element.getThread('left', range).rootId,
- 'betsys_confession');
- assert.deepEqual(element.getThread('left', range).comments.length, 1);
- });
-
- test('_sortByDate', () => {
- let threadGroups = [
- {
- start_datetime: '2015-12-23 15:00:20.396000000',
- comments: [],
- locationRange: 'line',
- },
- {
- start_datetime: '2015-12-22 15:00:10.396000000',
- comments: [],
- locationRange: 'range-1-1-1-2',
- },
- ];
-
- let expectedResult = [
- {
- start_datetime: '2015-12-22 15:00:10.396000000',
- comments: [],
- locationRange: 'range-1-1-1-2',
- }, {
- start_datetime: '2015-12-23 15:00:20.396000000',
- comments: [],
- locationRange: 'line',
- },
- ];
-
- assert.deepEqual(element._sortByDate(threadGroups), expectedResult);
-
- // When a comment doesn't have a date, the one without the date should be
- // last.
- threadGroups = [
- {
- start_datetime: '2015-12-23 15:00:20.396000000',
- comments: [],
- locationRange: 'line',
- },
- {
- comments: [],
- locationRange: 'range-1-1-1-2',
- },
- ];
-
- expectedResult = [
- {
- start_datetime: '2015-12-23 15:00:20.396000000',
- comments: [],
- locationRange: 'line',
- },
- {
- comments: [],
- locationRange: 'range-1-1-1-2',
- },
- ];
-
- assert.deepEqual(element._sortByDate(threadGroups), expectedResult);
- });
-
- test('_calculateLocationRange', () => {
- const comment = {__commentSide: 'left'};
- const range = {
- start_line: 1,
- start_character: 2,
- end_line: 3,
- end_character: 4,
- };
- assert.equal(
- element._calculateLocationRange(range, comment),
- 'range-1-2-3-4-left');
- });
-
- test('addNewThread', () => {
- const locationRange = 'range-1-2-3-4';
- element._threads = [{locationRange: 'line'}];
- element.addNewThread(locationRange);
- assert(element._threads.length, 2);
- });
-
- test('removeThread', () => {
- const locationRange = 'range-1-2-3-4';
- element._threads = [
- {locationRange: 'range-1-2-3-4', comments: []},
- {locationRange: 'line', comments: []},
- ];
- flushAsynchronousOperations();
- element.removeThread(locationRange);
- flushAsynchronousOperations();
- assert(element._threads.length, 1);
- });
-
- test('_rangesEqual', () => {
- const range1 =
- {startLine: 123, startChar: 345, endLine: 234, endChar: 456};
- const range2 =
- {startLine: 1, startChar: 2, endLine: 3, endChar: 4};
-
- assert.isTrue(element._rangesEqual(null, null));
- assert.isTrue(element._rangesEqual(null, undefined));
- assert.isTrue(element._rangesEqual(undefined, null));
- assert.isTrue(element._rangesEqual(undefined, undefined));
-
- assert.isFalse(element._rangesEqual(range1, null));
- assert.isFalse(element._rangesEqual(null, range1));
- assert.isFalse(element._rangesEqual(range1, range2));
-
- assert.isTrue(element._rangesEqual(range1, Object.assign({}, range1)));
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
deleted file mode 100644
index c3a1de4796..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
+++ /dev/null
@@ -1,126 +0,0 @@
-<!--
-@license
-Copyright (C) 2015 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.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.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">
-<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-storage/gr-storage.html">
-<link rel="import" href="../gr-diff-comment/gr-diff-comment.html">
-
-<dom-module id="gr-diff-comment-thread">
- <template>
- <style include="shared-styles">
- gr-button {
- margin-left: .5em;
- }
- #actions {
- margin-left: auto;
- padding: .5em .7em;
- }
- #container {
- background-color: var(--comment-background-color);
- border: 1px solid var(--border-color);
- color: var(--comment-text-color);
- display: block;
- margin-bottom: 1px;
- white-space: normal;
- }
- #container.unresolved {
- background-color: var(--unresolved-comment-background-color);
- }
- #commentInfoContainer {
- border-top: 1px dotted var(--border-color);
- display: flex;
- }
- #unresolvedLabel {
- font-family: var(--font-family);
- margin: auto 0;
- padding: .5em .7em;
- }
- .pathInfo {
- display: flex;
- align-items: baseline;
- }
- .descriptionText {
- margin-left: .5rem;
- font-style: italic;
- }
- </style>
- <template is="dom-if" if="[[showFilePath]]">
- <div class="pathInfo">
- <a href$="[[_getDiffUrlForComment(projectName, changeNum, path, patchNum)]]">[[_computeDisplayPath(path)]]</a>
- <span class="descriptionText">Patchset [[patchNum]]</span>
- </div>
- </template>
- <div id="container" class$="[[_computeHostClass(unresolved)]]">
- <template id="commentList" is="dom-repeat" items="[[_orderedComments]]"
- as="comment">
- <gr-diff-comment
- comment="{{comment}}"
- robot-button-disabled="[[_hideActions(_showActions, _lastComment)]]"
- change-num="[[changeNum]]"
- patch-num="[[patchNum]]"
- draft="[[_isDraft(comment)]]"
- show-actions="[[_showActions]]"
- comment-side="[[comment.__commentSide]]"
- side="[[comment.side]]"
- root-id="[[rootId]]"
- project-config="[[_projectConfig]]"
- on-create-fix-comment="_handleCommentFix"
- on-comment-discard="_handleCommentDiscard"
- on-comment-save="_handleCommentSavedOrDiscarded"></gr-diff-comment>
- </template>
- <div id="commentInfoContainer"
- hidden$="[[_hideActions(_showActions, _lastComment)]]">
- <span id="unresolvedLabel" hidden$="[[!unresolved]]">Unresolved</span>
- <div id="actions">
- <gr-button
- id="replyBtn"
- link
- secondary
- class="action reply"
- on-tap="_handleCommentReply">Reply</gr-button>
- <gr-button
- id="quoteBtn"
- link
- secondary
- class="action quote"
- on-tap="_handleCommentQuote">Quote</gr-button>
- <gr-button
- id="ackBtn"
- link
- secondary
- class="action ack"
- on-tap="_handleCommentAck">Ack</gr-button>
- <gr-button
- id="doneBtn"
- link
- secondary
- class="action done"
- on-tap="_handleCommentDone">Done</gr-button>
- </div>
- </div>
- </div>
- <gr-reporting id="reporting"></gr-reporting>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- <gr-storage id="storage"></gr-storage>
- </template>
- <script src="gr-diff-comment-thread.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
deleted file mode 100644
index 4ef173a467..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
+++ /dev/null
@@ -1,466 +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 UNRESOLVED_EXPAND_COUNT = 5;
- const NEWLINE_PATTERN = /\n/g;
-
- Polymer({
- is: 'gr-diff-comment-thread',
-
- /**
- * Fired when the thread should be discarded.
- *
- * @event thread-discard
- */
-
- /**
- * Fired when a comment in the thread is permanently modified.
- *
- * @event thread-changed
- */
-
- properties: {
- changeNum: String,
- comments: {
- type: Array,
- value() { return []; },
- },
- range: Object,
- keyEventTarget: {
- type: Object,
- value() { return document.body; },
- },
- commentSide: String,
- patchNum: String,
- path: String,
- projectName: {
- type: String,
- observer: '_projectNameChanged',
- },
- hasDraft: {
- type: Boolean,
- notify: true,
- reflectToAttribute: true,
- },
- isOnParent: {
- type: Boolean,
- value: false,
- },
- parentIndex: {
- type: Number,
- value: null,
- },
- rootId: {
- type: String,
- notify: true,
- computed: '_computeRootId(comments.*)',
- },
- /**
- * If this is true, the comment thread also needs to have the change and
- * line properties property set
- */
- showFilePath: {
- type: Boolean,
- value: false,
- },
- /** Necessary only if showFilePath is true */
- lineNum: Number,
- unresolved: {
- type: Boolean,
- notify: true,
- reflectToAttribute: true,
- },
- _showActions: Boolean,
- _lastComment: Object,
- _orderedComments: Array,
- _projectConfig: Object,
- },
-
- behaviors: [
- Gerrit.KeyboardShortcutBehavior,
- Gerrit.PathListBehavior,
- ],
-
- listeners: {
- 'comment-update': '_handleCommentUpdate',
- },
-
- observers: [
- '_commentsChanged(comments.*)',
- ],
-
- keyBindings: {
- 'e shift+e': '_handleEKey',
- },
-
- attached() {
- this._getLoggedIn().then(loggedIn => {
- this._showActions = loggedIn;
- });
- this._setInitialExpandedState();
- },
-
- addOrEditDraft(opt_lineNum, opt_range) {
- const lastComment = this.comments[this.comments.length - 1] || {};
- if (lastComment.__draft) {
- const commentEl = this._commentElWithDraftID(
- lastComment.id || lastComment.__draftID);
- commentEl.editing = true;
-
- // If the comment was collapsed, re-open it to make it clear which
- // actions are available.
- commentEl.collapsed = false;
- } else {
- const range = opt_range ? opt_range :
- lastComment ? lastComment.range : undefined;
- const unresolved = lastComment ? lastComment.unresolved : undefined;
- this.addDraft(opt_lineNum, range, unresolved);
- }
- },
-
- addDraft(opt_lineNum, opt_range, opt_unresolved) {
- const draft = this._newDraft(opt_lineNum, opt_range);
- draft.__editing = true;
- draft.unresolved = opt_unresolved === false ? opt_unresolved : true;
- this.push('comments', draft);
- },
-
- fireRemoveSelf() {
- this.dispatchEvent(new CustomEvent('thread-discard',
- {detail: {rootId: this.rootId}, bubbles: false}));
- },
-
- _getDiffUrlForComment(projectName, changeNum, path, patchNum) {
- return Gerrit.Nav.getUrlForDiffById(changeNum,
- projectName, path, patchNum,
- null, this.lineNum);
- },
-
- _computeDisplayPath(path) {
- const lineString = this.lineNum ? `#${this.lineNum}` : '';
- return this.computeDisplayPath(path) + lineString;
- },
-
- _getLoggedIn() {
- return this.$.restAPI.getLoggedIn();
- },
-
- _commentsChanged() {
- this._orderedComments = this._sortedComments(this.comments);
- this.updateThreadProperties();
- },
-
- updateThreadProperties() {
- if (this._orderedComments.length) {
- this._lastComment = this._getLastComment();
- this.unresolved = this._lastComment.unresolved;
- this.hasDraft = this._lastComment.__draft;
- }
- },
-
- _hideActions(_showActions, _lastComment) {
- return !_showActions || !_lastComment || !!_lastComment.__draft;
- },
-
- _getLastComment() {
- return this._orderedComments[this._orderedComments.length - 1] || {};
- },
-
- _handleEKey(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
-
- // Don’t preventDefault in this case because it will render the event
- // useless for other handlers (other gr-diff-comment-thread elements).
- if (e.detail.keyboardEvent.shiftKey) {
- this._expandCollapseComments(true);
- } else {
- if (this.modifierPressed(e)) { return; }
- this._expandCollapseComments(false);
- }
- },
-
- _expandCollapseComments(actionIsCollapse) {
- const comments =
- Polymer.dom(this.root).querySelectorAll('gr-diff-comment');
- for (const comment of comments) {
- comment.collapsed = actionIsCollapse;
- }
- },
-
- /**
- * Sets the initial state of the comment thread.
- * Expands the thread if one of the following is true:
- * - last {UNRESOLVED_EXPAND_COUNT} comments expanded by default if the
- * thread is unresolved,
- * - it's a robot comment.
- */
- _setInitialExpandedState() {
- if (this._orderedComments) {
- for (let i = 0; i < this._orderedComments.length; i++) {
- const comment = this._orderedComments[i];
- const isRobotComment = !!comment.robot_id;
- // False if it's an unresolved comment under UNRESOLVED_EXPAND_COUNT.
- const resolvedThread = !this.unresolved ||
- this._orderedComments.length - i - 1 >= UNRESOLVED_EXPAND_COUNT;
- comment.collapsed = !isRobotComment && resolvedThread;
- }
- }
- },
-
- _sortedComments(comments) {
- return comments.slice().sort((c1, c2) => {
- const c1Date = c1.__date || util.parseDate(c1.updated);
- const c2Date = c2.__date || util.parseDate(c2.updated);
- const dateCompare = c1Date - c2Date;
- // Ensure drafts are at the end. There should only be one but in edge
- // cases could be more. In the unlikely event two drafts are being
- // compared, use the typical date compare.
- if (c2.__draft && !c1.__draft ) { return 0; }
- if (c1.__draft && !c2.__draft ) { return 1; }
- if (dateCompare === 0 && (!c1.id || !c1.id.localeCompare)) { return 0; }
- // If same date, fall back to sorting by id.
- return dateCompare ? dateCompare : c1.id.localeCompare(c2.id);
- });
- },
-
- _createReplyComment(parent, content, opt_isEditing,
- opt_unresolved) {
- this.$.reporting.recordDraftInteraction();
- const reply = this._newReply(
- this._orderedComments[this._orderedComments.length - 1].id,
- parent.line,
- content,
- opt_unresolved,
- parent.range);
-
- // If there is currently a comment in an editing state, add an attribute
- // so that the gr-diff-comment knows not to populate the draft text.
- for (let i = 0; i < this.comments.length; i++) {
- if (this.comments[i].__editing) {
- reply.__otherEditing = true;
- break;
- }
- }
-
- if (opt_isEditing) {
- reply.__editing = true;
- }
-
- this.push('comments', reply);
-
- if (!opt_isEditing) {
- // Allow the reply to render in the dom-repeat.
- this.async(() => {
- const commentEl = this._commentElWithDraftID(reply.__draftID);
- commentEl.save();
- }, 1);
- }
- },
-
- _isDraft(comment) {
- return !!comment.__draft;
- },
-
- /**
- * @param {boolean=} opt_quote
- */
- _processCommentReply(opt_quote) {
- const comment = this._lastComment;
- let quoteStr;
- if (opt_quote) {
- const msg = comment.message;
- quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
- }
- this._createReplyComment(comment, quoteStr, true, comment.unresolved);
- },
-
- _handleCommentReply(e) {
- this._processCommentReply();
- },
-
- _handleCommentQuote(e) {
- this._processCommentReply(true);
- },
-
- _handleCommentAck(e) {
- const comment = this._lastComment;
- this._createReplyComment(comment, 'Ack', false, false);
- },
-
- _handleCommentDone(e) {
- const comment = this._lastComment;
- this._createReplyComment(comment, 'Done', false, false);
- },
-
- _handleCommentFix(e) {
- const comment = e.detail.comment;
- const msg = comment.message;
- const quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
- const response = quoteStr + 'Please Fix';
- this._createReplyComment(comment, response, false, true);
- },
-
- _commentElWithDraftID(id) {
- const els = Polymer.dom(this.root).querySelectorAll('gr-diff-comment');
- for (const el of els) {
- if (el.comment.id === id || el.comment.__draftID === id) {
- return el;
- }
- }
- return null;
- },
-
- _newReply(inReplyTo, opt_lineNum, opt_message, opt_unresolved,
- opt_range) {
- const d = this._newDraft(opt_lineNum);
- d.in_reply_to = inReplyTo;
- d.range = opt_range;
- if (opt_message != null) {
- d.message = opt_message;
- }
- if (opt_unresolved !== undefined) {
- d.unresolved = opt_unresolved;
- }
- return d;
- },
-
- /**
- * @param {number=} opt_lineNum
- * @param {!Object=} opt_range
- */
- _newDraft(opt_lineNum, opt_range) {
- const d = {
- __draft: true,
- __draftID: Math.random().toString(36),
- __date: new Date(),
- path: this.path,
- patchNum: this.patchNum,
- side: this._getSide(this.isOnParent),
- __commentSide: this.commentSide,
- };
- if (opt_lineNum) {
- d.line = opt_lineNum;
- }
- if (opt_range) {
- d.range = {
- start_line: opt_range.startLine,
- start_character: opt_range.startChar,
- end_line: opt_range.endLine,
- end_character: opt_range.endChar,
- };
- }
- if (this.parentIndex) {
- d.parent = this.parentIndex;
- }
- return d;
- },
-
- _getSide(isOnParent) {
- if (isOnParent) { return 'PARENT'; }
- return 'REVISION';
- },
-
- _computeRootId(comments) {
- // Keep the root ID even if the comment was removed, so that notification
- // to sync will know which thread to remove.
- if (!comments.base.length) { return this.rootId; }
- const rootComment = comments.base[0];
- return rootComment.id || rootComment.__draftID;
- },
-
- _handleCommentDiscard(e) {
- const diffCommentEl = Polymer.dom(e).rootTarget;
- const comment = diffCommentEl.comment;
- const idx = this._indexOf(comment, this.comments);
- if (idx == -1) {
- throw Error('Cannot find comment ' +
- JSON.stringify(diffCommentEl.comment));
- }
- this.splice('comments', idx, 1);
- if (this.comments.length === 0) {
- this.fireRemoveSelf();
- }
- this._handleCommentSavedOrDiscarded(e);
-
- // Check to see if there are any other open comments getting edited and
- // set the local storage value to its message value.
- for (const changeComment of this.comments) {
- if (changeComment.__editing) {
- const commentLocation = {
- changeNum: this.changeNum,
- patchNum: this.patchNum,
- path: changeComment.path,
- line: changeComment.line,
- };
- return this.$.storage.setDraftComment(commentLocation,
- changeComment.message);
- }
- }
- },
-
- _handleCommentSavedOrDiscarded(e) {
- this.dispatchEvent(new CustomEvent('thread-changed',
- {detail: {rootId: this.rootId, path: this.path},
- bubbles: false}));
- },
-
- _handleCommentUpdate(e) {
- const comment = e.detail.comment;
- const index = this._indexOf(comment, this.comments);
- if (index === -1) {
- // This should never happen: comment belongs to another thread.
- console.warn('Comment update for another comment thread.');
- return;
- }
- this.set(['comments', index], comment);
- // Because of the way we pass these comment objects around by-ref, in
- // combination with the fact that Polymer does dirty checking in
- // observers, the this.set() call above will not cause a thread update in
- // some situations.
- this.updateThreadProperties();
- },
-
- _indexOf(comment, arr) {
- for (let i = 0; i < arr.length; i++) {
- const c = arr[i];
- if ((c.__draftID != null && c.__draftID == comment.__draftID) ||
- (c.id != null && c.id == comment.id)) {
- return i;
- }
- }
- return -1;
- },
-
- _computeHostClass(unresolved) {
- return unresolved ? 'unresolved' : '';
- },
-
- /**
- * Load the project config when a project name has been provided.
- *
- * @param {string} name The project name.
- */
- _projectNameChanged(name) {
- if (!name) { return; }
- this.$.restAPI.getProjectConfig(name).then(config => {
- this._projectConfig = config;
- });
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
deleted file mode 100644
index 868be90a5e..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
+++ /dev/null
@@ -1,718 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2015 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-diff-comment-thread</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-diff-comment-thread.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-diff-comment-thread></gr-diff-comment-thread>
- </template>
-</test-fixture>
-
-<test-fixture id="withComment">
- <template>
- <gr-diff-comment-thread></gr-diff-comment-thread>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-diff-comment-thread tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- stub('gr-rest-api-interface', {
- getLoggedIn() { return Promise.resolve(false); },
- });
- sandbox = sinon.sandbox.create();
- element = fixture('basic');
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('comments are sorted correctly', () => {
- const comments = [
- {
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- __date: new Date('2015-12-25'),
- }, {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- id: 'sally_to_dr_finklestein',
- message: 'i’m running away',
- updated: '2015-10-31 09:00:20.396000000',
- }, {
- id: 'sallys_defiance',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'i will poison you so i can get away',
- updated: '2015-10-31 15:00:20.396000000',
- }, {
- id: 'dr_finklesteins_response',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'no i will pull a thread and your arm will fall off',
- updated: '2015-10-31 11:00:20.396000000',
- }, {
- id: 'sallys_mission',
- message: 'i have to find santa',
- updated: '2015-12-24 15:00:20.396000000',
- },
- ];
- const results = element._sortedComments(comments);
- assert.deepEqual(results, [
- {
- id: 'sally_to_dr_finklestein',
- message: 'i’m running away',
- updated: '2015-10-31 09:00:20.396000000',
- }, {
- id: 'dr_finklesteins_response',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'no i will pull a thread and your arm will fall off',
- updated: '2015-10-31 11:00:20.396000000',
- }, {
- id: 'sallys_defiance',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'i will poison you so i can get away',
- updated: '2015-10-31 15:00:20.396000000',
- }, {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- id: 'sallys_mission',
- message: 'i have to find santa',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- __date: new Date('2015-12-25'),
- },
- ]);
- });
-
- test('addOrEditDraft w/ edit draft', () => {
- element.comments = [{
- id: 'jacks_reply',
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- updated: '2015-12-25 15:00:20.396000000',
- __draft: true,
- }];
- const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
- () => { return {}; });
- const addDraftStub = sandbox.stub(element, 'addDraft');
-
- element.addOrEditDraft(123);
-
- assert.isTrue(commentElStub.called);
- assert.isFalse(addDraftStub.called);
- });
-
- test('addOrEditDraft w/o edit draft', () => {
- element.comments = [];
- const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
- () => { return {}; });
- const addDraftStub = sandbox.stub(element, 'addDraft');
-
- element.addOrEditDraft(123);
-
- assert.isFalse(commentElStub.called);
- assert.isTrue(addDraftStub.called);
- });
-
- test('_hideActions', () => {
- let showActions = true;
- const lastComment = {};
- assert.equal(element._hideActions(showActions, lastComment), false);
- showActions = false;
- assert.equal(element._hideActions(showActions, lastComment), true);
- showActions = true;
- lastComment.__draft = true;
- assert.equal(element._hideActions(showActions, lastComment), true);
- });
-
- test('setting project name loads the project config', done => {
- const projectName = 'foo/bar/baz';
- const getProjectStub = sandbox.stub(element.$.restAPI, 'getProjectConfig')
- .returns(Promise.resolve({}));
- element.projectName = projectName;
- flush(() => {
- assert.isTrue(getProjectStub.calledWithExactly(projectName));
- done();
- });
- });
-
- test('optionally show file path', () => {
- // Path info doesn't exist when showFilePath is false. Because it's in a
- // dom-if it is not yet in the dom.
- assert.isNotOk(element.$$('.pathInfo'));
-
- sandbox.stub(Gerrit.Nav, 'getUrlForDiffById');
- element.changeNum = 123;
- element.projectName = 'test project';
- element.path = 'path/to/file';
- element.patchNum = 3;
- element.lineNum = 5;
- element.showFilePath = true;
- flushAsynchronousOperations();
- assert.isOk(element.$$('.pathInfo'));
- assert.notEqual(getComputedStyle(element.$$('.pathInfo')).display,
- 'none');
- assert.isTrue(Gerrit.Nav.getUrlForDiffById.lastCall.calledWithExactly(
- element.changeNum, element.projectName, element.path,
- element.patchNum, null, element.lineNum));
- });
-
- test('_computeDisplayPath', () => {
- const path = 'path/to/file';
- assert.equal(element._computeDisplayPath(path), 'path/to/file');
-
- element.lineNum = 5;
- assert.equal(element._computeDisplayPath(path), 'path/to/file#5');
- });
- });
-
- suite('comment action tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- stub('gr-rest-api-interface', {
- getLoggedIn() { return Promise.resolve(false); },
- saveDiffDraft() {
- return Promise.resolve({
- ok: true,
- text() {
- return Promise.resolve(')]}\'\n' +
- JSON.stringify({
- id: '7afa4931_de3d65bd',
- path: '/path/to/file.txt',
- line: 5,
- in_reply_to: 'baf0414d_60047215',
- updated: '2015-12-21 02:01:10.850000000',
- message: 'Done',
- }));
- },
- });
- },
- deleteDiffDraft() { return Promise.resolve({ok: true}); },
- });
- element = fixture('withComment');
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:33.843000000',
- path: '/path/to/file.txt',
- }];
- flushAsynchronousOperations();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('reply', () => {
- const commentEl = element.$$('gr-diff-comment');
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- assert.ok(commentEl);
-
- const replyBtn = element.$.replyBtn;
- MockInteractions.tap(replyBtn);
- flushAsynchronousOperations();
-
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.notOk(drafts[0].message, 'message should be empty');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('quote reply', () => {
- const commentEl = element.$$('gr-diff-comment');
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- assert.ok(commentEl);
-
- const quoteBtn = element.$.quoteBtn;
- MockInteractions.tap(quoteBtn);
- flushAsynchronousOperations();
-
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message, '> is this a crossover episode!?\n\n');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('quote reply multiline', () => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?\nIt might be!',
- updated: '2015-12-08 19:48:33.843000000',
- }];
- flushAsynchronousOperations();
-
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const quoteBtn = element.$.quoteBtn;
- MockInteractions.tap(quoteBtn);
- flushAsynchronousOperations();
-
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message,
- '> is this a crossover episode!?\n> It might be!\n\n');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('ack', done => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.changeNum = '42';
- element.patchNum = '1';
-
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const ackBtn = element.$.ackBtn;
- MockInteractions.tap(ackBtn);
- flush(() => {
- const drafts = element.comments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message, 'Ack');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.equal(drafts[0].unresolved, false);
- assert.isTrue(reportStub.calledOnce);
- done();
- });
- });
-
- test('done', done => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.changeNum = '42';
- element.patchNum = '1';
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const doneBtn = element.$.doneBtn;
- MockInteractions.tap(doneBtn);
- flush(() => {
- const drafts = element.comments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message, 'Done');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isFalse(drafts[0].unresolved);
- assert.isTrue(reportStub.calledOnce);
- done();
- });
- });
-
- test('save', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.path = '/path/to/file.txt';
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const saveOrDiscardStub = sandbox.stub();
- element.addEventListener('thread-changed', saveOrDiscardStub);
- element.$$('gr-diff-comment')._fireSave();
-
- flush(() => {
- assert.isTrue(saveOrDiscardStub.called);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
- 'baf0414d_60047215');
- assert.equal(element.rootId, 'baf0414d_60047215');
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
- '/path/to/file.txt');
- done();
- });
- });
-
- test('please fix', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
- commentEl.addEventListener('create-fix-comment', () => {
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(
- drafts[0].message, '> is this a crossover episode!?\n\nPlease Fix');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(drafts[0].unresolved);
- done();
- });
- commentEl.fire('create-fix-comment', {comment: commentEl.comment},
- {bubbles: false});
- });
-
- test('discard', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.path = '/path/to/file.txt';
- element.push('comments', element._newReply(
- element.comments[0].id,
- element.comments[0].line,
- element.comments[0].path,
- 'it’s pronouced jiff, not giff'));
- flushAsynchronousOperations();
-
- const saveOrDiscardStub = sandbox.stub();
- element.addEventListener('thread-changed', saveOrDiscardStub);
- const draftEl =
- Polymer.dom(element.root).querySelectorAll('gr-diff-comment')[1];
- assert.ok(draftEl);
- draftEl.addEventListener('comment-discard', () => {
- const drafts = element.comments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 0);
- assert.isTrue(saveOrDiscardStub.called);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
- element.rootId);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
- element.path);
- done();
- });
- draftEl.fire('comment-discard', {comment: draftEl.comment},
- {bubbles: false});
- });
-
- test('discard with a single comment still fires event with previous rootId',
- done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.path = '/path/to/file.txt';
- element.comments = [];
- element.addOrEditDraft('1');
- flushAsynchronousOperations();
- const rootId = element.rootId;
- assert.isOk(rootId);
-
- const saveOrDiscardStub = sandbox.stub();
- element.addEventListener('thread-changed', saveOrDiscardStub);
- const draftEl =
- Polymer.dom(element.root).querySelectorAll('gr-diff-comment')[0];
- assert.ok(draftEl);
- draftEl.addEventListener('comment-discard', () => {
- assert.equal(element.comments.length, 0);
- assert.isTrue(saveOrDiscardStub.called);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
- rootId);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
- element.path);
- done();
- });
- draftEl.fire('comment-discard', {comment: draftEl.comment},
- {bubbles: false});
- });
-
- test('first editing comment does not add __otherEditing attribute', () => {
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:33.843000000',
- __draft: true,
- }];
-
- const replyBtn = element.$.replyBtn;
- MockInteractions.tap(replyBtn);
- flushAsynchronousOperations();
-
- const editing = element._orderedComments.filter(c => {
- return c.__editing == true;
- });
- assert.equal(editing.length, 1);
- assert.equal(!!editing[0].__otherEditing, false);
- });
-
- test('When not editing other comments, local storage not set' +
- ' after discard', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:31.843000000',
- },
- {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- __draftID: '1',
- in_reply_to: 'baf0414d_60047215',
- line: 5,
- message: 'yes',
- updated: '2015-12-08 19:48:32.843000000',
- __draft: true,
- __editing: true,
- },
- {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- __draftID: '2',
- in_reply_to: 'baf0414d_60047215',
- line: 5,
- message: 'no',
- updated: '2015-12-08 19:48:33.843000000',
- __draft: true,
- }];
- const storageStub = sinon.stub(element.$.storage, 'setDraftComment');
- flushAsynchronousOperations();
-
- const draftEl =
- Polymer.dom(element.root).querySelectorAll('gr-diff-comment')[1];
- assert.ok(draftEl);
- draftEl.addEventListener('comment-discard', () => {
- assert.isFalse(storageStub.called);
- storageStub.restore();
- done();
- });
- draftEl.fire('comment-discard', {comment: draftEl.comment},
- {bubbles: false});
- });
-
- test('comment-update', () => {
- const commentEl = element.$$('gr-diff-comment');
- const updatedComment = {
- id: element.comments[0].id,
- foo: 'bar',
- };
- commentEl.fire('comment-update', {comment: updatedComment});
- assert.strictEqual(element.comments[0], updatedComment);
- });
-
- suite('jack and sally comment data test consolidation', () => {
- setup(() => {
- element.comments = [
- {
- id: 'jacks_reply',
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- updated: '2015-12-25 15:00:20.396000000',
- unresolved: false,
- }, {
- id: 'sallys_confession',
- in_reply_to: 'nonexistent_comment',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- id: 'sally_to_dr_finklestein',
- in_reply_to: 'nonexistent_comment',
- message: 'i’m running away',
- updated: '2015-10-31 09:00:20.396000000',
- }, {
- id: 'sallys_defiance',
- message: 'i will poison you so i can get away',
- updated: '2015-10-31 15:00:20.396000000',
- }];
- });
-
- test('orphan replies', () => {
- assert.equal(4, element._orderedComments.length);
- });
-
- test('keyboard shortcuts', () => {
- const expandCollapseStub =
- sinon.stub(element, '_expandCollapseComments');
- MockInteractions.pressAndReleaseKeyOn(element, 69, null, 'e');
- assert.isTrue(expandCollapseStub.lastCall.calledWith(false));
-
- MockInteractions.pressAndReleaseKeyOn(element, 69, 'shift', 'e');
- assert.isTrue(expandCollapseStub.lastCall.calledWith(true));
- });
-
- test('comment in_reply_to is either null or most recent comment', () => {
- element._createReplyComment(element.comments[3], 'dummy', true);
- flushAsynchronousOperations();
- assert.equal(element._orderedComments.length, 5);
- assert.equal(element._orderedComments[4].in_reply_to, 'jacks_reply');
- });
-
- test('resolvable comments', () => {
- assert.isFalse(element.unresolved);
- element._createReplyComment(element.comments[3], 'dummy', true, true);
- flushAsynchronousOperations();
- assert.isTrue(element.unresolved);
- });
-
- test('_setInitialExpandedState', () => {
- element.unresolved = true;
- element._setInitialExpandedState();
- for (let i = 0; i < element.comments.length; i++) {
- assert.isFalse(element.comments[i].collapsed);
- }
- element.unresolved = false;
- element._setInitialExpandedState();
- for (let i = 0; i < element.comments.length; i++) {
- assert.isTrue(element.comments[i].collapsed);
- }
- for (let i = 0; i < element.comments.length; i++) {
- element.comments[i].robot_id = 123;
- }
- element._setInitialExpandedState();
- for (let i = 0; i < element.comments.length; i++) {
- assert.isFalse(element.comments[i].collapsed);
- }
- });
- });
-
- test('_computeHostClass', () => {
- assert.equal(element._computeHostClass(true), 'unresolved');
- assert.equal(element._computeHostClass(false), '');
- });
-
- test('addDraft sets unresolved state correctly', () => {
- let unresolved = true;
- element.comments = [];
- element.addDraft(null, null, unresolved);
- assert.equal(element.comments[0].unresolved, true);
-
- unresolved = false; // comment should get added as actually resolved.
- element.comments = [];
- element.addDraft(null, null, unresolved);
- assert.equal(element.comments[0].unresolved, false);
-
- element.comments = [];
- element.addDraft();
- assert.equal(element.comments[0].unresolved, true);
- });
-
- test('_newDraft', () => {
- element.commentSide = 'left';
- element.patchNum = 3;
- const draft = element._newDraft();
- assert.equal(draft.__commentSide, 'left');
- assert.equal(draft.patchNum, 3);
- });
-
- test('new comment gets created', () => {
- element.comments = [];
- element.addOrEditDraft(1);
- assert.equal(element.comments.length, 1);
- // Mock a submitted comment.
- element.comments[0].id = element.comments[0].__draftID;
- element.comments[0].__draft = false;
- element.addOrEditDraft(1);
- assert.equal(element.comments.length, 2);
- });
-
- test('unresolved label', () => {
- element.unresolved = false;
- assert.isTrue(element.$.unresolvedLabel.hasAttribute('hidden'));
- element.unresolved = true;
- assert.isFalse(element.$.unresolvedLabel.hasAttribute('hidden'));
- });
-
- test('draft comments are at the end of orderedComments', () => {
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 2,
- line: 5,
- message: 'Earlier draft',
- updated: '2015-12-08 19:48:33.843000000',
- __draft: true,
- },
- {
- author: {
- name: 'Mr. Peanutbutter2',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 1,
- line: 5,
- message: 'This comment was left last but is not a draft',
- updated: '2015-12-10 19:48:33.843000000',
- },
- {
- author: {
- name: 'Mr. Peanutbutter2',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 3,
- line: 5,
- message: 'Later draft',
- updated: '2015-12-09 19:48:33.843000000',
- __draft: true,
- }];
- assert.equal(element._orderedComments[0].id, '1');
- assert.equal(element._orderedComments[1].id, '2');
- assert.equal(element._orderedComments[2].id, '3');
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
deleted file mode 100644
index 6ea033088a..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
+++ /dev/null
@@ -1,389 +0,0 @@
-<!--
-@license
-Copyright (C) 2015 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.
--->
-
-<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="../../../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">
-<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-<link rel="import" href="../../shared/gr-dialog/gr-dialog.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">
-<link rel="import" href="../../shared/gr-icons/gr-icons.html">
-<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="../../shared/gr-textarea/gr-textarea.html">
-<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
-<link rel="import" href="../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html">
-<script src="../../../scripts/rootElement.js"></script>
-
-<dom-module id="gr-diff-comment">
- <template>
- <style include="shared-styles">
- :host {
- display: block;
- font-family: var(--font-family);
- padding: .7em .7em;
- --iron-autogrow-textarea: {
- box-sizing: border-box;
- padding: 2px;
- };
- }
- :host([disabled]) {
- pointer-events: none;
- }
- :host([disabled]) .actions,
- :host([disabled]) .robotActions,
- :host([disabled]) .date {
- opacity: .5;
- }
- :host([discarding]) {
- display: none;
- }
- .header {
- align-items: baseline;
- cursor: pointer;
- display: flex;
- font-family: 'Open Sans', sans-serif;
- margin: -.7em -.7em 0 -.7em;
- padding: .7em;
- }
- .container.collapsed .header {
- margin-bottom: -.7em;
- }
- .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);
- display: none;
- }
- .date {
- justify-content: flex-end;
- margin-left: 5px;
- min-width: 4.5em;
- text-align: right;
- white-space: nowrap;
- }
- a.date:link,
- a.date:visited {
- color: var(--deemphasized-text-color);
- }
- .actions {
- display: flex;
- justify-content: flex-end;
- padding-top: 0;
- }
- .action {
- margin-left: 1em;
- }
- .robotActions {
- display: flex;
- justify-content: flex-start;
- padding-top: 0;
- }
- .robotActions .action {
- /* Keep button text lined up with output text */
- margin-left: -.3rem;
- margin-right: 1em;
- }
- .rightActions {
- display: flex;
- justify-content: flex-end;
- }
- .editMessage {
- display: none;
- margin: .5em 0;
- width: 100%;
- }
- .container:not(.draft) .actions .hideOnPublished {
- display: none;
- }
- .draft .reply,
- .draft .quote,
- .draft .ack,
- .draft .done {
- display: none;
- }
- .draft .draftLabel,
- .draft .draftTooltip {
- display: inline;
- }
- .draft:not(.editing) .save,
- .draft:not(.editing) .cancel {
- display: none;
- }
- .editing .message,
- .editing .reply,
- .editing .quote,
- .editing .ack,
- .editing .done,
- .editing .edit,
- .editing .discard,
- .editing .unresolved {
- display: none;
- }
- .editing .editMessage {
- display: block;
- }
- .show-hide {
- margin-left: .4em;
- }
- .robotId {
- color: var(--deemphasized-text-color);
- margin-bottom: .8em;
- margin-top: -.4em;
- }
- .robotIcon {
- margin-right: .2em;
- /* 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;
- }
- .runIdInformation {
- margin: .7em 0;
- }
- .robotRun {
- margin-left: .5em;
- }
- .robotRunLink {
- margin-left: .5em;
- }
- 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;
- }
- #container .collapsedContent {
- display: none;
- }
- #container.collapsed {
- padding-bottom: 3px;
- }
- #container.collapsed .collapsedContent {
- display: block;
- overflow: hidden;
- padding-left: 5px;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- #container.collapsed .actions,
- #container.collapsed gr-formatted-text,
- #container.collapsed gr-textarea {
- display: none;
- }
- .resolve,
- .unresolved {
- align-items: center;
- display: flex;
- flex: 1;
- margin: 0;
- }
- .resolve label {
- color: var(--comment-text-color);
- }
- gr-dialog .main {
- display: flex;
- flex-direction: column;
- width: 100%;
- }
- #deleteBtn {
- display: none;
- --gr-button: {
- color: var(--deemphasized-text-color);
- padding: 0;
- }
- }
- #deleteBtn.showDeleteButtons {
- display: block;
- }
- </style>
- <div id="container"
- class="container"
- on-mouseenter="_handleMouseEnter"
- on-mouseleave="_handleMouseLeave">
- <div class="header" id="header" on-tap="_handleToggleCollapsed">
- <div class="headerLeft">
- <span class="authorName">[[comment.author.name]]</span>
- <span class="draftLabel">DRAFT</span>
- <gr-tooltip-content class="draftTooltip"
- has-tooltip
- title="This draft is only visible to you. To publish drafts, click the 'Reply' or 'Start review' button at the top of the change or press the 'A' key."
- max-width="20em"
- show-icon></gr-tooltip-content>
- </div>
- <div class="headerMiddle">
- <span class="collapsedContent">[[comment.message]]</span>
- </div>
- <gr-button
- id="deleteBtn"
- link
- secondary
- class$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]"
- on-tap="_handleCommentDelete">
- (Delete)
- </gr-button>
- <a class="date" href$="[[_computeLinkToComment(comment)]]" on-tap="_handleLinkTap">
- <gr-date-formatter
- has-tooltip
- date-str="[[comment.updated]]"></gr-date-formatter>
- </a>
- <div class="show-hide">
- <label class="show-hide">
- <input type="checkbox" class="show-hide"
- checked$="[[collapsed]]"
- on-change="_handleToggleCollapsed">
- [[_computeShowHideText(collapsed)]]
- </label>
- </div>
- </div>
- <div class="body">
- <template is="dom-if" if="[[comment.robot_id]]">
- <div class="robotId" hidden$="[[collapsed]]">
- <iron-icon class="robotIcon" icon="gr-icons:robot"></iron-icon>
- [[comment.robot_id]]
- </div>
- </template>
- <template is="dom-if" if="[[editing]]">
- <gr-textarea
- id="editTextarea"
- class="editMessage"
- autocomplete="on"
- monospace
- disabled="{{disabled}}"
- rows="4"
- text="{{_messageText}}"></gr-textarea>
- </template>
- <!--The message class is needed to ensure selectability from
- gr-diff-selection.-->
- <gr-formatted-text class="message"
- content="[[comment.message]]"
- no-trailing-margin="[[!comment.__draft]]"
- collapsed="[[collapsed]]"
- config="[[projectConfig.commentlinks]]"></gr-formatted-text>
- <div hidden$="[[!comment.robot_run_id]]" class="message">
- <div class="runIdInformation" hidden$="[[collapsed]]">
- Run ID:
- <template is="dom-if" if="[[comment.url]]">
- <a class="robotRunLink" href$="[[comment.url]]">
- <span class="robotRun link">[[comment.robot_run_id]]</span>
- </a>
- </template>
- <template is="dom-if" if="[[!comment.url]]">
- <span class="robotRun text">[[comment.robot_run_id]]</span>
- </template>
- </div>
- </div>
- <div class="actions humanActions" hidden$="[[!_showHumanActions]]">
- <div class="action resolve hideOnPublished">
- <label>
- <input type="checkbox"
- id="resolvedCheckbox"
- checked="[[resolved]]"
- on-change="_handleToggleResolved">
- Resolved
- </label>
- </div>
- <div class="rightActions">
- <gr-button
- link
- secondary
- class="action cancel hideOnPublished"
- on-tap="_handleCancel">Cancel</gr-button>
- <gr-button
- link
- secondary
- class="action discard hideOnPublished"
- on-tap="_handleDiscard">Discard</gr-button>
- <gr-button
- link
- secondary
- class="action edit hideOnPublished"
- on-tap="_handleEdit">Edit</gr-button>
- <gr-button
- link
- secondary
- disabled$="[[_computeSaveDisabled(_messageText, comment, resolved)]]"
- class="action save hideOnPublished"
- on-tap="_handleSave">Save</gr-button>
- </div>
- </div>
- <div class="robotActions" hidden$="[[!_showRobotActions]]">
- <template is="dom-if" if="[[isRobotComment]]">
- <gr-button
- link
- secondary
- class="action fix"
- on-tap="_handleFix"
- disabled="[[robotButtonDisabled]]">
- Please Fix
- </gr-button>
- <gr-endpoint-decorator name="robot-comment-controls">
- <gr-endpoint-param name="comment" value="[[comment]]">
- </gr-endpoint-param>
- </gr-endpoint-decorator>
- </template>
- </div>
- </div>
- </div>
- <template is="dom-if" if="[[_enableOverlay]]">
- <gr-overlay id="confirmDeleteOverlay" with-backdrop>
- <gr-confirm-delete-comment-dialog id="confirmDeleteComment"
- on-confirm="_handleConfirmDeleteComment"
- on-cancel="_handleCancelDeleteComment">
- </gr-confirm-delete-comment-dialog>
- </gr-overlay>
- <gr-overlay id="confirmDiscardOverlay" with-backdrop>
- <gr-dialog
- id="confirmDiscardDialog"
- confirm-label="Discard"
- confirm-on-enter
- on-confirm="_handleConfirmDiscard"
- on-cancel="_closeConfirmDiscardOverlay">
- <div class="header" slot="header">
- Discard comment
- </div>
- <div class="main" slot="main">
- Are you sure you want to discard this draft comment?
- </div>
- </gr-dialog>
- </gr-overlay>
- </template>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- <gr-storage id="storage"></gr-storage>
- <gr-reporting id="reporting"></gr-reporting>
- </template>
- <script src="gr-diff-comment.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
deleted file mode 100644
index 6de07468f5..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
+++ /dev/null
@@ -1,659 +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 STORAGE_DEBOUNCE_INTERVAL = 400;
- const TOAST_DEBOUNCE_INTERVAL = 200;
-
- const SAVING_MESSAGE = 'Saving';
- const DRAFT_SINGULAR = 'draft...';
- const DRAFT_PLURAL = 'drafts...';
- const SAVED_MESSAGE = 'All changes saved';
-
- const REPORT_CREATE_DRAFT = 'CreateDraftComment';
- const REPORT_UPDATE_DRAFT = 'UpdateDraftComment';
- const REPORT_DISCARD_DRAFT = 'DiscardDraftComment';
-
- Polymer({
- is: 'gr-diff-comment',
-
- /**
- * Fired when the create fix comment action is triggered.
- *
- * @event create-fix-comment
- */
-
- /**
- * Fired when this comment is discarded.
- *
- * @event comment-discard
- */
-
- /**
- * Fired when this comment is saved.
- *
- * @event comment-save
- */
-
- /**
- * Fired when this comment is updated.
- *
- * @event comment-update
- */
-
- /**
- * @event comment-mouse-over
- */
-
- /**
- * @event comment-mouse-out
- */
-
- properties: {
- changeNum: String,
- /** @type {?} */
- comment: {
- type: Object,
- notify: true,
- observer: '_commentChanged',
- },
- isRobotComment: {
- type: Boolean,
- value: false,
- reflectToAttribute: true,
- },
- disabled: {
- type: Boolean,
- value: false,
- reflectToAttribute: true,
- },
- draft: {
- type: Boolean,
- value: false,
- observer: '_draftChanged',
- },
- editing: {
- type: Boolean,
- value: false,
- observer: '_editingChanged',
- },
- discarding: {
- type: Boolean,
- value: false,
- reflectToAttribute: true,
- },
- hasChildren: Boolean,
- patchNum: String,
- showActions: Boolean,
- _showHumanActions: Boolean,
- _showRobotActions: Boolean,
- collapsed: {
- type: Boolean,
- value: true,
- observer: '_toggleCollapseClass',
- },
- /** @type {?} */
- projectConfig: Object,
- robotButtonDisabled: Boolean,
- _isAdmin: {
- type: Boolean,
- value: false,
- },
-
- _xhrPromise: Object, // Used for testing.
- _messageText: {
- type: String,
- value: '',
- observer: '_messageTextChanged',
- },
- commentSide: String,
-
- resolved: Boolean,
-
- _numPendingDraftRequests: {
- type: Object,
- value: {number: 0}, // Intentional to share the object across instances.
- },
-
- _enableOverlay: {
- type: Boolean,
- value: false,
- },
-
- /**
- * Property for storing references to overlay elements. When the overlays
- * are moved to Gerrit.getRootElement() to be shown they are no-longer
- * children, so they can't be queried along the tree, so they are stored
- * here.
- */
- _overlays: {
- type: Object,
- value: () => ({}),
- },
- },
-
- observers: [
- '_commentMessageChanged(comment.message)',
- '_loadLocalDraft(changeNum, patchNum, comment)',
- '_isRobotComment(comment)',
- '_calculateActionstoShow(showActions, isRobotComment)',
- ],
-
- behaviors: [
- Gerrit.KeyboardShortcutBehavior,
- ],
-
- keyBindings: {
- 'ctrl+enter meta+enter ctrl+s meta+s': '_handleSaveKey',
- 'esc': '_handleEsc',
- },
-
- attached() {
- if (this.editing) {
- this.collapsed = false;
- } else if (this.comment) {
- this.collapsed = this.comment.collapsed;
- }
- this._getIsAdmin().then(isAdmin => {
- this._isAdmin = isAdmin;
- });
- },
-
- detached() {
- this.cancelDebouncer('fire-update');
- if (this.textarea) {
- this.textarea.closeDropdown();
- }
- },
-
- get textarea() {
- return this.$$('#editTextarea');
- },
-
- get confirmDeleteOverlay() {
- if (!this._overlays.confirmDelete) {
- this._enableOverlay = true;
- Polymer.dom.flush();
- this._overlays.confirmDelete = this.$$('#confirmDeleteOverlay');
- }
- return this._overlays.confirmDelete;
- },
-
- get confirmDiscardOverlay() {
- if (!this._overlays.confirmDiscard) {
- this._enableOverlay = true;
- Polymer.dom.flush();
- this._overlays.confirmDiscard = this.$$('#confirmDiscardOverlay');
- }
- return this._overlays.confirmDiscard;
- },
-
- _computeShowHideText(collapsed) {
- return collapsed ? '◀' : '▼';
- },
-
- _calculateActionstoShow(showActions, isRobotComment) {
- this._showHumanActions = showActions && !isRobotComment;
- this._showRobotActions = showActions && isRobotComment;
- },
-
- _isRobotComment(comment) {
- this.isRobotComment = !!comment.robot_id;
- },
-
- isOnParent() {
- return this.side === 'PARENT';
- },
-
- _getIsAdmin() {
- return this.$.restAPI.getIsAdmin();
- },
-
- /**
- * @param {*=} opt_comment
- */
- save(opt_comment) {
- let comment = opt_comment;
- if (!comment) { comment = this.comment; }
-
- this.set('comment.message', this._messageText);
- this.editing = false;
- this.disabled = true;
-
- if (!this._messageText) {
- return this._discardDraft();
- }
-
- this._xhrPromise = this._saveDraft(comment).then(response => {
- this.disabled = false;
- if (!response.ok) { return response; }
-
- this._eraseDraftComment();
- return this.$.restAPI.getResponseObject(response).then(obj => {
- const resComment = obj;
- resComment.__draft = true;
- // Maintain the ephemeral draft ID for identification by other
- // elements.
- if (this.comment.__draftID) {
- resComment.__draftID = this.comment.__draftID;
- }
- resComment.__commentSide = this.commentSide;
- this.comment = resComment;
- this._fireSave();
- return obj;
- });
- }).catch(err => {
- this.disabled = false;
- throw err;
- });
-
- return this._xhrPromise;
- },
-
- _eraseDraftComment() {
- // Prevents a race condition in which removing the draft comment occurs
- // prior to it being saved.
- this.cancelDebouncer('store');
-
- this.$.storage.eraseDraftComment({
- changeNum: this.changeNum,
- patchNum: this._getPatchNum(),
- path: this.comment.path,
- line: this.comment.line,
- range: this.comment.range,
- });
- },
-
- _commentChanged(comment) {
- this.editing = !!comment.__editing;
- this.resolved = !comment.unresolved;
- if (this.editing) { // It's a new draft/reply, notify.
- this._fireUpdate();
- }
- },
-
- /**
- * @param {!Object=} opt_mixin
- *
- * @return {!Object}
- */
- _getEventPayload(opt_mixin) {
- return Object.assign({}, opt_mixin, {
- comment: this.comment,
- patchNum: this.patchNum,
- });
- },
-
- _fireSave() {
- this.fire('comment-save', this._getEventPayload());
- },
-
- _fireUpdate() {
- this.debounce('fire-update', () => {
- this.fire('comment-update', this._getEventPayload());
- });
- },
-
- _draftChanged(draft) {
- this.$.container.classList.toggle('draft', draft);
- },
-
- _editingChanged(editing, previousValue) {
- this.$.container.classList.toggle('editing', editing);
- if (this.comment && this.comment.id) {
- this.$$('.cancel').hidden = !editing;
- }
- if (this.comment) {
- this.comment.__editing = this.editing;
- }
- if (editing != !!previousValue) {
- // To prevent event firing on comment creation.
- this._fireUpdate();
- }
- if (editing) {
- this.async(() => {
- Polymer.dom.flush();
- this.textarea.putCursorAtEnd();
- }, 1);
- }
- },
-
- _computeLinkToComment(comment) {
- return '#' + comment.line;
- },
-
- _computeDeleteButtonClass(isAdmin, draft) {
- return isAdmin && !draft ? 'showDeleteButtons' : '';
- },
-
- _computeSaveDisabled(draft, comment, resolved) {
- // If resolved state has changed and a msg exists, save should be enabled.
- if (comment.unresolved === resolved && draft) { return false; }
- return !draft || draft.trim() === '';
- },
-
- _handleSaveKey(e) {
- if (!this._computeSaveDisabled(this._messageText, this.comment,
- this.resolved)) {
- e.preventDefault();
- this._handleSave(e);
- }
- },
-
- _handleEsc(e) {
- if (!this._messageText.length) {
- e.preventDefault();
- this._handleCancel(e);
- }
- },
-
- _handleToggleCollapsed() {
- this.collapsed = !this.collapsed;
- },
-
- _toggleCollapseClass(collapsed) {
- if (collapsed) {
- this.$.container.classList.add('collapsed');
- } else {
- this.$.container.classList.remove('collapsed');
- }
- },
-
- _commentMessageChanged(message) {
- this._messageText = message || '';
- },
-
- _messageTextChanged(newValue, oldValue) {
- if (!this.comment || (this.comment && this.comment.id)) { return; }
-
- this.debounce('store', () => {
- const message = this._messageText;
- const commentLocation = {
- changeNum: this.changeNum,
- patchNum: this._getPatchNum(),
- path: this.comment.path,
- line: this.comment.line,
- range: this.comment.range,
- };
-
- if ((!this._messageText || !this._messageText.length) && oldValue) {
- // If the draft has been modified to be empty, then erase the storage
- // entry.
- this.$.storage.eraseDraftComment(commentLocation);
- } else {
- this.$.storage.setDraftComment(commentLocation, message);
- }
- }, STORAGE_DEBOUNCE_INTERVAL);
- },
-
- _handleLinkTap(e) {
- e.preventDefault();
- const hash = this._computeLinkToComment(this.comment);
- // Don't add the hash to the window history if it's already there.
- // Otherwise you mess up expected back button behavior.
- if (window.location.hash == hash) { return; }
- // Change the URL but don’t trigger a nav event. Otherwise it will
- // reload the page.
- page.show(window.location.pathname + hash, null, false);
- },
-
- _handleEdit(e) {
- e.preventDefault();
- this._messageText = this.comment.message;
- this.editing = true;
- this.$.reporting.recordDraftInteraction();
- },
-
- _handleSave(e) {
- e.preventDefault();
-
- // Ignore saves started while already saving.
- if (this.disabled) { return; }
- const timingLabel = this.comment.id ?
- REPORT_UPDATE_DRAFT : REPORT_CREATE_DRAFT;
- const timer = this.$.reporting.getTimer(timingLabel);
- this.set('comment.__editing', false);
- return this.save().then(() => { timer.end(); });
- },
-
- _handleCancel(e) {
- e.preventDefault();
-
- if (!this.comment.message ||
- this.comment.message.trim().length === 0 ||
- !this.comment.id) {
- this._fireDiscard();
- return;
- }
- this._messageText = this.comment.message;
- this.editing = false;
- },
-
- _fireDiscard() {
- this.cancelDebouncer('fire-update');
- this.fire('comment-discard', this._getEventPayload());
- },
-
- _handleFix() {
- this.dispatchEvent(new CustomEvent('create-fix-comment', {
- bubbles: true,
- detail: this._getEventPayload(),
- }));
- },
-
- _handleDiscard(e) {
- e.preventDefault();
- this.$.reporting.recordDraftInteraction();
-
- if (!this._messageText) {
- this._discardDraft();
- return;
- }
-
- this._openOverlay(this.confirmDiscardOverlay).then(() => {
- this.confirmDiscardOverlay.querySelector('#confirmDiscardDialog')
- .resetFocus();
- });
- },
-
- _handleConfirmDiscard(e) {
- e.preventDefault();
- const timer = this.$.reporting.getTimer(REPORT_DISCARD_DRAFT);
- this._closeConfirmDiscardOverlay();
- return this._discardDraft().then(() => { timer.end(); });
- },
-
- _discardDraft() {
- if (!this.comment.__draft) {
- throw Error('Cannot discard a non-draft comment.');
- }
- this.discarding = true;
- this.editing = false;
- this.disabled = true;
- this._eraseDraftComment();
-
- if (!this.comment.id) {
- this.disabled = false;
- this._fireDiscard();
- return;
- }
-
- this._xhrPromise = this._deleteDraft(this.comment).then(response => {
- this.disabled = false;
- if (!response.ok) {
- this.discarding = false;
- return response;
- }
-
- this._fireDiscard();
- }).catch(err => {
- this.disabled = false;
- throw err;
- });
-
- return this._xhrPromise;
- },
-
- _closeConfirmDiscardOverlay() {
- this._closeOverlay(this.confirmDiscardOverlay);
- },
-
- _getSavingMessage(numPending) {
- if (numPending === 0) { return SAVED_MESSAGE; }
- return [
- SAVING_MESSAGE,
- numPending,
- numPending === 1 ? DRAFT_SINGULAR : DRAFT_PLURAL,
- ].join(' ');
- },
-
- _showStartRequest() {
- const numPending = ++this._numPendingDraftRequests.number;
- this._updateRequestToast(numPending);
- },
-
- _showEndRequest() {
- const numPending = --this._numPendingDraftRequests.number;
- this._updateRequestToast(numPending);
- },
-
- _handleFailedDraftRequest() {
- this._numPendingDraftRequests.number--;
-
- // Cancel the debouncer so that error toasts from the error-manager will
- // not be overridden.
- this.cancelDebouncer('draft-toast');
- },
-
- _updateRequestToast(numPending) {
- const message = this._getSavingMessage(numPending);
- this.debounce('draft-toast', () => {
- // 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}));
- }, TOAST_DEBOUNCE_INTERVAL);
- },
-
- _saveDraft(draft) {
- this._showStartRequest();
- return this.$.restAPI.saveDiffDraft(this.changeNum, this.patchNum, draft)
- .then(result => {
- if (result.ok) {
- this._showEndRequest();
- } else {
- this._handleFailedDraftRequest();
- }
- return result;
- });
- },
-
- _deleteDraft(draft) {
- this._showStartRequest();
- return this.$.restAPI.deleteDiffDraft(this.changeNum, this.patchNum,
- draft).then(result => {
- if (result.ok) {
- this._showEndRequest();
- } else {
- this._handleFailedDraftRequest();
- }
- return result;
- });
- },
-
- _getPatchNum() {
- return this.isOnParent() ? 'PARENT' : this.patchNum;
- },
-
- _loadLocalDraft(changeNum, patchNum, comment) {
- // Only apply local drafts to comments that haven't been saved
- // remotely, and haven't been given a default message already.
- //
- // Don't get local draft if there is another comment that is currently
- // in an editing state.
- if (!comment || comment.id || comment.message || comment.__otherEditing) {
- delete comment.__otherEditing;
- return;
- }
-
- const draft = this.$.storage.getDraftComment({
- changeNum,
- patchNum: this._getPatchNum(),
- path: comment.path,
- line: comment.line,
- range: comment.range,
- });
-
- if (draft) {
- this.set('comment.message', draft.message);
- }
- },
-
- _handleMouseEnter(e) {
- this.fire('comment-mouse-over', this._getEventPayload());
- },
-
- _handleMouseLeave(e) {
- this.fire('comment-mouse-out', this._getEventPayload());
- },
-
- _handleToggleResolved() {
- this.$.reporting.recordDraftInteraction();
- this.resolved = !this.resolved;
- // Modify payload instead of this.comment, as this.comment is passed from
- // the parent by ref.
- const payload = this._getEventPayload();
- payload.comment.unresolved = !this.$.resolvedCheckbox.checked;
- this.fire('comment-update', payload);
- if (!this.editing) {
- // Save the resolved state immediately.
- this.save(payload.comment);
- }
- },
-
- _handleCommentDelete() {
- this._openOverlay(this.confirmDeleteOverlay);
- },
-
- _handleCancelDeleteComment() {
- this._closeOverlay(this.confirmDeleteOverlay);
- },
-
- _openOverlay(overlay) {
- Polymer.dom(Gerrit.getRootElement()).appendChild(overlay);
- return overlay.open();
- },
-
- _closeOverlay(overlay) {
- Polymer.dom(Gerrit.getRootElement()).removeChild(overlay);
- overlay.close();
- },
-
- _handleConfirmDeleteComment() {
- const dialog =
- this.confirmDeleteOverlay.querySelector('#confirmDeleteComment');
- this.$.restAPI.deleteComment(
- this.changeNum, this.patchNum, this.comment.id, dialog.message)
- .then(newComment => {
- this._handleCancelDeleteComment();
- this.comment = newComment;
- });
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
deleted file mode 100644
index ca85892b40..0000000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
+++ /dev/null
@@ -1,856 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2015 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-diff-comment</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="../../../bower_components/page/page.js"></script>
-<script src="../../../scripts/util.js"></script>
-
-<link rel="import" href="gr-diff-comment.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-diff-comment></gr-diff-comment>
- </template>
-</test-fixture>
-
-<test-fixture id="draft">
- <template>
- <gr-diff-comment draft="true"></gr-diff-comment>
- </template>
-</test-fixture>
-
-<script>
-
- function isVisible(el) {
- assert.ok(el);
- return getComputedStyle(el).getPropertyValue('display') !== 'none';
- }
-
- suite('gr-diff-comment tests', () => {
- let element;
- let sandbox;
- setup(() => {
- stub('gr-rest-api-interface', {
- getAccount() { return Promise.resolve(null); },
- });
- element = fixture('basic');
- element.comment = {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:33.843000000',
- };
- sandbox = sinon.sandbox.create();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('collapsible comments', () => {
- // When a comment (not draft) is loaded, it should be collapsed
- assert.isTrue(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
-
- // The header middle content is only visible when comments are collapsed.
- // It shows the message in a condensed way, and limits to a single line.
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- // When the header row is clicked, the comment should expand
- MockInteractions.tap(element.$.header);
- assert.isFalse(element.collapsed);
- assert.isTrue(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is not visible');
- });
-
- test('clicking on date link does not trigger nav', () => {
- const showStub = sinon.stub(page, 'show');
- const dateEl = element.$$('.date');
- assert.ok(dateEl);
- MockInteractions.tap(dateEl);
- const dest = window.location.pathname + '#5';
- assert(showStub.lastCall.calledWithExactly(dest, null, false),
- 'Should navigate to ' + dest + ' without triggering nav');
- showStub.restore();
- });
-
- test('message is not retrieved from storage when other edits', done => {
- const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
- const loadSpy = sandbox.spy(element, '_loadLocalDraft');
-
- element.changeNum = 1;
- element.patchNum = 1;
- element.comment = {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- line: 5,
- __otherEditing: true,
- };
- flush(() => {
- assert.isTrue(loadSpy.called);
- assert.isFalse(storageStub.called);
- done();
- });
- });
-
- test('message is retrieved from storage when no other edits', done => {
- const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
- const loadSpy = sandbox.spy(element, '_loadLocalDraft');
-
- element.changeNum = 1;
- element.patchNum = 1;
- element.comment = {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- line: 5,
- };
- flush(() => {
- assert.isTrue(loadSpy.called);
- assert.isTrue(storageStub.called);
- done();
- });
- });
-
- test('_getPatchNum', () => {
- element.side = 'PARENT';
- element.patchNum = 1;
- assert.equal(element._getPatchNum(), 'PARENT');
- element.side = 'REVISION';
- assert.equal(element._getPatchNum(), 1);
- });
-
- test('comment expand and collapse', () => {
- element.collapsed = true;
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- element.collapsed = false;
- assert.isFalse(element.collapsed);
- assert.isTrue(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is is not visible');
- });
-
- suite('while editing', () => {
- setup(() => {
- element.editing = true;
- element._messageText = 'test';
- sandbox.stub(element, '_handleCancel');
- sandbox.stub(element, '_handleSave');
- flushAsynchronousOperations();
- });
-
- suite('when text is empty', () => {
- setup(() => {
- element._messageText = '';
- element.comment = {};
- });
-
- test('esc closes comment when text is empty', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 27); // esc
- assert.isTrue(element._handleCancel.called);
- });
-
- test('ctrl+enter does not save', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'ctrl'); // ctrl + enter
- assert.isFalse(element._handleSave.called);
- });
-
- test('meta+enter does not save', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'meta'); // meta + enter
- assert.isFalse(element._handleSave.called);
- });
-
- test('ctrl+s does not save', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 83, 'ctrl'); // ctrl + s
- assert.isFalse(element._handleSave.called);
- });
- });
-
- test('esc does not close comment that has content', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 27); // esc
- assert.isFalse(element._handleCancel.called);
- });
-
- test('ctrl+enter saves', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'ctrl'); // ctrl + enter
- assert.isTrue(element._handleSave.called);
- });
-
- test('meta+enter saves', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'meta'); // meta + enter
- assert.isTrue(element._handleSave.called);
- });
-
- test('ctrl+s saves', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 83, 'ctrl'); // ctrl + s
- assert.isTrue(element._handleSave.called);
- });
- });
- test('delete comment button for non-admins is hidden', () => {
- element._isAdmin = false;
- assert.isFalse(element.$$('.action.delete')
- .classList.contains('showDeleteButtons'));
- });
-
- test('delete comment button for admins with draft is hidden', () => {
- element._isAdmin = false;
- element.draft = true;
- assert.isFalse(element.$$('.action.delete')
- .classList.contains('showDeleteButtons'));
- });
-
- test('delete comment', done => {
- sandbox.stub(
- element.$.restAPI, 'deleteComment').returns(Promise.resolve({}));
- sandbox.spy(element.confirmDeleteOverlay, 'open');
- element.changeNum = 42;
- element.patchNum = 0xDEADBEEF;
- element._isAdmin = true;
- assert.isTrue(element.$$('.action.delete')
- .classList.contains('showDeleteButtons'));
- MockInteractions.tap(element.$$('.action.delete'));
- flush(() => {
- element.confirmDeleteOverlay.open.lastCall.returnValue.then(() => {
- const dialog =
- this.confirmDeleteOverlay.querySelector('#confirmDeleteComment');
- dialog.message = 'removal reason';
- element._handleConfirmDeleteComment();
- assert.isTrue(element.$.restAPI.deleteComment.calledWith(
- 42, 0xDEADBEEF, 'baf0414d_60047215', 'removal reason'));
- done();
- });
- });
- });
-
- suite('draft update reporting', () => {
- let endStub;
- let getTimerStub;
- let mockEvent;
-
- setup(() => {
- mockEvent = {preventDefault() {}};
- sandbox.stub(element, 'save')
- .returns(Promise.resolve({}));
- sandbox.stub(element, '_discardDraft')
- .returns(Promise.resolve({}));
- endStub = sinon.stub();
- getTimerStub = sandbox.stub(element.$.reporting, 'getTimer')
- .returns({end: endStub});
- });
-
- test('create', () => {
- element.comment = {};
- return element._handleSave(mockEvent).then(() => {
- assert.isTrue(endStub.calledOnce);
- assert.isTrue(getTimerStub.calledOnce);
- assert.equal(getTimerStub.lastCall.args[0], 'CreateDraftComment');
- });
- });
-
- test('update', () => {
- element.comment = {id: 'abc_123'};
- return element._handleSave(mockEvent).then(() => {
- assert.isTrue(endStub.calledOnce);
- assert.isTrue(getTimerStub.calledOnce);
- assert.equal(getTimerStub.lastCall.args[0], 'UpdateDraftComment');
- });
- });
-
- test('discard', () => {
- element.comment = {id: 'abc_123'};
- sandbox.stub(element, '_closeConfirmDiscardOverlay');
- return element._handleConfirmDiscard(mockEvent).then(() => {
- assert.isTrue(endStub.calledOnce);
- assert.isTrue(getTimerStub.calledOnce);
- assert.equal(getTimerStub.lastCall.args[0], 'DiscardDraftComment');
- });
- });
- });
-
- test('edit reports interaction', () => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- MockInteractions.tap(element.$$('.edit'));
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('discard reports interaction', () => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.draft = true;
- MockInteractions.tap(element.$$('.discard'));
- assert.isTrue(reportStub.calledOnce);
- });
- });
-
- suite('gr-diff-comment draft tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- stub('gr-rest-api-interface', {
- getAccount() { return Promise.resolve(null); },
- saveDiffDraft() {
- return Promise.resolve({
- ok: true,
- text() {
- return Promise.resolve(
- ')]}\'\n{' +
- '"id": "baf0414d_40572e03",' +
- '"path": "/path/to/file",' +
- '"line": 5,' +
- '"updated": "2015-12-08 21:52:36.177000000",' +
- '"message": "saved!"' +
- '}'
- );
- },
- });
- },
- removeChangeReviewer() {
- return Promise.resolve({ok: true});
- },
- });
- stub('gr-storage', {
- getDraftComment() { return null; },
- });
- element = fixture('draft');
- element.changeNum = 42;
- element.patchNum = 1;
- element.editing = false;
- element.comment = {
- __commentSide: 'right',
- __draft: true,
- __draftID: 'temp_draft_id',
- path: '/path/to/file',
- line: 5,
- };
- element.commentSide = 'right';
- sandbox = sinon.sandbox.create();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('button visibility states', () => {
- element.showActions = false;
- assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.showActions = true;
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.draft = true;
- assert.isTrue(isVisible(element.$$('.edit')), 'edit is visible');
- assert.isTrue(isVisible(element.$$('.discard')), 'discard is visible');
- assert.isFalse(isVisible(element.$$('.save')), 'save is not visible');
- assert.isFalse(isVisible(element.$$('.cancel')), 'cancel is not visible');
- assert.isTrue(isVisible(element.$$('.resolve')), 'resolve is visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.editing = true;
- flushAsynchronousOperations();
- assert.isFalse(isVisible(element.$$('.edit')), 'edit is not visible');
- assert.isFalse(isVisible(element.$$('.discard')), 'discard not visible');
- assert.isTrue(isVisible(element.$$('.save')), 'save is visible');
- assert.isTrue(isVisible(element.$$('.cancel')), 'cancel is visible');
- assert.isTrue(isVisible(element.$$('.resolve')), 'resolve is visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.draft = false;
- element.editing = false;
- flushAsynchronousOperations();
- assert.isFalse(isVisible(element.$$('.edit')), 'edit is not visible');
- assert.isFalse(isVisible(element.$$('.discard')),
- 'discard is not visible');
- assert.isFalse(isVisible(element.$$('.save')), 'save is not visible');
- assert.isFalse(isVisible(element.$$('.cancel')), 'cancel is not visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.comment.id = 'foo';
- element.draft = true;
- element.editing = true;
- flushAsynchronousOperations();
- assert.isTrue(isVisible(element.$$('.cancel')), 'cancel is visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.isRobotComment = true;
- element.draft = true;
- assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isFalse(element.$$('.robotActions').hasAttribute('hidden'));
-
- // It is not expected to see Robot comment drafts, but if they appear,
- // they will behave the same as non-drafts.
- element.draft = false;
- assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isFalse(element.$$('.robotActions').hasAttribute('hidden'));
-
- // A robot comment with run ID should display plain text.
- element.set(['comment', 'robot_run_id'], 'text');
- element.editing = false;
- element.collapsed = false;
- flushAsynchronousOperations();
- assert.isNotOk(element.$$('.robotRun.link'));
- assert.notEqual(getComputedStyle(element.$$('.robotRun.text')).display,
- 'none');
-
- // A robot comment with run ID and url should display a link.
- element.set(['comment', 'url'], '/path/to/run');
- flushAsynchronousOperations();
- assert.notEqual(getComputedStyle(element.$$('.robotRun.link')).display,
- 'none');
- assert.equal(getComputedStyle(element.$$('.robotRun.text')).display,
- 'none');
- });
-
- test('collapsible drafts', () => {
- assert.isTrue(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- MockInteractions.tap(element.$.header);
- assert.isFalse(element.collapsed);
- assert.isTrue(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is is not visible');
-
- // When the edit button is pressed, should still see the actions
- // and also textarea
- MockInteractions.tap(element.$$('.edit'));
- flushAsynchronousOperations();
- assert.isFalse(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isTrue(isVisible(element.textarea), 'textarea is visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is not visible');
-
- // When toggle again, everything should be hidden except for textarea
- // and header middle content should be visible
- MockInteractions.tap(element.$.header);
- assert.isTrue(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isFalse(isVisible(element.$$('gr-textarea')),
- 'textarea is not visible');
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- // When toggle again, textarea should remain open in the state it was
- // before
- MockInteractions.tap(element.$.header);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isTrue(isVisible(element.textarea), 'textarea is visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is not visible');
- });
-
- test('draft creation/cancellation', done => {
- assert.isFalse(element.editing);
- MockInteractions.tap(element.$$('.edit'));
- assert.isTrue(element.editing);
-
- element._messageText = '';
- const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
-
- // Save should be disabled on an empty message.
- let disabled = element.$$('.save').hasAttribute('disabled');
- assert.isTrue(disabled, 'save button should be disabled.');
- element._messageText = ' ';
- disabled = element.$$('.save').hasAttribute('disabled');
- assert.isTrue(disabled, 'save button should be disabled.');
-
- const updateStub = sinon.stub();
- element.addEventListener('comment-update', updateStub);
-
- let numDiscardEvents = 0;
- element.addEventListener('comment-discard', e => {
- numDiscardEvents++;
- assert.isFalse(eraseMessageDraftSpy.called);
- if (numDiscardEvents === 2) {
- assert.isFalse(updateStub.called);
- done();
- }
- });
- MockInteractions.tap(element.$$('.cancel'));
- element.flushDebouncer('fire-update');
- element._messageText = '';
- flushAsynchronousOperations();
- MockInteractions.pressAndReleaseKeyOn(element.textarea, 27); // esc
- });
-
- test('draft discard removes message from storage', done => {
- element._messageText = '';
- const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
- sandbox.stub(element, '_closeConfirmDiscardOverlay');
-
- element.addEventListener('comment-discard', e => {
- assert.isTrue(eraseMessageDraftSpy.called);
- done();
- });
- element._handleConfirmDiscard({preventDefault: sinon.stub()});
- });
-
- test('storage is cleared only after save success', () => {
- element._messageText = 'test';
- const eraseStub = sandbox.stub(element, '_eraseDraftComment');
- sandbox.stub(element.$.restAPI, 'getResponseObject')
- .returns(Promise.resolve({}));
-
- sandbox.stub(element, '_saveDraft').returns(Promise.resolve({ok: false}));
-
- const savePromise = element.save();
- assert.isFalse(eraseStub.called);
- return savePromise.then(() => {
- assert.isFalse(eraseStub.called);
-
- element._saveDraft.restore();
- sandbox.stub(element, '_saveDraft')
- .returns(Promise.resolve({ok: true}));
- return element.save().then(() => {
- assert.isTrue(eraseStub.called);
- });
- });
- });
-
- test('_computeSaveDisabled', () => {
- const comment = {unresolved: true};
- const msgComment = {message: 'test', unresolved: true};
- assert.equal(element._computeSaveDisabled('', comment, false), true);
- assert.equal(element._computeSaveDisabled('test', comment, false), false);
- assert.equal(element._computeSaveDisabled('', msgComment, false), true);
- assert.equal(
- element._computeSaveDisabled('test', msgComment, false), false);
- assert.equal(
- element._computeSaveDisabled('test2', msgComment, false), false);
- assert.equal(element._computeSaveDisabled('test', comment, true), false);
- assert.equal(element._computeSaveDisabled('', comment, true), true);
- assert.equal(element._computeSaveDisabled('', comment, false), true);
- });
-
- suite('confirm discard', () => {
- let discardStub;
- let overlayStub;
- let mockEvent;
-
- setup(() => {
- discardStub = sandbox.stub(element, '_discardDraft');
- overlayStub = sandbox.stub(element, '_openOverlay')
- .returns(Promise.resolve());
- mockEvent = {preventDefault: sinon.stub()};
- });
-
- test('confirms discard of comments with message text', () => {
- element._messageText = 'test';
- element._handleDiscard(mockEvent);
- assert.isTrue(overlayStub.calledWith(element.confirmDiscardOverlay));
- assert.isFalse(discardStub.called);
- });
-
- test('no confirmation for comments without message text', () => {
- element._messageText = '';
- element._handleDiscard(mockEvent);
- assert.isFalse(overlayStub.called);
- assert.isTrue(discardStub.calledOnce);
- });
- });
-
- test('ctrl+s saves comment', done => {
- const stub = sinon.stub(element, 'save', () => {
- assert.isTrue(stub.called);
- stub.restore();
- done();
- return Promise.resolve();
- });
- element._messageText = 'is that the horse from horsing around??';
- element.editing = true;
- flushAsynchronousOperations();
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea.$.textarea.textarea,
- 83, 'ctrl'); // 'ctrl + s'
- });
-
- test('draft saving/editing', done => {
- const fireStub = sinon.stub(element, 'fire');
- const cancelDebounce = sandbox.stub(element, 'cancelDebouncer');
-
- element.draft = true;
- MockInteractions.tap(element.$$('.edit'));
- element._messageText = 'good news, everyone!';
- element.flushDebouncer('fire-update');
- element.flushDebouncer('store');
- assert(fireStub.calledWith('comment-update'),
- 'comment-update should be sent');
- assert.isTrue(fireStub.calledOnce);
-
- element._messageText = 'good news, everyone!';
- element.flushDebouncer('fire-update');
- element.flushDebouncer('store');
- assert.isTrue(fireStub.calledOnce,
- 'No events should fire for text editing');
-
- MockInteractions.tap(element.$$('.save'));
-
- assert.isTrue(element.disabled,
- 'Element should be disabled when creating draft.');
-
- element._xhrPromise.then(draft => {
- assert(fireStub.calledWith('comment-save'),
- 'comment-save should be sent');
- assert(cancelDebounce.calledWith('store'));
-
- assert.deepEqual(fireStub.lastCall.args[1], {
- comment: {
- __commentSide: 'right',
- __draft: true,
- __draftID: 'temp_draft_id',
- id: 'baf0414d_40572e03',
- line: 5,
- message: 'saved!',
- path: '/path/to/file',
- updated: '2015-12-08 21:52:36.177000000',
- },
- patchNum: 1,
- });
- assert.isFalse(element.disabled,
- 'Element should be enabled when done creating draft.');
- assert.equal(draft.message, 'saved!');
- assert.isFalse(element.editing);
- }).then(() => {
- MockInteractions.tap(element.$$('.edit'));
- element._messageText = 'You’ll be delivering a package to Chapek 9, ' +
- 'a world where humans are killed on sight.';
- MockInteractions.tap(element.$$('.save'));
- assert.isTrue(element.disabled,
- 'Element should be disabled when updating draft.');
-
- element._xhrPromise.then(draft => {
- assert.isFalse(element.disabled,
- 'Element should be enabled when done updating draft.');
- assert.equal(draft.message, 'saved!');
- assert.isFalse(element.editing);
- fireStub.restore();
- done();
- });
- });
- });
-
- test('draft prevent save when disabled', () => {
- const saveStub = sandbox.stub(element, 'save').returns(Promise.resolve());
- element.showActions = true;
- element.draft = true;
- MockInteractions.tap(element.$.header);
- MockInteractions.tap(element.$$('.edit'));
- element._messageText = 'good news, everyone!';
- element.flushDebouncer('fire-update');
- element.flushDebouncer('store');
-
- element.disabled = true;
- MockInteractions.tap(element.$$('.save'));
- assert.isFalse(saveStub.called);
-
- element.disabled = false;
- MockInteractions.tap(element.$$('.save'));
- assert.isTrue(saveStub.calledOnce);
- });
-
- test('clicking on date link does not trigger nav', () => {
- const showStub = sinon.stub(page, 'show');
- const dateEl = element.$$('.date');
- assert.ok(dateEl);
- MockInteractions.tap(dateEl);
- const dest = window.location.pathname + '#5';
- assert(showStub.lastCall.calledWithExactly(dest, null, false),
- 'Should navigate to ' + dest + ' without triggering nav');
- showStub.restore();
- });
-
- test('proper event fires on resolve, comment is not saved', done => {
- const save = sandbox.stub(element, 'save');
- element.addEventListener('comment-update', e => {
- assert.isTrue(e.detail.comment.unresolved);
- assert.isFalse(save.called);
- done();
- });
- MockInteractions.tap(element.$$('.resolve input'));
- });
-
- test('resolved comment state indicated by checkbox', () => {
- sandbox.stub(element, 'save');
- element.comment = {unresolved: false};
- assert.isTrue(element.$$('.resolve input').checked);
- element.comment = {unresolved: true};
- assert.isFalse(element.$$('.resolve input').checked);
- });
-
- test('resolved checkbox saves with tap when !editing', () => {
- element.editing = false;
- const save = sandbox.stub(element, 'save');
-
- element.comment = {unresolved: false};
- assert.isTrue(element.$$('.resolve input').checked);
- element.comment = {unresolved: true};
- assert.isFalse(element.$$('.resolve input').checked);
- assert.isFalse(save.called);
- MockInteractions.tap(element.$.resolvedCheckbox);
- assert.isTrue(element.$$('.resolve input').checked);
- assert.isTrue(save.called);
- });
-
- suite('draft saving messages', () => {
- test('_getSavingMessage', () => {
- assert.equal(element._getSavingMessage(0), 'All changes saved');
- assert.equal(element._getSavingMessage(1), 'Saving 1 draft...');
- assert.equal(element._getSavingMessage(2), 'Saving 2 drafts...');
- assert.equal(element._getSavingMessage(3), 'Saving 3 drafts...');
- });
-
- test('_show{Start,End}Request', () => {
- const updateStub = sandbox.stub(element, '_updateRequestToast');
- element._numPendingDraftRequests.number = 1;
-
- element._showStartRequest();
- assert.isTrue(updateStub.calledOnce);
- assert.equal(updateStub.lastCall.args[0], 2);
- assert.equal(element._numPendingDraftRequests.number, 2);
-
- element._showEndRequest();
- assert.isTrue(updateStub.calledTwice);
- assert.equal(updateStub.lastCall.args[0], 1);
- assert.equal(element._numPendingDraftRequests.number, 1);
-
- element._showEndRequest();
- assert.isTrue(updateStub.calledThrice);
- assert.equal(updateStub.lastCall.args[0], 0);
- assert.equal(element._numPendingDraftRequests.number, 0);
- });
- });
-
- test('cancelling an unsaved draft discards, persists in storage', () => {
- const discardSpy = sandbox.spy(element, '_fireDiscard');
- const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
- const eraseStub = sandbox.stub(element.$.storage, 'eraseDraftComment');
- element._messageText = 'test text';
- flushAsynchronousOperations();
- element.flushDebouncer('store');
-
- assert.isTrue(storeStub.called);
- assert.equal(storeStub.lastCall.args[1], 'test text');
- element._handleCancel({preventDefault: () => {}});
- assert.isTrue(discardSpy.called);
- assert.isFalse(eraseStub.called);
- });
-
- test('cancelling edit on a saved draft does not store', () => {
- element.comment.id = 'foo';
- const discardSpy = sandbox.spy(element, '_fireDiscard');
- const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
- element._messageText = 'test text';
- flushAsynchronousOperations();
- element.flushDebouncer('store');
-
- assert.isFalse(storeStub.called);
- element._handleCancel({preventDefault: () => {}});
- assert.isTrue(discardSpy.called);
- });
-
- test('deleting text from saved draft and saving deletes the draft', () => {
- element.comment = {id: 'foo', message: 'test'};
- element._messageText = '';
- const discardStub = sandbox.stub(element, '_discardDraft');
-
- element.save();
- assert.isTrue(discardStub.called);
- });
-
- test('_handleFix fires create-fix event', done => {
- element.addEventListener('create-fix-comment', e => {
- assert.deepEqual(e.detail, element._getEventPayload());
- done();
- });
- element.isRobotComment = true;
- flushAsynchronousOperations();
-
- MockInteractions.tap(element.$$('.fix'));
- });
- });
-</script>
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 556a53abf2..1cfd5e75b6 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,6 +37,7 @@
Polymer({
is: 'gr-diff-cursor',
+ _legacyUndefinedCheck: true,
properties: {
/**
@@ -295,7 +296,7 @@
},
_rowHasThread(row) {
- return row.querySelector('gr-diff-comment-thread');
+ return row.querySelector('.comment-thread');
},
/**
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 9668a54d48..f111378a74 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
@@ -60,7 +60,11 @@ limitations under the License.
diffElement.loggedIn = false;
diffElement.patchRange = {basePatchNum: 1, patchNum: 2};
- diffElement.comments = {left: [], right: []};
+ diffElement.comments = {
+ left: [],
+ right: [],
+ meta: {patchRange: undefined},
+ };
const setupDone = () => {
cursorElement._updateStops();
cursorElement.moveToFirstChunk();
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 cd5b53a108..c820668547 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,9 +19,14 @@
Polymer({
is: 'gr-diff-highlight',
+ _legacyUndefinedCheck: true,
properties: {
- comments: Object,
+ /** @type {!Array<!Gerrit.HoveredRange>} */
+ commentRanges: {
+ type: Array,
+ notify: true,
+ },
loggedIn: Boolean,
/**
* querySelector can return null, so needs to be nullable.
@@ -29,19 +34,14 @@
* @type {?HTMLElement}
* */
_cachedDiffBuilder: Object,
- isAttached: Boolean,
},
listeners: {
- 'comment-mouse-out': '_handleCommentMouseOut',
- 'comment-mouse-over': '_handleCommentMouseOver',
- 'create-comment': '_createComment',
+ 'comment-thread-mouseleave': '_handleCommentThreadMouseleave',
+ 'comment-thread-mouseenter': '_handleCommentThreadMouseenter',
+ 'create-range-comment': '_createRangeComment',
},
- observers: [
- '_enableSelectionObserver(loggedIn, isAttached)',
- ],
-
get diffBuilder() {
if (!this._cachedDiffBuilder) {
this._cachedDiffBuilder =
@@ -50,56 +50,91 @@
return this._cachedDiffBuilder;
},
- _enableSelectionObserver(loggedIn, isAttached) {
- if (loggedIn && isAttached) {
- this.listen(document, 'selectionchange', '_handleSelectionChange');
- } else {
- this.unlisten(document, 'selectionchange', '_handleSelectionChange');
- }
- },
isRangeSelected() {
return !!this.$$('gr-selection-action-box');
},
- _handleSelectionChange() {
- // Can't use up or down events to handle selection started and/or ended in
- // in comment threads or outside of diff.
- // Debounce removeActionBox to give it a chance to react to click/tap.
- this._removeActionBoxDebounced();
- this.debounce('selectionChange', this._handleSelection, 200);
+ /**
+ * Determines side/line/range for a DOM selection and shows a tooltip.
+ *
+ * With native shadow DOM, gr-diff-highlight cannot access a selection that
+ * references the DOM elements making up the diff because they are in the
+ * shadow DOM the gr-diff element. For this reason, we listen to the
+ * selectionchange event and retrieve the selection in gr-diff, and then
+ * call this method to process the Selection.
+ *
+ * @param {Selection} selection A DOM Selection living in the shadow DOM of
+ * the diff element.
+ * @param {boolean} isMouseUp If true, this is called due to a mouseup
+ * event, in which case we might want to immediately create a comment,
+ * because isMouseUp === true combined with an existing selection must
+ * mean that this is the end of a double-click.
+ */
+ handleSelectionChange(selection, isMouseUp) {
+ // Debounce is not just nice for waiting until the selection has settled,
+ // it is also vital for being able to click on the action box before it is
+ // removed.
+ // If you wait longer than 50 ms, then you don't properly catch a very
+ // quick 'c' press after the selection change. If you wait less than 10
+ // ms, then you will have about 50 _handleSelection calls when doing a
+ // simple drag for select.
+ this.debounce(
+ 'selectionChange', () => this._handleSelection(selection, isMouseUp),
+ 10);
},
- _handleCommentMouseOver(e) {
- const comment = e.detail.comment;
- if (!comment.range) { return; }
- const lineEl = this.diffBuilder.getLineElByChild(e.target);
- const side = this.diffBuilder.getSideByLineEl(lineEl);
- const index = this._indexOfComment(side, comment);
+ _getThreadEl(e) {
+ const path = Polymer.dom(e).path || [];
+ for (const pathEl of path) {
+ if (pathEl.classList.contains('comment-thread')) return pathEl;
+ }
+ return null;
+ },
+
+ _handleCommentThreadMouseenter(e) {
+ const threadEl = this._getThreadEl(e);
+ const index = this._indexForThreadEl(threadEl);
+
if (index !== undefined) {
- this.set(['comments', side, index, '__hovering'], true);
+ this.set(['commentRanges', index, 'hovering'], true);
}
},
- _handleCommentMouseOut(e) {
- const comment = e.detail.comment;
- if (!comment.range) { return; }
- const lineEl = this.diffBuilder.getLineElByChild(e.target);
- const side = this.diffBuilder.getSideByLineEl(lineEl);
- const index = this._indexOfComment(side, comment);
+ _handleCommentThreadMouseleave(e) {
+ const threadEl = this._getThreadEl(e);
+ const index = this._indexForThreadEl(threadEl);
+
if (index !== undefined) {
- this.set(['comments', side, index, '__hovering'], false);
+ this.set(['commentRanges', index, 'hovering'], false);
}
},
- _indexOfComment(side, comment) {
- const idProp = comment.id ? 'id' : '__draftID';
- for (let i = 0; i < this.comments[side].length; i++) {
- if (comment[idProp] &&
- this.comments[side][i][idProp] === comment[idProp]) {
- return i;
+ _indexForThreadEl(threadEl) {
+ const side = threadEl.getAttribute('comment-side');
+ const range = JSON.parse(threadEl.getAttribute('range'));
+
+ if (!range) return undefined;
+
+ return this._indexOfCommentRange(side, range);
+ },
+
+ _indexOfCommentRange(side, range) {
+ function rangesEqual(a, b) {
+ if (!a && !b) {
+ return true;
}
+ if (!a || !b) {
+ return false;
+ }
+ return a.start_line === b.start_line &&
+ a.start_character === b.start_character &&
+ a.end_line === b.end_line &&
+ a.end_character === b.end_character;
}
+
+ return this.commentRanges.findIndex(commentRange =>
+ commentRange.side === side && rangesEqual(commentRange.range, range));
},
/**
@@ -108,6 +143,7 @@
* syntax highligh, convert native DOM Range objects to Gerrit concepts
* (line, side, etc).
*
+ * @param {Selection} selection
* @return {({
* start: {
* node: Node,
@@ -123,8 +159,7 @@
* }
* })|null|!Object}
*/
- _getNormalizedRange() {
- const selection = window.getSelection();
+ _getNormalizedRange(selection) {
const rangeCount = selection.rangeCount;
if (rangeCount === 0) {
return null;
@@ -237,7 +272,7 @@
node = contentText;
column = 0;
} else {
- const thread = contentTd.querySelector('gr-diff-comment-thread');
+ const thread = contentTd.querySelector('.comment-thread');
if (thread && thread.contains(node)) {
column = this._getLength(contentText);
node = contentText;
@@ -270,37 +305,72 @@
actionBox.placeBelow(range);
},
- _handleSelection() {
- const normalizedRange = this._getNormalizedRange();
- if (!normalizedRange) {
- return;
- }
- const domRange = window.getSelection().getRangeAt(0);
- /** @type {?} */
- const start = normalizedRange.start;
- if (!start) {
- return;
- }
- const end = normalizedRange.end;
- if (!end) {
- return;
+ _isRangeValid(range) {
+ if (!range || !range.start || !range.end) {
+ return false;
}
+ const start = range.start;
+ const end = range.end;
if (start.side !== end.side ||
end.line < start.line ||
(start.line === end.line && start.column === end.column)) {
+ return false;
+ }
+ return true;
+ },
+
+ _handleSelection(selection, isMouseUp) {
+ const normalizedRange = this._getNormalizedRange(selection);
+ if (!this._isRangeValid(normalizedRange)) {
+ this._removeActionBox();
return;
}
+ const domRange = selection.getRangeAt(0);
+ const start = normalizedRange.start;
+ const end = normalizedRange.end;
// TODO (viktard): Drop empty first and last lines from selection.
- const actionBox = document.createElement('gr-selection-action-box');
- const root = Polymer.dom(this.root);
- root.insertBefore(actionBox, root.firstElementChild);
+ // If the selection is from the end of one line to the start of the next
+ // line, then this must have been a double-click, or you have started
+ // dragging. Showing the action box is bad in the former case and not very
+ // useful in the latter, so never do that.
+ // If this was a mouse-up event, we create a comment immediately if
+ // the selection is from the end of a line to the start of the next line.
+ // In a perfect world we would only do this for double-click, but it is
+ // extremely rare that a user would drag from the end of one line to the
+ // start of the next and release the mouse, so we don't bother.
+ // TODO(brohlfs): This does not work, if the double-click is before a new
+ // diff chunk (start will be equal to end), and neither before an "expand
+ // the diff context" block (end line will match the first line of the new
+ // section and thus be greater than start line + 1).
+ if (start.line === end.line - 1 && end.column === 0) {
+ // Rather than trying to find the line contents (for comparing
+ // start.column with the content length), we just check if the selection
+ // is empty to see that it's at the end of a line.
+ const content = domRange.cloneContents().querySelector('.contentText');
+ if (isMouseUp && this._getLength(content) === 0) {
+ this.fire('create-range-comment', {side: start.side, range: {
+ start_line: start.line,
+ start_character: 0,
+ end_line: start.line,
+ end_character: start.column,
+ }});
+ }
+ return;
+ }
+
+ let actionBox = this.$$('gr-selection-action-box');
+ if (!actionBox) {
+ actionBox = document.createElement('gr-selection-action-box');
+ const root = Polymer.dom(this.root);
+ root.insertBefore(actionBox, root.firstElementChild);
+ }
actionBox.range = {
- startLine: start.line,
- startChar: start.column,
- endLine: end.line,
- endChar: end.column,
+ start_line: start.line,
+ start_character: start.column,
+ end_line: end.line,
+ end_character: end.column,
};
actionBox.side = start.side;
if (start.line === end.line) {
@@ -319,14 +389,10 @@
}
},
- _createComment(e) {
+ _createRangeComment(e) {
this._removeActionBox();
},
- _removeActionBoxDebounced() {
- this.debounce('removeActionBox', this._removeActionBox, 10);
- },
-
_removeActionBox() {
const actionBox = this.$$('gr-selection-action-box');
if (actionBox) {
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 1c37380e06..2e50fdbab1 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
@@ -72,9 +72,9 @@ limitations under the License.
<tr class="diff-row side-by-side" left-type="remove" right-type="add">
<td class="left lineNum" data-value="140"></td>
<!-- Next tag is formatted to eliminate zero-length text nodes. -->
- <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;"> </span>quod <hl>Epicurum</hl></div><gr-diff-comment-thread>
+ <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;"> </span>quod <hl>Epicurum</hl></div><div class="comment-thread">
[Yet another random diff thread content here]
- </gr-diff-comment-thread></td>
+ </div></td>
<td class="right lineNum" data-value="120"></td>
<!-- Next tag is formatted to eliminate zero-length text nodes. -->
<td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl> otiosum, <span class="tab-indicator" style="tab-size:8;"> </span> audiam, sit, quod</div></td>
@@ -158,33 +158,6 @@ limitations under the License.
sandbox.restore();
});
- suite('selectionchange event handling', () => {
- const emulateSelection = function() {
- document.dispatchEvent(new CustomEvent('selectionchange'));
- element.flushDebouncer('selectionChange');
- element.flushDebouncer('removeActionBox');
- };
-
- setup(() => {
- sandbox.stub(element, '_handleSelection');
- sandbox.stub(element, '_removeActionBox');
- });
-
- test('enabled if logged in', () => {
- element.loggedIn = true;
- emulateSelection();
- assert.isTrue(element._handleSelection.called);
- assert.isTrue(element._removeActionBox.called);
- });
-
- test('ignored if logged out', () => {
- element.loggedIn = false;
- emulateSelection();
- assert.isFalse(element._handleSelection.called);
- assert.isFalse(element._removeActionBox.called);
- });
- });
-
suite('comment events', () => {
let builder;
@@ -197,27 +170,65 @@ limitations under the License.
element._cachedDiffBuilder = builder;
});
- test('comment-mouse-over from line comments is ignored', () => {
+ test('comment-thread-mouseenter from line comments is ignored', () => {
+ const threadEl = document.createElement('div');
+ threadEl.className = 'comment-thread';
+ threadEl.setAttribute('comment-side', 'right');
+ threadEl.setAttribute('line-num', 3);
+ element.appendChild(threadEl);
+ element.commentRanges = [{side: 'right'}];
+
sandbox.stub(element, 'set');
- element.fire('comment-mouse-over', {comment: {}});
+ threadEl.dispatchEvent(
+ new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
assert.isFalse(element.set.called);
});
- test('comment-mouse-over from ranged comment causes set', () => {
+ test('comment-thread-mouseenter from ranged comment causes set', () => {
+ const threadEl = document.createElement('div');
+ threadEl.className = 'comment-thread';
+ threadEl.setAttribute('comment-side', 'right');
+ threadEl.setAttribute('line-num', 3);
+ threadEl.setAttribute('range', JSON.stringify({
+ start_line: 3,
+ start_character: 4,
+ end_line: 5,
+ end_character: 6,
+ }));
+ element.appendChild(threadEl);
+ element.commentRanges = [{side: 'right', range: {
+ start_line: 3,
+ start_character: 4,
+ end_line: 5,
+ end_character: 6,
+ }}];
+
sandbox.stub(element, 'set');
- sandbox.stub(element, '_indexOfComment').returns(0);
- element.fire('comment-mouse-over', {comment: {range: {}}});
+ threadEl.dispatchEvent(
+ new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
assert.isTrue(element.set.called);
+ const args = element.set.lastCall.args;
+ assert.deepEqual(args[0], ['commentRanges', 0, 'hovering']);
+ assert.deepEqual(args[1], true);
});
- test('comment-mouse-out from line comments is ignored', () => {
- element.fire('comment-mouse-over', {comment: {}});
- assert.isFalse(builder.getContentsByLineRange.called);
+ test('comment-thread-mouseleave from line comments is ignored', () => {
+ const threadEl = document.createElement('div');
+ threadEl.className = 'comment-thread';
+ threadEl.setAttribute('comment-side', 'right');
+ threadEl.setAttribute('line-num', 3);
+ element.appendChild(threadEl);
+ element.commentRanges = [{side: 'right'}];
+
+ sandbox.stub(element, 'set');
+ threadEl.dispatchEvent(
+ new CustomEvent('comment-thread-mouseleave', {bubbles: true}));
+ assert.isFalse(element.set.called);
});
- test('on create-comment action box is removed', () => {
+ test('on create-range-comment action box is removed', () => {
sandbox.stub(element, '_removeActionBox');
- element.fire('create-comment', {
+ element.fire('create-range-comment', {
comment: {
range: {},
},
@@ -255,7 +266,7 @@ limitations under the License.
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);
selection.addRange(range);
- element._handleSelection();
+ element._handleSelection(selection);
};
const getActionRange = () =>
@@ -318,10 +329,10 @@ limitations under the License.
const actionBox = element.$$('gr-selection-action-box');
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 138,
- startChar: 5,
- endLine: 138,
- endChar: 12,
+ start_line: 138,
+ start_character: 5,
+ end_line: 138,
+ end_character: 12,
});
assert.equal(getActionSide(), 'left');
assert.notOk(actionBox.positionBelow);
@@ -337,10 +348,10 @@ limitations under the License.
const actionBox = element.$$('gr-selection-action-box');
assert.deepEqual(getActionRange(), {
- startLine: 119,
- startChar: 10,
- endLine: 120,
- endChar: 36,
+ start_line: 119,
+ start_character: 10,
+ end_line: 120,
+ end_character: 36,
});
assert.equal(getActionSide(), 'right');
assert.notOk(actionBox.positionBelow);
@@ -362,18 +373,18 @@ limitations under the License.
getRangeAtStub
.onFirstCall().returns(startRange)
.onSecondCall().returns(endRange);
- sandbox.stub(window, 'getSelection').returns({
+ const selection = {
rangeCount: 2,
getRangeAt: getRangeAtStub,
removeAllRanges: sandbox.stub(),
- });
- element._handleSelection();
+ };
+ element._handleSelection(selection);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 119,
- startChar: 10,
- endLine: 120,
- endChar: 36,
+ start_line: 119,
+ start_character: 10,
+ end_line: 120,
+ end_character: 36,
});
});
@@ -383,10 +394,10 @@ limitations under the License.
emulateSelection(startContent.firstChild, 10, endContent.firstChild, 2);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 119,
- startChar: 10,
- endLine: 120,
- endChar: 2,
+ start_line: 119,
+ start_character: 10,
+ end_line: 120,
+ end_character: 2,
});
assert.equal(getActionSide(), 'right');
});
@@ -404,10 +415,10 @@ limitations under the License.
emulateSelection(hl.firstChild, 2, hl.nextSibling, 7);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 8,
- endLine: 140,
- endChar: 23,
+ start_line: 140,
+ start_character: 8,
+ end_line: 140,
+ end_character: 23,
});
assert.equal(getActionSide(), 'left');
});
@@ -418,10 +429,10 @@ limitations under the License.
emulateSelection(hl.previousSibling, 2, hl.firstChild, 3);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 18,
- endLine: 140,
- endChar: 27,
+ start_line: 140,
+ start_character: 18,
+ end_line: 140,
+ end_character: 27,
});
});
@@ -431,10 +442,10 @@ limitations under the License.
emulateSelection(content.firstChild, 2, hl.firstChild, 2);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 2,
- endLine: 140,
- endChar: 61,
+ start_line: 140,
+ start_character: 2,
+ end_line: 140,
+ end_character: 61,
});
assert.equal(getActionSide(), 'left');
});
@@ -465,15 +476,15 @@ limitations under the License.
test('starts in comment thread element', () => {
const startContent = stubContent(140, 'left');
const comment = startContent.parentElement.querySelector(
- 'gr-diff-comment-thread');
+ '.comment-thread');
const endContent = stubContent(141, 'left');
emulateSelection(comment.firstChild, 2, endContent.firstChild, 4);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 83,
- endLine: 141,
- endChar: 4,
+ start_line: 140,
+ start_character: 83,
+ end_line: 141,
+ end_character: 4,
});
assert.equal(getActionSide(), 'left');
});
@@ -481,14 +492,14 @@ limitations under the License.
test('ends in comment thread element', () => {
const content = stubContent(140, 'left');
const comment = content.parentElement.querySelector(
- 'gr-diff-comment-thread');
+ '.comment-thread');
emulateSelection(content.firstChild, 4, comment.firstChild, 1);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 4,
- endLine: 140,
- endChar: 83,
+ start_line: 140,
+ start_character: 4,
+ end_line: 140,
+ end_character: 83,
});
assert.equal(getActionSide(), 'left');
});
@@ -517,10 +528,10 @@ limitations under the License.
emulateSelection(startContent.firstChild, 3, endContent.firstChild, 14);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 130,
- startChar: 3,
- endLine: 146,
- endChar: 14,
+ start_line: 130,
+ start_character: 3,
+ end_line: 146,
+ end_character: 14,
});
assert.equal(getActionSide(), 'right');
});
@@ -531,10 +542,10 @@ limitations under the License.
content.firstChild, 1, content.querySelector('span'), 0);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 1,
- endLine: 140,
- endChar: 51,
+ start_line: 140,
+ start_character: 1,
+ end_line: 140,
+ end_character: 51,
});
assert.equal(getActionSide(), 'left');
});
@@ -546,10 +557,10 @@ limitations under the License.
content.querySelectorAll('span')[1].nextSibling, 1);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 140,
- startChar: 51,
- endLine: 140,
- endChar: 71,
+ start_line: 140,
+ start_character: 51,
+ end_line: 140,
+ end_character: 71,
});
assert.equal(getActionSide(), 'left');
});
@@ -582,10 +593,10 @@ limitations under the License.
emulateSelection(startContent.firstChild, 0, endContent.firstChild, 0);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 119,
- startChar: 0,
- endLine: 119,
- endChar: element._getLength(startContent),
+ start_line: 119,
+ start_character: 0,
+ end_line: 119,
+ end_character: element._getLength(startContent),
});
assert.equal(getActionSide(), 'right');
});
@@ -597,10 +608,10 @@ limitations under the License.
endContent.parentElement.previousElementSibling, 0);
assert.isTrue(element.isRangeSelected());
assert.deepEqual(getActionRange(), {
- startLine: 146,
- startChar: 0,
- endLine: 146,
- endChar: 84,
+ start_line: 146,
+ start_character: 0,
+ end_line: 146,
+ end_character: 84,
});
assert.equal(getActionSide(), 'right');
});
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 305374b656..53ce6e6975 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
@@ -16,9 +16,11 @@ limitations under the License.
-->
<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="../../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">
<dom-module id="gr-diff-host">
@@ -30,14 +32,12 @@ limitations under the License.
patch-range="[[patchRange]]"
path="[[path]]"
prefs="[[prefs]]"
- project-config="[[projectConfig]]"
project-name="[[projectName]]"
display-line="[[displayLine]]"
is-image-diff="[[isImageDiff]]"
commit-range="[[commitRange]]"
hidden$="[[hidden]]"
no-render-on-prefs-change="[[noRenderOnPrefsChange]]"
- comments="[[comments]]"
line-wrapping="[[lineWrapping]]"
view-mode="[[viewMode]]"
line-of-interest="[[lineOfInterest]]"
@@ -46,8 +46,10 @@ limitations under the License.
error-message="[[_errorMessage]]"
base-image="[[_baseImage]]"
revision-image=[[_revisionImage]]
+ coverage-ranges="[[_coverageRanges]]"
blame="[[_blame]]"
diff="[[diff]]"></gr-diff>
+ <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>
</template>
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 9b1084720b..9760d50580 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
@@ -28,6 +28,13 @@
UNIFIED: 'UNIFIED_DIFF',
};
+ /** @enum {string} */
+ const TimingLabel = {
+ TOTAL: 'Diff Total Render',
+ CONTENT: 'Diff Content Render',
+ SYNTAX: 'Diff Syntax Render',
+ };
+
const WHITESPACE_IGNORE_NONE = 'IGNORE_NONE';
/**
@@ -45,15 +52,21 @@
return !!(diff.binary && (isA || isB));
}
+ /** @enum {string} */
+ Gerrit.DiffSide = {
+ LEFT: 'left',
+ RIGHT: 'right',
+ };
+
/**
* Wrapper around gr-diff.
*
* Webcomponent fetching diffs and related data from restAPI and passing them
* to the presentational gr-diff for rendering.
*/
- // TODO(oler): Move all calls to restAPI from gr-diff here.
Polymer({
is: 'gr-diff-host',
+ _legacyUndefinedCheck: true,
/**
* Fired when the user selects a line.
@@ -85,9 +98,6 @@
prefs: {
type: Object,
},
- projectConfig: {
- type: Object,
- },
projectName: String,
displayLine: {
type: Boolean,
@@ -112,7 +122,10 @@
type: Boolean,
value: false,
},
- comments: Object,
+ comments: {
+ type: Object,
+ observer: '_commentsChanged',
+ },
lineWrapping: {
type: Boolean,
value: false,
@@ -180,11 +193,45 @@
value: null,
},
+ /**
+ * TODO(brohlfs): Replace Object type by Gerrit.CoverageRange.
+ *
+ * @type {!Array<!Object>}
+ */
+ _coverageRanges: {
+ type: Array,
+ value: () => [],
+ },
+
_loadedWhitespaceLevel: String,
+
+ _parentIndex: {
+ type: Number,
+ computed: '_computeParentIndex(patchRange.*)',
+ },
},
+ behaviors: [
+ Gerrit.PatchSetBehavior,
+ ],
+
listeners: {
- 'draft-interaction': '_handleDraftInteraction',
+ // These are named inconsistently for a reason:
+ // The create-comment event is fired to indicate that we should
+ // create a comment.
+ // The comment-* events are just notifying that the comments did already
+ // change in some way, and that we should update any models we may want
+ // to keep in sync.
+ 'create-comment': '_handleCreateComment',
+ 'comment-discard': '_handleCommentDiscard',
+ 'comment-update': '_handleCommentUpdate',
+ 'comment-save': '_handleCommentSave',
+
+ 'render-start': '_handleRenderStart',
+ 'render-content': '_handleRenderContent',
+ 'render-syntax': '_handleRenderSyntax',
+
+ 'normalize-range': '_handleNormalizeRange',
},
observers: [
@@ -210,6 +257,21 @@
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 diffRequest = this._getDiff()
.then(diff => {
this._loadedWhitespaceLevel = whitespaceLevel;
@@ -307,9 +369,15 @@
this._blame = null;
},
- /** @return {!Array<!HTMLElement>} */
+ /**
+ * The thread elements in this diff, in no particular order.
+ *
+ * @return {!Array<!HTMLElement>}
+ */
getThreadEls() {
- return this.$.diff.getThreadEls();
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ return Array.from(
+ Polymer.dom(this.$.diff).querySelectorAll('.comment-thread'));
},
/** @param {HTMLElement} el */
@@ -436,6 +504,69 @@
return isImageDiff(diff);
},
+ _commentsChanged(newComments) {
+ const allComments = [];
+ for (const side of [GrDiffBuilder.Side.LEFT, GrDiffBuilder.Side.RIGHT]) {
+ // This is needed by the threading.
+ for (const comment of newComments[side]) {
+ comment.__commentSide = side;
+ }
+ allComments.push(...newComments[side]);
+ }
+ // Currently, the only way this is ever changed here is when the initial
+ // comments are loaded, so it's okay performance wise to clear the threads
+ // and recreate them. If this changes in future, we might want to reuse
+ // some DOM nodes here.
+ this._clearThreads();
+ const threads = this._createThreads(allComments);
+ for (const thread of threads) {
+ const threadEl = this._createThreadElement(thread);
+ this._attachThreadElement(threadEl);
+ }
+ },
+
+ /**
+ * @param {!Array<!Object>} comments
+ * @return {!Array<!Object>} Threads for the given comments.
+ */
+ _createThreads(comments) {
+ const sortedComments = comments.slice(0).sort((a, b) => {
+ if (b.__draft && !a.__draft ) { return 0; }
+ if (a.__draft && !b.__draft ) { return 1; }
+ return util.parseDate(a.updated) - util.parseDate(b.updated);
+ });
+
+ const threads = [];
+ for (const comment of sortedComments) {
+ // If the comment is in reply to another comment, find that comment's
+ // thread and append to it.
+ if (comment.in_reply_to) {
+ const thread = threads.find(thread =>
+ thread.comments.some(c => c.id === comment.in_reply_to));
+ if (thread) {
+ thread.comments.push(comment);
+ continue;
+ }
+ }
+
+ // Otherwise, this comment starts its own thread.
+ const newThread = {
+ start_datetime: comment.updated,
+ comments: [comment],
+ commentSide: comment.__commentSide,
+ patchNum: comment.patch_set,
+ rootId: comment.id || comment.__draftID,
+ lineNum: comment.line,
+ isOnParent: comment.side === 'PARENT',
+ };
+ if (comment.range) {
+ newThread.range = Object.assign({}, comment.range);
+ }
+ threads.push(newThread);
+ }
+ return threads;
+ },
+
/**
* @param {Object} blame
* @return {boolean}
@@ -453,11 +584,160 @@
this.patchRange);
},
- _handleDraftInteraction() {
+ /** @param {CustomEvent} e */
+ _handleCreateComment(e) {
+ const {lineNum, side, patchNum, isOnParent, range} = e.detail;
+ const threadEl = this._getOrCreateThread(patchNum, lineNum, side, range,
+ isOnParent);
+ threadEl.addOrEditDraft(lineNum, range);
+
this.$.reporting.recordDraftInteraction();
},
/**
+ * Gets or creates a comment thread at a given location.
+ * May provide a range, to get/create a range comment.
+ *
+ * @param {string} patchNum
+ * @param {?number} lineNum
+ * @param {string} commentSide
+ * @param {Gerrit.Range|undefined} range
+ * @param {boolean} isOnParent
+ * @return {!Object}
+ */
+ _getOrCreateThread(patchNum, lineNum, commentSide, range, isOnParent) {
+ let threadEl = this._getThreadEl(lineNum, commentSide, range);
+ if (!threadEl) {
+ threadEl = this._createThreadElement({
+ comments: [],
+ commentSide,
+ patchNum,
+ lineNum,
+ range,
+ isOnParent,
+ });
+ this._attachThreadElement(threadEl);
+ }
+ return threadEl;
+ },
+
+ _attachThreadElement(threadEl) {
+ Polymer.dom(this.$.diff).appendChild(threadEl);
+ },
+
+ _clearThreads() {
+ for (const threadEl of this.getThreadEls()) {
+ const parent = Polymer.dom(threadEl).parentNode;
+ Polymer.dom(parent).removeChild(threadEl);
+ }
+ },
+
+ _createThreadElement(thread) {
+ const threadEl = document.createElement('gr-comment-thread');
+ threadEl.className = 'comment-thread';
+ threadEl.setAttribute('slot', `${thread.commentSide}-${thread.lineNum}`);
+ threadEl.comments = thread.comments;
+ threadEl.commentSide = thread.commentSide;
+ threadEl.isOnParent = !!thread.isOnParent;
+ threadEl.parentIndex = this._parentIndex;
+ threadEl.changeNum = this.changeNum;
+ threadEl.patchNum = thread.patchNum;
+ threadEl.lineNum = thread.lineNum;
+ const rootIdChangedListener = changeEvent => {
+ thread.rootId = changeEvent.detail.value;
+ };
+ threadEl.addEventListener('root-id-changed', rootIdChangedListener);
+ threadEl.path = this.path;
+ threadEl.projectName = this.projectName;
+ threadEl.range = thread.range;
+ const threadDiscardListener = e => {
+ const threadEl = /** @type {!Node} */ (e.currentTarget);
+
+ const parent = Polymer.dom(threadEl).parentNode;
+ Polymer.dom(parent).removeChild(threadEl);
+
+ threadEl.removeEventListener('root-id-changed', rootIdChangedListener);
+ threadEl.removeEventListener('thread-discard', threadDiscardListener);
+ };
+ threadEl.addEventListener('thread-discard', threadDiscardListener);
+ return threadEl;
+ },
+
+ /**
+ * Gets a comment thread element at a given location.
+ * May provide a range, to get a range comment.
+ *
+ * @param {?number} lineNum
+ * @param {string} commentSide
+ * @param {!Gerrit.Range=} range
+ * @return {?Node}
+ */
+ _getThreadEl(lineNum, commentSide, range=undefined) {
+ let line;
+ if (commentSide === GrDiffBuilder.Side.LEFT) {
+ line = {beforeNumber: lineNum};
+ } else if (commentSide === GrDiffBuilder.Side.RIGHT) {
+ line = {afterNumber: lineNum};
+ } else {
+ throw new Error(`Unknown side: ${commentSide}`);
+ }
+ function matchesRange(threadEl) {
+ const threadRange = /** @type {!Gerrit.Range} */(
+ JSON.parse(threadEl.getAttribute('range')));
+ return Gerrit.rangesEqual(threadRange, range);
+ }
+
+ const filteredThreadEls = this._filterThreadElsForLocation(
+ this.getThreadEls(), line, commentSide).filter(matchesRange);
+ return filteredThreadEls.length ? filteredThreadEls[0] : null;
+ },
+
+ /**
+ * @param {!Array<!HTMLElement>} threadEls
+ * @param {!{beforeNumber: (number|string|undefined|null),
+ * afterNumber: (number|string|undefined|null)}}
+ * lineInfo
+ * @param {!Gerrit.DiffSide=} side The side (LEFT, RIGHT) for
+ * which to return the threads.
+ * @return {!Array<!HTMLElement>} The thread elements matching the given
+ * location.
+ */
+ _filterThreadElsForLocation(threadEls, lineInfo, side) {
+ function matchesLeftLine(threadEl) {
+ return threadEl.getAttribute('comment-side') ==
+ Gerrit.DiffSide.LEFT &&
+ threadEl.getAttribute('line-num') == lineInfo.beforeNumber;
+ }
+ function matchesRightLine(threadEl) {
+ return threadEl.getAttribute('comment-side') ==
+ Gerrit.DiffSide.RIGHT &&
+ threadEl.getAttribute('line-num') == lineInfo.afterNumber;
+ }
+ function matchesFileComment(threadEl) {
+ return threadEl.getAttribute('comment-side') == side &&
+ // line/range comments have 1-based line set, if line is falsy it's
+ // a file comment
+ !threadEl.getAttribute('line-num');
+ }
+
+ // Select the appropriate matchers for the desired side and line
+ // If side is BOTH, we want both the left and right matcher.
+ const matchers = [];
+ if (side !== Gerrit.DiffSide.RIGHT) {
+ matchers.push(matchesLeftLine);
+ }
+ if (side !== Gerrit.DiffSide.LEFT) {
+ matchers.push(matchesRightLine);
+ }
+ if (lineInfo.afterNumber === 'FILE' ||
+ lineInfo.beforeNumber === 'FILE') {
+ matchers.push(matchesFileComment);
+ }
+ return threadEls.filter(threadEl =>
+ 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.
@@ -515,5 +795,107 @@
this.reload();
}
},
+
+ /**
+ * @param {Object} patchRangeRecord
+ * @return {number|null}
+ */
+ _computeParentIndex(patchRangeRecord) {
+ return this.isMergeParent(patchRangeRecord.base.basePatchNum) ?
+ this.getParentIndex(patchRangeRecord.base.basePatchNum) : null;
+ },
+
+ _handleCommentSave(e) {
+ const comment = e.detail.comment;
+ const side = e.detail.comment.__commentSide;
+ const idx = this._findDraftIndex(comment, side);
+ this.set(['comments', side, idx], comment);
+ this._handleCommentSaveOrDiscard();
+ },
+
+ _handleCommentDiscard(e) {
+ const comment = e.detail.comment;
+ this._removeComment(comment);
+ this._handleCommentSaveOrDiscard();
+ },
+
+ /**
+ * Closure annotation for Polymer.prototype.push is off. Submitted PR:
+ * https://github.com/Polymer/polymer/pull/4776
+ * but for not supressing annotations.
+ *
+ * @suppress {checkTypes}
+ */
+ _handleCommentUpdate(e) {
+ const comment = e.detail.comment;
+ const side = e.detail.comment.__commentSide;
+ let idx = this._findCommentIndex(comment, side);
+ if (idx === -1) {
+ idx = this._findDraftIndex(comment, side);
+ }
+ if (idx !== -1) { // Update draft or comment.
+ this.set(['comments', side, idx], comment);
+ } else { // Create new draft.
+ this.push(['comments', side], comment);
+ }
+ },
+
+ _handleCommentSaveOrDiscard() {
+ this.dispatchEvent(new CustomEvent('diff-comments-modified',
+ {bubbles: true}));
+ },
+
+ _removeComment(comment) {
+ const side = comment.__commentSide;
+ this._removeCommentFromSide(comment, side);
+ },
+
+ _removeCommentFromSide(comment, side) {
+ let idx = this._findCommentIndex(comment, side);
+ if (idx === -1) {
+ idx = this._findDraftIndex(comment, side);
+ }
+ if (idx !== -1) {
+ this.splice('comments.' + side, idx, 1);
+ }
+ },
+
+ /** @return {number} */
+ _findCommentIndex(comment, side) {
+ if (!comment.id || !this.comments[side]) {
+ return -1;
+ }
+ return this.comments[side].findIndex(item => item.id === comment.id);
+ },
+
+ /** @return {number} */
+ _findDraftIndex(comment, side) {
+ if (!comment.__draftID || !this.comments[side]) {
+ return -1;
+ }
+ return this.comments[side].findIndex(
+ item => item.__draftID === comment.__draftID);
+ },
+
+ _handleRenderStart() {
+ this.$.reporting.time(TimingLabel.TOTAL);
+ this.$.reporting.time(TimingLabel.CONTENT);
+ },
+
+ _handleRenderContent() {
+ this.$.reporting.timeEnd(TimingLabel.CONTENT);
+ this.$.reporting.time(TimingLabel.SYNTAX);
+ },
+
+ _handleRenderSyntax() {
+ this.$.reporting.timeEnd(TimingLabel.SYNTAX);
+ this.$.reporting.timeEnd(TimingLabel.TOTAL);
+ },
+
+ _handleNormalizeRange(event) {
+ this.$.reporting.reportInteraction('normalize-range',
+ `Modified invalid comment range on l. ${event.detail.lineNum}` +
+ ` of the ${event.detail.side} side`);
+ },
});
})();
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 b553ae6903..6e7c239e08 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
@@ -37,9 +37,19 @@ limitations under the License.
suite('gr-diff-host tests', () => {
let element;
let sandbox;
+ let getLoggedIn;
setup(() => {
sandbox = sinon.sandbox.create();
+ getLoggedIn = false;
+ stub('gr-rest-api-interface', {
+ async getLoggedIn() { return getLoggedIn; },
+ });
+ stub('gr-reporting', {
+ time: sandbox.stub(),
+ timeEnd: sandbox.stub(),
+ });
+
element = fixture('basic');
});
@@ -47,6 +57,247 @@ limitations under the License.
sandbox.restore();
});
+ suite('handle comment-update', () => {
+ setup(() => {
+ sandbox.stub(element, '_commentsChanged');
+ element.comments = {
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ ],
+ right: [
+ {id: 'c1', __commentSide: 'right'},
+ {id: 'c2', __commentSide: 'right'},
+ {id: 'd1', __draft: true, __commentSide: 'right'},
+ {id: 'd2', __draft: true, __commentSide: 'right'},
+ ],
+ };
+ });
+
+ test('creating a draft', () => {
+ const comment = {__draft: true, __draftID: 'tempID', side: 'PARENT',
+ __commentSide: 'left'};
+ element.fire('comment-update', {comment});
+ assert.include(element.comments.left, comment);
+ });
+
+ test('discarding a draft', () => {
+ const draftID = 'tempID';
+ const id = 'savedID';
+ const comment = {
+ __draft: true,
+ __draftID: draftID,
+ side: 'PARENT',
+ __commentSide: 'left',
+ };
+ const diffCommentsModifiedStub = sandbox.stub();
+ element.addEventListener('diff-comments-modified',
+ diffCommentsModifiedStub);
+ element.comments.left.push(comment);
+ comment.id = id;
+ element.fire('comment-discard', {comment});
+ const drafts = element.comments.left.filter(item => {
+ return item.__draftID === draftID;
+ });
+ assert.equal(drafts.length, 0);
+ assert.isTrue(diffCommentsModifiedStub.called);
+ });
+
+ test('saving a draft', () => {
+ const draftID = 'tempID';
+ const id = 'savedID';
+ const comment = {
+ __draft: true,
+ __draftID: draftID,
+ side: 'PARENT',
+ __commentSide: 'left',
+ };
+ const diffCommentsModifiedStub = sandbox.stub();
+ element.addEventListener('diff-comments-modified',
+ diffCommentsModifiedStub);
+ element.comments.left.push(comment);
+ comment.id = id;
+ element.fire('comment-save', {comment});
+ const drafts = element.comments.left.filter(item => {
+ return item.__draftID === draftID;
+ });
+ assert.equal(drafts.length, 1);
+ assert.equal(drafts[0].id, id);
+ assert.isTrue(diffCommentsModifiedStub.called);
+ });
+ });
+
+ test('remove comment', () => {
+ sandbox.stub(element, '_commentsChanged');
+ element.comments = {
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ ],
+ right: [
+ {id: 'c1', __commentSide: 'right'},
+ {id: 'c2', __commentSide: 'right'},
+ {id: 'd1', __draft: true, __commentSide: 'right'},
+ {id: 'd2', __draft: true, __commentSide: 'right'},
+ ],
+ };
+
+ element._removeComment({});
+ // Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesn’t seem
+ // to believe that one object deepEquals another even when they do :-/.
+ assert.equal(JSON.stringify(element.comments), JSON.stringify({
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ ],
+ right: [
+ {id: 'c1', __commentSide: 'right'},
+ {id: 'c2', __commentSide: 'right'},
+ {id: 'd1', __draft: true, __commentSide: 'right'},
+ {id: 'd2', __draft: true, __commentSide: 'right'},
+ ],
+ }));
+
+ element._removeComment({id: 'bc2', side: 'PARENT',
+ __commentSide: 'left'});
+ assert.deepEqual(element.comments, {
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ ],
+ right: [
+ {id: 'c1', __commentSide: 'right'},
+ {id: 'c2', __commentSide: 'right'},
+ {id: 'd1', __draft: true, __commentSide: 'right'},
+ {id: 'd2', __draft: true, __commentSide: 'right'},
+ ],
+ });
+
+ element._removeComment({id: 'd2', __commentSide: 'right'});
+ assert.deepEqual(element.comments, {
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
+ ],
+ right: [
+ {id: 'c1', __commentSide: 'right'},
+ {id: 'c2', __commentSide: 'right'},
+ {id: 'd1', __draft: true, __commentSide: 'right'},
+ ],
+ });
+ });
+
+ test('thread-discard handling', () => {
+ const threads = [
+ {comments: [{id: 4711}]},
+ {comments: [{id: 42}]},
+ ];
+ element._parentIndex = 1;
+ element.changeNum = '2';
+ element.path = 'some/path';
+ element.projectName = 'Some project';
+ const threadEls = threads.map(
+ thread => element._createThreadElement(thread));
+ assert.equal(threadEls.length, 2);
+ assert.equal(threadEls[0].rootId, 4711);
+ assert.equal(threadEls[1].rootId, 42);
+ for (const threadEl of threadEls) {
+ Polymer.dom(element).appendChild(threadEl);
+ }
+
+ threadEls[0].dispatchEvent(
+ new CustomEvent('thread-discard', {detail: {rootId: 4711}}));
+ const attachedThreads = element.queryAllEffectiveChildren(
+ 'gr-comment-thread');
+ assert.equal(attachedThreads.length, 1);
+ assert.equal(attachedThreads[0].rootId, 42);
+ });
+
+ suite('render reporting', () => {
+ test('starts total and content timer on render-start', done => {
+ element.dispatchEvent(
+ new CustomEvent('render-start', {bubbles: true}));
+ assert.isTrue(element.$.reporting.time.calledWithExactly(
+ 'Diff Total Render'));
+ assert.isTrue(element.$.reporting.time.calledWithExactly(
+ 'Diff Content Render'));
+ done();
+ });
+
+ test('ends content and starts syntax timer on render-content', done => {
+ element.dispatchEvent(
+ new CustomEvent('render-content', {bubbles: true}));
+ assert.isTrue(element.$.reporting.time.calledWithExactly(
+ 'Diff Syntax Render'));
+ 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('reload() cancels before network resolves', () => {
const cancelStub = sandbox.stub(element.$.diff, 'cancel');
@@ -59,12 +310,8 @@ limitations under the License.
suite('not logged in', () => {
setup(() => {
- const getLoggedInPromise = Promise.resolve(false);
- stub('gr-rest-api-interface', {
- getLoggedIn() { return getLoggedInPromise; },
- });
+ getLoggedIn = false;
element = fixture('basic');
- return getLoggedInPromise;
});
test('reload() loads files weblinks', () => {
@@ -181,7 +428,11 @@ limitations under the License.
});
element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
- element.comments = {left: [], right: []};
+ element.comments = {
+ left: [],
+ right: [],
+ meta: {patchRange: element.patchRange},
+ };
});
test('renders image diffs with same file name', done => {
@@ -555,13 +806,11 @@ limitations under the License.
});
});
- test('delegates getThreadEls()', () => {
- const returnValue = [document.createElement('b')];
- const stub = sandbox.stub(element.$.diff, 'getThreadEls')
- .returns(returnValue);
- assert.equal(element.getThreadEls(), returnValue);
- assert.isTrue(stub.calledOnce);
- assert.equal(stub.lastCall.args.length, 0);
+ test('getThreadEls() returns .comment-threads', () => {
+ const threadEl = document.createElement('div');
+ threadEl.className = 'comment-thread';
+ Polymer.dom(element.$.diff).appendChild(threadEl);
+ assert.deepEqual(element.getThreadEls(), [threadEl]);
});
test('delegates addDraftAtLine(el)', () => {
@@ -617,12 +866,6 @@ limitations under the License.
assert.equal(element.$.diff.prefs, value);
});
- test('passes in projectConfig', () => {
- const value = {};
- element.projectConfig = value;
- assert.equal(element.$.diff.projectConfig, value);
- });
-
test('passes in changeNum', () => {
const value = '12345';
element.changeNum = value;
@@ -660,12 +903,6 @@ limitations under the License.
assert.equal(element.$.diff.noRenderOnPrefsChange, value);
});
- test('passes in comments', () => {
- const value = {left: [], right: []};
- element.comments = value;
- assert.equal(element.$.diff.comments, value);
- });
-
test('passes in lineWrapping', () => {
const value = true;
element.lineWrapping = value;
@@ -776,6 +1013,249 @@ limitations under the License.
});
});
+ test('_createThreads', () => {
+ const comments = [
+ {
+ id: 'sallys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-23 15:00:20.396000000',
+ line: 1,
+ __commentSide: 'left',
+ }, {
+ id: 'jacks_reply',
+ message: 'i like you, too',
+ updated: '2015-12-24 15:01:20.396000000',
+ __commentSide: 'left',
+ line: 1,
+ in_reply_to: 'sallys_confession',
+ },
+ {
+ id: 'new_draft',
+ message: 'i do not like either of you',
+ __commentSide: 'left',
+ __draft: true,
+ updated: '2015-12-20 15:01:20.396000000',
+ },
+ ];
+
+ const actualThreads = element._createThreads(comments);
+
+ assert.equal(actualThreads.length, 2);
+
+ assert.equal(
+ actualThreads[0].start_datetime, '2015-12-23 15:00:20.396000000');
+ assert.equal(actualThreads[0].commentSide, 'left');
+ assert.equal(actualThreads[0].comments.length, 2);
+ assert.deepEqual(actualThreads[0].comments[0], comments[0]);
+ assert.deepEqual(actualThreads[0].comments[1], comments[1]);
+ assert.equal(actualThreads[0].patchNum, undefined);
+ assert.equal(actualThreads[0].rootId, 'sallys_confession');
+ assert.equal(actualThreads[0].lineNum, 1);
+
+ assert.equal(
+ actualThreads[1].start_datetime, '2015-12-20 15:01:20.396000000');
+ assert.equal(actualThreads[1].commentSide, 'left');
+ assert.equal(actualThreads[1].comments.length, 1);
+ assert.deepEqual(actualThreads[1].comments[0], comments[2]);
+ assert.equal(actualThreads[1].patchNum, undefined);
+ assert.equal(actualThreads[1].rootId, 'new_draft');
+ assert.equal(actualThreads[1].lineNum, undefined);
+ });
+
+ test('_createThreads inherits patchNum and range', () => {
+ const comments = [{
+ id: 'betsys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-24 15:00:10.396000000',
+ range: {
+ start_line: 1,
+ start_character: 1,
+ end_line: 1,
+ end_character: 2,
+ },
+ patch_set: 5,
+ __commentSide: 'left',
+ line: 1,
+ }];
+
+ expectedThreads = [
+ {
+ start_datetime: '2015-12-24 15:00:10.396000000',
+ commentSide: 'left',
+ comments: [{
+ id: 'betsys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-24 15:00:10.396000000',
+ range: {
+ start_line: 1,
+ start_character: 1,
+ end_line: 1,
+ end_character: 2,
+ },
+ patch_set: 5,
+ __commentSide: 'left',
+ line: 1,
+ }],
+ patchNum: 5,
+ rootId: 'betsys_confession',
+ range: {
+ start_line: 1,
+ start_character: 1,
+ end_line: 1,
+ end_character: 2,
+ },
+ lineNum: 1,
+ isOnParent: false,
+ },
+ ];
+
+ assert.deepEqual(
+ element._createThreads(comments),
+ expectedThreads);
+ });
+
+ test('_createThreads does not thread unrelated comments at same location',
+ () => {
+ const comments = [
+ {
+ id: 'sallys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-23 15:00:20.396000000',
+ __commentSide: 'left',
+ }, {
+ id: 'jacks_reply',
+ message: 'i like you, too',
+ updated: '2015-12-24 15:01:20.396000000',
+ __commentSide: 'left',
+ },
+ ];
+ assert.equal(element._createThreads(comments).length, 2);
+ });
+
+ test('_createThreads derives isOnParent using side from first comment',
+ () => {
+ const comments = [
+ {
+ id: 'sallys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-23 15:00:20.396000000',
+ // line: 1,
+ // __commentSide: 'left',
+ }, {
+ id: 'jacks_reply',
+ message: 'i like you, too',
+ updated: '2015-12-24 15:01:20.396000000',
+ // __commentSide: 'left',
+ // line: 1,
+ in_reply_to: 'sallys_confession',
+ },
+ ];
+
+ assert.equal(element._createThreads(comments)[0].isOnParent, false);
+
+ comments[0].side = 'REVISION';
+ assert.equal(element._createThreads(comments)[0].isOnParent, false);
+
+ comments[0].side = 'PARENT';
+ assert.equal(element._createThreads(comments)[0].isOnParent, true);
+ });
+
+ test('_getOrCreateThread', () => {
+ const commentSide = 'left';
+
+ assert.isOk(element._getOrCreateThread('2', 3,
+ commentSide, undefined, false));
+
+ let threads = Polymer.dom(element.$.diff)
+ .queryDistributedElements('gr-comment-thread');
+
+ assert.equal(threads.length, 1);
+ assert.equal(threads[0].commentSide, commentSide);
+ assert.equal(threads[0].range, undefined);
+ assert.equal(threads[0].isOnParent, false);
+ assert.equal(threads[0].patchNum, 2);
+
+
+ // Try to fetch a thread with a different range.
+ range = {
+ start_line: 1,
+ start_character: 1,
+ end_line: 1,
+ end_character: 3,
+ };
+
+ assert.isOk(element._getOrCreateThread(
+ '3', 1, commentSide, range, true));
+
+ threads = Polymer.dom(element.$.diff)
+ .queryDistributedElements('gr-comment-thread');
+
+ assert.equal(threads.length, 2);
+ assert.equal(threads[1].commentSide, commentSide);
+ assert.equal(threads[1].range, range);
+ assert.equal(threads[1].isOnParent, true);
+ assert.equal(threads[1].patchNum, 3);
+ });
+
+ test('_filterThreadElsForLocation with no threads', () => {
+ const line = {beforeNumber: 3, afterNumber: 5};
+
+ const threads = [];
+ assert.deepEqual(element._filterThreadElsForLocation(threads, line), []);
+ assert.deepEqual(element._filterThreadElsForLocation(threads, line,
+ Gerrit.DiffSide.LEFT), []);
+ assert.deepEqual(element._filterThreadElsForLocation(threads, line,
+ Gerrit.DiffSide.RIGHT), []);
+ });
+
+ test('_filterThreadElsForLocation for line comments', () => {
+ const line = {beforeNumber: 3, afterNumber: 5};
+
+ const l3 = document.createElement('div');
+ l3.setAttribute('line-num', 3);
+ l3.setAttribute('comment-side', 'left');
+
+ const l5 = document.createElement('div');
+ l5.setAttribute('line-num', 5);
+ l5.setAttribute('comment-side', 'left');
+
+ const r3 = document.createElement('div');
+ r3.setAttribute('line-num', 3);
+ r3.setAttribute('comment-side', 'right');
+
+ const r5 = document.createElement('div');
+ r5.setAttribute('line-num', 5);
+ r5.setAttribute('comment-side', 'right');
+
+ const threadEls = [l3, l5, r3, r5];
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line),
+ [l3, r5]);
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
+ Gerrit.DiffSide.LEFT), [l3]);
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
+ Gerrit.DiffSide.RIGHT), [r5]);
+ });
+
+ test('_filterThreadElsForLocation for file comments', () => {
+ const line = {beforeNumber: 'FILE', afterNumber: 'FILE'};
+
+ const l = document.createElement('div');
+ l.setAttribute('comment-side', 'left');
+
+ const r = document.createElement('div');
+ r.setAttribute('comment-side', 'right');
+
+ const threadEls = [l, r];
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line),
+ [l, r]);
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
+ Gerrit.DiffSide.BOTH), [l, r]);
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
+ Gerrit.DiffSide.LEFT), [l]);
+ assert.deepEqual(element._filterThreadElsForLocation(threadEls, line,
+ Gerrit.DiffSide.RIGHT), [r]);
+ });
+
suite('_translateChunksToIgnore', () => {
let content;
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 88dd91a8ca..e2d6a28415 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,6 +19,7 @@
Polymer({
is: 'gr-diff-mode-selector',
+ _legacyUndefinedCheck: true,
properties: {
mode: {
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 f2a6363972..7f7cd733a7 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,6 +19,7 @@
Polymer({
is: 'gr-diff-preferences-dialog',
+ _legacyUndefinedCheck: true,
properties: {
/** @type {?} */
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 baa8bba009..663cf258a6 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
@@ -20,5 +20,7 @@ limitations under the License.
<dom-module id="gr-diff-processor">
<script src="../gr-diff/gr-diff-line.js"></script>
<script src="../gr-diff/gr-diff-group.js"></script>
+
+ <script src="../../../scripts/util.js"></script>
<script src="gr-diff-processor.js"></script>
</dom-module>
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 b934d02764..2cc69e664d 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
@@ -45,8 +45,22 @@
*/
const MAX_GROUP_SIZE = 120;
+ /**
+ * Converts the API's `DiffContent`s to `GrDiffGroup`s for rendering.
+ *
+ * This includes a number of tasks:
+ * - 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
+ * context setting and do not have comments with a group representing the
+ * "expand context" widget. This may require splitting a `DiffContent` 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: {
@@ -80,8 +94,19 @@
value: 64,
},
- /** @type {number|undefined} */
+ /** @type {?number} */
_nextStepHandle: Number,
+ /**
+ * The promise last returned from `process()` while the asynchronous
+ * processing is running - `null` otherwise. Provides a `cancel()`
+ * method that rejects it with `{isCancelled: true}`.
+ *
+ * @type {?Object}
+ */
+ _processPromise: {
+ type: Object,
+ value: null,
+ },
_isScrolling: Boolean,
},
@@ -109,6 +134,10 @@
* processed.
*/
process(content, isBinary) {
+ // Cancel any still running process() calls, because they append to the
+ // same groups field.
+ this.cancel();
+
this.groups = [];
this.push('groups', this._makeFileComments());
@@ -116,57 +145,65 @@
// so finish processing.
if (isBinary) { return Promise.resolve(); }
- return new Promise(resolve => {
- const state = {
- lineNums: {left: 0, right: 0},
- sectionIndex: 0,
- };
- content = this._splitCommonGroupsWithComments(content);
+ this._processPromise = util.makeCancelable(
+ new Promise(resolve => {
+ const state = {
+ lineNums: {left: 0, right: 0},
+ sectionIndex: 0,
+ };
- let currentBatch = 0;
- const nextStep = () => {
- if (this._isScrolling) {
- this._nextStepHandle = this.async(nextStep, 100);
- return;
- }
- // If we are done, resolve the promise.
- if (state.sectionIndex >= content.length) {
- resolve(this.groups);
- this._nextStepHandle = undefined;
- return;
- }
+ content = this._splitLargeChunks(content);
+ content = this._splitUnchangedChunksWithComments(content);
+
+ let currentBatch = 0;
+ const nextStep = () => {
+ if (this._isScrolling) {
+ this._nextStepHandle = this.async(nextStep, 100);
+ return;
+ }
+ // If we are done, resolve the promise.
+ if (state.sectionIndex >= content.length) {
+ resolve(this.groups);
+ this._nextStepHandle = null;
+ return;
+ }
+
+ // Process the next section and incorporate the result.
+ const result = this._processNext(state, content);
+ for (const group of result.groups) {
+ this.push('groups', group);
+ currentBatch += group.lines.length;
+ }
+ state.lineNums.left += result.lineDelta.left;
+ state.lineNums.right += result.lineDelta.right;
+
+ // Increment the index and recurse.
+ state.sectionIndex++;
+ if (currentBatch >= this._asyncThreshold) {
+ currentBatch = 0;
+ this._nextStepHandle = this.async(nextStep, 1);
+ } else {
+ nextStep.call(this);
+ }
+ };
- // Process the next section and incorporate the result.
- const result = this._processNext(state, content);
- for (const group of result.groups) {
- this.push('groups', group);
- currentBatch += group.lines.length;
- }
- state.lineNums.left += result.lineDelta.left;
- state.lineNums.right += result.lineDelta.right;
-
- // Increment the index and recurse.
- state.sectionIndex++;
- if (currentBatch >= this._asyncThreshold) {
- currentBatch = 0;
- this._nextStepHandle = this.async(nextStep, 1);
- } else {
nextStep.call(this);
- }
- };
-
- nextStep.call(this);
- });
+ }));
+ return this._processPromise
+ .finally(() => { this._processPromise = null; });
},
/**
* Cancel any jobs that are running.
*/
cancel() {
- if (this._nextStepHandle !== undefined) {
+ if (this._nextStepHandle != null) {
this.cancelAsync(this._nextStepHandle);
- this._nextStepHandle = undefined;
+ this._nextStepHandle = null;
+ }
+ if (this._processPromise) {
+ this._processPromise.cancel();
}
},
@@ -352,61 +389,79 @@
return new GrDiffGroup(GrDiffGroup.Type.BOTH, [line]);
},
+
+ /**
+ * Split chunks into smaller chunks of the same kind.
+ *
+ * This is done to prevent doing too much work on the main thread in one
+ * uninterrupted rendering step, which would make the browser unresponsive.
+ *
+ * Note that in the case of unmodified chunks, we only split chunks if the
+ * context is set to file (because otherwise they are split up further down
+ * the processing into the visible and hidden context), and only split it
+ * 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.
+ */
+ _splitLargeChunks(chunks) {
+ const newChunks = [];
+
+ for (const chunk of chunks) {
+ if (!chunk.ab) {
+ for (const group of this._breakdownGroup(chunk)) {
+ newChunks.push(group);
+ }
+ continue;
+ }
+
+ // If the context is set to "whole file", then break down the shared
+ // chunks so they can be rendered incrementally. Note: this is not
+ // 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
+ // group size.
+ newChunks.push({ab: chunk.ab.slice(0, MAX_GROUP_SIZE)});
+ newChunks.push({ab: chunk.ab.slice(MAX_GROUP_SIZE)});
+ } else {
+ newChunks.push(chunk);
+ }
+ }
+ return newChunks;
+ },
+
/**
* 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.
*
- * @param {?} content The diff content object. (has to be iterable)
- * @return {!Object} A new diff content object with regions split up.
+ * @param {!Array<!Object>} chunks DiffContents as returned from server.
+ * @return {!Array<!Object>} Finer grained DiffContents.
*/
- _splitCommonGroupsWithComments(content) {
+ _splitUnchangedChunksWithComments(chunks) {
const result = [];
let leftLineNum = 0;
let rightLineNum = 0;
- // If the context is set to "whole file", then break down the shared
- // chunks so they can be rendered incrementally. Note: this is not enabled
- // for any other context preference because manipulating the chunks in
- // this way violates assumptions by the context grouper logic.
- if (this.context === -1) {
- const newContent = [];
- for (const group of content) {
- if (group.ab && group.ab.length > MAX_GROUP_SIZE * 2) {
- // Split large shared groups in two, where the first is the maximum
- // group size.
- newContent.push({ab: group.ab.slice(0, MAX_GROUP_SIZE)});
- newContent.push({ab: group.ab.slice(MAX_GROUP_SIZE)});
- } else {
- newContent.push(group);
+ 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.a) {
+ leftLineNum += chunk.a.length;
}
- }
- content = newContent;
- }
-
- // For each section in the diff.
- for (let i = 0; i < content.length; i++) {
- // If it isn't a common group, append it as-is and update line numbers.
- if (!content[i].ab) {
- if (content[i].a) {
- leftLineNum += content[i].a.length;
- }
- if (content[i].b) {
- rightLineNum += content[i].b.length;
+ if (chunk.b) {
+ rightLineNum += chunk.b.length;
}
-
- for (const group of this._breakdownGroup(content[i])) {
- result.push(group);
- }
-
+ result.push(chunk);
continue;
}
- const chunk = content[i].ab;
let currentChunk = {ab: []};
// For each line in the common group.
- for (const subChunk of chunk) {
+ for (const line of chunk.ab) {
leftLineNum++;
rightLineNum++;
@@ -422,10 +477,10 @@
}
// Add the non-collapse line as its own chunk.
- result.push({ab: [subChunk]});
+ result.push({ab: [line]});
} else {
// Append the current line to the current chunk.
- currentChunk.ab.push(subChunk);
+ currentChunk.ab.push(line);
}
}
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 7ccd9f86d3..186a49e6a7 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
@@ -280,7 +280,6 @@ limitations under the License.
left: {1: true},
right: {10: true},
};
- const lineNums = {left: 0, right: 0};
const content = [
{
@@ -304,7 +303,7 @@ limitations under the License.
},
];
const result =
- element._splitCommonGroupsWithComments(content, lineNums);
+ element._splitUnchangedChunksWithComments(content);
assert.deepEqual(result, [
{
ab: ['Copyright (C) 2015 The Android Open Source Project'],
@@ -337,28 +336,25 @@ limitations under the License.
]);
});
- test('breaks-down shared chunks w/ whole-file', () => {
+ test('breaks down shared chunks w/ whole-file', () => {
const size = 120 * 2 + 5;
- const lineNums = {left: 0, right: 0};
const content = [{
ab: _.times(size, () => { return `${Math.random()}`; }),
}];
element.context = -1;
- const result =
- element._splitCommonGroupsWithComments(content, lineNums);
+ const result = element._splitLargeChunks(content);
assert.equal(result.length, 2);
assert.deepEqual(result[0].ab, content[0].ab.slice(0, 120));
assert.deepEqual(result[1].ab, content[0].ab.slice(120));
});
test('does not break-down shared chunks w/ context', () => {
- const lineNums = {left: 0, right: 0};
const content = [{
ab: _.times(75, () => { return `${Math.random()}`; }),
}];
element.context = 4;
const result =
- element._splitCommonGroupsWithComments(content, lineNums);
+ element._splitUnchangedChunksWithComments(content);
assert.deepEqual(result, content);
});
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 7f25b3e1c3..3484693d9b 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,6 +32,7 @@
Polymer({
is: 'gr-diff-selection',
+ _legacyUndefinedCheck: true,
properties: {
diff: Object,
@@ -83,7 +84,7 @@
targetClasses.push(SelectionClass.BLAME);
} else {
const commentSelected =
- this._elementDescendedFromClass(e.target, 'gr-diff-comment');
+ this._elementDescendedFromClass(e.target, 'gr-comment');
const side = this.diffBuilder.getSideByLineEl(lineEl);
targetClasses.push(side === 'left' ?
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 f34429b8d6..469a894ab5 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
@@ -36,7 +36,7 @@ limitations under the License.
<td class="content">
<div class="contentText" data-side="left">ba ba</div>
<div data-side="left">
- <div class="gr-diff-comment-thread">
+ <div class="comment-thread">
<div class="gr-formatted-text message">
<span id="output" class="gr-linked-text">This is a comment</span>
</div>
@@ -58,7 +58,7 @@ limitations under the License.
<td class="content">
<div class="contentText" data-side="right">more more more</div>
<div data-side="right">
- <div class="gr-diff-comment-thread">
+ <div class="comment-thread">
<div class="gr-formatted-text message">
<span id="output" class="gr-linked-text">This is a comment on the right</span>
</div>
@@ -72,7 +72,7 @@ limitations under the License.
<td class="content">
<div class="contentText" data-side="left">ga ga</div>
<div data-side="left">
- <div class="gr-diff-comment-thread">
+ <div class="comment-thread">
<div class="gr-formatted-text message">
<span id="output" class="gr-linked-text">This is <a>a</a> different comment 💩 unicode is fun</span>
</div>
@@ -87,7 +87,7 @@ limitations under the License.
<td class="content">
<div class="contentText" data-side="left">ga ga</div>
<div data-side="left">
- <div class="gr-diff-comment-thread">
+ <div class="comment-thread">
<textarea data-side="right">test for textarea copying</textarea>
</div>
</div>
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 8948f33703..5ebe73825f 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
@@ -303,11 +303,11 @@ limitations under the License.
<span>Diff view:</span>
<gr-diff-mode-selector
id="modeSelect"
- save-on-change="[[_loggedIn]]"
+ save-on-change="[[!_diffPrefsDisabled]]"
mode="{{changeViewState.diffMode}}"></gr-diff-mode-selector>
</div>
<span id="diffPrefsContainer"
- hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]" hidden>
+ hidden$="[[_computePrefsButtonHidden(_prefs, _diffPrefsDisabled)]]" hidden>
<span class="preferences desktop">
<gr-button
link
@@ -350,10 +350,10 @@ limitations under the License.
patch-range="[[_patchRange]]"
path="[[_path]]"
prefs="[[_prefs]]"
- project-config="[[_projectConfig]]"
project-name="[[_change.project]]"
view-mode="[[_diffMode]]"
is-blame-loaded="{{_isBlameLoaded}}"
+ on-comment-anchor-tap="_onLineSelected"
on-line-selected="_onLineSelected">
</gr-diff-host>
<gr-diff-preferences-dialog
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 5a2f1f32df..d73042e5a9 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,6 +35,7 @@
Polymer({
is: 'gr-diff-view',
+ _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
@@ -69,6 +70,14 @@
value() { return {}; },
observer: '_changeViewStateChanged',
},
+ disableDiffPrefs: {
+ type: Boolean,
+ value: false,
+ },
+ _diffPrefsDisabled: {
+ type: Boolean,
+ computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
+ },
/** @type {?} */
_patchRange: Object,
/** @type {?} */
@@ -217,7 +226,7 @@
[this.Shortcut.EXPAND_ALL_DIFF_CONTEXT]: '_handleExpandAllDiffContext',
[this.Shortcut.NEXT_UNREVIEWED_FILE]: '_handleNextUnreviewedFile',
- // Final two are actually handled by gr-diff-comment-thread.
+ // Final two are actually handled by gr-comment-thread.
[this.Shortcut.EXPAND_ALL_COMMENT_THREADS]: null,
[this.Shortcut.COLLAPSE_ALL_COMMENT_THREADS]: null,
};
@@ -461,6 +470,7 @@
_handleCommaKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
+ if (this._diffPrefsDisabled) { return; }
e.preventDefault();
this.$.diffPreferencesDialog.open();
@@ -814,8 +824,8 @@
(unresolvedString ? `${unresolvedString}` : '');
},
- _computePrefsButtonHidden(prefs, loggedIn) {
- return !loggedIn || !prefs;
+ _computePrefsButtonHidden(prefs, prefsDisabled) {
+ return prefsDisabled || !prefs;
},
_handleFileChange(e) {
@@ -1080,6 +1090,10 @@
this.$.diffHost.expandAllContext();
},
+ _computeDiffPrefsDisabled(disableDiffPrefs, loggedIn) {
+ return disableDiffPrefs || !loggedIn;
+ },
+
_handleNextUnreviewedFile(e) {
if (this.shouldSuppressKeyboardShortcut(e)) { return; }
this._setReviewed(true);
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 034acbb04c..b33c54cf22 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
@@ -156,6 +156,7 @@ limitations under the License.
element._fileList = ['chell.go', 'glados.txt', 'wheatley.md'];
element._path = 'glados.txt';
element.changeViewState.selectedFileIndex = 1;
+ element._loggedIn = true;
const diffNavStub = sandbox.stub(Gerrit.Nav, 'navigateToDiff');
const changeNavStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
@@ -198,6 +199,10 @@ limitations under the License.
MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
assert(showPrefsStub.calledOnce);
+ element.disableDiffPrefs = true;
+ MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
+ assert(showPrefsStub.calledOnce);
+
let scrollStub = sandbox.stub(element.$.cursor, 'moveToNextChunk');
MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
assert(scrollStub.calledOnce);
@@ -447,23 +452,39 @@ limitations under the License.
{loggedIn: true, changeStatus: element.ChangeStatus.MERGED}));
});
- test('Diff preferences hidden when no prefs or logged out', () => {
- element._loggedIn = false;
- flushAsynchronousOperations();
- assert.isTrue(element.$.diffPrefsContainer.hidden);
+ suite('diff prefs hidden', () => {
+ test('when no prefs or logged out', () => {
+ element.disableDiffPrefs = false;
+ element._loggedIn = false;
+ flushAsynchronousOperations();
+ assert.isTrue(element.$.diffPrefsContainer.hidden);
- element._loggedIn = true;
- flushAsynchronousOperations();
- assert.isTrue(element.$.diffPrefsContainer.hidden);
+ element._loggedIn = true;
+ flushAsynchronousOperations();
+ assert.isTrue(element.$.diffPrefsContainer.hidden);
- element._loggedIn = false;
- element._prefs = {font_size: '12'};
- flushAsynchronousOperations();
- assert.isTrue(element.$.diffPrefsContainer.hidden);
+ element._loggedIn = false;
+ element._prefs = {font_size: '12'};
+ flushAsynchronousOperations();
+ assert.isTrue(element.$.diffPrefsContainer.hidden);
- element._loggedIn = true;
- flushAsynchronousOperations();
- assert.isFalse(element.$.diffPrefsContainer.hidden);
+ element._loggedIn = true;
+ flushAsynchronousOperations();
+ assert.isFalse(element.$.diffPrefsContainer.hidden);
+ });
+
+ test('when disableDiffPrefs is set', () => {
+ element._loggedIn = true;
+ element._prefs = {font_size: '12'};
+ element.disableDiffPrefs = false;
+ flushAsynchronousOperations();
+
+ assert.isFalse(element.$.diffPrefsContainer.hidden);
+ element.disableDiffPrefs = true;
+ flushAsynchronousOperations();
+
+ assert.isTrue(element.$.diffPrefsContainer.hidden);
+ });
});
test('prefsButton opens gr-diff-preferences', () => {
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 88fcd0e740..dd69724096 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
@@ -20,11 +20,20 @@
// Prevent redefinition.
if (window.GrDiffGroup) { return; }
+ /**
+ * A chunk of the diff that should be rendered together.
+ */
function GrDiffGroup(type, opt_lines) {
this.type = type;
+
+ /** @type{!Array<!GrDiffLine>} */
this.lines = [];
+ /** @type{!Array<!GrDiffLine>} */
this.adds = [];
+ /** @type{!Array<!GrDiffLine>} */
this.removes = [];
+
+ /** @type{boolean|undefined} */
this.dueToRebase = undefined;
this.lineRange = {
@@ -40,8 +49,13 @@
GrDiffGroup.prototype.element = null;
GrDiffGroup.Type = {
+ /** Unchanged context. */
BOTH: 'both',
+
+ /** A widget used to show more context. */
CONTEXT_CONTROL: 'contextControl',
+
+ /** Added, removed or modified chunk. */
DELTA: 'delta',
};
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 9613df8858..72fc1ee469 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -20,7 +20,6 @@ limitations under the License.
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../gr-diff-builder/gr-diff-builder.html">
-<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
<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">
@@ -36,6 +35,11 @@ limitations under the License.
:host(.no-left) .sideBySide ::content .right:not([data-value]) + td {
display: none;
}
+ .thread-group, ::slotted(*) .thread-group {
+ display: block;
+ max-width: var(--content-width, 80ch);
+ white-space: normal;
+ }
.diffContainer {
display: flex;
font-family: var(--monospace-font-family);
@@ -267,6 +271,15 @@ limitations under the License.
.newlineWarning.hidden {
display: none;
}
+ .lineNum.COVERED {
+ background-color: #E0F2F1;
+ }
+ .lineNum.NOT_COVERED {
+ background-color: #FFD1A4;
+ }
+ .lineNum.PARTIALLY_COVERED {
+ background: linear-gradient(to right bottom, #FFD1A4 0%, #FFD1A4 50%, #E0F2F1 50%, #E0F2F1 100%);
+ }
</style>
<style include="gr-syntax-theme"></style>
<div id="diffHeader" hidden$="[[_computeDiffHeaderHidden(_diffHeaderItems)]]">
@@ -282,10 +295,11 @@ limitations under the License.
<gr-diff-highlight
id="highlights"
logged-in="[[loggedIn]]"
- comments="{{comments}}">
+ comment-ranges="{{_commentRanges}}">
<gr-diff-builder
id="diffBuilder"
- comments="[[comments]]"
+ comment-ranges="[[_commentRanges]]"
+ coverage-ranges="[[coverageRanges]]"
project-name="[[projectName]]"
diff="[[diff]]"
diff-path="[[path]]"
@@ -295,10 +309,7 @@ limitations under the License.
line-wrapping="[[lineWrapping]]"
is-image-diff="[[isImageDiff]]"
base-image="[[baseImage]]"
- revision-image="[[revisionImage]]"
- parent-index="[[_parentIndex]]"
- create-comment-fn="[[_createThreadGroupFn]]"
- line-of-interest="[[lineOfInterest]]">
+ revision-image="[[revisionImage]]">
<table
id="diffTable"
class$="[[_diffTableClass]]"
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 da7dea1212..4e023d4e85 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -35,10 +35,63 @@
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
+ * values.
+ *
+ * @param {Gerrit.Range=} a range 1
+ * @param {Gerrit.Range=} b range 2
+ * @return {boolean}
+ */
+ Gerrit.rangesEqual = function(a, b) {
+ if (!a && !b) { return true; }
+ if (!a || !b) { return false; }
+ return a.start_line === b.start_line &&
+ a.start_character === b.start_character &&
+ a.end_line === b.end_line &&
+ a.end_character === b.end_character;
+ };
+
+ function isThreadEl(node) {
+ return node.nodeType === Node.ELEMENT_NODE &&
+ node.classList.contains('comment-thread');
+ }
+
+ /**
+ * Turn a slot element into the corresponding content element.
+ * Slots are only fully supported in Polymer 2 - in Polymer 1, they are
+ * replaced with content elements during template parsing. This conversion is
+ * not applied for imperatively created slot elements, so this method
+ * implements the same behavior as the template parsing for imperative slots.
+ */
+ Gerrit.slotToContent = function(slot) {
+ const content = document.createElement('content');
+ content.name = slot.name;
+ content.setAttribute('select', `[slot='${slot.name}']`);
+ return content;
+ };
+
const COMMIT_MSG_PATH = '/COMMIT_MSG';
/**
* 72 is the inofficial length standard for git commit messages.
@@ -49,8 +102,11 @@
*/
const COMMIT_MSG_LINE_LENGTH = 72;
+ const RENDER_DIFF_TABLE_DEBOUNCE_NAME = 'renderDiffTable';
+
Polymer({
is: 'gr-diff',
+ _legacyUndefinedCheck: true,
/**
* Fired when the user selects a line.
@@ -65,15 +121,16 @@
*/
/**
- * Fired when a comment is saved or discarded
+ * Fired when a comment is created
*
- * @event diff-comments-modified
+ * @event create-comment
*/
/**
- * Fired when a draft is added or edited.
+ * Fired when rendering, including syntax highlighting, is done. Also fired
+ * when no rendering can be done because required preferences are not set.
*
- * @event draft-interaction
+ * @event render
*/
properties: {
@@ -92,10 +149,6 @@
type: Object,
observer: '_prefsObserver',
},
- projectConfig: {
- type: Object,
- observer: '_projectConfigChanged',
- },
projectName: String,
displayLine: {
type: Boolean,
@@ -110,9 +163,15 @@
reflectToAttribute: true,
},
noRenderOnPrefsChange: Boolean,
- comments: {
- type: Object,
- value: {left: [], right: []},
+ /** @type {!Array<!Gerrit.HoveredRange>} */
+ _commentRanges: {
+ type: Array,
+ value: () => [],
+ },
+ /** @type {!Array<!Gerrit.CoverageRange>} */
+ coverageRanges: {
+ type: Array,
+ value: () => [],
},
lineWrapping: {
type: Boolean,
@@ -125,14 +184,7 @@
observer: '_viewModeObserver',
},
- /**
- * Special line number which should not be collapsed into a shared region.
- *
- * @type {{
- * number: number,
- * leftSide: {boolean}
- * }|null}
- */
+ /** @type {?Defs.LineOfInterest} */
lineOfInterest: Object,
loading: {
@@ -191,27 +243,34 @@
observer: '_blameChanged',
},
- _parentIndex: {
- type: Number,
- computed: '_computeParentIndex(patchRange.*)',
- },
+ parentIndex: Number,
_newlineWarning: {
type: String,
computed: '_computeNewlineWarning(diff)',
},
+ _diffLength: Number,
+
/**
- * @type {function(number, boolean, !string)}
+ * Observes comment nodes added or removed after the initial render.
+ * Can be used to unregister when the entire diff is (re-)rendered or upon
+ * detachment.
+ *
+ * @type {?PolymerDomApi.ObserveHandle}
*/
- _createThreadGroupFn: {
- type: Function,
- value() {
- return this._createCommentThreadGroup.bind(this);
- },
- },
+ _incrementalNodeObserver: Object,
- _diffLength: Number,
+ /**
+ * Observes comment nodes added or removed at any point.
+ * Can be used to unregister upon detachment.
+ *
+ * @type {?PolymerDomApi.ObserveHandle}
+ */
+ _nodeObserver: Object,
+
+ /** Set by Polymer. */
+ isAttached: Boolean,
},
behaviors: [
@@ -219,15 +278,135 @@
],
listeners: {
- 'comment-discard': '_handleCommentDiscard',
- 'comment-update': '_handleCommentUpdate',
- 'comment-save': '_handleCommentSave',
- 'create-comment': '_handleCreateComment',
+ 'create-range-comment': '_handleCreateRangeComment',
+ 'render-content': '_handleRenderContent',
+ },
+
+ observers: [
+ '_enableSelectionObserver(loggedIn, isAttached)',
+ ],
+
+ attached() {
+ this._observeNodes();
+ },
+
+ detached() {
+ this._unobserveIncrementalNodes();
+ this._unobserveNodes();
+ },
+
+ _enableSelectionObserver(loggedIn, isAttached) {
+ if (loggedIn && isAttached) {
+ this.listen(document, 'selectionchange', '_handleSelectionChange');
+ this.listen(document, 'mouseup', '_handleMouseUp');
+ } else {
+ this.unlisten(document, 'selectionchange', '_handleSelectionChange');
+ this.unlisten(document, 'mouseup', '_handleMouseUp');
+ }
+ },
+
+ _handleSelectionChange() {
+ // Because of shadow DOM selections, we handle the selectionchange here,
+ // and pass the shadow DOM selection into gr-diff-highlight, where the
+ // corresponding range is determined and normalized.
+ const selection = this._getShadowOrDocumentSelection();
+ this.$.highlights.handleSelectionChange(selection, false);
+ },
+
+ _handleMouseUp(e) {
+ // To handle double-click outside of text creating comments, we check on
+ // mouse-up if there's a selection that just covers a line change. We
+ // can't do that on selection change since the user may still be dragging.
+ const selection = this._getShadowOrDocumentSelection();
+ this.$.highlights.handleSelectionChange(selection, true);
+ },
+
+ /** Gets the current selection, preferring the shadow DOM selection. */
+ _getShadowOrDocumentSelection() {
+ // When using native shadow DOM, the selection returned by
+ // document.getSelection() cannot reference the actual DOM elements making
+ // up the diff, because they are in the shadow DOM of the gr-diff element.
+ // This takes the shadow DOM selection if one exists.
+ return this.root.getSelection ?
+ this.root.getSelection() :
+ document.getSelection();
+ },
+
+ _observeNodes() {
+ this._nodeObserver = Polymer.dom(this).observeNodes(info => {
+ const addedThreadEls = info.addedNodes.filter(isThreadEl);
+ const removedThreadEls = info.removedNodes.filter(isThreadEl);
+ this._updateRanges(addedThreadEls, removedThreadEls);
+ this._redispatchHoverEvents(addedThreadEls);
+ });
+ },
+
+ _updateRanges(addedThreadEls, removedThreadEls) {
+ function commentRangeFromThreadEl(threadEl) {
+ const side = threadEl.getAttribute('comment-side');
+ const range = JSON.parse(threadEl.getAttribute('range'));
+ return {side, range, hovering: false};
+ }
+
+ const addedCommentRanges = addedThreadEls
+ .map(commentRangeFromThreadEl)
+ .filter(({range}) => range);
+ const removedCommentRanges = removedThreadEls
+ .map(commentRangeFromThreadEl)
+ .filter(({range}) => range);
+ for (const removedCommentRange of removedCommentRanges) {
+ const i = this._commentRanges.findIndex(commentRange => {
+ return commentRange.side === removedCommentRange.side &&
+ Gerrit.rangesEqual(commentRange.range, removedCommentRange.range);
+ });
+ this.splice('_commentRanges', i, 1);
+ }
+ this.push('_commentRanges', ...addedCommentRanges);
+ },
+
+ /**
+ * The key locations based on the comments and line of interests,
+ * where lines should not be collapsed.
+ *
+ * @return {{left: Object<(string|number), boolean>,
+ * right: Object<(string|number), boolean>}}
+ */
+ _computeKeyLocations() {
+ const keyLocations = {left: {}, right: {}};
+ if (this.lineOfInterest) {
+ const side = this.lineOfInterest.leftSide ? 'left' : 'right';
+ keyLocations[side][this.lineOfInterest.number] = true;
+ }
+ const threadEls = Polymer.dom(this).getEffectiveChildNodes()
+ .filter(isThreadEl);
+
+ for (const threadEl of threadEls) {
+ const commentSide = threadEl.getAttribute('comment-side');
+ const lineNum = Number(threadEl.getAttribute('line-num')) ||
+ GrDiffLine.FILE;
+ keyLocations[commentSide][lineNum] = true;
+ }
+ return keyLocations;
+ },
+
+ // Dispatch events that are handled by the gr-diff-highlight.
+ _redispatchHoverEvents(addedThreadEls) {
+ for (const threadEl of addedThreadEls) {
+ threadEl.addEventListener('mouseenter', () => {
+ threadEl.dispatchEvent(
+ new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
+ });
+ threadEl.addEventListener('mouseleave', () => {
+ threadEl.dispatchEvent(
+ new CustomEvent('comment-thread-mouseleave', {bubbles: true}));
+ });
+ }
},
/** Cancel any remaining diff builder rendering work. */
cancel() {
this.$.diffBuilder.cancel();
+ this.cancelDebouncer(RENDER_DIFF_TABLE_DEBOUNCE_NAME);
},
/** @return {!Array<!HTMLElement>} */
@@ -236,7 +415,9 @@
return [];
}
- return Polymer.dom(this.root).querySelectorAll('.diff-row');
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ return Array.from(
+ Polymer.dom(this.root).querySelectorAll('.diff-row'));
},
/** @return {boolean} */
@@ -257,22 +438,6 @@
}
},
- _handleCommentSaveOrDiscard() {
- this.dispatchEvent(new CustomEvent('diff-comments-modified',
- {bubbles: true}));
- },
-
- /** @return {!Array<!HTMLElement>} */
- getThreadEls() {
- let threads = [];
- const threadGroupEls = Polymer.dom(this.root)
- .querySelectorAll('gr-diff-comment-thread-group');
- for (const threadGroupEl of threadGroupEls) {
- threads = threads.concat(threadGroupEl.threadEls);
- }
- return threads;
- },
-
/** @return {string} */
_computeContainerClass(loggedIn, viewMode, displayLine) {
const classes = ['diffContainer'];
@@ -337,10 +502,10 @@
this._createComment(el, lineNum);
},
- _handleCreateComment(e) {
+ _handleCreateRangeComment(e) {
const range = e.detail.range;
const side = e.detail.side;
- const lineNum = range.endLine;
+ const lineNum = range.end_line;
const lineEl = this.$.diffBuilder.getLineElByNumber(lineNum, side);
if (this._isValidElForComment(lineEl)) {
@@ -374,88 +539,52 @@
/**
* @param {!Object} lineEl
- * @param {number=} opt_lineNum
- * @param {string=} opt_side
- * @param {!Object=} opt_range
+ * @param {number=} lineNum
+ * @param {string=} side
+ * @param {!Object=} range
*/
- _createComment(lineEl, opt_lineNum, opt_side, opt_range) {
- this.dispatchEvent(new CustomEvent('draft-interaction', {bubbles: true}));
+ _createComment(lineEl, lineNum=undefined, side=undefined, range=undefined) {
const contentText = this.$.diffBuilder.getContentByLineEl(lineEl);
const contentEl = contentText.parentElement;
- const side = opt_side ||
+ side = side ||
this._getCommentSideByLineAndContent(lineEl, contentEl);
- const patchNum = this._getPatchNumByLineAndContent(lineEl, contentEl);
+ const patchForNewThreads = this._getPatchNumByLineAndContent(
+ lineEl, contentEl);
const isOnParent =
- this._getIsParentCommentByLineAndContent(lineEl, contentEl);
- const threadEl = this._getOrCreateThread(contentEl, patchNum,
- side, isOnParent, opt_range);
- threadEl.addOrEditDraft(opt_lineNum, opt_range);
- },
-
- /**
- * Fetch the thread group at the given range, or the range-less thread
- * on the line if no range is provided.
- *
- * @param {!Object} threadGroupEl
- * @param {string} commentSide
- * @param {!Object=} opt_range
- * @return {!Object}
- */
- _getThread(threadGroupEl, commentSide, opt_range) {
- return threadGroupEl.getThread(commentSide, opt_range);
+ this._getIsParentCommentByLineAndContent(lineEl, contentEl);
+ this.dispatchEvent(new CustomEvent('create-comment', {
+ bubbles: true,
+ detail: {
+ lineNum,
+ side,
+ patchNum: patchForNewThreads,
+ isOnParent,
+ range,
+ },
+ }));
},
_getThreadGroupForLine(contentEl) {
- return contentEl.querySelector('gr-diff-comment-thread-group');
+ return contentEl.querySelector('.thread-group');
},
/**
- * Gets or creates a comment thread for a specific spot on a diff.
- * May include a range, if the comment is a range comment.
+ * Gets or creates a comment thread group for a specific line and side on a
+ * diff.
*
* @param {!Object} contentEl
- * @param {number} patchNum
- * @param {string} commentSide
- * @param {boolean} isOnParent
- * @param {!Object=} opt_range
- * @return {!Object}
+ * @param {!Gerrit.DiffSide} commentSide
+ * @return {!Node}
*/
- _getOrCreateThread(contentEl, patchNum, commentSide,
- isOnParent, opt_range) {
+ _getOrCreateThreadGroup(contentEl, commentSide) {
// Check if thread group exists.
let threadGroupEl = this._getThreadGroupForLine(contentEl);
if (!threadGroupEl) {
- threadGroupEl = this._createCommentThreadGroup(patchNum, isOnParent,
- commentSide);
+ threadGroupEl = document.createElement('div');
+ threadGroupEl.className = 'thread-group';
+ threadGroupEl.setAttribute('data-side', commentSide);
contentEl.appendChild(threadGroupEl);
}
-
- let threadEl = this._getThread(threadGroupEl, commentSide, opt_range);
-
- if (!threadEl) {
- threadGroupEl.addNewThread(commentSide, opt_range);
- Polymer.dom.flush();
- threadEl = this._getThread(threadGroupEl, commentSide, opt_range);
- }
- return threadEl;
- },
-
- /**
- * @param {number} patchNum
- * @param {boolean} isOnParent
- * @param {!string} commentSide
- * @return {!Object}
- */
- _createCommentThreadGroup(patchNum, isOnParent, commentSide) {
- const threadGroupEl =
- document.createElement('gr-diff-comment-thread-group');
- threadGroupEl.changeNum = this.changeNum;
- threadGroupEl.commentSide = commentSide;
- threadGroupEl.patchForNewThreads = patchNum;
- threadGroupEl.path = this.path;
- threadGroupEl.isOnParent = isOnParent;
- threadGroupEl.projectName = this.projectName;
- threadGroupEl.parentIndex = this._parentIndex;
return threadGroupEl;
},
@@ -506,75 +635,6 @@
return side;
},
- _handleCommentDiscard(e) {
- const comment = e.detail.comment;
- this._removeComment(comment);
- this._handleCommentSaveOrDiscard();
- },
-
- _removeComment(comment) {
- const side = comment.__commentSide;
- this._removeCommentFromSide(comment, side);
- },
-
- _handleCommentSave(e) {
- const comment = e.detail.comment;
- const side = e.detail.comment.__commentSide;
- const idx = this._findDraftIndex(comment, side);
- this.set(['comments', side, idx], comment);
- this._handleCommentSaveOrDiscard();
- },
-
- /**
- * Closure annotation for Polymer.prototype.push is off. Submitted PR:
- * https://github.com/Polymer/polymer/pull/4776
- * but for not supressing annotations.
- *
- * @suppress {checkTypes} */
- _handleCommentUpdate(e) {
- const comment = e.detail.comment;
- const side = e.detail.comment.__commentSide;
- let idx = this._findCommentIndex(comment, side);
- if (idx === -1) {
- idx = this._findDraftIndex(comment, side);
- }
- if (idx !== -1) { // Update draft or comment.
- this.set(['comments', side, idx], comment);
- } else { // Create new draft.
- this.push(['comments', side], comment);
- }
- },
-
- _removeCommentFromSide(comment, side) {
- let idx = this._findCommentIndex(comment, side);
- if (idx === -1) {
- idx = this._findDraftIndex(comment, side);
- }
- if (idx !== -1) {
- this.splice('comments.' + side, idx, 1);
- }
- },
-
- /** @return {number} */
- _findCommentIndex(comment, side) {
- if (!comment.id || !this.comments[side]) {
- return -1;
- }
- return this.comments[side].findIndex(item => {
- return item.id === comment.id;
- });
- },
-
- /** @return {number} */
- _findDraftIndex(comment, side) {
- if (!comment.__draftID || !this.comments[side]) {
- return -1;
- }
- return this.comments[side].findIndex(item => {
- return item.__draftID === comment.__draftID;
- });
- },
-
_prefsObserver(newPrefs, oldPrefs) {
if (!this._prefsEqual(newPrefs, oldPrefs)) {
this._prefsChanged(newPrefs);
@@ -646,19 +706,35 @@
this.updateStyles(stylesToUpdate);
- if (this.diff && this.comments && !this.noRenderOnPrefsChange) {
- this._renderDiffTable();
+ if (this.diff && !this.noRenderOnPrefsChange) {
+ this._debounceRenderDiffTable();
}
},
_diffChanged(newValue) {
if (newValue) {
this._diffLength = this.$.diffBuilder.getDiffLength();
- this._renderDiffTable();
+ this._debounceRenderDiffTable();
}
},
+ /**
+ * When called multiple times from the same microtask, will call
+ * _renderDiffTable only once, in the next microtask, unless it is cancelled
+ * before that microtask runs.
+ *
+ * This should be used instead of calling _renderDiffTable directly to
+ * render the diff in response to an input change, because there may be
+ * multiple inputs changing in the same microtask, but we only want to
+ * render once.
+ */
+ _debounceRenderDiffTable() {
+ this.debounce(
+ RENDER_DIFF_TABLE_DEBOUNCE_NAME, () => this._renderDiffTable());
+ },
+
_renderDiffTable() {
+ this._unobserveIncrementalNodes();
if (!this.prefs) {
this.dispatchEvent(new CustomEvent('render', {bubbles: true}));
return;
@@ -672,7 +748,55 @@
}
this._showWarning = false;
- this.$.diffBuilder.render(this.comments, this._getBypassPrefs());
+
+ const keyLocations = this._computeKeyLocations();
+ this.$.diffBuilder.render(keyLocations, this._getBypassPrefs())
+ .then(() => {
+ this.dispatchEvent(
+ new CustomEvent('render', {bubbles: true}));
+ });
+ },
+
+ _handleRenderContent() {
+ this._incrementalNodeObserver = Polymer.dom(this).observeNodes(info => {
+ const addedThreadEls = info.addedNodes.filter(isThreadEl);
+ // Removed nodes do not need to be handled because all this code does is
+ // adding a slot for the added thread elements, and the extra slots do
+ // 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.
+ for (const threadEl of addedThreadEls) {
+ const lineNumString = threadEl.getAttribute('line-num') || 'FILE';
+ const commentSide = threadEl.getAttribute('comment-side');
+ const lineEl = this.$.diffBuilder.getLineElByNumber(
+ lineNumString, commentSide);
+ const contentText = this.$.diffBuilder.getContentByLineEl(lineEl);
+ const contentEl = contentText.parentElement;
+ const threadGroupEl = this._getOrCreateThreadGroup(
+ contentEl, commentSide);
+ // Create a slot for the thread and attach it to the thread group.
+ // The Polyfill has some bugs and this only works if the slot is
+ // attached to the group after the group is attached to the DOM.
+ // The thread group may already have a slot with the right name, but
+ // that is okay because the first matching slot is used and the rest
+ // are ignored.
+ const slot = document.createElement('slot');
+ slot.name = threadEl.getAttribute('slot');
+ Polymer.dom(threadGroupEl).appendChild(Gerrit.slotToContent(slot));
+ }
+ });
+ },
+
+ _unobserveIncrementalNodes() {
+ if (this._incrementalNodeObserver) {
+ Polymer.dom(this).unobserveNodes(this._incrementalNodeObserver);
+ }
+ },
+
+ _unobserveNodes() {
+ if (this._nodeObserver) {
+ Polymer.dom(this).unobserveNodes(this._nodeObserver);
+ }
},
/**
@@ -686,16 +810,10 @@
},
clearDiffContent() {
+ this._unobserveIncrementalNodes();
this.$.diffTable.innerHTML = null;
},
- _projectConfigChanged(projectConfig) {
- const threadEls = this.getThreadEls();
- for (let i = 0; i < threadEls.length; i++) {
- threadEls[i].projectConfig = projectConfig;
- }
- },
-
/** @return {!Array} */
_computeDiffHeaderItems(diffInfoRecord) {
const diffInfo = diffInfoRecord.base;
@@ -716,12 +834,12 @@
_handleFullBypass() {
this._safetyBypass = FULL_CONTEXT;
- this._renderDiffTable();
+ this._debounceRenderDiffTable();
},
_handleLimitedBypass() {
this._safetyBypass = LIMITED_CONTEXT;
- this._renderDiffTable();
+ this._debounceRenderDiffTable();
},
/** @return {string} */
@@ -737,16 +855,6 @@
return errorMessage ? 'showError' : '';
},
- /**
- * @return {number|null}
- */
- _computeParentIndex(patchRangeRecord) {
- if (!this.isMergeParent(patchRangeRecord.base.basePatchNum)) {
- return null;
- }
- return this.getParentIndex(patchRangeRecord.base.basePatchNum);
- },
-
expandAllContext() {
this._handleFullBypass();
},
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 5e204993ec..594d60eda3 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
@@ -50,6 +50,30 @@ limitations under the License.
sandbox.restore();
});
+ suite('selectionchange event handling', () => {
+ const emulateSelection = function() {
+ document.dispatchEvent(new CustomEvent('selectionchange'));
+ };
+
+ setup(() => {
+ element = fixture('basic');
+ sandbox.stub(element.$.highlights, 'handleSelectionChange');
+ });
+
+ test('enabled if logged in', () => {
+ element.loggedIn = true;
+ emulateSelection();
+ assert.isTrue(element.$.highlights.handleSelectionChange.called);
+ });
+
+ test('ignored if logged out', () => {
+ element.loggedIn = false;
+ emulateSelection();
+ assert.isFalse(element.$.highlights.handleSelectionChange.called);
+ });
+ });
+
+
test('cancel', () => {
element = fixture('basic');
const cancelStub = sandbox.stub(element.$.diffBuilder, 'cancel');
@@ -59,14 +83,14 @@ limitations under the License.
test('line limit with line_wrapping', () => {
element = fixture('basic');
- element.prefs = {line_wrapping: true, line_length: 80, tab_size: 2};
+ element.prefs = Object.assign({}, MINIMAL_PREFS, {line_wrapping: true});
flushAsynchronousOperations();
assert.equal(element.customStyle['--line-limit'], '80ch');
});
test('line limit without line_wrapping', () => {
element = fixture('basic');
- element.prefs = {line_wrapping: false, line_length: 80, tab_size: 2};
+ element.prefs = Object.assign({}, MINIMAL_PREFS, {line_wrapping: false});
flushAsynchronousOperations();
assert.isNotOk(element.customStyle['--line-limit']);
});
@@ -194,118 +218,8 @@ limitations under the License.
element.$$('.diffContainer').classList.contains('displayLine'));
});
- test('remove comment', () => {
- element.comments = {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
- {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
- {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
- {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
- ],
- right: [
- {id: 'c1', __commentSide: 'right'},
- {id: 'c2', __commentSide: 'right'},
- {id: 'd1', __draft: true, __commentSide: 'right'},
- {id: 'd2', __draft: true, __commentSide: 'right'},
- ],
- };
-
- element._removeComment({});
- // Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesn’t seem
- // to believe that one object deepEquals another even when they do :-/.
- assert.equal(JSON.stringify(element.comments), JSON.stringify({
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
- {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
- {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
- {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
- ],
- right: [
- {id: 'c1', __commentSide: 'right'},
- {id: 'c2', __commentSide: 'right'},
- {id: 'd1', __draft: true, __commentSide: 'right'},
- {id: 'd2', __draft: true, __commentSide: 'right'},
- ],
- }));
-
- element._removeComment({id: 'bc2', side: 'PARENT',
- __commentSide: 'left'});
- assert.deepEqual(element.comments, {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
- {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
- {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
- ],
- right: [
- {id: 'c1', __commentSide: 'right'},
- {id: 'c2', __commentSide: 'right'},
- {id: 'd1', __draft: true, __commentSide: 'right'},
- {id: 'd2', __draft: true, __commentSide: 'right'},
- ],
- });
-
- element._removeComment({id: 'd2', __commentSide: 'right'});
- assert.deepEqual(element.comments, {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
- {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
- {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
- ],
- right: [
- {id: 'c1', __commentSide: 'right'},
- {id: 'c2', __commentSide: 'right'},
- {id: 'd1', __draft: true, __commentSide: 'right'},
- ],
- });
- });
-
test('thread groups', () => {
const contentEl = document.createElement('div');
- const commentSide = 'left';
- const patchNum = 1;
- const side = 'PARENT';
- let range = {
- startLine: 1,
- startChar: 1,
- endLine: 1,
- endChar: 2,
- };
element.changeNum = 123;
element.patchRange = {basePatchNum: 1, patchNum: 2};
@@ -313,36 +227,19 @@ limitations under the License.
const mock = document.createElement('mock-diff-response');
element.$.diffBuilder._builder = element.$.diffBuilder._getDiffBuilder(
- mock.diffResponse, {}, MINIMAL_PREFS);
+ mock.diffResponse, Object.assign({}, MINIMAL_PREFS));
// No thread groups.
assert.isNotOk(element._getThreadGroupForLine(contentEl));
// A thread group gets created.
- assert.isOk(element._getOrCreateThread(contentEl,
- patchNum, commentSide, side));
-
- // Try to fetch a thread with a different range.
- range = {
- startLine: 1,
- startChar: 1,
- endLine: 1,
- endChar: 3,
- };
+ const threadGroupEl = element._getOrCreateThreadGroup(contentEl);
+ assert.isOk(threadGroupEl);
- assert.isOk(element._getOrCreateThread(
- contentEl, patchNum, commentSide, side, range));
// The new thread group can be fetched.
assert.isOk(element._getThreadGroupForLine(contentEl));
- assert.equal(contentEl.querySelectorAll(
- 'gr-diff-comment-thread-group').length, 1);
-
- const threadGroup = contentEl.querySelector(
- 'gr-diff-comment-thread-group');
- const threadLength = Polymer.dom(threadGroup.root).
- querySelectorAll('gr-diff-comment-thread').length;
- assert.equal(threadLength, 2);
+ assert.equal(contentEl.querySelectorAll('.thread-group').length, 1);
});
suite('image diffs', () => {
@@ -361,7 +258,6 @@ limitations under the License.
};
element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
- element.comments = {left: [], right: []};
element.isImageDiff = true;
element.prefs = {
auto_hide_diff_table_header: true,
@@ -554,7 +450,7 @@ limitations under the License.
binary: true,
};
- element.addEventListener('render', () => {
+ function rendered() {
// Recognizes that it should be an image diff.
assert.isTrue(element.isImageDiff);
assert.instanceOf(
@@ -566,7 +462,9 @@ limitations under the License.
assert.isNotOk(leftImage);
assert.isOk(rightImage);
done();
- });
+ element.removeEventListener('render', rendered);
+ }
+ element.addEventListener('render', rendered);
element.revisionImage = mockFile2;
element.diff = mockDiff;
@@ -589,7 +487,7 @@ limitations under the License.
binary: true,
};
- element.addEventListener('render', () => {
+ function rendered() {
// Recognizes that it should be an image diff.
assert.isTrue(element.isImageDiff);
assert.instanceOf(
@@ -601,7 +499,9 @@ limitations under the License.
assert.isOk(leftImage);
assert.isNotOk(rightImage);
done();
- });
+ element.removeEventListener('render', rendered);
+ }
+ element.addEventListener('render', rendered);
element.baseImage = mockFile1;
element.diff = mockDiff;
@@ -625,7 +525,7 @@ limitations under the License.
};
mockFile1.type = 'image/jpeg-evil';
- element.addEventListener('render', () => {
+ function rendered() {
// Recognizes that it should be an image diff.
assert.isTrue(element.isImageDiff);
assert.instanceOf(
@@ -633,7 +533,9 @@ limitations under the License.
const leftImage = element.$.diffTable.querySelector('td.left img');
assert.isNotOk(leftImage);
done();
- });
+ element.removeEventListener('render', rendered);
+ }
+ element.addEventListener('render', rendered);
element.baseImage = mockFile1;
element.diff = mockDiff;
@@ -687,10 +589,6 @@ limitations under the License.
const setupDiff = function() {
const mock = document.createElement('mock-diff-response');
element.diff = mock.diffResponse;
- element.comments = {
- left: [],
- right: [],
- };
element.prefs = {
context: 10,
tab_size: 8,
@@ -789,35 +687,14 @@ limitations under the License.
change_type: 'MODIFIED',
content: [{skip: 66}],
};
- element.comments = {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
- {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
- {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
- {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
- ],
- right: [
- {id: 'c1', __commentSide: 'right'},
- {id: 'c2', __commentSide: 'right'},
- {id: 'd1', __draft: true, __commentSide: 'right'},
- {id: 'd2', __draft: true, __commentSide: 'right'},
- ],
- };
+ element.flushDebouncer('renderDiffTable');
});
test('change in preferences re-renders diff', () => {
sandbox.stub(element, '_renderDiffTable');
- element.prefs = {};
- element.prefs = {time_format: 'HHMM_12'};
+ element.prefs = Object.assign(
+ {}, MINIMAL_PREFS, {time_format: 'HHMM_12'});
+ element.flushDebouncer('renderDiffTable');
assert.isTrue(element._renderDiffTable.called);
});
@@ -841,91 +718,12 @@ limitations under the License.
'noRenderOnPrefsChange', () => {
sandbox.stub(element, '_renderDiffTable');
element.noRenderOnPrefsChange = true;
- element.prefs = {};
- element.prefs = {time_format: 'HHMM_12'};
+ element.prefs = Object.assign(
+ {}, MINIMAL_PREFS, {time_format: 'HHMM_12'});
+ element.flushDebouncer('renderDiffTable');
assert.isFalse(element._renderDiffTable.called);
});
});
-
- suite('handle comment-update', () => {
- setup(() => {
- element.comments = {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1', side: 'PARENT', __commentSide: 'left'},
- {id: 'bc2', side: 'PARENT', __commentSide: 'left'},
- {id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
- {id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
- ],
- right: [
- {id: 'c1', __commentSide: 'right'},
- {id: 'c2', __commentSide: 'right'},
- {id: 'd1', __draft: true, __commentSide: 'right'},
- {id: 'd2', __draft: true, __commentSide: 'right'},
- ],
- };
- });
-
- test('creating a draft', () => {
- const comment = {__draft: true, __draftID: 'tempID', side: 'PARENT',
- __commentSide: 'left'};
- element.fire('comment-update', {comment});
- assert.include(element.comments.left, comment);
- });
-
- test('discarding a draft', () => {
- const draftID = 'tempID';
- const id = 'savedID';
- const comment = {
- __draft: true,
- __draftID: draftID,
- side: 'PARENT',
- __commentSide: 'left',
- };
- const diffCommentsModifiedStub = sandbox.stub();
- element.addEventListener('diff-comments-modified',
- diffCommentsModifiedStub);
- element.comments.left.push(comment);
- comment.id = id;
- element.fire('comment-discard', {comment});
- const drafts = element.comments.left.filter(item => {
- return item.__draftID === draftID;
- });
- assert.equal(drafts.length, 0);
- assert.isTrue(diffCommentsModifiedStub.called);
- });
-
- test('saving a draft', () => {
- const draftID = 'tempID';
- const id = 'savedID';
- const comment = {
- __draft: true,
- __draftID: draftID,
- side: 'PARENT',
- __commentSide: 'left',
- };
- const diffCommentsModifiedStub = sandbox.stub();
- element.addEventListener('diff-comments-modified',
- diffCommentsModifiedStub);
- element.comments.left.push(comment);
- comment.id = id;
- element.fire('comment-save', {comment});
- const drafts = element.comments.left.filter(item => {
- return item.__draftID === draftID;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].id, id);
- assert.isTrue(diffCommentsModifiedStub.called);
- });
- });
});
suite('diff header', () => {
@@ -985,38 +783,43 @@ limitations under the License.
const mock = document.createElement('mock-diff-response');
sandbox.stub(element.$.diffBuilder, 'getDiffLength').returns(10000);
element.diff = mock.diffResponse;
- element.comments = {left: [], right: []};
element.noRenderOnPrefsChange = true;
});
test('large render w/ context = 10', done => {
- element.prefs = {context: 10};
- element.addEventListener('render', () => {
+ element.prefs = Object.assign({}, MINIMAL_PREFS, {context: 10});
+ function rendered() {
assert.isTrue(renderStub.called);
assert.isFalse(element._showWarning);
done();
- });
+ element.removeEventListener('render', rendered);
+ }
+ element.addEventListener('render', rendered);
element._renderDiffTable();
});
test('large render w/ whole file and bypass', done => {
- element.prefs = {context: -1};
+ element.prefs = Object.assign({}, MINIMAL_PREFS, {context: -1});
element._safetyBypass = 10;
- element.addEventListener('render', () => {
+ function rendered() {
assert.isTrue(renderStub.called);
assert.isFalse(element._showWarning);
done();
- });
+ element.removeEventListener('render', rendered);
+ }
+ element.addEventListener('render', rendered);
element._renderDiffTable();
});
test('large render w/ whole file and no bypass', done => {
- element.prefs = {context: -1};
- element.addEventListener('render', () => {
+ element.prefs = Object.assign({}, MINIMAL_PREFS, {context: -1});
+ function rendered() {
assert.isFalse(renderStub.called);
assert.isTrue(element._showWarning);
done();
- });
+ element.removeEventListener('render', rendered);
+ }
+ element.addEventListener('render', rendered);
element._renderDiffTable();
});
});
@@ -1184,6 +987,58 @@ limitations under the License.
assert.isFalse(element._prefsEqual({x: 1}, {x: 1, y: 'abc'}));
});
});
+
+ suite('key locations', () => {
+ let renderStub;
+
+ setup(() => {
+ element = fixture('basic');
+ element.prefs = {};
+ renderStub = sandbox.stub(element.$.diffBuilder, 'render')
+ .returns(new Promise(() => {}));
+ });
+
+ test('lineOfInterest is a key location', () => {
+ element.lineOfInterest = {number: 789, leftSide: true};
+ element._renderDiffTable();
+ assert.isTrue(renderStub.called);
+ assert.deepEqual(renderStub.lastCall.args[0], {
+ left: {789: true},
+ right: {},
+ });
+ });
+
+ test('line comments are key locations', () => {
+ const threadEl = document.createElement('div');
+ threadEl.className = 'comment-thread';
+ threadEl.setAttribute('comment-side', 'right');
+ threadEl.setAttribute('line-num', 3);
+ Polymer.dom(element).appendChild(threadEl);
+ Polymer.dom.flush();
+
+ element._renderDiffTable();
+ assert.isTrue(renderStub.called);
+ assert.deepEqual(renderStub.lastCall.args[0], {
+ left: {},
+ right: {3: true},
+ });
+ });
+
+ test('file comments are key locations', () => {
+ const threadEl = document.createElement('div');
+ threadEl.className = 'comment-thread';
+ threadEl.setAttribute('comment-side', 'left');
+ Polymer.dom(element).appendChild(threadEl);
+ Polymer.dom.flush();
+
+ element._renderDiffTable();
+ assert.isTrue(renderStub.called);
+ assert.deepEqual(renderStub.lastCall.args[0], {
+ left: {FILE: true},
+ right: {},
+ });
+ });
+ });
});
a11ySuite('basic');
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 ad2e599aea..f913080e30 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,6 +31,7 @@
Polymer({
is: 'gr-patch-range-select',
+ _legacyUndefinedCheck: true,
properties: {
availablePatches: Array,
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 71b5bc361b..c9e9f50fc8 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
@@ -16,10 +16,8 @@ limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<dom-module id="gr-ranged-comment-layer">
<template>
- <gr-reporting id="reporting" category="comments"></gr-reporting>
</template>
<script src="../gr-diff-highlight/gr-annotation.js"></script>
<script src="gr-ranged-comment-layer.js"></script>
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 fb48b39659..2183e7df05 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,31 +17,42 @@
(function() {
'use strict';
- const HOVER_PATH_PATTERN = /^comments\.(left|right)\.\#(\d+)\.__hovering$/;
- const SPLICE_PATH_PATTERN = /^comments\.(left|right)\.splices$/;
+ const HOVER_PATH_PATTERN = /^commentRanges\.\#(\d+)\.hovering$/;
const RANGE_HIGHLIGHT = 'range';
const HOVER_HIGHLIGHT = 'rangeHighlight';
- const NORMALIZE_RANGE_EVENT = 'normalize-range';
+ /** @typedef {{side: string, range: Gerrit.Range, hovering: boolean}} */
+ Gerrit.HoveredRange;
Polymer({
is: 'gr-ranged-comment-layer',
+ _legacyUndefinedCheck: true,
+
+ /**
+ * Fired when the range in a range comment was malformed and had to be
+ * normalized.
+ *
+ * It's `detail` has a `lineNum` and `side` parameter.
+ *
+ * @event normalize-range
+ */
properties: {
- comments: Object,
+ /** @type {!Array<!Gerrit.HoveredRange>} */
+ commentRanges: Array,
_listeners: {
type: Array,
value() { return []; },
},
- _commentMap: {
+ _rangesMap: {
type: Object,
- value() { return {left: [], right: []}; },
+ value() { return {left: {}, right: {}}; },
},
},
observers: [
- '_handleCommentChange(comments.*)',
+ '_handleCommentRangesChange(commentRanges.*)',
],
/**
@@ -49,9 +60,10 @@
*
* @param {!HTMLElement} el The DIV.contentText element to apply the
* annotation to.
+ * @param {!HTMLElement} lineNumberEl
* @param {!Object} line The line object. (GrDiffLine)
*/
- annotate(el, line) {
+ annotate(el, lineNumberEl, line) {
let ranges = [];
if (line.type === GrDiffLine.Type.REMOVE || (
line.type === GrDiffLine.Type.BOTH &&
@@ -96,108 +108,92 @@
},
/**
- * Handle change in the comments by updating the comment maps and by
+ * Handle change in the ranges by updating the ranges maps and by
* emitting appropriate update notifications.
*
* @param {Object} record The change record.
*/
- _handleCommentChange(record) {
- if (!record.path) { return; }
+ _handleCommentRangesChange(record) {
+ if (!record) return;
// If the entire set of comments was changed.
- if (record.path === 'comments') {
- this._commentMap.left = this._computeCommentMap(this.comments.left);
- this._commentMap.right = this._computeCommentMap(this.comments.right);
- return;
+ if (record.path === 'commentRanges') {
+ this._rangesMap = {left: {}, right: {}};
+ for (const {side, range, hovering} of record.value) {
+ this._updateRangesMap(
+ side, range, hovering, (forLine, start, end, hovering) => {
+ forLine.push({start, end, hovering});
+ });
+ }
}
// If the change only changed the `hovering` property of a comment.
- let match = record.path.match(HOVER_PATH_PATTERN);
- let side;
-
+ const match = record.path.match(HOVER_PATH_PATTERN);
if (match) {
- side = match[1];
- const index = match[2];
- const comment = this.comments[side][index];
- if (comment && comment.range) {
- this._commentMap[side] = this._computeCommentMap(this.comments[side]);
- this._notifyUpdateRange(
- comment.range.start_line, comment.range.end_line, side);
- }
- return;
+ const commentRangesIndex = match[1];
+ const {side, range, hovering} = this.commentRanges[commentRangesIndex];
+ this._updateRangesMap(
+ side, range, hovering, (forLine, start, end, hovering) => {
+ const index = forLine.findIndex(lineRange =>
+ lineRange.start === start && lineRange.end === end);
+ forLine[index].hovering = hovering;
+ });
}
// If comments were spliced in or out.
- match = record.path.match(SPLICE_PATH_PATTERN);
- if (match) {
- side = match[1];
- this._commentMap[side] = this._computeCommentMap(this.comments[side]);
- this._handleCommentSplice(record.value, side);
- }
- },
-
- /**
- * Take a list of comments and return a sparse list mapping line numbers to
- * partial ranges. Uses an end-character-index of -1 to indicate the end of
- * the line.
- *
- * @param {?} commentList The list of comments.
- * Getting this param to match closure requirements caused problems.
- * @return {!Object} The sparse list.
- */
- _computeCommentMap(commentList) {
- const result = {};
- for (const comment of commentList) {
- if (!comment.range) { continue; }
- const range = comment.range;
- for (let line = range.start_line; line <= range.end_line; line++) {
- if (!result[line]) { result[line] = []; }
- result[line].push({
- comment,
- start: line === range.start_line ? range.start_character : 0,
- end: line === range.end_line ? range.end_character : -1,
- });
+ if (record.path === 'commentRanges.splices') {
+ for (const indexSplice of record.value.indexSplices) {
+ const removed = indexSplice.removed;
+ for (const {side, range, hovering} of removed) {
+ this._updateRangesMap(
+ side, range, hovering, (forLine, start, end) => {
+ const index = forLine.findIndex(lineRange =>
+ lineRange.start === start && lineRange.end === end);
+ forLine.splice(index, 1);
+ });
+ }
+ const added = indexSplice.object.slice(
+ indexSplice.index, indexSplice.index + indexSplice.addedCount);
+ for (const {side, range, hovering} of added) {
+ this._updateRangesMap(
+ side, range, hovering, (forLine, start, end, hovering) => {
+ forLine.push({start, end, hovering});
+ });
+ }
}
}
- return result;
},
- /**
- * Translate a splice record into range update notifications.
- */
- _handleCommentSplice(record, side) {
- if (!record || !record.indexSplices) { return; }
-
- for (const splice of record.indexSplices) {
- const ranges = splice.removed.length ?
- splice.removed.map(c => { return c.range; }) :
- [splice.object[splice.index].range];
- for (const range of ranges) {
- if (!range) { continue; }
- this._notifyUpdateRange(range.start_line, range.end_line, side);
- }
+ _updateRangesMap(side, range, hovering, operation) {
+ const forSide = this._rangesMap[side] || (this._rangesMap[side] = {});
+ for (let line = range.start_line; line <= range.end_line; line++) {
+ const forLine = forSide[line] || (forSide[line] = []);
+ const start = line === range.start_line ? range.start_character : 0;
+ const end = line === range.end_line ? range.end_character : -1;
+ operation(forLine, start, end, hovering);
}
+ this._notifyUpdateRange(range.start_line, range.end_line, side);
},
_getRangesForLine(line, side) {
const lineNum = side === 'left' ? line.beforeNumber : line.afterNumber;
- const ranges = this.get(['_commentMap', side, lineNum]) || [];
+ const ranges = this.get(['_rangesMap', side, lineNum]) || [];
return ranges
.map(range => {
- range = {
- start: range.start,
- end: range.end === -1 ? line.text.length : range.end,
- hovering: !!range.comment.__hovering,
- };
+ // Make a copy, so that the normalization below does not mess with
+ // our map.
+ range = Object.assign({}, range);
+ range.end = range.end === -1 ? line.text.length : range.end;
// Normalize invalid ranges where the start is after the end but the
// start still makes sense. Set the end to the end of the line.
// @see Issue 5744
if (range.start >= range.end && range.start < line.text.length) {
range.end = line.text.length;
- this.$.reporting.reportInteraction(NORMALIZE_RANGE_EVENT,
- 'Modified invalid comment range on l.' + lineNum +
- ' of the ' + side + ' side');
+ this.dispatchEvent(new CustomEvent('normalize-range', {
+ bubbles: true,
+ detail: {lineNum, side},
+ }));
}
return range;
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 c541e26bf6..682c02633b 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
@@ -40,62 +40,48 @@ limitations under the License.
let sandbox;
setup(() => {
- const initialComments = {
- left: [
- {
- id: '12345',
- line: 39,
- message: 'range comment',
- range: {
- end_character: 9,
- end_line: 39,
- start_character: 6,
- start_line: 36,
- },
- }, {
- id: '23456',
- line: 100,
- message: 'non range comment',
+ const initialCommentRanges = [
+ {
+ side: 'left',
+ range: {
+ end_character: 9,
+ end_line: 39,
+ start_character: 6,
+ start_line: 36,
},
- ],
- right: [
- {
- id: '34567',
- line: 10,
- message: 'range comment',
- range: {
- end_character: 22,
- end_line: 12,
- start_character: 10,
- start_line: 10,
- },
- }, {
- id: '45678',
- line: 100,
- message: 'single line range comment',
- range: {
- end_character: 15,
- end_line: 100,
- start_character: 5,
- start_line: 100,
- },
- }, {
- id: '8675309',
- line: 55,
- message: 'nonsense range',
- range: {
- end_character: 2,
- end_line: 55,
- start_character: 32,
- start_line: 55,
- },
+ },
+ {
+ side: 'right',
+ range: {
+ end_character: 22,
+ end_line: 12,
+ start_character: 10,
+ start_line: 10,
},
- ],
- };
+ },
+ {
+ side: 'right',
+ range: {
+ end_character: 15,
+ end_line: 100,
+ start_character: 5,
+ start_line: 100,
+ },
+ },
+ {
+ side: 'right',
+ range: {
+ end_character: 2,
+ end_line: 55,
+ start_character: 32,
+ start_line: 55,
+ },
+ },
+ ];
sandbox = sinon.sandbox.create();
element = fixture('basic');
- element.comments = initialComments;
+ element.commentRanges = initialCommentRanges;
});
teardown(() => {
@@ -107,6 +93,7 @@ limitations under the License.
let el;
let line;
let annotateElementStub;
+ const lineNumberEl = document.createElement('td');
setup(() => {
sandbox = sinon.sandbox.create();
@@ -125,7 +112,7 @@ limitations under the License.
line.type = GrDiffLine.Type.REMOVE;
line.beforeNumber = 40;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -136,7 +123,7 @@ limitations under the License.
const expectedStart = 6;
const expectedLength = line.text.length - expectedStart;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
const lastCall = annotateElementStub.lastCall;
@@ -149,12 +136,12 @@ limitations under the License.
test('type=Remove has-comment hovering', () => {
line.type = GrDiffLine.Type.REMOVE;
line.beforeNumber = 36;
- element.set(['comments', 'left', 0, '__hovering'], true);
+ element.set(['commentRanges', 0, 'hovering'], true);
const expectedStart = 6;
const expectedLength = line.text.length - expectedStart;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
const lastCall = annotateElementStub.lastCall;
@@ -171,7 +158,7 @@ limitations under the License.
const expectedStart = 6;
const expectedLength = line.text.length - expectedStart;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
const lastCall = annotateElementStub.lastCall;
@@ -186,7 +173,7 @@ limitations under the License.
line.beforeNumber = 36;
el.setAttribute('data-side', 'right');
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isFalse(annotateElementStub.called);
});
@@ -199,7 +186,7 @@ limitations under the License.
const expectedStart = 0;
const expectedLength = 22;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isTrue(annotateElementStub.called);
const lastCall = annotateElementStub.lastCall;
@@ -210,29 +197,18 @@ limitations under the License.
});
});
- test('_handleCommentChange overwrite', () => {
- const handlerSpy = sandbox.spy(element, '_handleCommentChange');
- const mapSpy = sandbox.spy(element, '_computeCommentMap');
-
- element.set('comments', {left: [], right: []});
-
- assert.isTrue(handlerSpy.called);
- assert.equal(mapSpy.callCount, 2);
+ test('_handleCommentRangesChange overwrite', () => {
+ element.set('commentRanges', []);
- assert.equal(Object.keys(element._commentMap.left).length, 0);
- assert.equal(Object.keys(element._commentMap.right).length, 0);
+ assert.equal(Object.keys(element._rangesMap.left).length, 0);
+ assert.equal(Object.keys(element._rangesMap.right).length, 0);
});
- test('_handleCommentChange hovering', () => {
- const handlerSpy = sandbox.spy(element, '_handleCommentChange');
- const mapSpy = sandbox.spy(element, '_computeCommentMap');
+ test('_handleCommentRangesChange hovering', () => {
const notifyStub = sinon.stub();
element.addListener(notifyStub);
- element.set(['comments', 'right', 0, '__hovering'], true);
-
- assert.isTrue(handlerSpy.called);
- assert.isTrue(mapSpy.called);
+ element.set(['commentRanges', 1, 'hovering'], true);
assert.isTrue(notifyStub.called);
const lastCall = notifyStub.lastCall;
@@ -241,16 +217,11 @@ limitations under the License.
assert.equal(lastCall.args[2], 'right');
});
- test('_handleCommentChange splice out', () => {
- const handlerSpy = sandbox.spy(element, '_handleCommentChange');
- const mapSpy = sandbox.spy(element, '_computeCommentMap');
+ test('_handleCommentRangesChange splice out', () => {
const notifyStub = sinon.stub();
element.addListener(notifyStub);
- element.splice('comments.right', 0, 1);
-
- assert.isTrue(handlerSpy.called);
- assert.isTrue(mapSpy.called);
+ element.splice('commentRanges', 1, 1);
assert.isTrue(notifyStub.called);
const lastCall = notifyStub.lastCall;
@@ -259,16 +230,12 @@ limitations under the License.
assert.equal(lastCall.args[2], 'right');
});
- test('_handleCommentChange splice in', () => {
- const handlerSpy = sandbox.spy(element, '_handleCommentChange');
- const mapSpy = sandbox.spy(element, '_computeCommentMap');
+ test('_handleCommentRangesChange splice in', () => {
const notifyStub = sinon.stub();
element.addListener(notifyStub);
- element.splice('comments.left', element.comments.left.length, 0, {
- id: '56123',
- line: 250,
- message: 'new range comment',
+ element.splice('commentRanges', 1, 0, {
+ side: 'left',
range: {
end_character: 15,
end_line: 275,
@@ -277,9 +244,6 @@ limitations under the License.
},
});
- assert.isTrue(handlerSpy.called);
- assert.isTrue(mapSpy.called);
-
assert.isTrue(notifyStub.called);
const lastCall = notifyStub.lastCall;
assert.equal(lastCall.args[0], 250);
@@ -291,48 +255,48 @@ limitations under the License.
// There is only one ranged comment on the left, but it spans ll.36-39.
const leftKeys = [];
for (let i = 36; i <= 39; i++) { leftKeys.push('' + i); }
- assert.deepEqual(Object.keys(element._commentMap.left).sort(),
+ assert.deepEqual(Object.keys(element._rangesMap.left).sort(),
leftKeys.sort());
- assert.equal(element._commentMap.left[36].length, 1);
- assert.equal(element._commentMap.left[36][0].start, 6);
- assert.equal(element._commentMap.left[36][0].end, -1);
+ assert.equal(element._rangesMap.left[36].length, 1);
+ assert.equal(element._rangesMap.left[36][0].start, 6);
+ assert.equal(element._rangesMap.left[36][0].end, -1);
- assert.equal(element._commentMap.left[37].length, 1);
- assert.equal(element._commentMap.left[37][0].start, 0);
- assert.equal(element._commentMap.left[37][0].end, -1);
+ assert.equal(element._rangesMap.left[37].length, 1);
+ assert.equal(element._rangesMap.left[37][0].start, 0);
+ assert.equal(element._rangesMap.left[37][0].end, -1);
- assert.equal(element._commentMap.left[38].length, 1);
- assert.equal(element._commentMap.left[38][0].start, 0);
- assert.equal(element._commentMap.left[38][0].end, -1);
+ assert.equal(element._rangesMap.left[38].length, 1);
+ assert.equal(element._rangesMap.left[38][0].start, 0);
+ assert.equal(element._rangesMap.left[38][0].end, -1);
- assert.equal(element._commentMap.left[39].length, 1);
- assert.equal(element._commentMap.left[39][0].start, 0);
- assert.equal(element._commentMap.left[39][0].end, 9);
+ assert.equal(element._rangesMap.left[39].length, 1);
+ assert.equal(element._rangesMap.left[39][0].start, 0);
+ assert.equal(element._rangesMap.left[39][0].end, 9);
// The right has two ranged comments, one spanning ll.10-12 and the other
// on line 100.
const rightKeys = [];
for (let i = 10; i <= 12; i++) { rightKeys.push('' + i); }
rightKeys.push('55', '100');
- assert.deepEqual(Object.keys(element._commentMap.right).sort(),
+ assert.deepEqual(Object.keys(element._rangesMap.right).sort(),
rightKeys.sort());
- assert.equal(element._commentMap.right[10].length, 1);
- assert.equal(element._commentMap.right[10][0].start, 10);
- assert.equal(element._commentMap.right[10][0].end, -1);
+ assert.equal(element._rangesMap.right[10].length, 1);
+ assert.equal(element._rangesMap.right[10][0].start, 10);
+ assert.equal(element._rangesMap.right[10][0].end, -1);
- assert.equal(element._commentMap.right[11].length, 1);
- assert.equal(element._commentMap.right[11][0].start, 0);
- assert.equal(element._commentMap.right[11][0].end, -1);
+ assert.equal(element._rangesMap.right[11].length, 1);
+ assert.equal(element._rangesMap.right[11][0].start, 0);
+ assert.equal(element._rangesMap.right[11][0].end, -1);
- assert.equal(element._commentMap.right[12].length, 1);
- assert.equal(element._commentMap.right[12][0].start, 0);
- assert.equal(element._commentMap.right[12][0].end, 22);
+ assert.equal(element._rangesMap.right[12].length, 1);
+ assert.equal(element._rangesMap.right[12][0].start, 0);
+ assert.equal(element._rangesMap.right[12][0].end, 22);
- assert.equal(element._commentMap.right[100].length, 1);
- assert.equal(element._commentMap.right[100][0].start, 5);
- assert.equal(element._commentMap.right[100][0].end, 15);
+ assert.equal(element._rangesMap.right[100].length, 1);
+ assert.equal(element._rangesMap.right[100][0].start, 5);
+ assert.equal(element._rangesMap.right[100][0].end, 15);
});
test('_getRangesForLine normalizes invalid ranges', () => {
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 6349ab663c..26bf738ee1 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,11 +19,12 @@
Polymer({
is: 'gr-selection-action-box',
+ _legacyUndefinedCheck: true,
/**
* Fired when the comment creation action was taken (hotkey, click).
*
- * @event create-comment
+ * @event create-range-comment
*/
properties: {
@@ -34,10 +35,10 @@
range: {
type: Object,
value: {
- startLine: NaN,
- startChar: NaN,
- endLine: NaN,
- endChar: NaN,
+ start_line: NaN,
+ start_character: NaN,
+ end_line: NaN,
+ end_character: NaN,
},
},
positionBelow: Boolean,
@@ -63,7 +64,7 @@
Polymer.dom.flush();
const rect = this._getTargetBoundingRect(el);
const boxRect = this.$.tooltip.getBoundingClientRect();
- const parentRect = this.parentElement.getBoundingClientRect();
+ const parentRect = this._getParentBoundingClientRect();
this.style.top =
rect.top - parentRect.top - boxRect.height - 6 + 'px';
this.style.left =
@@ -74,11 +75,18 @@
Polymer.dom.flush();
const rect = this._getTargetBoundingRect(el);
const boxRect = this.$.tooltip.getBoundingClientRect();
- const parentRect = this.parentElement.getBoundingClientRect();
+ const parentRect = this._getParentBoundingClientRect();
this.style.top =
- rect.top - parentRect.top + boxRect.height - 6 + 'px';
+ rect.top - parentRect.top + boxRect.height - 6 + 'px';
this.style.left =
- rect.left - parentRect.left + (rect.width - boxRect.width) / 2 + 'px';
+ rect.left - parentRect.left + (rect.width - boxRect.width) / 2 + 'px';
+ },
+
+ _getParentBoundingClientRect() {
+ // With native shadow DOM, the parent is the shadow root, not the gr-diff
+ // element
+ const parent = this.parentElement || this.parentNode.host;
+ return parent.getBoundingClientRect();
},
_getTargetBoundingRect(el) {
@@ -110,7 +118,7 @@
},
_fireCreateComment() {
- this.fire('create-comment', {side: this.side, range: this.range});
+ this.fire('create-range-comment', {side: this.side, range: this.range});
},
});
})();
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 19155e4037..dece36600f 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
@@ -89,16 +89,16 @@ limitations under the License.
test('event fired contains playload', () => {
const side = 'left';
const range = {
- startLine: 1,
- startChar: 11,
- endLine: 2,
- endChar: 42,
+ start_line: 1,
+ start_character: 11,
+ end_line: 2,
+ end_character: 42,
};
element.side = 'left';
element.range = range;
MockInteractions.pressAndReleaseKeyOn(document.body, 67, null, 'c');
assert(element.fire.calledWithExactly(
- 'create-comment',
+ 'create-range-comment',
{
side,
range,
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 017cd5d5b5..67c32bb641 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
@@ -21,6 +21,7 @@ limitations under the License.
<template>
<gr-lib-loader id="libLoader"></gr-lib-loader>
</template>
+ <script src="../../../scripts/util.js"></script>
<script src="../gr-diff/gr-diff-line.js"></script>
<script src="../gr-diff-highlight/gr-annotation.js"></script>
<script src="gr-syntax-layer.js"></script>
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 c11fe451eb..944fa494d2 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
@@ -86,6 +86,7 @@
'text/x-swift': 'swift',
'text/x-systemverilog': 'sv',
'text/x-tcl': 'tcl',
+ 'text/x-torque': 'torque',
'text/x-twig': 'twig',
'text/x-vb': 'vb',
'text/x-verilog': 'v',
@@ -133,6 +134,7 @@
Polymer({
is: 'gr-syntax-layer',
+ _legacyUndefinedCheck: true,
properties: {
diff: {
@@ -159,6 +161,17 @@
},
/** @type {?number} */
_processHandle: Number,
+ /**
+ * The promise last returned from `process()` while the asynchronous
+ * processing is running - `null` otherwise. Provides a `cancel()`
+ * method that rejects it with `{isCancelled: true}`.
+ *
+ * @type {?Object}
+ */
+ _processPromise: {
+ type: Object,
+ value: null,
+ },
_hljs: Object,
},
@@ -171,9 +184,10 @@
* for the given line.
*
* @param {!HTMLElement} el
+ * @param {!HTMLElement} lineNumberEl
* @param {!Object} line (GrDiffLine)
*/
- annotate(el, line) {
+ annotate(el, lineNumberEl, line) {
if (!this.enabled) { return; }
// Determine the side.
@@ -203,6 +217,13 @@
}
},
+ _getLanguage(diffFileMetaInfo) {
+ // The Gerrit API provides only content-type, but for other users of
+ // gr-diff it may be more convenient to specify the language directly.
+ return diffFileMetaInfo.language ||
+ LANGUAGE_MAP[diffFileMetaInfo.content_type];
+ },
+
/**
* Start processing symtax for the loaded diff and notify layer listeners
* as syntax info comes online.
@@ -210,6 +231,10 @@
* @return {Promise}
*/
process() {
+ // Cancel any still running process() calls, because they append to the
+ // same _baseRanges and _revisionRanges fields.
+ this.cancel();
+
// Discard existing ranges.
this._baseRanges = [];
this._revisionRanges = [];
@@ -218,13 +243,11 @@
return Promise.resolve();
}
- this.cancel();
-
if (this.diff.meta_a) {
- this._baseLanguage = LANGUAGE_MAP[this.diff.meta_a.content_type];
+ this._baseLanguage = this._getLanguage(this.diff.meta_a);
}
if (this.diff.meta_b) {
- this._revisionLanguage = LANGUAGE_MAP[this.diff.meta_b.content_type];
+ this._revisionLanguage = this._getLanguage(this.diff.meta_b);
}
if (!this._baseLanguage && !this._revisionLanguage) {
return Promise.resolve();
@@ -239,49 +262,55 @@
lastNotify: {left: 1, right: 1},
};
- return this._loadHLJS().then(() => {
- return new Promise(resolve => {
- const nextStep = () => {
- this._processHandle = null;
- this._processNextLine(state);
-
- // Move to the next line in the section.
- state.lineIndex++;
-
- // If the section has been exhausted, move to the next one.
- if (this._isSectionDone(state)) {
- state.lineIndex = 0;
- state.sectionIndex++;
- }
-
- // If all sections have been exhausted, finish.
- if (state.sectionIndex >= this.diff.content.length) {
- resolve();
- this._notify(state);
- return;
- }
-
- if (state.lineIndex % 100 === 0) {
- this._notify(state);
- this._processHandle = this.async(nextStep, ASYNC_DELAY);
- } else {
- nextStep.call(this);
- }
- };
-
- this._processHandle = this.async(nextStep, 1);
- });
- });
+ this._processPromise = util.makeCancelable(this._loadHLJS()
+ .then(() => {
+ return new Promise(resolve => {
+ const nextStep = () => {
+ this._processHandle = null;
+ this._processNextLine(state);
+
+ // Move to the next line in the section.
+ state.lineIndex++;
+
+ // If the section has been exhausted, move to the next one.
+ if (this._isSectionDone(state)) {
+ state.lineIndex = 0;
+ state.sectionIndex++;
+ }
+
+ // If all sections have been exhausted, finish.
+ if (state.sectionIndex >= this.diff.content.length) {
+ resolve();
+ this._notify(state);
+ return;
+ }
+
+ if (state.lineIndex % 100 === 0) {
+ this._notify(state);
+ this._processHandle = this.async(nextStep, ASYNC_DELAY);
+ } else {
+ nextStep.call(this);
+ }
+ };
+
+ this._processHandle = this.async(nextStep, 1);
+ });
+ }));
+ return this._processPromise
+ .finally(() => { this._processPromise = null; });
},
/**
* Cancel any asynchronous syntax processing jobs.
*/
cancel() {
- if (this._processHandle) {
+ if (this._processHandle != null) {
this.cancelAsync(this._processHandle);
this._processHandle = null;
}
+ if (this._processPromise) {
+ this._processPromise.cancel();
+ }
},
_diffChanged() {
@@ -357,7 +386,8 @@
// To store the result of the syntax highlighter.
let result;
- if (this._baseLanguage && baseLine !== undefined) {
+ if (this._baseLanguage && baseLine !== undefined &&
+ this._hljs.getLanguage(this._baseLanguage)) {
baseLine = this._workaround(this._baseLanguage, baseLine);
result = this._hljs.highlight(this._baseLanguage, baseLine, true,
state.baseContext);
@@ -365,7 +395,8 @@
state.baseContext = result.top;
}
- if (this._revisionLanguage && revisionLine !== undefined) {
+ if (this._revisionLanguage && revisionLine !== undefined &&
+ this._hljs.getLanguage(this._revisionLanguage)) {
revisionLine = this._workaround(this._revisionLanguage, revisionLine);
result = this._hljs.highlight(this._revisionLanguage, revisionLine,
true, state.revisionContext);
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 f2458fcbd1..b63675aafb 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
@@ -38,6 +38,7 @@ limitations under the License.
let sandbox;
let diff;
let element;
+ const lineNumberEl = document.createElement('td');
function getMockHLJS() {
const html = '<span class="gr-diff gr-syntax gr-syntax-string">' +
@@ -50,6 +51,11 @@ limitations under the License.
top: state === undefined ? 1 : state + 1,
};
},
+ // Return something truthy because this method is used to check if the
+ // language is supported.
+ getLanguage(s) {
+ return {};
+ },
};
}
@@ -72,7 +78,7 @@ limitations under the License.
const line = new GrDiffLine(GrDiffLine.Type.REMOVE);
line.beforeNumber = 12;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isFalse(annotationSpy.called);
});
@@ -94,7 +100,7 @@ limitations under the License.
className,
}];
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isTrue(annotationSpy.called);
assert.equal(annotationSpy.lastCall.args[0], el);
@@ -122,7 +128,7 @@ limitations under the License.
}];
element.enabled = false;
- element.annotate(el, line);
+ element.annotate(el, lineNumberEl, line);
assert.isFalse(annotationSpy.called);
});
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 f850b9d2bb..995326f576 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,6 +19,7 @@
Polymer({
is: 'gr-documentation-search',
+ _legacyUndefinedCheck: true,
properties: {
/**
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 168ded84f5..01cd9df808 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,6 +19,7 @@
Polymer({
is: 'gr-default-editor',
+ _legacyUndefinedCheck: true,
/**
* Fired when the content of the editor changes.
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 610e9ed5af..3567d06f10 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,6 +19,7 @@
Polymer({
is: 'gr-edit-controls',
+ _legacyUndefinedCheck: true,
properties: {
change: Object,
patchNum: String,
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 82010f53f7..9407f18ca6 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,6 +19,7 @@
Polymer({
is: 'gr-edit-file-controls',
+ _legacyUndefinedCheck: true,
/**
* Fired when an action in the overflow menu is tapped.
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 f89139ba63..3ed165e6b4 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,6 +26,7 @@
Polymer({
is: 'gr-editor-view',
+ _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/gr-app-it_test.html b/polygerrit-ui/app/elements/gr-app-it_test.html
index 929156eb46..2601aebe14 100644
--- a/polygerrit-ui/app/elements/gr-app-it_test.html
+++ b/polygerrit-ui/app/elements/gr-app-it_test.html
@@ -50,7 +50,6 @@ limitations under the License.
getAccountCapabilities() { return Promise.resolve({}); },
getConfig() {
return Promise.resolve({
- gerrit: {web_uis: ['GWT', 'POLYGERRIT']},
plugin: {
js_resource_paths: [],
html_resource_paths: [
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 4fff9e6221..8c50358992 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -15,16 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
- // 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.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,
+ };
+ }
}
// Needed for JSCompiler to understand it's global.
// eslint-disable-next-line no-unused-vars, prefer-const
@@ -34,6 +36,7 @@ limitations under the License.
<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="../behaviors/safe-types-behavior/safe-types-behavior.html">
<script>
security.polymer_resin.install({
@@ -42,6 +45,7 @@ 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">
@@ -55,10 +59,10 @@ limitations under the License.
<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-message-header/gr-message-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">
@@ -156,16 +160,16 @@ limitations under the License.
<gr-main-header
id="mainHeader"
search-query="{{params.query}}"
- class$="[[_computeShadowClass(_isShadowDom)]]">
+ class$="[[_computeShadowClass(_isShadowDom)]]"
+ on-mobile-search="_mobileSearchToggle">
</gr-main-header>
</gr-fixed-panel>
- <template
- is="dom-repeat"
- items="[[_getMessages(_serverConfig)]]"
- as="message">
- <gr-message-header message="{{message}}"></gr-message-header>
- </template>
<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]]"
@@ -236,9 +240,6 @@ limitations under the License.
rel="noopener"
target="_blank">Send feedback</a> |
</template>
- <template is="dom-if" if="[[_computeShowGwtUiLink(_serverConfig)]]">
- <a id="gwtLink" href$="[[computeGwtUrl(_path)]]" rel="external">Switch to Old UI</a> |
- </template>
Press &ldquo;?&rdquo; for keyboard shortcuts
<gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
</div>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index ea7e86dc0b..b89d90ede7 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -20,12 +20,14 @@
// Eagerly render Polymer components when backgrounded. (Skips
// requestAnimationFrame.)
// @see https://github.com/Polymer/polymer/issues/3851
- // TODO: Reassess after Polymer 2.0 upgrade.
// @see Issue 4699
- Polymer.RenderStatus._makeReady();
+ if (!window.POLYMER2) {
+ Polymer.RenderStatus._makeReady();
+ }
Polymer({
is: 'gr-app',
+ _legacyUndefinedCheck: true,
/**
* Fired when the URL location changes.
@@ -87,6 +89,11 @@
},
_settingsUrl: String,
_feedbackUrl: String,
+ // Used to allow searching on mobile
+ mobileSearch: {
+ type: Boolean,
+ value: false,
+ },
},
listeners: {
@@ -334,15 +341,6 @@
this.$.header.unfloat();
},
- _computeShowGwtUiLink(config) {
- return !window.DEPRECATE_GWT_UI &&
- config.gerrit.web_uis && config.gerrit.web_uis.includes('GWT');
- },
-
- _getMessages(config) {
- return config.messages ? config.messages : [];
- },
-
_handlePageError(e) {
const props = [
'_showChangeListView',
@@ -470,5 +468,10 @@
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 734d2fe96c..71ceab4750 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -50,7 +50,6 @@ limitations under the License.
getAccountCapabilities() { return Promise.resolve({}); },
getConfig() {
return Promise.resolve({
- gerrit: {web_uis: ['GWT', 'POLYGERRIT']},
plugin: {},
});
},
@@ -73,35 +72,6 @@ limitations under the License.
assert.isTrue(element.$.reporting.appStarted.calledOnce);
});
- test('location change updates gwt footer', done => {
- element._path = '/test/path';
- flush(() => {
- const gwtLink = element.$$('#gwtLink');
- assert.equal(gwtLink.href, 'http://' + location.host +
- element.getBaseUrl() + '/?polygerrit=0#/test/path');
- done();
- });
- });
-
- test('_handleLocationChange handles hashes', done => {
- const curLocation = {
- pathname: '/c/1/1/testfile.txt',
- hash: '#2',
- host: location.host,
- };
- element._handleLocationChange({detail: curLocation});
-
- flush(() => {
- const gwtLink = element.$$('#gwtLink');
- assert.equal(
- gwtLink.href,
- 'http://' + location.host + element.getBaseUrl() +
- '/?polygerrit=0#/c/1/1/testfile.txt@2'
- );
- done();
- });
- });
-
test('passes config to gr-plugin-host', () => {
return element.$.restAPI.getConfig.lastCall.returnValue.then(config => {
assert.deepEqual(element.$.plugins.config, config);
diff --git a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api.js b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api.js
index 39591866ac..d1f8e5693a 100644
--- a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api.js
+++ b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api.js
@@ -30,8 +30,8 @@
* @param {string} text
* @param {string} url
*/
- GrAdminApi.prototype.addMenuLink = function(text, url) {
- this._menuLinks.push({text, url});
+ GrAdminApi.prototype.addMenuLink = function(text, url, opt_capability) {
+ this._menuLinks.push({text, url, capability: opt_capability || null});
};
GrAdminApi.prototype.getMenuLinks = function() {
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 966efac2d2..159f50a603 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
@@ -54,7 +54,15 @@ limitations under the License.
adminApi.addMenuLink('text', 'url');
const links = adminApi.getMenuLinks();
assert.equal(links.length, 1);
- assert.deepEqual(links[0], {text: 'text', url: 'url'});
+ assert.deepEqual(links[0], {text: 'text', url: 'url', capability: null});
+ });
+
+ test('addMenuLinkWithCapability', () => {
+ adminApi.addMenuLink('text', 'url', 'capability');
+ const links = adminApi.getMenuLinks();
+ assert.equal(links.length, 1);
+ assert.deepEqual(links[0],
+ {text: 'text', url: 'url', capability: 'capability'});
});
});
</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 cd59f7d026..86238cf96f 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
@@ -30,6 +30,7 @@ limitations under the License.
<script>
Polymer({
is: 'some-element',
+ _legacyUndefinedCheck: true,
properties: {
fooBar: {
type: Object,
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 2d07382b1f..c6d3e0f764 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
@@ -27,7 +27,12 @@
if (opt_moduleName) {
return endpointName + ' ' + opt_moduleName;
} else {
- return this._plugin.getPluginName() + '-autogenerated-' + endpointName;
+ // lowercase in case plugin's name contains uppercase letters
+ // TODO: this still can not prevent if plugin has invalid char
+ // other than uppercase, but is the first step
+ // https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+ const pluginName = this._plugin.getPluginName() || 'unknown_plugin';
+ return pluginName.toLowerCase() + '-autogenerated-' + endpointName;
}
};
@@ -54,6 +59,7 @@
GrDomHook.prototype._createPlaceholder = function(hookName) {
Polymer({
is: hookName,
+ _legacyUndefinedCheck: true,
properties: {
plugin: Object,
content: Object,
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 fdd0e6e08c..f3d6eb4ac0 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,6 +21,7 @@
Polymer({
is: 'gr-endpoint-decorator',
+ _legacyUndefinedCheck: true,
properties: {
name: String,
@@ -73,7 +74,9 @@
},
_getEndpointParams() {
- return Polymer.dom(this).querySelectorAll('gr-endpoint-param');
+ // 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-param/gr-endpoint-param.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
index cbc3d6aeee..e21fc7294e 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,6 +19,7 @@
Polymer({
is: 'gr-endpoint-param',
+ _legacyUndefinedCheck: true,
properties: {
name: String,
value: {
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 709c0428fd..47274f6297 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
@@ -30,6 +30,7 @@ limitations under the License.
<script>
Polymer({
is: 'some-element',
+ _legacyUndefinedCheck: true,
properties: {
fooBar: {
type: Object,
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 0e8bb459e0..7924e27ca6 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,6 +19,7 @@
Polymer({
is: 'gr-external-style',
+ _legacyUndefinedCheck: true,
properties: {
name: String,
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 dcd9049eec..65f2207689 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,6 +19,7 @@
Polymer({
is: 'gr-plugin-host',
+ _legacyUndefinedCheck: true,
properties: {
config: {
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 dd37f846d4..3ef93e4467 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
@@ -18,6 +18,7 @@
'use strict';
Polymer({
is: 'gr-plugin-popup',
+ _legacyUndefinedCheck: true,
get opened() {
return this.$.overlay.opened;
},
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 e47ba157eb..c9486aee6a 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
@@ -25,6 +25,7 @@ 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-theme-api/gr-custom-plugin-header.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
index e6e8fa559e..496d0e71df 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
@@ -37,6 +37,7 @@ 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/settings/gr-account-info/gr-account-info.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
index cc32690af1..51a22fc32d 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,6 +19,7 @@
Polymer({
is: 'gr-account-info',
+ _legacyUndefinedCheck: true,
/**
* Fired when account details are changed.
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 41595a9832..fe36a86014 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,6 +19,7 @@
Polymer({
is: 'gr-agreements-list',
+ _legacyUndefinedCheck: true,
properties: {
_agreements: Array,
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 1ed91b806c..d660ee58bb 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,6 +19,7 @@
Polymer({
is: 'gr-change-table-editor',
+ _legacyUndefinedCheck: true,
properties: {
displayedColumns: {
@@ -42,8 +43,9 @@
* @return {!Array<string>}
*/
_getDisplayedColumns() {
- return Polymer.dom(this.root)
- .querySelectorAll('.checkboxContainer input:not([name=number])')
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ return Array.from(Polymer.dom(this.root)
+ .querySelectorAll('.checkboxContainer input:not([name=number])'))
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.name);
},
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 ae534899f9..2a931558a0 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,6 +19,7 @@
Polymer({
is: 'gr-cla-view',
+ _legacyUndefinedCheck: true,
properties: {
_groups: Object,
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 86350f9af1..37bce08614 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,6 +19,7 @@
Polymer({
is: 'gr-edit-preferences',
+ _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
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 d08cc90a90..71d75cc3ea 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,6 +19,7 @@
Polymer({
is: 'gr-email-editor',
+ _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
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 890061e87b..a50509cbda 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,6 +19,7 @@
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 c29bfe2d56..aa53aca1bd 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
@@ -169,7 +169,7 @@ limitations under the License.
const newKeyString = 'not even close to valid';
const addStub = sinon.stub(element.$.restAPI, 'addAccountGPGKey',
- () => { return Promise.reject(); });
+ () => { return Promise.reject(new Error('error')); });
element._newKey = newKeyString;
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 0f43563a72..4de24aa193 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,6 +19,7 @@
Polymer({
is: 'gr-group-list',
+ _legacyUndefinedCheck: true,
properties: {
_groups: Array,
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 003e47105f..99f4504f13 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,6 +19,7 @@
Polymer({
is: 'gr-http-password',
+ _legacyUndefinedCheck: true,
properties: {
_username: String,
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 c927f1ee24..4560a2e3b8 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
@@ -24,6 +24,7 @@
Polymer({
is: 'gr-identities',
+ _legacyUndefinedCheck: true,
properties: {
_identities: Object,
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 aa83bf7de6..8587338dd7 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,6 +19,7 @@
Polymer({
is: 'gr-menu-editor',
+ _legacyUndefinedCheck: true,
properties: {
menuItems: Array,
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 c6cd578ea6..6b4ee18559 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,6 +19,7 @@
Polymer({
is: 'gr-registration-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when account details are changed.
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 b61c7e0b6b..30a3801a49 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
@@ -18,13 +18,13 @@ limitations under the License.
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<dom-module id="gr-settings-item">
- <style>
- :host {
- display: block;
- margin-bottom: 2em;
- }
- </style>
<template>
+ <style>
+ :host {
+ display: block;
+ margin-bottom: 2em;
+ }
+ </style>
<h2 id="[[anchor]]">[[title]]</h2>
<slot></slot>
</template>
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 0ee1b28f9e..dc1aa936ca 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,6 +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 3b47190240..f64d898c4d 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
@@ -19,9 +19,9 @@ limitations under the License.
<link rel="import" href="../../../styles/gr-page-nav-styles.html">
<dom-module id="gr-settings-menu-item">
- <style include="shared-styles"></style>
- <style include="gr-page-nav-styles"></style>
<template>
+ <style include="shared-styles"></style>
+ <style include="gr-page-nav-styles"></style>
<div class="navStyles">
<li><a href$="[[href]]">[[title]]</a></li>
</div>
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 38147cd91f..2a56b0935f 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,6 +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 db4d31a8a7..f0c4d6ed15 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
@@ -55,6 +55,9 @@ limitations under the License.
#email {
margin-bottom: 1em;
}
+ main section.darkToggle {
+ display: block;
+ }
.filters p,
.darkToggle p {
margin-bottom: 1em;
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 23808ff16f..a66c38d8de 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,6 +47,7 @@
Polymer({
is: 'gr-settings-view',
+ _legacyUndefinedCheck: true,
/**
* Fired when the title of the page should change.
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 874173a4a7..4c423e8920 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,6 +19,7 @@
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 1a884c9250..8ed07309aa 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
@@ -155,7 +155,7 @@ limitations under the License.
const newKeyString = 'not even close to valid';
const addStub = sinon.stub(element.$.restAPI, 'addAccountSSHKey',
- () => { return Promise.reject(); });
+ () => { return Promise.reject(new Error('error')); });
element._newKey = newKeyString;
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 e751ea3d7a..bd18456692 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,6 +27,7 @@
Polymer({
is: 'gr-watched-projects-editor',
+ _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
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 880cbc04df..827e33b527 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,6 +20,7 @@
Polymer({
is: 'gr-account-chip',
+ _legacyUndefinedCheck: true,
/**
* Fired to indicate a key was pressed while this chip was focused.
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 5b1b975433..7983fad80e 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,6 +19,7 @@
Polymer({
is: 'gr-account-label',
+ _legacyUndefinedCheck: true,
properties: {
/**
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 faaf9c3ddd..03967f10ea 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,6 +19,7 @@
Polymer({
is: 'gr-account-link',
+ _legacyUndefinedCheck: true,
properties: {
additionalText: String,
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 e7c8b2ce97..ec7b6eb965 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-alert',
+ _legacyUndefinedCheck: true,
/**
* Fired when the action button is pressed.
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 2e55010ec2..1af629d246 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,6 +19,7 @@
Polymer({
is: 'gr-autocomplete-dropdown',
+ _legacyUndefinedCheck: true,
/**
* Fired when the dropdown is closed.
@@ -162,7 +163,9 @@
_resetCursorStops() {
if (this.suggestions.length > 0) {
Polymer.dom.flush();
- this._suggestionEls = this.$.suggestions.querySelectorAll('li');
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ this._suggestionEls = Array.from(
+ this.$.suggestions.querySelectorAll('li'));
} else {
this._suggestionEls = [];
}
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 4b447d18d0..a878174469 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -29,7 +29,7 @@ limitations under the License.
display: none;
}
.searchIcon.showSearchIcon {
- display: initial;
+ display: inline-block;
}
iron-icon {
margin: 0 .25em;
@@ -68,7 +68,6 @@ limitations under the License.
no-label-float
id="input"
class$="[[_computeClass(borderless)]]"
- is="iron-input"
disabled$="[[disabled]]"
value="{{text}}"
placeholder="[[placeholder]]"
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 04815efdf8..9d16b9c63e 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -22,6 +22,7 @@
Polymer({
is: 'gr-autocomplete',
+ _legacyUndefinedCheck: true,
/**
* Fired when a value is chosen.
@@ -210,7 +211,8 @@
},
get _inputElement() {
- return this.$.input;
+ // Polymer2: this.$ can be undefined when this is first evaluated.
+ return this.$ && this.$.input;
},
/**
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 bf563823ac..2435e587bc 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-avatar',
+ _legacyUndefinedCheck: true,
properties: {
account: {
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 fe18180ccf..cdf617fbbd 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -94,7 +94,9 @@ limitations under the License.
}
/* Styles for the optional down arrow */
- :host:not([down-arrow]) .downArrow {display: none; }
+ :host(:not([down-arrow])) .downArrow {
+ display: none;
+ }
:host([down-arrow]) .downArrow {
border-top: .36em solid #ccc;
border-left: .36em solid transparent;
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 ca6705e44c..5988cdec88 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-button',
+ _legacyUndefinedCheck: true,
properties: {
tooltip: String,
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 3c46d1b464..44f8c00dbe 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,6 +19,7 @@
Polymer({
is: 'gr-change-star',
+ _legacyUndefinedCheck: true,
/**
* Fired when star state is toggled.
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 e256bf375f..99ddff1cc0 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
@@ -68,7 +68,7 @@ limitations under the License.
background-color: transparent;
padding: .1em;
}
- :host:not([flat]) .chip {
+ :host(:not([flat])) .chip {
color: white;
}
</style>
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 8efd3090a7..70c5d729a2 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
@@ -26,14 +26,15 @@
};
const WIP_TOOLTIP = 'This change isn\'t ready to be reviewed or submitted. ' +
- 'It will not appear in dashboards, and email notifications will be ' +
- 'silenced until the review is started.';
+ '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 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: {
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
new file mode 100644
index 0000000000..8c80b379a2
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.html
@@ -0,0 +1,126 @@
+<!--
+@license
+Copyright (C) 2015 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.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">
+<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-storage/gr-storage.html">
+<link rel="import" href="../gr-comment/gr-comment.html">
+
+<dom-module id="gr-comment-thread">
+ <template>
+ <style include="shared-styles">
+ gr-button {
+ margin-left: .5em;
+ }
+ #actions {
+ margin-left: auto;
+ padding: .5em .7em;
+ }
+ #container {
+ background-color: var(--comment-background-color);
+ border: 1px solid var(--border-color);
+ color: var(--comment-text-color);
+ display: block;
+ margin-bottom: 1px;
+ white-space: normal;
+ }
+ #container.unresolved {
+ background-color: var(--unresolved-comment-background-color);
+ }
+ #commentInfoContainer {
+ border-top: 1px dotted var(--border-color);
+ display: flex;
+ }
+ #unresolvedLabel {
+ font-family: var(--font-family);
+ margin: auto 0;
+ padding: .5em .7em;
+ }
+ .pathInfo {
+ display: flex;
+ align-items: baseline;
+ }
+ .descriptionText {
+ margin-left: .5rem;
+ font-style: italic;
+ }
+ </style>
+ <template is="dom-if" if="[[showFilePath]]">
+ <div class="pathInfo">
+ <a href$="[[_getDiffUrlForComment(projectName, changeNum, path, patchNum)]]">[[_computeDisplayPath(path)]]</a>
+ <span class="descriptionText">Patchset [[patchNum]]</span>
+ </div>
+ </template>
+ <div id="container" class$="[[_computeHostClass(unresolved)]]">
+ <template id="commentList" is="dom-repeat" items="[[_orderedComments]]"
+ as="comment">
+ <gr-comment
+ comment="{{comment}}"
+ robot-button-disabled="[[_hideActions(_showActions, _lastComment)]]"
+ change-num="[[changeNum]]"
+ patch-num="[[patchNum]]"
+ draft="[[_isDraft(comment)]]"
+ show-actions="[[_showActions]]"
+ comment-side="[[comment.__commentSide]]"
+ side="[[comment.side]]"
+ root-id="[[rootId]]"
+ project-config="[[_projectConfig]]"
+ on-create-fix-comment="_handleCommentFix"
+ on-comment-discard="_handleCommentDiscard"
+ on-comment-save="_handleCommentSavedOrDiscarded"></gr-comment>
+ </template>
+ <div id="commentInfoContainer"
+ hidden$="[[_hideActions(_showActions, _lastComment)]]">
+ <span id="unresolvedLabel" hidden$="[[!unresolved]]">Unresolved</span>
+ <div id="actions">
+ <gr-button
+ id="replyBtn"
+ link
+ secondary
+ class="action reply"
+ on-tap="_handleCommentReply">Reply</gr-button>
+ <gr-button
+ id="quoteBtn"
+ link
+ secondary
+ class="action quote"
+ on-tap="_handleCommentQuote">Quote</gr-button>
+ <gr-button
+ id="ackBtn"
+ link
+ secondary
+ class="action ack"
+ on-tap="_handleCommentAck">Ack</gr-button>
+ <gr-button
+ id="doneBtn"
+ link
+ secondary
+ class="action done"
+ on-tap="_handleCommentDone">Done</gr-button>
+ </div>
+ </div>
+ </div>
+ <gr-reporting id="reporting"></gr-reporting>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ <gr-storage id="storage"></gr-storage>
+ </template>
+ <script src="gr-comment-thread.js"></script>
+</dom-module>
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
new file mode 100644
index 0000000000..0b4f80604d
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
@@ -0,0 +1,495 @@
+/**
+ * @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 UNRESOLVED_EXPAND_COUNT = 5;
+ const NEWLINE_PATTERN = /\n/g;
+
+ Polymer({
+ is: 'gr-comment-thread',
+ _legacyUndefinedCheck: true,
+
+ /**
+ * Fired when the thread should be discarded.
+ *
+ * @event thread-discard
+ */
+
+ /**
+ * Fired when a comment in the thread is permanently modified.
+ *
+ * @event thread-changed
+ */
+
+ /**
+ * gr-comment-thread exposes the following attributes that allow a
+ * diff widget like gr-diff to show the thread in the right location:
+ *
+ * line-num:
+ * 1-based line number or undefined if it refers to the entire file.
+ *
+ * comment-side:
+ * "left" or "right". These indicate which of the two diffed versions
+ * the comment relates to. In the case of unified diff, the left
+ * version is the one whose line number column is further to the left.
+ *
+ * range:
+ * The range of text that the comment refers to (start_line,
+ * start_character, end_line, end_character), serialized as JSON. If
+ * set, range's end_line will have the same value as line-num. Line
+ * numbers are 1-based, char numbers are 0-based. The start position
+ * (start_line, start_character) is inclusive, and the end position
+ * (end_line, end_character) is exclusive.
+ */
+ properties: {
+ changeNum: String,
+ comments: {
+ type: Array,
+ value() { return []; },
+ },
+ /**
+ * @type {?{start_line: number, start_character: number, end_line: number,
+ * end_character: number}}
+ */
+ range: {
+ type: Object,
+ reflectToAttribute: true,
+ },
+ keyEventTarget: {
+ type: Object,
+ value() { return document.body; },
+ },
+ commentSide: {
+ type: String,
+ reflectToAttribute: true,
+ },
+ patchNum: String,
+ path: String,
+ projectName: {
+ type: String,
+ observer: '_projectNameChanged',
+ },
+ hasDraft: {
+ type: Boolean,
+ notify: true,
+ reflectToAttribute: true,
+ },
+ isOnParent: {
+ type: Boolean,
+ value: false,
+ },
+ parentIndex: {
+ type: Number,
+ value: null,
+ },
+ rootId: {
+ type: String,
+ notify: true,
+ computed: '_computeRootId(comments.*)',
+ },
+ /**
+ * If this is true, the comment thread also needs to have the change and
+ * line properties property set
+ */
+ showFilePath: {
+ type: Boolean,
+ value: false,
+ },
+ /** Necessary only if showFilePath is true or when used with gr-diff */
+ lineNum: {
+ type: Number,
+ reflectToAttribute: true,
+ },
+ unresolved: {
+ type: Boolean,
+ notify: true,
+ reflectToAttribute: true,
+ },
+ _showActions: Boolean,
+ _lastComment: Object,
+ _orderedComments: Array,
+ _projectConfig: Object,
+ },
+
+ behaviors: [
+ Gerrit.KeyboardShortcutBehavior,
+ Gerrit.PathListBehavior,
+ ],
+
+ listeners: {
+ 'comment-update': '_handleCommentUpdate',
+ },
+
+ observers: [
+ '_commentsChanged(comments.*)',
+ ],
+
+ keyBindings: {
+ 'e shift+e': '_handleEKey',
+ },
+
+ attached() {
+ this._getLoggedIn().then(loggedIn => {
+ this._showActions = loggedIn;
+ });
+ this._setInitialExpandedState();
+ },
+
+ addOrEditDraft(opt_lineNum, opt_range) {
+ const lastComment = this.comments[this.comments.length - 1] || {};
+ if (lastComment.__draft) {
+ const commentEl = this._commentElWithDraftID(
+ lastComment.id || lastComment.__draftID);
+ commentEl.editing = true;
+
+ // If the comment was collapsed, re-open it to make it clear which
+ // actions are available.
+ commentEl.collapsed = false;
+ } else {
+ const range = opt_range ? opt_range :
+ lastComment ? lastComment.range : undefined;
+ const unresolved = lastComment ? lastComment.unresolved : undefined;
+ this.addDraft(opt_lineNum, range, unresolved);
+ }
+ },
+
+ addDraft(opt_lineNum, opt_range, opt_unresolved) {
+ const draft = this._newDraft(opt_lineNum, opt_range);
+ draft.__editing = true;
+ draft.unresolved = opt_unresolved === false ? opt_unresolved : true;
+ this.push('comments', draft);
+ },
+
+ fireRemoveSelf() {
+ this.dispatchEvent(new CustomEvent('thread-discard',
+ {detail: {rootId: this.rootId}, bubbles: false}));
+ },
+
+ _getDiffUrlForComment(projectName, changeNum, path, patchNum) {
+ return Gerrit.Nav.getUrlForDiffById(changeNum,
+ projectName, path, patchNum,
+ null, this.lineNum);
+ },
+
+ _computeDisplayPath(path) {
+ const lineString = this.lineNum ? `#${this.lineNum}` : '';
+ return this.computeDisplayPath(path) + lineString;
+ },
+
+ _getLoggedIn() {
+ return this.$.restAPI.getLoggedIn();
+ },
+
+ _commentsChanged() {
+ this._orderedComments = this._sortedComments(this.comments);
+ this.updateThreadProperties();
+ },
+
+ updateThreadProperties() {
+ if (this._orderedComments.length) {
+ this._lastComment = this._getLastComment();
+ this.unresolved = this._lastComment.unresolved;
+ this.hasDraft = this._lastComment.__draft;
+ }
+ },
+
+ _hideActions(_showActions, _lastComment) {
+ return !_showActions || !_lastComment || !!_lastComment.__draft;
+ },
+
+ _getLastComment() {
+ return this._orderedComments[this._orderedComments.length - 1] || {};
+ },
+
+ _handleEKey(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+
+ // Don’t preventDefault in this case because it will render the event
+ // useless for other handlers (other gr-comment-thread elements).
+ if (e.detail.keyboardEvent.shiftKey) {
+ this._expandCollapseComments(true);
+ } else {
+ if (this.modifierPressed(e)) { return; }
+ this._expandCollapseComments(false);
+ }
+ },
+
+ _expandCollapseComments(actionIsCollapse) {
+ const comments =
+ Polymer.dom(this.root).querySelectorAll('gr-comment');
+ for (const comment of comments) {
+ comment.collapsed = actionIsCollapse;
+ }
+ },
+
+ /**
+ * Sets the initial state of the comment thread.
+ * Expands the thread if one of the following is true:
+ * - last {UNRESOLVED_EXPAND_COUNT} comments expanded by default if the
+ * thread is unresolved,
+ * - it's a robot comment.
+ */
+ _setInitialExpandedState() {
+ if (this._orderedComments) {
+ for (let i = 0; i < this._orderedComments.length; i++) {
+ const comment = this._orderedComments[i];
+ const isRobotComment = !!comment.robot_id;
+ // False if it's an unresolved comment under UNRESOLVED_EXPAND_COUNT.
+ const resolvedThread = !this.unresolved ||
+ this._orderedComments.length - i - 1 >= UNRESOLVED_EXPAND_COUNT;
+ comment.collapsed = !isRobotComment && resolvedThread;
+ }
+ }
+ },
+
+ _sortedComments(comments) {
+ return comments.slice().sort((c1, c2) => {
+ const c1Date = c1.__date || util.parseDate(c1.updated);
+ const c2Date = c2.__date || util.parseDate(c2.updated);
+ const dateCompare = c1Date - c2Date;
+ // Ensure drafts are at the end. There should only be one but in edge
+ // cases could be more. In the unlikely event two drafts are being
+ // compared, use the typical date compare.
+ if (c2.__draft && !c1.__draft ) { return -1; }
+ if (c1.__draft && !c2.__draft ) { return 1; }
+ if (dateCompare === 0 && (!c1.id || !c1.id.localeCompare)) { return 0; }
+ // If same date, fall back to sorting by id.
+ return dateCompare ? dateCompare : c1.id.localeCompare(c2.id);
+ });
+ },
+
+ _createReplyComment(parent, content, opt_isEditing,
+ opt_unresolved) {
+ this.$.reporting.recordDraftInteraction();
+ const reply = this._newReply(
+ this._orderedComments[this._orderedComments.length - 1].id,
+ parent.line,
+ content,
+ opt_unresolved,
+ parent.range);
+
+ // If there is currently a comment in an editing state, add an attribute
+ // so that the gr-comment knows not to populate the draft text.
+ for (let i = 0; i < this.comments.length; i++) {
+ if (this.comments[i].__editing) {
+ reply.__otherEditing = true;
+ break;
+ }
+ }
+
+ if (opt_isEditing) {
+ reply.__editing = true;
+ }
+
+ this.push('comments', reply);
+
+ if (!opt_isEditing) {
+ // Allow the reply to render in the dom-repeat.
+ this.async(() => {
+ const commentEl = this._commentElWithDraftID(reply.__draftID);
+ commentEl.save();
+ }, 1);
+ }
+ },
+
+ _isDraft(comment) {
+ return !!comment.__draft;
+ },
+
+ /**
+ * @param {boolean=} opt_quote
+ */
+ _processCommentReply(opt_quote) {
+ const comment = this._lastComment;
+ let quoteStr;
+ if (opt_quote) {
+ const msg = comment.message;
+ quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
+ }
+ this._createReplyComment(comment, quoteStr, true, comment.unresolved);
+ },
+
+ _handleCommentReply(e) {
+ this._processCommentReply();
+ },
+
+ _handleCommentQuote(e) {
+ this._processCommentReply(true);
+ },
+
+ _handleCommentAck(e) {
+ const comment = this._lastComment;
+ this._createReplyComment(comment, 'Ack', false, false);
+ },
+
+ _handleCommentDone(e) {
+ const comment = this._lastComment;
+ this._createReplyComment(comment, 'Done', false, false);
+ },
+
+ _handleCommentFix(e) {
+ const comment = e.detail.comment;
+ const msg = comment.message;
+ const quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
+ const response = quoteStr + 'Please Fix';
+ this._createReplyComment(comment, response, false, true);
+ },
+
+ _commentElWithDraftID(id) {
+ const els = Polymer.dom(this.root).querySelectorAll('gr-comment');
+ for (const el of els) {
+ if (el.comment.id === id || el.comment.__draftID === id) {
+ return el;
+ }
+ }
+ return null;
+ },
+
+ _newReply(inReplyTo, opt_lineNum, opt_message, opt_unresolved,
+ opt_range) {
+ const d = this._newDraft(opt_lineNum);
+ d.in_reply_to = inReplyTo;
+ d.range = opt_range;
+ if (opt_message != null) {
+ d.message = opt_message;
+ }
+ if (opt_unresolved !== undefined) {
+ d.unresolved = opt_unresolved;
+ }
+ return d;
+ },
+
+ /**
+ * @param {number=} opt_lineNum
+ * @param {!Object=} opt_range
+ */
+ _newDraft(opt_lineNum, opt_range) {
+ const d = {
+ __draft: true,
+ __draftID: Math.random().toString(36),
+ __date: new Date(),
+ path: this.path,
+ patchNum: this.patchNum,
+ side: this._getSide(this.isOnParent),
+ __commentSide: this.commentSide,
+ };
+ if (opt_lineNum) {
+ d.line = opt_lineNum;
+ }
+ if (opt_range) {
+ d.range = opt_range;
+ }
+ if (this.parentIndex) {
+ d.parent = this.parentIndex;
+ }
+ return d;
+ },
+
+ _getSide(isOnParent) {
+ if (isOnParent) { return 'PARENT'; }
+ return 'REVISION';
+ },
+
+ _computeRootId(comments) {
+ // Keep the root ID even if the comment was removed, so that notification
+ // to sync will know which thread to remove.
+ if (!comments.base.length) { return this.rootId; }
+ const rootComment = comments.base[0];
+ return rootComment.id || rootComment.__draftID;
+ },
+
+ _handleCommentDiscard(e) {
+ const diffCommentEl = Polymer.dom(e).rootTarget;
+ const comment = diffCommentEl.comment;
+ const idx = this._indexOf(comment, this.comments);
+ if (idx == -1) {
+ throw Error('Cannot find comment ' +
+ JSON.stringify(diffCommentEl.comment));
+ }
+ this.splice('comments', idx, 1);
+ if (this.comments.length === 0) {
+ this.fireRemoveSelf();
+ }
+ this._handleCommentSavedOrDiscarded(e);
+
+ // Check to see if there are any other open comments getting edited and
+ // set the local storage value to its message value.
+ for (const changeComment of this.comments) {
+ if (changeComment.__editing) {
+ const commentLocation = {
+ changeNum: this.changeNum,
+ patchNum: this.patchNum,
+ path: changeComment.path,
+ line: changeComment.line,
+ };
+ return this.$.storage.setDraftComment(commentLocation,
+ changeComment.message);
+ }
+ }
+ },
+
+ _handleCommentSavedOrDiscarded(e) {
+ this.dispatchEvent(new CustomEvent('thread-changed',
+ {detail: {rootId: this.rootId, path: this.path},
+ bubbles: false}));
+ },
+
+ _handleCommentUpdate(e) {
+ const comment = e.detail.comment;
+ const index = this._indexOf(comment, this.comments);
+ if (index === -1) {
+ // This should never happen: comment belongs to another thread.
+ console.warn('Comment update for another comment thread.');
+ return;
+ }
+ this.set(['comments', index], comment);
+ // Because of the way we pass these comment objects around by-ref, in
+ // combination with the fact that Polymer does dirty checking in
+ // observers, the this.set() call above will not cause a thread update in
+ // some situations.
+ this.updateThreadProperties();
+ },
+
+ _indexOf(comment, arr) {
+ for (let i = 0; i < arr.length; i++) {
+ const c = arr[i];
+ if ((c.__draftID != null && c.__draftID == comment.__draftID) ||
+ (c.id != null && c.id == comment.id)) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ _computeHostClass(unresolved) {
+ return unresolved ? 'unresolved' : '';
+ },
+
+ /**
+ * Load the project config when a project name has been provided.
+ *
+ * @param {string} name The project name.
+ */
+ _projectNameChanged(name) {
+ if (!name) { return; }
+ this.$.restAPI.getProjectConfig(name).then(config => {
+ this._projectConfig = config;
+ });
+ },
+ });
+})();
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
new file mode 100644
index 0000000000..74c6f5fb6a
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.html
@@ -0,0 +1,751 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2015 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-comment-thread</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-comment-thread.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-comment-thread></gr-comment-thread>
+ </template>
+</test-fixture>
+
+<test-fixture id="withComment">
+ <template>
+ <gr-comment-thread></gr-comment-thread>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-comment-thread tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ stub('gr-rest-api-interface', {
+ getLoggedIn() { return Promise.resolve(false); },
+ });
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('comments are sorted correctly', () => {
+ const comments = [
+ {
+ message: 'i like you, too',
+ in_reply_to: 'sallys_confession',
+ __date: new Date('2015-12-25'),
+ }, {
+ id: 'sallys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-24 15:00:20.396000000',
+ }, {
+ id: 'sally_to_dr_finklestein',
+ message: 'i’m running away',
+ updated: '2015-10-31 09:00:20.396000000',
+ }, {
+ id: 'sallys_defiance',
+ in_reply_to: 'sally_to_dr_finklestein',
+ message: 'i will poison you so i can get away',
+ updated: '2015-10-31 15:00:20.396000000',
+ }, {
+ id: 'dr_finklesteins_response',
+ in_reply_to: 'sally_to_dr_finklestein',
+ message: 'no i will pull a thread and your arm will fall off',
+ updated: '2015-10-31 11:00:20.396000000',
+ }, {
+ id: 'sallys_mission',
+ message: 'i have to find santa',
+ updated: '2015-12-24 15:00:20.396000000',
+ },
+ ];
+ const results = element._sortedComments(comments);
+ assert.deepEqual(results, [
+ {
+ id: 'sally_to_dr_finklestein',
+ message: 'i’m running away',
+ updated: '2015-10-31 09:00:20.396000000',
+ }, {
+ id: 'dr_finklesteins_response',
+ in_reply_to: 'sally_to_dr_finklestein',
+ message: 'no i will pull a thread and your arm will fall off',
+ updated: '2015-10-31 11:00:20.396000000',
+ }, {
+ id: 'sallys_defiance',
+ in_reply_to: 'sally_to_dr_finklestein',
+ message: 'i will poison you so i can get away',
+ updated: '2015-10-31 15:00:20.396000000',
+ }, {
+ id: 'sallys_confession',
+ message: 'i like you, jack',
+ updated: '2015-12-24 15:00:20.396000000',
+ }, {
+ id: 'sallys_mission',
+ message: 'i have to find santa',
+ updated: '2015-12-24 15:00:20.396000000',
+ }, {
+ message: 'i like you, too',
+ in_reply_to: 'sallys_confession',
+ __date: new Date('2015-12-25'),
+ },
+ ]);
+ });
+
+ test('addOrEditDraft w/ edit draft', () => {
+ element.comments = [{
+ id: 'jacks_reply',
+ message: 'i like you, too',
+ in_reply_to: 'sallys_confession',
+ updated: '2015-12-25 15:00:20.396000000',
+ __draft: true,
+ }];
+ const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
+ () => { return {}; });
+ const addDraftStub = sandbox.stub(element, 'addDraft');
+
+ element.addOrEditDraft(123);
+
+ assert.isTrue(commentElStub.called);
+ assert.isFalse(addDraftStub.called);
+ });
+
+ test('addOrEditDraft w/o edit draft', () => {
+ element.comments = [];
+ const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
+ () => { return {}; });
+ const addDraftStub = sandbox.stub(element, 'addDraft');
+
+ element.addOrEditDraft(123);
+
+ assert.isFalse(commentElStub.called);
+ assert.isTrue(addDraftStub.called);
+ });
+
+ test('_hideActions', () => {
+ let showActions = true;
+ const lastComment = {};
+ assert.equal(element._hideActions(showActions, lastComment), false);
+ showActions = false;
+ assert.equal(element._hideActions(showActions, lastComment), true);
+ showActions = true;
+ lastComment.__draft = true;
+ assert.equal(element._hideActions(showActions, lastComment), true);
+ });
+
+ test('setting project name loads the project config', done => {
+ const projectName = 'foo/bar/baz';
+ const getProjectStub = sandbox.stub(element.$.restAPI, 'getProjectConfig')
+ .returns(Promise.resolve({}));
+ element.projectName = projectName;
+ flush(() => {
+ assert.isTrue(getProjectStub.calledWithExactly(projectName));
+ done();
+ });
+ });
+
+ test('optionally show file path', () => {
+ // Path info doesn't exist when showFilePath is false. Because it's in a
+ // dom-if it is not yet in the dom.
+ assert.isNotOk(element.$$('.pathInfo'));
+
+ sandbox.stub(Gerrit.Nav, 'getUrlForDiffById');
+ element.changeNum = 123;
+ element.projectName = 'test project';
+ element.path = 'path/to/file';
+ element.patchNum = 3;
+ element.lineNum = 5;
+ element.showFilePath = true;
+ flushAsynchronousOperations();
+ assert.isOk(element.$$('.pathInfo'));
+ assert.notEqual(getComputedStyle(element.$$('.pathInfo')).display,
+ 'none');
+ assert.isTrue(Gerrit.Nav.getUrlForDiffById.lastCall.calledWithExactly(
+ element.changeNum, element.projectName, element.path,
+ element.patchNum, null, element.lineNum));
+ });
+
+ test('_computeDisplayPath', () => {
+ const path = 'path/to/file';
+ assert.equal(element._computeDisplayPath(path), 'path/to/file');
+
+ element.lineNum = 5;
+ assert.equal(element._computeDisplayPath(path), 'path/to/file#5');
+ });
+ });
+
+ suite('comment action tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ stub('gr-rest-api-interface', {
+ getLoggedIn() { return Promise.resolve(false); },
+ saveDiffDraft() {
+ return Promise.resolve({
+ ok: true,
+ text() {
+ return Promise.resolve(')]}\'\n' +
+ JSON.stringify({
+ id: '7afa4931_de3d65bd',
+ path: '/path/to/file.txt',
+ line: 5,
+ in_reply_to: 'baf0414d_60047215',
+ updated: '2015-12-21 02:01:10.850000000',
+ message: 'Done',
+ }));
+ },
+ });
+ },
+ deleteDiffDraft() { return Promise.resolve({ok: true}); },
+ });
+ element = fixture('withComment');
+ element.comments = [{
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 'baf0414d_60047215',
+ line: 5,
+ message: 'is this a crossover episode!?',
+ updated: '2015-12-08 19:48:33.843000000',
+ path: '/path/to/file.txt',
+ }];
+ flushAsynchronousOperations();
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('reply', () => {
+ const commentEl = element.$$('gr-comment');
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ assert.ok(commentEl);
+
+ const replyBtn = element.$.replyBtn;
+ MockInteractions.tap(replyBtn);
+ flushAsynchronousOperations();
+
+ const drafts = element._orderedComments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 1);
+ assert.notOk(drafts[0].message, 'message should be empty');
+ assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
+ assert.isTrue(reportStub.calledOnce);
+ });
+
+ test('quote reply', () => {
+ const commentEl = element.$$('gr-comment');
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ assert.ok(commentEl);
+
+ const quoteBtn = element.$.quoteBtn;
+ MockInteractions.tap(quoteBtn);
+ flushAsynchronousOperations();
+
+ const drafts = element._orderedComments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 1);
+ assert.equal(drafts[0].message, '> is this a crossover episode!?\n\n');
+ assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
+ assert.isTrue(reportStub.calledOnce);
+ });
+
+ test('quote reply multiline', () => {
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ element.comments = [{
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 'baf0414d_60047215',
+ line: 5,
+ message: 'is this a crossover episode!?\nIt might be!',
+ updated: '2015-12-08 19:48:33.843000000',
+ }];
+ flushAsynchronousOperations();
+
+ const commentEl = element.$$('gr-comment');
+ assert.ok(commentEl);
+
+ const quoteBtn = element.$.quoteBtn;
+ MockInteractions.tap(quoteBtn);
+ flushAsynchronousOperations();
+
+ const drafts = element._orderedComments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 1);
+ assert.equal(drafts[0].message,
+ '> is this a crossover episode!?\n> It might be!\n\n');
+ assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
+ assert.isTrue(reportStub.calledOnce);
+ });
+
+ test('ack', done => {
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ element.changeNum = '42';
+ element.patchNum = '1';
+
+ const commentEl = element.$$('gr-comment');
+ assert.ok(commentEl);
+
+ const ackBtn = element.$.ackBtn;
+ MockInteractions.tap(ackBtn);
+ flush(() => {
+ const drafts = element.comments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 1);
+ assert.equal(drafts[0].message, 'Ack');
+ assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
+ assert.equal(drafts[0].unresolved, false);
+ assert.isTrue(reportStub.calledOnce);
+ done();
+ });
+ });
+
+ test('done', done => {
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ element.changeNum = '42';
+ element.patchNum = '1';
+ const commentEl = element.$$('gr-comment');
+ assert.ok(commentEl);
+
+ const doneBtn = element.$.doneBtn;
+ MockInteractions.tap(doneBtn);
+ flush(() => {
+ const drafts = element.comments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 1);
+ assert.equal(drafts[0].message, 'Done');
+ assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
+ assert.isFalse(drafts[0].unresolved);
+ assert.isTrue(reportStub.calledOnce);
+ done();
+ });
+ });
+
+ test('save', done => {
+ element.changeNum = '42';
+ element.patchNum = '1';
+ element.path = '/path/to/file.txt';
+ const commentEl = element.$$('gr-comment');
+ assert.ok(commentEl);
+
+ const saveOrDiscardStub = sandbox.stub();
+ element.addEventListener('thread-changed', saveOrDiscardStub);
+ element.$$('gr-comment')._fireSave();
+
+ flush(() => {
+ assert.isTrue(saveOrDiscardStub.called);
+ assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
+ 'baf0414d_60047215');
+ assert.equal(element.rootId, 'baf0414d_60047215');
+ assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
+ '/path/to/file.txt');
+ done();
+ });
+ });
+
+ test('please fix', done => {
+ element.changeNum = '42';
+ element.patchNum = '1';
+ const commentEl = element.$$('gr-comment');
+ assert.ok(commentEl);
+ commentEl.addEventListener('create-fix-comment', () => {
+ const drafts = element._orderedComments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 1);
+ assert.equal(
+ drafts[0].message, '> is this a crossover episode!?\n\nPlease Fix');
+ assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
+ assert.isTrue(drafts[0].unresolved);
+ done();
+ });
+ commentEl.fire('create-fix-comment', {comment: commentEl.comment},
+ {bubbles: false});
+ });
+
+ test('discard', done => {
+ element.changeNum = '42';
+ element.patchNum = '1';
+ element.path = '/path/to/file.txt';
+ element.push('comments', element._newReply(
+ element.comments[0].id,
+ element.comments[0].line,
+ element.comments[0].path,
+ 'it’s pronouced jiff, not giff'));
+ flushAsynchronousOperations();
+
+ const saveOrDiscardStub = sandbox.stub();
+ element.addEventListener('thread-changed', saveOrDiscardStub);
+ const draftEl =
+ Polymer.dom(element.root).querySelectorAll('gr-comment')[1];
+ assert.ok(draftEl);
+ draftEl.addEventListener('comment-discard', () => {
+ const drafts = element.comments.filter(c => {
+ return c.__draft == true;
+ });
+ assert.equal(drafts.length, 0);
+ assert.isTrue(saveOrDiscardStub.called);
+ assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
+ element.rootId);
+ assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
+ element.path);
+ done();
+ });
+ draftEl.fire('comment-discard', {comment: draftEl.comment},
+ {bubbles: false});
+ });
+
+ test('discard with a single comment still fires event with previous rootId',
+ done => {
+ element.changeNum = '42';
+ element.patchNum = '1';
+ element.path = '/path/to/file.txt';
+ element.comments = [];
+ element.addOrEditDraft('1');
+ flushAsynchronousOperations();
+ const rootId = element.rootId;
+ assert.isOk(rootId);
+
+ const saveOrDiscardStub = sandbox.stub();
+ element.addEventListener('thread-changed', saveOrDiscardStub);
+ const draftEl =
+ Polymer.dom(element.root).querySelectorAll('gr-comment')[0];
+ assert.ok(draftEl);
+ draftEl.addEventListener('comment-discard', () => {
+ assert.equal(element.comments.length, 0);
+ assert.isTrue(saveOrDiscardStub.called);
+ assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
+ rootId);
+ assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
+ element.path);
+ done();
+ });
+ draftEl.fire('comment-discard', {comment: draftEl.comment},
+ {bubbles: false});
+ });
+
+ test('first editing comment does not add __otherEditing attribute', () => {
+ element.comments = [{
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 'baf0414d_60047215',
+ line: 5,
+ message: 'is this a crossover episode!?',
+ updated: '2015-12-08 19:48:33.843000000',
+ __draft: true,
+ }];
+
+ const replyBtn = element.$.replyBtn;
+ MockInteractions.tap(replyBtn);
+ flushAsynchronousOperations();
+
+ const editing = element._orderedComments.filter(c => {
+ return c.__editing == true;
+ });
+ assert.equal(editing.length, 1);
+ assert.equal(!!editing[0].__otherEditing, false);
+ });
+
+ test('When not editing other comments, local storage not set' +
+ ' after discard', done => {
+ element.changeNum = '42';
+ element.patchNum = '1';
+ element.comments = [{
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 'baf0414d_60047215',
+ line: 5,
+ message: 'is this a crossover episode!?',
+ updated: '2015-12-08 19:48:31.843000000',
+ },
+ {
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ __draftID: '1',
+ in_reply_to: 'baf0414d_60047215',
+ line: 5,
+ message: 'yes',
+ updated: '2015-12-08 19:48:32.843000000',
+ __draft: true,
+ __editing: true,
+ },
+ {
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ __draftID: '2',
+ in_reply_to: 'baf0414d_60047215',
+ line: 5,
+ message: 'no',
+ updated: '2015-12-08 19:48:33.843000000',
+ __draft: true,
+ }];
+ const storageStub = sinon.stub(element.$.storage, 'setDraftComment');
+ flushAsynchronousOperations();
+
+ const draftEl =
+ Polymer.dom(element.root).querySelectorAll('gr-comment')[1];
+ assert.ok(draftEl);
+ draftEl.addEventListener('comment-discard', () => {
+ assert.isFalse(storageStub.called);
+ storageStub.restore();
+ done();
+ });
+ draftEl.fire('comment-discard', {comment: draftEl.comment},
+ {bubbles: false});
+ });
+
+ test('comment-update', () => {
+ const commentEl = element.$$('gr-comment');
+ const updatedComment = {
+ id: element.comments[0].id,
+ foo: 'bar',
+ };
+ commentEl.fire('comment-update', {comment: updatedComment});
+ assert.strictEqual(element.comments[0], updatedComment);
+ });
+
+ suite('jack and sally comment data test consolidation', () => {
+ setup(() => {
+ element.comments = [
+ {
+ id: 'jacks_reply',
+ message: 'i like you, too',
+ in_reply_to: 'sallys_confession',
+ updated: '2015-12-25 15:00:20.396000000',
+ unresolved: false,
+ }, {
+ id: 'sallys_confession',
+ in_reply_to: 'nonexistent_comment',
+ message: 'i like you, jack',
+ updated: '2015-12-24 15:00:20.396000000',
+ }, {
+ id: 'sally_to_dr_finklestein',
+ in_reply_to: 'nonexistent_comment',
+ message: 'i’m running away',
+ updated: '2015-10-31 09:00:20.396000000',
+ }, {
+ id: 'sallys_defiance',
+ message: 'i will poison you so i can get away',
+ updated: '2015-10-31 15:00:20.396000000',
+ }];
+ });
+
+ test('orphan replies', () => {
+ assert.equal(4, element._orderedComments.length);
+ });
+
+ test('keyboard shortcuts', () => {
+ const expandCollapseStub =
+ sinon.stub(element, '_expandCollapseComments');
+ MockInteractions.pressAndReleaseKeyOn(element, 69, null, 'e');
+ assert.isTrue(expandCollapseStub.lastCall.calledWith(false));
+
+ MockInteractions.pressAndReleaseKeyOn(element, 69, 'shift', 'e');
+ assert.isTrue(expandCollapseStub.lastCall.calledWith(true));
+ });
+
+ test('comment in_reply_to is either null or most recent comment', () => {
+ element._createReplyComment(element.comments[3], 'dummy', true);
+ flushAsynchronousOperations();
+ assert.equal(element._orderedComments.length, 5);
+ assert.equal(element._orderedComments[4].in_reply_to, 'jacks_reply');
+ });
+
+ test('resolvable comments', () => {
+ assert.isFalse(element.unresolved);
+ element._createReplyComment(element.comments[3], 'dummy', true, true);
+ flushAsynchronousOperations();
+ assert.isTrue(element.unresolved);
+ });
+
+ test('_setInitialExpandedState', () => {
+ element.unresolved = true;
+ element._setInitialExpandedState();
+ for (let i = 0; i < element.comments.length; i++) {
+ assert.isFalse(element.comments[i].collapsed);
+ }
+ element.unresolved = false;
+ element._setInitialExpandedState();
+ for (let i = 0; i < element.comments.length; i++) {
+ assert.isTrue(element.comments[i].collapsed);
+ }
+ for (let i = 0; i < element.comments.length; i++) {
+ element.comments[i].robot_id = 123;
+ }
+ element._setInitialExpandedState();
+ for (let i = 0; i < element.comments.length; i++) {
+ assert.isFalse(element.comments[i].collapsed);
+ }
+ });
+ });
+
+ test('_computeHostClass', () => {
+ assert.equal(element._computeHostClass(true), 'unresolved');
+ assert.equal(element._computeHostClass(false), '');
+ });
+
+ test('addDraft sets unresolved state correctly', () => {
+ let unresolved = true;
+ element.comments = [];
+ element.addDraft(null, null, unresolved);
+ assert.equal(element.comments[0].unresolved, true);
+
+ unresolved = false; // comment should get added as actually resolved.
+ element.comments = [];
+ element.addDraft(null, null, unresolved);
+ assert.equal(element.comments[0].unresolved, false);
+
+ element.comments = [];
+ element.addDraft();
+ assert.equal(element.comments[0].unresolved, true);
+ });
+
+ test('_newDraft', () => {
+ element.commentSide = 'left';
+ element.patchNum = 3;
+ const draft = element._newDraft();
+ assert.equal(draft.__commentSide, 'left');
+ assert.equal(draft.patchNum, 3);
+ });
+
+ test('new comment gets created', () => {
+ element.comments = [];
+ element.addOrEditDraft(1);
+ assert.equal(element.comments.length, 1);
+ // Mock a submitted comment.
+ element.comments[0].id = element.comments[0].__draftID;
+ element.comments[0].__draft = false;
+ element.addOrEditDraft(1);
+ assert.equal(element.comments.length, 2);
+ });
+
+ test('unresolved label', () => {
+ element.unresolved = false;
+ assert.isTrue(element.$.unresolvedLabel.hasAttribute('hidden'));
+ element.unresolved = true;
+ assert.isFalse(element.$.unresolvedLabel.hasAttribute('hidden'));
+ });
+
+ test('draft comments are at the end of orderedComments', () => {
+ element.comments = [{
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 2,
+ line: 5,
+ message: 'Earlier draft',
+ updated: '2015-12-08 19:48:33.843000000',
+ __draft: true,
+ },
+ {
+ author: {
+ name: 'Mr. Peanutbutter2',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 1,
+ line: 5,
+ message: 'This comment was left last but is not a draft',
+ updated: '2015-12-10 19:48:33.843000000',
+ },
+ {
+ author: {
+ name: 'Mr. Peanutbutter2',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 3,
+ line: 5,
+ message: 'Later draft',
+ updated: '2015-12-09 19:48:33.843000000',
+ __draft: true,
+ }];
+ assert.equal(element._orderedComments[0].id, '1');
+ assert.equal(element._orderedComments[1].id, '2');
+ assert.equal(element._orderedComments[2].id, '3');
+ });
+
+ test('reflects lineNum and commentSide to attributes', () => {
+ element.lineNum = 7;
+ element.commentSide = 'left';
+
+ assert.equal(element.getAttribute('line-num'), '7');
+ assert.equal(element.getAttribute('comment-side'), 'left');
+ });
+
+ test('reflects range to JSON serialized attribute if set', () => {
+ element.range = {
+ start_line: 4,
+ end_line: 5,
+ start_character: 6,
+ end_character: 7,
+ };
+
+ assert.deepEqual(
+ JSON.parse(element.getAttribute('range')),
+ {start_line: 4, end_line: 5, start_character: 6, end_character: 7});
+ });
+
+ test('removes range attribute if range is unset', () => {
+ element.range = {
+ start_line: 4,
+ end_line: 5,
+ start_character: 6,
+ end_character: 7,
+ };
+ element.range = undefined;
+
+ assert.notOk(element.hasAttribute('range'));
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
new file mode 100644
index 0000000000..a470285fdc
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.html
@@ -0,0 +1,388 @@
+<!--
+@license
+Copyright (C) 2015 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.
+-->
+
+<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="../../../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">
+<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-dialog/gr-dialog.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">
+<link rel="import" href="../../shared/gr-icons/gr-icons.html">
+<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="../../shared/gr-textarea/gr-textarea.html">
+<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
+<link rel="import" href="../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html">
+<script src="../../../scripts/rootElement.js"></script>
+
+<dom-module id="gr-comment">
+ <template>
+ <style include="shared-styles">
+ :host {
+ display: block;
+ font-family: var(--font-family);
+ padding: .7em .7em;
+ --iron-autogrow-textarea: {
+ box-sizing: border-box;
+ padding: 2px;
+ };
+ }
+ :host([disabled]) {
+ pointer-events: none;
+ }
+ :host([disabled]) .actions,
+ :host([disabled]) .robotActions,
+ :host([disabled]) .date {
+ opacity: .5;
+ }
+ :host([discarding]) {
+ display: none;
+ }
+ .header {
+ align-items: baseline;
+ cursor: pointer;
+ display: flex;
+ font-family: 'Open Sans', sans-serif;
+ margin: -.7em -.7em 0 -.7em;
+ padding: .7em;
+ }
+ .container.collapsed .header {
+ margin-bottom: -.7em;
+ }
+ .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);
+ display: none;
+ }
+ .date {
+ justify-content: flex-end;
+ margin-left: 5px;
+ min-width: 4.5em;
+ text-align: right;
+ white-space: nowrap;
+ }
+ span.date {
+ color: var(--deemphasized-text-color);
+ }
+ span.date:hover {
+ text-decoration: underline;
+ }
+ .actions {
+ display: flex;
+ justify-content: flex-end;
+ padding-top: 0;
+ }
+ .action {
+ margin-left: 1em;
+ }
+ .robotActions {
+ display: flex;
+ justify-content: flex-start;
+ padding-top: 0;
+ }
+ .robotActions .action {
+ /* Keep button text lined up with output text */
+ margin-left: -.3rem;
+ margin-right: 1em;
+ }
+ .rightActions {
+ display: flex;
+ justify-content: flex-end;
+ }
+ .editMessage {
+ display: none;
+ margin: .5em 0;
+ width: 100%;
+ }
+ .container:not(.draft) .actions .hideOnPublished {
+ display: none;
+ }
+ .draft .reply,
+ .draft .quote,
+ .draft .ack,
+ .draft .done {
+ display: none;
+ }
+ .draft .draftLabel,
+ .draft .draftTooltip {
+ display: inline;
+ }
+ .draft:not(.editing) .save,
+ .draft:not(.editing) .cancel {
+ display: none;
+ }
+ .editing .message,
+ .editing .reply,
+ .editing .quote,
+ .editing .ack,
+ .editing .done,
+ .editing .edit,
+ .editing .discard,
+ .editing .unresolved {
+ display: none;
+ }
+ .editing .editMessage {
+ display: block;
+ }
+ .show-hide {
+ margin-left: .4em;
+ }
+ .robotId {
+ color: var(--deemphasized-text-color);
+ margin-bottom: .8em;
+ margin-top: -.4em;
+ }
+ .robotIcon {
+ margin-right: .2em;
+ /* 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;
+ }
+ .runIdInformation {
+ margin: .7em 0;
+ }
+ .robotRun {
+ margin-left: .5em;
+ }
+ .robotRunLink {
+ margin-left: .5em;
+ }
+ 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;
+ }
+ #container .collapsedContent {
+ display: none;
+ }
+ #container.collapsed {
+ padding-bottom: 3px;
+ }
+ #container.collapsed .collapsedContent {
+ display: block;
+ overflow: hidden;
+ padding-left: 5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ #container.collapsed .actions,
+ #container.collapsed gr-formatted-text,
+ #container.collapsed gr-textarea {
+ display: none;
+ }
+ .resolve,
+ .unresolved {
+ align-items: center;
+ display: flex;
+ flex: 1;
+ margin: 0;
+ }
+ .resolve label {
+ color: var(--comment-text-color);
+ }
+ gr-dialog .main {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ }
+ #deleteBtn {
+ display: none;
+ --gr-button: {
+ color: var(--deemphasized-text-color);
+ padding: 0;
+ }
+ }
+ #deleteBtn.showDeleteButtons {
+ display: block;
+ }
+ </style>
+ <div id="container" class="container">
+ <div class="header" id="header" on-tap="_handleToggleCollapsed">
+ <div class="headerLeft">
+ <span class="authorName">[[comment.author.name]]</span>
+ <span class="draftLabel">DRAFT</span>
+ <gr-tooltip-content class="draftTooltip"
+ has-tooltip
+ title="This draft is only visible to you. To publish drafts, click the 'Reply' or 'Start review' button at the top of the change or press the 'A' key."
+ max-width="20em"
+ show-icon></gr-tooltip-content>
+ </div>
+ <div class="headerMiddle">
+ <span class="collapsedContent">[[comment.message]]</span>
+ </div>
+ <gr-button
+ id="deleteBtn"
+ link
+ secondary
+ class$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]"
+ on-tap="_handleCommentDelete">
+ (Delete)
+ </gr-button>
+ <span class="date" on-tap="_handleAnchorTap">
+ <gr-date-formatter
+ has-tooltip
+ date-str="[[comment.updated]]"></gr-date-formatter>
+ </span>
+ <div class="show-hide">
+ <label class="show-hide">
+ <input type="checkbox" class="show-hide"
+ checked$="[[collapsed]]"
+ on-change="_handleToggleCollapsed">
+ [[_computeShowHideText(collapsed)]]
+ </label>
+ </div>
+ </div>
+ <div class="body">
+ <template is="dom-if" if="[[comment.robot_id]]">
+ <div class="robotId" hidden$="[[collapsed]]">
+ <iron-icon class="robotIcon" icon="gr-icons:robot"></iron-icon>
+ [[comment.robot_id]]
+ </div>
+ </template>
+ <template is="dom-if" if="[[editing]]">
+ <gr-textarea
+ id="editTextarea"
+ class="editMessage"
+ autocomplete="on"
+ monospace
+ disabled="{{disabled}}"
+ rows="4"
+ text="{{_messageText}}"></gr-textarea>
+ </template>
+ <!--The message class is needed to ensure selectability from
+ gr-diff-selection.-->
+ <gr-formatted-text class="message"
+ content="[[comment.message]]"
+ no-trailing-margin="[[!comment.__draft]]"
+ collapsed="[[collapsed]]"
+ config="[[projectConfig.commentlinks]]"></gr-formatted-text>
+ <div hidden$="[[!comment.robot_run_id]]" class="message">
+ <div class="runIdInformation" hidden$="[[collapsed]]">
+ Run ID:
+ <template is="dom-if" if="[[comment.url]]">
+ <a class="robotRunLink" href$="[[comment.url]]">
+ <span class="robotRun link">[[comment.robot_run_id]]</span>
+ </a>
+ </template>
+ <template is="dom-if" if="[[!comment.url]]">
+ <span class="robotRun text">[[comment.robot_run_id]]</span>
+ </template>
+ </div>
+ </div>
+ <div class="actions humanActions" hidden$="[[!_showHumanActions]]">
+ <div class="action resolve hideOnPublished">
+ <label>
+ <input type="checkbox"
+ id="resolvedCheckbox"
+ checked="[[resolved]]"
+ on-change="_handleToggleResolved">
+ Resolved
+ </label>
+ </div>
+ <div class="rightActions">
+ <gr-button
+ link
+ secondary
+ class="action cancel hideOnPublished"
+ on-tap="_handleCancel">Cancel</gr-button>
+ <gr-button
+ link
+ secondary
+ class="action discard hideOnPublished"
+ on-tap="_handleDiscard">Discard</gr-button>
+ <gr-button
+ link
+ secondary
+ class="action edit hideOnPublished"
+ on-tap="_handleEdit">Edit</gr-button>
+ <gr-button
+ link
+ secondary
+ disabled$="[[_computeSaveDisabled(_messageText, comment, resolved)]]"
+ class="action save hideOnPublished"
+ on-tap="_handleSave">Save</gr-button>
+ </div>
+ </div>
+ <div class="robotActions" hidden$="[[!_showRobotActions]]">
+ <template is="dom-if" if="[[isRobotComment]]">
+ <gr-button
+ link
+ secondary
+ class="action fix"
+ on-tap="_handleFix"
+ disabled="[[robotButtonDisabled]]">
+ Please Fix
+ </gr-button>
+ <gr-endpoint-decorator name="robot-comment-controls">
+ <gr-endpoint-param name="comment" value="[[comment]]">
+ </gr-endpoint-param>
+ </gr-endpoint-decorator>
+ </template>
+ </div>
+ </div>
+ </div>
+ <template is="dom-if" if="[[_enableOverlay]]">
+ <gr-overlay id="confirmDeleteOverlay" with-backdrop>
+ <gr-confirm-delete-comment-dialog id="confirmDeleteComment"
+ on-confirm="_handleConfirmDeleteComment"
+ on-cancel="_handleCancelDeleteComment">
+ </gr-confirm-delete-comment-dialog>
+ </gr-overlay>
+ <gr-overlay id="confirmDiscardOverlay" with-backdrop>
+ <gr-dialog
+ id="confirmDiscardDialog"
+ confirm-label="Discard"
+ confirm-on-enter
+ on-confirm="_handleConfirmDiscard"
+ on-cancel="_closeConfirmDiscardOverlay">
+ <div class="header" slot="header">
+ Discard comment
+ </div>
+ <div class="main" slot="main">
+ Are you sure you want to discard this draft comment?
+ </div>
+ </gr-dialog>
+ </gr-overlay>
+ </template>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ <gr-storage id="storage"></gr-storage>
+ <gr-reporting id="reporting"></gr-reporting>
+ </template>
+ <script src="gr-comment.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
new file mode 100644
index 0000000000..b699b8b37d
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -0,0 +1,649 @@
+/**
+ * @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 STORAGE_DEBOUNCE_INTERVAL = 400;
+ const TOAST_DEBOUNCE_INTERVAL = 200;
+
+ const SAVING_MESSAGE = 'Saving';
+ const DRAFT_SINGULAR = 'draft...';
+ const DRAFT_PLURAL = 'drafts...';
+ const SAVED_MESSAGE = 'All changes saved';
+
+ const REPORT_CREATE_DRAFT = 'CreateDraftComment';
+ const REPORT_UPDATE_DRAFT = 'UpdateDraftComment';
+ const REPORT_DISCARD_DRAFT = 'DiscardDraftComment';
+
+ const FILE = 'FILE';
+
+ Polymer({
+ is: 'gr-comment',
+ _legacyUndefinedCheck: true,
+
+ /**
+ * Fired when the create fix comment action is triggered.
+ *
+ * @event create-fix-comment
+ */
+
+ /**
+ * Fired when this comment is discarded.
+ *
+ * @event comment-discard
+ */
+
+ /**
+ * Fired when this comment is saved.
+ *
+ * @event comment-save
+ */
+
+ /**
+ * Fired when this comment is updated.
+ *
+ * @event comment-update
+ */
+
+ /**
+ * Fired when the comment's timestamp is tapped.
+ *
+ * @event comment-anchor-tap
+ */
+
+ properties: {
+ changeNum: String,
+ /** @type {?} */
+ comment: {
+ type: Object,
+ notify: true,
+ observer: '_commentChanged',
+ },
+ isRobotComment: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
+ disabled: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
+ draft: {
+ type: Boolean,
+ value: false,
+ observer: '_draftChanged',
+ },
+ editing: {
+ type: Boolean,
+ value: false,
+ observer: '_editingChanged',
+ },
+ discarding: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
+ hasChildren: Boolean,
+ patchNum: String,
+ showActions: Boolean,
+ _showHumanActions: Boolean,
+ _showRobotActions: Boolean,
+ collapsed: {
+ type: Boolean,
+ value: true,
+ observer: '_toggleCollapseClass',
+ },
+ /** @type {?} */
+ projectConfig: Object,
+ robotButtonDisabled: Boolean,
+ _isAdmin: {
+ type: Boolean,
+ value: false,
+ },
+
+ _xhrPromise: Object, // Used for testing.
+ _messageText: {
+ type: String,
+ value: '',
+ observer: '_messageTextChanged',
+ },
+ commentSide: String,
+
+ resolved: Boolean,
+
+ _numPendingDraftRequests: {
+ type: Object,
+ value: {number: 0}, // Intentional to share the object across instances.
+ },
+
+ _enableOverlay: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * Property for storing references to overlay elements. When the overlays
+ * are moved to Gerrit.getRootElement() to be shown they are no-longer
+ * children, so they can't be queried along the tree, so they are stored
+ * here.
+ */
+ _overlays: {
+ type: Object,
+ value: () => ({}),
+ },
+ },
+
+ observers: [
+ '_commentMessageChanged(comment.message)',
+ '_loadLocalDraft(changeNum, patchNum, comment)',
+ '_isRobotComment(comment)',
+ '_calculateActionstoShow(showActions, isRobotComment)',
+ ],
+
+ behaviors: [
+ Gerrit.KeyboardShortcutBehavior,
+ ],
+
+ keyBindings: {
+ 'ctrl+enter meta+enter ctrl+s meta+s': '_handleSaveKey',
+ 'esc': '_handleEsc',
+ },
+
+ attached() {
+ if (this.editing) {
+ this.collapsed = false;
+ } else if (this.comment) {
+ this.collapsed = this.comment.collapsed;
+ }
+ this._getIsAdmin().then(isAdmin => {
+ this._isAdmin = isAdmin;
+ });
+ },
+
+ detached() {
+ this.cancelDebouncer('fire-update');
+ if (this.textarea) {
+ this.textarea.closeDropdown();
+ }
+ },
+
+ get textarea() {
+ return this.$$('#editTextarea');
+ },
+
+ get confirmDeleteOverlay() {
+ if (!this._overlays.confirmDelete) {
+ this._enableOverlay = true;
+ Polymer.dom.flush();
+ this._overlays.confirmDelete = this.$$('#confirmDeleteOverlay');
+ }
+ return this._overlays.confirmDelete;
+ },
+
+ get confirmDiscardOverlay() {
+ if (!this._overlays.confirmDiscard) {
+ this._enableOverlay = true;
+ Polymer.dom.flush();
+ this._overlays.confirmDiscard = this.$$('#confirmDiscardOverlay');
+ }
+ return this._overlays.confirmDiscard;
+ },
+
+ _computeShowHideText(collapsed) {
+ return collapsed ? '◀' : '▼';
+ },
+
+ _calculateActionstoShow(showActions, isRobotComment) {
+ this._showHumanActions = showActions && !isRobotComment;
+ this._showRobotActions = showActions && isRobotComment;
+ },
+
+ _isRobotComment(comment) {
+ this.isRobotComment = !!comment.robot_id;
+ },
+
+ isOnParent() {
+ return this.side === 'PARENT';
+ },
+
+ _getIsAdmin() {
+ return this.$.restAPI.getIsAdmin();
+ },
+
+ /**
+ * @param {*=} opt_comment
+ */
+ save(opt_comment) {
+ let comment = opt_comment;
+ if (!comment) { comment = this.comment; }
+
+ this.set('comment.message', this._messageText);
+ this.editing = false;
+ this.disabled = true;
+
+ if (!this._messageText) {
+ return this._discardDraft();
+ }
+
+ this._xhrPromise = this._saveDraft(comment).then(response => {
+ this.disabled = false;
+ if (!response.ok) { return response; }
+
+ this._eraseDraftComment();
+ return this.$.restAPI.getResponseObject(response).then(obj => {
+ const resComment = obj;
+ resComment.__draft = true;
+ // Maintain the ephemeral draft ID for identification by other
+ // elements.
+ if (this.comment.__draftID) {
+ resComment.__draftID = this.comment.__draftID;
+ }
+ resComment.__commentSide = this.commentSide;
+ this.comment = resComment;
+ this._fireSave();
+ return obj;
+ });
+ }).catch(err => {
+ this.disabled = false;
+ throw err;
+ });
+
+ return this._xhrPromise;
+ },
+
+ _eraseDraftComment() {
+ // Prevents a race condition in which removing the draft comment occurs
+ // prior to it being saved.
+ this.cancelDebouncer('store');
+
+ this.$.storage.eraseDraftComment({
+ changeNum: this.changeNum,
+ patchNum: this._getPatchNum(),
+ path: this.comment.path,
+ line: this.comment.line,
+ range: this.comment.range,
+ });
+ },
+
+ _commentChanged(comment) {
+ this.editing = !!comment.__editing;
+ this.resolved = !comment.unresolved;
+ if (this.editing) { // It's a new draft/reply, notify.
+ this._fireUpdate();
+ }
+ },
+
+ /**
+ * @param {!Object=} opt_mixin
+ *
+ * @return {!Object}
+ */
+ _getEventPayload(opt_mixin) {
+ return Object.assign({}, opt_mixin, {
+ comment: this.comment,
+ patchNum: this.patchNum,
+ });
+ },
+
+ _fireSave() {
+ this.fire('comment-save', this._getEventPayload());
+ },
+
+ _fireUpdate() {
+ this.debounce('fire-update', () => {
+ this.fire('comment-update', this._getEventPayload());
+ });
+ },
+
+ _draftChanged(draft) {
+ this.$.container.classList.toggle('draft', draft);
+ },
+
+ _editingChanged(editing, previousValue) {
+ this.$.container.classList.toggle('editing', editing);
+ if (this.comment && this.comment.id) {
+ this.$$('.cancel').hidden = !editing;
+ }
+ if (this.comment) {
+ this.comment.__editing = this.editing;
+ }
+ if (editing != !!previousValue) {
+ // To prevent event firing on comment creation.
+ this._fireUpdate();
+ }
+ if (editing) {
+ this.async(() => {
+ Polymer.dom.flush();
+ this.textarea.putCursorAtEnd();
+ }, 1);
+ }
+ },
+
+ _computeDeleteButtonClass(isAdmin, draft) {
+ return isAdmin && !draft ? 'showDeleteButtons' : '';
+ },
+
+ _computeSaveDisabled(draft, comment, resolved) {
+ // If resolved state has changed and a msg exists, save should be enabled.
+ if (comment.unresolved === resolved && draft) { return false; }
+ return !draft || draft.trim() === '';
+ },
+
+ _handleSaveKey(e) {
+ if (!this._computeSaveDisabled(this._messageText, this.comment,
+ this.resolved)) {
+ e.preventDefault();
+ this._handleSave(e);
+ }
+ },
+
+ _handleEsc(e) {
+ if (!this._messageText.length) {
+ e.preventDefault();
+ this._handleCancel(e);
+ }
+ },
+
+ _handleToggleCollapsed() {
+ this.collapsed = !this.collapsed;
+ },
+
+ _toggleCollapseClass(collapsed) {
+ if (collapsed) {
+ this.$.container.classList.add('collapsed');
+ } else {
+ this.$.container.classList.remove('collapsed');
+ }
+ },
+
+ _commentMessageChanged(message) {
+ this._messageText = message || '';
+ },
+
+ _messageTextChanged(newValue, oldValue) {
+ if (!this.comment || (this.comment && this.comment.id)) { return; }
+
+ this.debounce('store', () => {
+ const message = this._messageText;
+ const commentLocation = {
+ changeNum: this.changeNum,
+ patchNum: this._getPatchNum(),
+ path: this.comment.path,
+ line: this.comment.line,
+ range: this.comment.range,
+ };
+
+ if ((!this._messageText || !this._messageText.length) && oldValue) {
+ // If the draft has been modified to be empty, then erase the storage
+ // entry.
+ this.$.storage.eraseDraftComment(commentLocation);
+ } else {
+ this.$.storage.setDraftComment(commentLocation, message);
+ }
+ }, STORAGE_DEBOUNCE_INTERVAL);
+ },
+
+ _handleAnchorTap(e) {
+ e.preventDefault();
+ if (!this.comment.line) { return; }
+ this.dispatchEvent(new CustomEvent('comment-anchor-tap', {
+ bubbles: true,
+ detail: {
+ number: this.comment.line || FILE,
+ side: this.side,
+ },
+ }));
+ },
+
+ _handleEdit(e) {
+ e.preventDefault();
+ this._messageText = this.comment.message;
+ this.editing = true;
+ this.$.reporting.recordDraftInteraction();
+ },
+
+ _handleSave(e) {
+ e.preventDefault();
+
+ // Ignore saves started while already saving.
+ if (this.disabled) { return; }
+ const timingLabel = this.comment.id ?
+ REPORT_UPDATE_DRAFT : REPORT_CREATE_DRAFT;
+ const timer = this.$.reporting.getTimer(timingLabel);
+ this.set('comment.__editing', false);
+ return this.save().then(() => { timer.end(); });
+ },
+
+ _handleCancel(e) {
+ e.preventDefault();
+
+ if (!this.comment.message ||
+ this.comment.message.trim().length === 0 ||
+ !this.comment.id) {
+ this._fireDiscard();
+ return;
+ }
+ this._messageText = this.comment.message;
+ this.editing = false;
+ },
+
+ _fireDiscard() {
+ this.cancelDebouncer('fire-update');
+ this.fire('comment-discard', this._getEventPayload());
+ },
+
+ _handleFix() {
+ this.dispatchEvent(new CustomEvent('create-fix-comment', {
+ bubbles: true,
+ detail: this._getEventPayload(),
+ }));
+ },
+
+ _handleDiscard(e) {
+ e.preventDefault();
+ this.$.reporting.recordDraftInteraction();
+
+ if (!this._messageText) {
+ this._discardDraft();
+ return;
+ }
+
+ this._openOverlay(this.confirmDiscardOverlay).then(() => {
+ this.confirmDiscardOverlay.querySelector('#confirmDiscardDialog')
+ .resetFocus();
+ });
+ },
+
+ _handleConfirmDiscard(e) {
+ e.preventDefault();
+ const timer = this.$.reporting.getTimer(REPORT_DISCARD_DRAFT);
+ this._closeConfirmDiscardOverlay();
+ return this._discardDraft().then(() => { timer.end(); });
+ },
+
+ _discardDraft() {
+ if (!this.comment.__draft) {
+ throw Error('Cannot discard a non-draft comment.');
+ }
+ this.discarding = true;
+ this.editing = false;
+ this.disabled = true;
+ this._eraseDraftComment();
+
+ if (!this.comment.id) {
+ this.disabled = false;
+ this._fireDiscard();
+ return;
+ }
+
+ this._xhrPromise = this._deleteDraft(this.comment).then(response => {
+ this.disabled = false;
+ if (!response.ok) {
+ this.discarding = false;
+ return response;
+ }
+
+ this._fireDiscard();
+ }).catch(err => {
+ this.disabled = false;
+ throw err;
+ });
+
+ return this._xhrPromise;
+ },
+
+ _closeConfirmDiscardOverlay() {
+ this._closeOverlay(this.confirmDiscardOverlay);
+ },
+
+ _getSavingMessage(numPending) {
+ if (numPending === 0) { return SAVED_MESSAGE; }
+ return [
+ SAVING_MESSAGE,
+ numPending,
+ numPending === 1 ? DRAFT_SINGULAR : DRAFT_PLURAL,
+ ].join(' ');
+ },
+
+ _showStartRequest() {
+ const numPending = ++this._numPendingDraftRequests.number;
+ this._updateRequestToast(numPending);
+ },
+
+ _showEndRequest() {
+ const numPending = --this._numPendingDraftRequests.number;
+ this._updateRequestToast(numPending);
+ },
+
+ _handleFailedDraftRequest() {
+ this._numPendingDraftRequests.number--;
+
+ // Cancel the debouncer so that error toasts from the error-manager will
+ // not be overridden.
+ this.cancelDebouncer('draft-toast');
+ },
+
+ _updateRequestToast(numPending) {
+ const message = this._getSavingMessage(numPending);
+ this.debounce('draft-toast', () => {
+ // 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}));
+ }, TOAST_DEBOUNCE_INTERVAL);
+ },
+
+ _saveDraft(draft) {
+ this._showStartRequest();
+ return this.$.restAPI.saveDiffDraft(this.changeNum, this.patchNum, draft)
+ .then(result => {
+ if (result.ok) {
+ this._showEndRequest();
+ } else {
+ this._handleFailedDraftRequest();
+ }
+ return result;
+ });
+ },
+
+ _deleteDraft(draft) {
+ this._showStartRequest();
+ return this.$.restAPI.deleteDiffDraft(this.changeNum, this.patchNum,
+ draft).then(result => {
+ if (result.ok) {
+ this._showEndRequest();
+ } else {
+ this._handleFailedDraftRequest();
+ }
+ return result;
+ });
+ },
+
+ _getPatchNum() {
+ return this.isOnParent() ? 'PARENT' : this.patchNum;
+ },
+
+ _loadLocalDraft(changeNum, patchNum, comment) {
+ // Only apply local drafts to comments that haven't been saved
+ // remotely, and haven't been given a default message already.
+ //
+ // Don't get local draft if there is another comment that is currently
+ // in an editing state.
+ if (!comment || comment.id || comment.message || comment.__otherEditing) {
+ delete comment.__otherEditing;
+ return;
+ }
+
+ const draft = this.$.storage.getDraftComment({
+ changeNum,
+ patchNum: this._getPatchNum(),
+ path: comment.path,
+ line: comment.line,
+ range: comment.range,
+ });
+
+ if (draft) {
+ this.set('comment.message', draft.message);
+ }
+ },
+
+ _handleToggleResolved() {
+ this.$.reporting.recordDraftInteraction();
+ this.resolved = !this.resolved;
+ // Modify payload instead of this.comment, as this.comment is passed from
+ // the parent by ref.
+ const payload = this._getEventPayload();
+ payload.comment.unresolved = !this.$.resolvedCheckbox.checked;
+ this.fire('comment-update', payload);
+ if (!this.editing) {
+ // Save the resolved state immediately.
+ this.save(payload.comment);
+ }
+ },
+
+ _handleCommentDelete() {
+ this._openOverlay(this.confirmDeleteOverlay);
+ },
+
+ _handleCancelDeleteComment() {
+ this._closeOverlay(this.confirmDeleteOverlay);
+ },
+
+ _openOverlay(overlay) {
+ Polymer.dom(Gerrit.getRootElement()).appendChild(overlay);
+ return overlay.open();
+ },
+
+ _closeOverlay(overlay) {
+ Polymer.dom(Gerrit.getRootElement()).removeChild(overlay);
+ overlay.close();
+ },
+
+ _handleConfirmDeleteComment() {
+ const dialog =
+ this.confirmDeleteOverlay.querySelector('#confirmDeleteComment');
+ this.$.restAPI.deleteComment(
+ this.changeNum, this.patchNum, this.comment.id, dialog.message)
+ .then(newComment => {
+ this._handleCancelDeleteComment();
+ this.comment = newComment;
+ });
+ },
+ });
+})();
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
new file mode 100644
index 0000000000..7ca524291a
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html
@@ -0,0 +1,847 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2015 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-comment</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="../../../bower_components/page/page.js"></script>
+<script src="../../../scripts/util.js"></script>
+
+<link rel="import" href="gr-comment.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-comment></gr-comment>
+ </template>
+</test-fixture>
+
+<test-fixture id="draft">
+ <template>
+ <gr-comment draft="true"></gr-comment>
+ </template>
+</test-fixture>
+
+<script>
+
+ function isVisible(el) {
+ assert.ok(el);
+ return getComputedStyle(el).getPropertyValue('display') !== 'none';
+ }
+
+ suite('gr-comment tests', () => {
+ let element;
+ let sandbox;
+ setup(() => {
+ stub('gr-rest-api-interface', {
+ getAccount() { return Promise.resolve(null); },
+ });
+ element = fixture('basic');
+ element.comment = {
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ id: 'baf0414d_60047215',
+ line: 5,
+ message: 'is this a crossover episode!?',
+ updated: '2015-12-08 19:48:33.843000000',
+ };
+ sandbox = sinon.sandbox.create();
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('collapsible comments', () => {
+ // When a comment (not draft) is loaded, it should be collapsed
+ assert.isTrue(element.collapsed);
+ assert.isFalse(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is not visible');
+ assert.isFalse(isVisible(element.$$('.actions')),
+ 'actions are not visible');
+ assert.isNotOk(element.textarea, 'textarea is not visible');
+
+ // The header middle content is only visible when comments are collapsed.
+ // It shows the message in a condensed way, and limits to a single line.
+ assert.isTrue(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is visible');
+
+ // When the header row is clicked, the comment should expand
+ MockInteractions.tap(element.$.header);
+ assert.isFalse(element.collapsed);
+ assert.isTrue(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is visible');
+ assert.isTrue(isVisible(element.$$('.actions')),
+ 'actions are visible');
+ assert.isNotOk(element.textarea, 'textarea is not visible');
+ assert.isFalse(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is not visible');
+ });
+
+ test('clicking on date link fires event', () => {
+ element.side = 'PARENT';
+ const stub = sinon.stub();
+ element.addEventListener('comment-anchor-tap', stub);
+ const dateEl = element.$$('.date');
+ assert.ok(dateEl);
+ MockInteractions.tap(dateEl);
+
+ assert.isTrue(stub.called);
+ assert.deepEqual(stub.lastCall.args[0].detail,
+ {side: element.side, number: element.comment.line});
+ });
+
+ test('message is not retrieved from storage when other edits', done => {
+ const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
+ const loadSpy = sandbox.spy(element, '_loadLocalDraft');
+
+ element.changeNum = 1;
+ element.patchNum = 1;
+ element.comment = {
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ line: 5,
+ __otherEditing: true,
+ };
+ flush(() => {
+ assert.isTrue(loadSpy.called);
+ assert.isFalse(storageStub.called);
+ done();
+ });
+ });
+
+ test('message is retrieved from storage when no other edits', done => {
+ const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
+ const loadSpy = sandbox.spy(element, '_loadLocalDraft');
+
+ element.changeNum = 1;
+ element.patchNum = 1;
+ element.comment = {
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com',
+ },
+ line: 5,
+ };
+ flush(() => {
+ assert.isTrue(loadSpy.called);
+ assert.isTrue(storageStub.called);
+ done();
+ });
+ });
+
+ test('_getPatchNum', () => {
+ element.side = 'PARENT';
+ element.patchNum = 1;
+ assert.equal(element._getPatchNum(), 'PARENT');
+ element.side = 'REVISION';
+ assert.equal(element._getPatchNum(), 1);
+ });
+
+ test('comment expand and collapse', () => {
+ element.collapsed = true;
+ assert.isFalse(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is not visible');
+ assert.isFalse(isVisible(element.$$('.actions')),
+ 'actions are not visible');
+ assert.isNotOk(element.textarea, 'textarea is not visible');
+ assert.isTrue(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is visible');
+
+ element.collapsed = false;
+ assert.isFalse(element.collapsed);
+ assert.isTrue(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is visible');
+ assert.isTrue(isVisible(element.$$('.actions')),
+ 'actions are visible');
+ assert.isNotOk(element.textarea, 'textarea is not visible');
+ assert.isFalse(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is is not visible');
+ });
+
+ suite('while editing', () => {
+ setup(() => {
+ element.editing = true;
+ element._messageText = 'test';
+ sandbox.stub(element, '_handleCancel');
+ sandbox.stub(element, '_handleSave');
+ flushAsynchronousOperations();
+ });
+
+ suite('when text is empty', () => {
+ setup(() => {
+ element._messageText = '';
+ element.comment = {};
+ });
+
+ test('esc closes comment when text is empty', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 27); // esc
+ assert.isTrue(element._handleCancel.called);
+ });
+
+ test('ctrl+enter does not save', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 13, 'ctrl'); // ctrl + enter
+ assert.isFalse(element._handleSave.called);
+ });
+
+ test('meta+enter does not save', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 13, 'meta'); // meta + enter
+ assert.isFalse(element._handleSave.called);
+ });
+
+ test('ctrl+s does not save', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 83, 'ctrl'); // ctrl + s
+ assert.isFalse(element._handleSave.called);
+ });
+ });
+
+ test('esc does not close comment that has content', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 27); // esc
+ assert.isFalse(element._handleCancel.called);
+ });
+
+ test('ctrl+enter saves', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 13, 'ctrl'); // ctrl + enter
+ assert.isTrue(element._handleSave.called);
+ });
+
+ test('meta+enter saves', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 13, 'meta'); // meta + enter
+ assert.isTrue(element._handleSave.called);
+ });
+
+ test('ctrl+s saves', () => {
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea, 83, 'ctrl'); // ctrl + s
+ assert.isTrue(element._handleSave.called);
+ });
+ });
+ test('delete comment button for non-admins is hidden', () => {
+ element._isAdmin = false;
+ assert.isFalse(element.$$('.action.delete')
+ .classList.contains('showDeleteButtons'));
+ });
+
+ test('delete comment button for admins with draft is hidden', () => {
+ element._isAdmin = false;
+ element.draft = true;
+ assert.isFalse(element.$$('.action.delete')
+ .classList.contains('showDeleteButtons'));
+ });
+
+ test('delete comment', done => {
+ sandbox.stub(
+ element.$.restAPI, 'deleteComment').returns(Promise.resolve({}));
+ sandbox.spy(element.confirmDeleteOverlay, 'open');
+ element.changeNum = 42;
+ element.patchNum = 0xDEADBEEF;
+ element._isAdmin = true;
+ assert.isTrue(element.$$('.action.delete')
+ .classList.contains('showDeleteButtons'));
+ MockInteractions.tap(element.$$('.action.delete'));
+ flush(() => {
+ element.confirmDeleteOverlay.open.lastCall.returnValue.then(() => {
+ const dialog =
+ this.confirmDeleteOverlay.querySelector('#confirmDeleteComment');
+ dialog.message = 'removal reason';
+ element._handleConfirmDeleteComment();
+ assert.isTrue(element.$.restAPI.deleteComment.calledWith(
+ 42, 0xDEADBEEF, 'baf0414d_60047215', 'removal reason'));
+ done();
+ });
+ });
+ });
+
+ suite('draft update reporting', () => {
+ let endStub;
+ let getTimerStub;
+ let mockEvent;
+
+ setup(() => {
+ mockEvent = {preventDefault() {}};
+ sandbox.stub(element, 'save')
+ .returns(Promise.resolve({}));
+ sandbox.stub(element, '_discardDraft')
+ .returns(Promise.resolve({}));
+ endStub = sinon.stub();
+ getTimerStub = sandbox.stub(element.$.reporting, 'getTimer')
+ .returns({end: endStub});
+ });
+
+ test('create', () => {
+ element.comment = {};
+ return element._handleSave(mockEvent).then(() => {
+ assert.isTrue(endStub.calledOnce);
+ assert.isTrue(getTimerStub.calledOnce);
+ assert.equal(getTimerStub.lastCall.args[0], 'CreateDraftComment');
+ });
+ });
+
+ test('update', () => {
+ element.comment = {id: 'abc_123'};
+ return element._handleSave(mockEvent).then(() => {
+ assert.isTrue(endStub.calledOnce);
+ assert.isTrue(getTimerStub.calledOnce);
+ assert.equal(getTimerStub.lastCall.args[0], 'UpdateDraftComment');
+ });
+ });
+
+ test('discard', () => {
+ element.comment = {id: 'abc_123'};
+ sandbox.stub(element, '_closeConfirmDiscardOverlay');
+ return element._handleConfirmDiscard(mockEvent).then(() => {
+ assert.isTrue(endStub.calledOnce);
+ assert.isTrue(getTimerStub.calledOnce);
+ assert.equal(getTimerStub.lastCall.args[0], 'DiscardDraftComment');
+ });
+ });
+ });
+
+ test('edit reports interaction', () => {
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ MockInteractions.tap(element.$$('.edit'));
+ assert.isTrue(reportStub.calledOnce);
+ });
+
+ test('discard reports interaction', () => {
+ const reportStub = sandbox.stub(element.$.reporting,
+ 'recordDraftInteraction');
+ element.draft = true;
+ MockInteractions.tap(element.$$('.discard'));
+ assert.isTrue(reportStub.calledOnce);
+ });
+ });
+
+ suite('gr-comment draft tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ stub('gr-rest-api-interface', {
+ getAccount() { return Promise.resolve(null); },
+ saveDiffDraft() {
+ return Promise.resolve({
+ ok: true,
+ text() {
+ return Promise.resolve(
+ ')]}\'\n{' +
+ '"id": "baf0414d_40572e03",' +
+ '"path": "/path/to/file",' +
+ '"line": 5,' +
+ '"updated": "2015-12-08 21:52:36.177000000",' +
+ '"message": "saved!"' +
+ '}'
+ );
+ },
+ });
+ },
+ removeChangeReviewer() {
+ return Promise.resolve({ok: true});
+ },
+ });
+ stub('gr-storage', {
+ getDraftComment() { return null; },
+ });
+ element = fixture('draft');
+ element.changeNum = 42;
+ element.patchNum = 1;
+ element.editing = false;
+ element.comment = {
+ __commentSide: 'right',
+ __draft: true,
+ __draftID: 'temp_draft_id',
+ path: '/path/to/file',
+ line: 5,
+ };
+ element.commentSide = 'right';
+ sandbox = sinon.sandbox.create();
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('button visibility states', () => {
+ element.showActions = false;
+ assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
+
+ element.showActions = true;
+ assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
+
+ element.draft = true;
+ assert.isTrue(isVisible(element.$$('.edit')), 'edit is visible');
+ assert.isTrue(isVisible(element.$$('.discard')), 'discard is visible');
+ assert.isFalse(isVisible(element.$$('.save')), 'save is not visible');
+ assert.isFalse(isVisible(element.$$('.cancel')), 'cancel is not visible');
+ assert.isTrue(isVisible(element.$$('.resolve')), 'resolve is visible');
+ assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
+
+ element.editing = true;
+ flushAsynchronousOperations();
+ assert.isFalse(isVisible(element.$$('.edit')), 'edit is not visible');
+ assert.isFalse(isVisible(element.$$('.discard')), 'discard not visible');
+ assert.isTrue(isVisible(element.$$('.save')), 'save is visible');
+ assert.isTrue(isVisible(element.$$('.cancel')), 'cancel is visible');
+ assert.isTrue(isVisible(element.$$('.resolve')), 'resolve is visible');
+ assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
+
+ element.draft = false;
+ element.editing = false;
+ flushAsynchronousOperations();
+ assert.isFalse(isVisible(element.$$('.edit')), 'edit is not visible');
+ assert.isFalse(isVisible(element.$$('.discard')),
+ 'discard is not visible');
+ assert.isFalse(isVisible(element.$$('.save')), 'save is not visible');
+ assert.isFalse(isVisible(element.$$('.cancel')), 'cancel is not visible');
+ assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
+
+ element.comment.id = 'foo';
+ element.draft = true;
+ element.editing = true;
+ flushAsynchronousOperations();
+ assert.isTrue(isVisible(element.$$('.cancel')), 'cancel is visible');
+ assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
+
+ element.isRobotComment = true;
+ element.draft = true;
+ assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isFalse(element.$$('.robotActions').hasAttribute('hidden'));
+
+ // It is not expected to see Robot comment drafts, but if they appear,
+ // they will behave the same as non-drafts.
+ element.draft = false;
+ assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
+ assert.isFalse(element.$$('.robotActions').hasAttribute('hidden'));
+
+ // A robot comment with run ID should display plain text.
+ element.set(['comment', 'robot_run_id'], 'text');
+ element.editing = false;
+ element.collapsed = false;
+ flushAsynchronousOperations();
+ assert.isNotOk(element.$$('.robotRun.link'));
+ assert.notEqual(getComputedStyle(element.$$('.robotRun.text')).display,
+ 'none');
+
+ // A robot comment with run ID and url should display a link.
+ element.set(['comment', 'url'], '/path/to/run');
+ flushAsynchronousOperations();
+ assert.notEqual(getComputedStyle(element.$$('.robotRun.link')).display,
+ 'none');
+ assert.equal(getComputedStyle(element.$$('.robotRun.text')).display,
+ 'none');
+ });
+
+ test('collapsible drafts', () => {
+ assert.isTrue(element.collapsed);
+ assert.isFalse(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is not visible');
+ assert.isFalse(isVisible(element.$$('.actions')),
+ 'actions are not visible');
+ assert.isNotOk(element.textarea, 'textarea is not visible');
+ assert.isTrue(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is visible');
+
+ MockInteractions.tap(element.$.header);
+ assert.isFalse(element.collapsed);
+ assert.isTrue(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is visible');
+ assert.isTrue(isVisible(element.$$('.actions')),
+ 'actions are visible');
+ assert.isNotOk(element.textarea, 'textarea is not visible');
+ assert.isFalse(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is is not visible');
+
+ // When the edit button is pressed, should still see the actions
+ // and also textarea
+ MockInteractions.tap(element.$$('.edit'));
+ flushAsynchronousOperations();
+ assert.isFalse(element.collapsed);
+ assert.isFalse(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is not visible');
+ assert.isTrue(isVisible(element.$$('.actions')),
+ 'actions are visible');
+ assert.isTrue(isVisible(element.textarea), 'textarea is visible');
+ assert.isFalse(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is not visible');
+
+ // When toggle again, everything should be hidden except for textarea
+ // and header middle content should be visible
+ MockInteractions.tap(element.$.header);
+ assert.isTrue(element.collapsed);
+ assert.isFalse(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is not visible');
+ assert.isFalse(isVisible(element.$$('.actions')),
+ 'actions are not visible');
+ assert.isFalse(isVisible(element.$$('gr-textarea')),
+ 'textarea is not visible');
+ assert.isTrue(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is visible');
+
+ // When toggle again, textarea should remain open in the state it was
+ // before
+ MockInteractions.tap(element.$.header);
+ assert.isFalse(isVisible(element.$$('gr-formatted-text')),
+ 'gr-formatted-text is not visible');
+ assert.isTrue(isVisible(element.$$('.actions')),
+ 'actions are visible');
+ assert.isTrue(isVisible(element.textarea), 'textarea is visible');
+ assert.isFalse(isVisible(element.$$('.collapsedContent')),
+ 'header middle content is not visible');
+ });
+
+ test('draft creation/cancellation', done => {
+ assert.isFalse(element.editing);
+ MockInteractions.tap(element.$$('.edit'));
+ assert.isTrue(element.editing);
+
+ element._messageText = '';
+ const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
+
+ // Save should be disabled on an empty message.
+ let disabled = element.$$('.save').hasAttribute('disabled');
+ assert.isTrue(disabled, 'save button should be disabled.');
+ element._messageText = ' ';
+ disabled = element.$$('.save').hasAttribute('disabled');
+ assert.isTrue(disabled, 'save button should be disabled.');
+
+ const updateStub = sinon.stub();
+ element.addEventListener('comment-update', updateStub);
+
+ let numDiscardEvents = 0;
+ element.addEventListener('comment-discard', e => {
+ numDiscardEvents++;
+ assert.isFalse(eraseMessageDraftSpy.called);
+ if (numDiscardEvents === 2) {
+ assert.isFalse(updateStub.called);
+ done();
+ }
+ });
+ MockInteractions.tap(element.$$('.cancel'));
+ element.flushDebouncer('fire-update');
+ element._messageText = '';
+ flushAsynchronousOperations();
+ MockInteractions.pressAndReleaseKeyOn(element.textarea, 27); // esc
+ });
+
+ test('draft discard removes message from storage', done => {
+ element._messageText = '';
+ const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
+ sandbox.stub(element, '_closeConfirmDiscardOverlay');
+
+ element.addEventListener('comment-discard', e => {
+ assert.isTrue(eraseMessageDraftSpy.called);
+ done();
+ });
+ element._handleConfirmDiscard({preventDefault: sinon.stub()});
+ });
+
+ test('storage is cleared only after save success', () => {
+ element._messageText = 'test';
+ const eraseStub = sandbox.stub(element, '_eraseDraftComment');
+ sandbox.stub(element.$.restAPI, 'getResponseObject')
+ .returns(Promise.resolve({}));
+
+ sandbox.stub(element, '_saveDraft').returns(Promise.resolve({ok: false}));
+
+ const savePromise = element.save();
+ assert.isFalse(eraseStub.called);
+ return savePromise.then(() => {
+ assert.isFalse(eraseStub.called);
+
+ element._saveDraft.restore();
+ sandbox.stub(element, '_saveDraft')
+ .returns(Promise.resolve({ok: true}));
+ return element.save().then(() => {
+ assert.isTrue(eraseStub.called);
+ });
+ });
+ });
+
+ test('_computeSaveDisabled', () => {
+ const comment = {unresolved: true};
+ const msgComment = {message: 'test', unresolved: true};
+ assert.equal(element._computeSaveDisabled('', comment, false), true);
+ assert.equal(element._computeSaveDisabled('test', comment, false), false);
+ assert.equal(element._computeSaveDisabled('', msgComment, false), true);
+ assert.equal(
+ element._computeSaveDisabled('test', msgComment, false), false);
+ assert.equal(
+ element._computeSaveDisabled('test2', msgComment, false), false);
+ assert.equal(element._computeSaveDisabled('test', comment, true), false);
+ assert.equal(element._computeSaveDisabled('', comment, true), true);
+ assert.equal(element._computeSaveDisabled('', comment, false), true);
+ });
+
+ suite('confirm discard', () => {
+ let discardStub;
+ let overlayStub;
+ let mockEvent;
+
+ setup(() => {
+ discardStub = sandbox.stub(element, '_discardDraft');
+ overlayStub = sandbox.stub(element, '_openOverlay')
+ .returns(Promise.resolve());
+ mockEvent = {preventDefault: sinon.stub()};
+ });
+
+ test('confirms discard of comments with message text', () => {
+ element._messageText = 'test';
+ element._handleDiscard(mockEvent);
+ assert.isTrue(overlayStub.calledWith(element.confirmDiscardOverlay));
+ assert.isFalse(discardStub.called);
+ });
+
+ test('no confirmation for comments without message text', () => {
+ element._messageText = '';
+ element._handleDiscard(mockEvent);
+ assert.isFalse(overlayStub.called);
+ assert.isTrue(discardStub.calledOnce);
+ });
+ });
+
+ test('ctrl+s saves comment', done => {
+ const stub = sinon.stub(element, 'save', () => {
+ assert.isTrue(stub.called);
+ stub.restore();
+ done();
+ return Promise.resolve();
+ });
+ element._messageText = 'is that the horse from horsing around??';
+ element.editing = true;
+ flushAsynchronousOperations();
+ MockInteractions.pressAndReleaseKeyOn(
+ element.textarea.$.textarea.textarea,
+ 83, 'ctrl'); // 'ctrl + s'
+ });
+
+ test('draft saving/editing', done => {
+ const fireStub = sinon.stub(element, 'fire');
+ const cancelDebounce = sandbox.stub(element, 'cancelDebouncer');
+
+ element.draft = true;
+ MockInteractions.tap(element.$$('.edit'));
+ element._messageText = 'good news, everyone!';
+ element.flushDebouncer('fire-update');
+ element.flushDebouncer('store');
+ assert(fireStub.calledWith('comment-update'),
+ 'comment-update should be sent');
+ assert.isTrue(fireStub.calledOnce);
+
+ element._messageText = 'good news, everyone!';
+ element.flushDebouncer('fire-update');
+ element.flushDebouncer('store');
+ assert.isTrue(fireStub.calledOnce,
+ 'No events should fire for text editing');
+
+ MockInteractions.tap(element.$$('.save'));
+
+ assert.isTrue(element.disabled,
+ 'Element should be disabled when creating draft.');
+
+ element._xhrPromise.then(draft => {
+ assert(fireStub.calledWith('comment-save'),
+ 'comment-save should be sent');
+ assert(cancelDebounce.calledWith('store'));
+
+ assert.deepEqual(fireStub.lastCall.args[1], {
+ comment: {
+ __commentSide: 'right',
+ __draft: true,
+ __draftID: 'temp_draft_id',
+ id: 'baf0414d_40572e03',
+ line: 5,
+ message: 'saved!',
+ path: '/path/to/file',
+ updated: '2015-12-08 21:52:36.177000000',
+ },
+ patchNum: 1,
+ });
+ assert.isFalse(element.disabled,
+ 'Element should be enabled when done creating draft.');
+ assert.equal(draft.message, 'saved!');
+ assert.isFalse(element.editing);
+ }).then(() => {
+ MockInteractions.tap(element.$$('.edit'));
+ element._messageText = 'You’ll be delivering a package to Chapek 9, ' +
+ 'a world where humans are killed on sight.';
+ MockInteractions.tap(element.$$('.save'));
+ assert.isTrue(element.disabled,
+ 'Element should be disabled when updating draft.');
+
+ element._xhrPromise.then(draft => {
+ assert.isFalse(element.disabled,
+ 'Element should be enabled when done updating draft.');
+ assert.equal(draft.message, 'saved!');
+ assert.isFalse(element.editing);
+ fireStub.restore();
+ done();
+ });
+ });
+ });
+
+ test('draft prevent save when disabled', () => {
+ const saveStub = sandbox.stub(element, 'save').returns(Promise.resolve());
+ element.showActions = true;
+ element.draft = true;
+ MockInteractions.tap(element.$.header);
+ MockInteractions.tap(element.$$('.edit'));
+ element._messageText = 'good news, everyone!';
+ element.flushDebouncer('fire-update');
+ element.flushDebouncer('store');
+
+ element.disabled = true;
+ MockInteractions.tap(element.$$('.save'));
+ assert.isFalse(saveStub.called);
+
+ element.disabled = false;
+ MockInteractions.tap(element.$$('.save'));
+ assert.isTrue(saveStub.calledOnce);
+ });
+
+ test('proper event fires on resolve, comment is not saved', done => {
+ const save = sandbox.stub(element, 'save');
+ element.addEventListener('comment-update', e => {
+ assert.isTrue(e.detail.comment.unresolved);
+ assert.isFalse(save.called);
+ done();
+ });
+ MockInteractions.tap(element.$$('.resolve input'));
+ });
+
+ test('resolved comment state indicated by checkbox', () => {
+ sandbox.stub(element, 'save');
+ element.comment = {unresolved: false};
+ assert.isTrue(element.$$('.resolve input').checked);
+ element.comment = {unresolved: true};
+ assert.isFalse(element.$$('.resolve input').checked);
+ });
+
+ test('resolved checkbox saves with tap when !editing', () => {
+ element.editing = false;
+ const save = sandbox.stub(element, 'save');
+
+ element.comment = {unresolved: false};
+ assert.isTrue(element.$$('.resolve input').checked);
+ element.comment = {unresolved: true};
+ assert.isFalse(element.$$('.resolve input').checked);
+ assert.isFalse(save.called);
+ MockInteractions.tap(element.$.resolvedCheckbox);
+ assert.isTrue(element.$$('.resolve input').checked);
+ assert.isTrue(save.called);
+ });
+
+ suite('draft saving messages', () => {
+ test('_getSavingMessage', () => {
+ assert.equal(element._getSavingMessage(0), 'All changes saved');
+ assert.equal(element._getSavingMessage(1), 'Saving 1 draft...');
+ assert.equal(element._getSavingMessage(2), 'Saving 2 drafts...');
+ assert.equal(element._getSavingMessage(3), 'Saving 3 drafts...');
+ });
+
+ test('_show{Start,End}Request', () => {
+ const updateStub = sandbox.stub(element, '_updateRequestToast');
+ element._numPendingDraftRequests.number = 1;
+
+ element._showStartRequest();
+ assert.isTrue(updateStub.calledOnce);
+ assert.equal(updateStub.lastCall.args[0], 2);
+ assert.equal(element._numPendingDraftRequests.number, 2);
+
+ element._showEndRequest();
+ assert.isTrue(updateStub.calledTwice);
+ assert.equal(updateStub.lastCall.args[0], 1);
+ assert.equal(element._numPendingDraftRequests.number, 1);
+
+ element._showEndRequest();
+ assert.isTrue(updateStub.calledThrice);
+ assert.equal(updateStub.lastCall.args[0], 0);
+ assert.equal(element._numPendingDraftRequests.number, 0);
+ });
+ });
+
+ test('cancelling an unsaved draft discards, persists in storage', () => {
+ const discardSpy = sandbox.spy(element, '_fireDiscard');
+ const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
+ const eraseStub = sandbox.stub(element.$.storage, 'eraseDraftComment');
+ element._messageText = 'test text';
+ flushAsynchronousOperations();
+ element.flushDebouncer('store');
+
+ assert.isTrue(storeStub.called);
+ assert.equal(storeStub.lastCall.args[1], 'test text');
+ element._handleCancel({preventDefault: () => {}});
+ assert.isTrue(discardSpy.called);
+ assert.isFalse(eraseStub.called);
+ });
+
+ test('cancelling edit on a saved draft does not store', () => {
+ element.comment.id = 'foo';
+ const discardSpy = sandbox.spy(element, '_fireDiscard');
+ const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
+ element._messageText = 'test text';
+ flushAsynchronousOperations();
+ element.flushDebouncer('store');
+
+ assert.isFalse(storeStub.called);
+ element._handleCancel({preventDefault: () => {}});
+ assert.isTrue(discardSpy.called);
+ });
+
+ test('deleting text from saved draft and saving deletes the draft', () => {
+ element.comment = {id: 'foo', message: 'test'};
+ element._messageText = '';
+ const discardStub = sandbox.stub(element, '_discardDraft');
+
+ element.save();
+ assert.isTrue(discardStub.called);
+ });
+
+ test('_handleFix fires create-fix event', done => {
+ element.addEventListener('create-fix-comment', e => {
+ assert.deepEqual(e.detail, element._getEventPayload());
+ done();
+ });
+ element.isRobotComment = true;
+ flushAsynchronousOperations();
+
+ MockInteractions.tap(element.$$('.fix'));
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/diff/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..9decfa9563 100644
--- a/polygerrit-ui/app/elements/diff/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
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
new file mode 100644
index 0000000000..4ac059dd2e
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
@@ -0,0 +1,54 @@
+/**
+ * @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.
+ */
+(function() {
+ 'use strict';
+
+ Polymer({
+ is: 'gr-confirm-delete-comment-dialog',
+ _legacyUndefinedCheck: true,
+
+ /**
+ * Fired when the confirm button is pressed.
+ *
+ * @event confirm
+ */
+
+ /**
+ * Fired when the cancel button is pressed.
+ *
+ * @event cancel
+ */
+
+ properties: {
+ message: String,
+ },
+
+ resetFocus() {
+ this.$.messageInput.textarea.focus();
+ },
+
+ _handleConfirmTap(e) {
+ e.preventDefault();
+ this.fire('confirm', {reason: this.message}, {bubbles: false});
+ },
+
+ _handleCancelTap(e) {
+ e.preventDefault();
+ this.fire('cancel', null, {bubbles: false});
+ },
+ });
+})();
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 cabee36bcc..550f1df1e7 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,6 +21,7 @@
Polymer({
is: 'gr-copy-clipboard',
+ _legacyUndefinedCheck: true,
properties: {
text: String,
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 5e1f5872cb..766ac7fe84 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,6 +24,7 @@
Polymer({
is: 'gr-cursor-manager',
+ _legacyUndefinedCheck: true,
properties: {
stops: {
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 1090fea170..481dd2f912 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
@@ -20,7 +20,6 @@ limitations under the License.
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
-<script src="../../../bower_components/moment/moment.js"></script>
<script src="../../../scripts/util.js"></script>
<dom-module id="gr-date-formatter">
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 43ec20e3b6..a9ce4c9121 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
@@ -33,6 +33,7 @@
Polymer({
is: 'gr-date-formatter',
+ _legacyUndefinedCheck: true,
properties: {
dateStr: {
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 6163b09d90..b8b2af4a2e 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-dialog',
+ _legacyUndefinedCheck: true,
/**
* Fired when the confirm button is pressed.
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 36fdf5b051..89c3d74eed 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,6 +19,7 @@
Polymer({
is: 'gr-diff-preferences',
+ _legacyUndefinedCheck: true,
properties: {
hasUnsavedChanges: {
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 ca77a30e92..ed7c2cc046 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,6 +19,7 @@
Polymer({
is: 'gr-download-commands',
+ _legacyUndefinedCheck: true,
properties: {
commands: Array,
_loggedIn: {
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 2962dbe1d2..6b3905f5d2 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,6 +47,7 @@
Polymer({
is: 'gr-dropdown-list',
+ _legacyUndefinedCheck: true,
/**
* Fired when the selected value changes
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 7ee2f44f5a..b4b70875ab 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -22,6 +22,7 @@
Polymer({
is: 'gr-dropdown',
+ _legacyUndefinedCheck: true,
/**
* Fired when a non-link dropdown item with the given ID is tapped.
@@ -295,7 +296,9 @@
*/
_resetCursorStops() {
Polymer.dom.flush();
- this._listElements = Polymer.dom(this.root).querySelectorAll('li');
+ // Polymer2: querySelectorAll returns NodeList instead of Array.
+ this._listElements = Array.from(
+ Polymer.dom(this.root).querySelectorAll('li'));
},
_computeHasTooltip(tooltip) {
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 e5c58e0ba2..5463564aa0 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,6 +22,7 @@
Polymer({
is: 'gr-editable-content',
+ _legacyUndefinedCheck: true,
/**
* Fired when the save button is pressed.
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 3514492b11..f23afeae08 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,6 +22,7 @@
Polymer({
is: 'gr-editable-label',
+ _legacyUndefinedCheck: true,
/**
* Fired when the value is changed.
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 2c32709176..87cd4b4433 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,6 +19,7 @@
Polymer({
is: 'gr-fixed-panel',
+ _legacyUndefinedCheck: true,
properties: {
floatingDisabled: Boolean,
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 feae17314e..d84c38e759 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,6 +22,7 @@
Polymer({
is: 'gr-formatted-text',
+ _legacyUndefinedCheck: true,
properties: {
content: {
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 71621fab9e..f8ff549f95 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
@@ -27,6 +27,7 @@
Polymer({
is: 'gr-hovercard',
+ _legacyUndefinedCheck: true,
properties: {
/**
@@ -164,8 +165,8 @@
// If the hovercard is already hidden or the user is now hovering over the
// hovercard or the user is returning from the hovercard but now hovering
// over the target (to stop an annoying flicker effect), just return.
- if (!this._isShowing || e.toElement === this ||
- (e.fromElement === this && e.toElement === this._target)) {
+ if (!this._isShowing || e.relatedTarget === this ||
+ (e.target === this && e.relatedTarget === this._target)) {
return;
}
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 af137e5ec2..2581ff985d 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
@@ -20,15 +20,19 @@
/**
* Used to create a context for GrAnnotationActionsInterface.
*
- * @param {HTMLElement} el The DIV.contentText element to apply the
- * annotation to using annotateRange.
+ * @param {HTMLElement} contentEl The DIV.contentText element of the line
+ * content to apply the annotation to using annotateRange.
+ * @param {HTMLElement} lineNumberEl The TD element of the line number to
+ * apply the annotation to using annotateLineNumber.
* @param {GrDiffLine} line The line object.
- * @param {String} path The file path (eg: /COMMIT_MSG').
- * @param {String} changeNum The Gerrit change number.
- * @param {String} patchNum The Gerrit patch number.
+ * @param {string} path The file path (eg: /COMMIT_MSG').
+ * @param {string} changeNum The Gerrit change number.
+ * @param {string} patchNum The Gerrit patch number.
*/
- function GrAnnotationActionsContext(el, line, path, changeNum, patchNum) {
- this._el = el;
+ function GrAnnotationActionsContext(
+ contentEl, lineNumberEl, line, path, changeNum, patchNum) {
+ this._contentEl = contentEl;
+ this._lineNumberEl = lineNumberEl;
this.line = line;
this.path = path;
@@ -37,17 +41,30 @@
}
/**
- * Method to add annotations to a line.
+ * Method to add annotations to a content line.
*
- * @param {Number} start The line number where the update starts.
- * @param {Number} end The line number where the update ends.
- * @param {String} cssClass The name of a CSS class created using Gerrit.css.
- * @param {String} side The side of the update. ('left' or 'right')
+ * @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 {string} side The side of the update. ('left' or 'right')
*/
GrAnnotationActionsContext.prototype.annotateRange = function(
- start, end, cssClass, side) {
- if (this._el.getAttribute('data-side') == side) {
- GrAnnotation.annotateElement(this._el, start, end, cssClass);
+ offset, length, cssClass, side) {
+ if (this._contentEl && this._contentEl.getAttribute('data-side') == side) {
+ GrAnnotation.annotateElement(this._contentEl, offset, length, cssClass);
+ }
+ };
+
+ /**
+ * 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 {string} side The side of the update. ('left' or 'right')
+ */
+ GrAnnotationActionsContext.prototype.annotateLineNumber = function(
+ cssClass, side) {
+ if (this._lineNumberEl && this._lineNumberEl.classList.contains(side)) {
+ this._lineNumberEl.classList.add(cssClass);
}
};
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 cd86fa9f24..03c8c5e384 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
@@ -39,6 +39,7 @@ limitations under the License.
let instance;
let sandbox;
let el;
+ let lineNumberEl;
setup(() => {
sandbox = sinon.sandbox.create();
@@ -47,8 +48,10 @@ limitations under the License.
el = document.createElement('div');
el.textContent = str;
el.setAttribute('data-side', 'right');
+ lineNumberEl = document.createElement('td');
+ lineNumberEl.classList.add('right');
instance = new GrAnnotationActionsContext(
- el, line, 'dummy/path', '123', '1');
+ el, lineNumberEl, line, 'dummy/path', '123', '1');
});
teardown(() => {
@@ -74,5 +77,17 @@ limitations under the License.
assert.equal(args[2], end);
assert.equal(args[3], cssClass);
});
+
+ test('test annotateLineNumber', () => {
+ const cssClass = Gerrit.css('background-color: #000000');
+
+ // Assert that css class is *not* applied when side is different.
+ instance.annotateLineNumber(cssClass, 'left');
+ assert.isFalse(lineNumberEl.classList.contains(cssClass));
+
+ // Assert that css class is applied when side is the same.
+ instance.annotateLineNumber(cssClass, 'right');
+ assert.isTrue(lineNumberEl.classList.contains(cssClass));
+ });
});
</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 0be557fff6..3f91a826c5 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
@@ -26,14 +26,17 @@
// notifying their listeners in the notify function.
this._annotationLayers = [];
+ this._coverageProvider = null;
+
// Default impl is a no-op.
this._addLayerFunc = annotationActionsContext => {};
}
/**
* Register a function to call to apply annotations. Plugins should use
- * GrAnnotationActionsContext.annotateRange to apply a CSS class to a range
- * within a line.
+ * GrAnnotationActionsContext.annotateRange and
+ * GrAnnotationActionsContext.annotateLineNumber to apply a CSS class to the
+ * line content or the line number.
*
* @param {function(GrAnnotationActionsContext)} addLayerFunc The function
* that will be called when the AnnotationLayer is ready to annotate.
@@ -57,6 +60,37 @@
};
/**
+ * The specified function will be called when a gr-diff component is built,
+ * and feeds the returned coverage data into the diff. Optional.
+ *
+ * Be sure to call this only once and only from one plugin. Multiple coverage
+ * 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
+ * @return {GrAnnotationActionsInterface}
+ */
+ GrAnnotationActionsInterface.prototype.setCoverageProvider = function(
+ coverageProvider) {
+ if (this._coverageProvider) {
+ console.warn('Overwriting an existing coverage provider.');
+ }
+ this._coverageProvider = coverageProvider;
+ return this;
+ };
+
+ /**
+ * Used by Gerrit to look up the coverage provider. Not intended to be called
+ * by plugins.
+ */
+ GrAnnotationActionsInterface.prototype.getCoverageProvider = function() {
+ return this._coverageProvider;
+ };
+
+ /**
* Returns a checkbox HTMLElement that can be used to toggle annotations
* on/off. The checkbox will be initially disabled. Plugins should enable it
* when data is ready and should add a click handler to toggle CSS on/off.
@@ -165,13 +199,16 @@
/**
* Layer method to add annotations to a line.
*
- * @param {HTMLElement} el The DIV.contentText element to apply the
- * annotation to.
+ * @param {HTMLElement} contentEl The DIV.contentText element of the line
+ * content to apply the annotation to using annotateRange.
+ * @param {HTMLElement} lineNumberEl The TD element of the line number to
+ * apply the annotation to using annotateLineNumber.
* @param {GrDiffLine} line The line object.
*/
- AnnotationLayer.prototype.annotate = function(el, line) {
+ AnnotationLayer.prototype.annotate = function(contentEl, lineNumberEl, line) {
const annotationActionsContext = new GrAnnotationActionsContext(
- el, line, this._path, this._changeNum, this._patchNum);
+ contentEl, lineNumberEl, line, this._path, this._changeNum,
+ this._patchNum);
this._addLayerFunc(annotationActionsContext);
};
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 bfb8b47ad0..bf7c2cb939 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
@@ -71,7 +71,8 @@ limitations under the License.
const annotationLayer = annotationActions.getLayer(
'/dummy/path', changeNum, patchNum);
- annotationLayer.annotate(el, line);
+ const lineNumberEl = document.createElement('td');
+ annotationLayer.annotate(el, lineNumberEl, line);
assert.isTrue(testLayerFuncCalled);
});
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 b5686bd04d..ad318d7a74 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
@@ -28,6 +28,7 @@
POST_REVERT: 'postrevert',
ANNOTATE_DIFF: 'annotatediff',
ADMIN_MENU_LINKS: 'admin-menu-links',
+ HIGHLIGHTJS_LOADED: 'highlightjs-loaded',
};
const Element = {
@@ -37,6 +38,7 @@
Polymer({
is: 'gr-js-api-interface',
+ _legacyUndefinedCheck: true,
properties: {
_elements: {
@@ -69,6 +71,9 @@
case EventType.LABEL_CHANGE:
this._handleLabelChange(detail);
break;
+ case EventType.HIGHLIGHTJS_LOADED:
+ this._handleHighlightjsLoaded(detail);
+ break;
default:
console.warn('handleEvent called with unsupported event type:',
type);
@@ -188,6 +193,16 @@
}
},
+ _handleHighlightjsLoaded(detail) {
+ for (const cb of this._getEventCallbacks(EventType.HIGHLIGHTJS_LOADED)) {
+ try {
+ cb(detail.hljs);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+ },
+
modifyRevertMsg(change, revertMsg, origMsg) {
for (const cb of this._getEventCallbacks(EventType.REVERT)) {
try {
@@ -213,6 +228,36 @@
return layers;
},
+ /**
+ * 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>>}
+ */
+ 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 [];
+ });
+ },
+
getAdminMenuLinks() {
const links = [];
for (const adminApi of
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 e0c7c37f04..f03aa09879 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
@@ -300,6 +300,17 @@ limitations under the License.
assert.isTrue(errorStub.calledTwice);
});
+ test('highlightjs-loaded event', done => {
+ const testHljs = {_number: 42};
+ plugin.on(element.EventType.HIGHLIGHTJS_LOADED, throwErrFn);
+ plugin.on(element.EventType.HIGHLIGHTJS_LOADED, hljs => {
+ assert.deepEqual(hljs, testHljs);
+ assert.isTrue(errorStub.calledOnce);
+ done();
+ });
+ element.handleEvent(element.EventType.HIGHLIGHTJS_LOADED, {hljs: testHljs});
+ });
+
test('versioning', () => {
const callback = sandbox.spy();
Gerrit.install(callback, '0.0pre-alpha');
@@ -384,19 +395,6 @@ limitations under the License.
});
});
- test('installGwt calls _pluginInstalled', () => {
- sandbox.stub(Gerrit, '_pluginInstalled');
- Gerrit.installGwt('http://test.com/plugins/testplugin/static/test.js');
- assert.isTrue(Gerrit._pluginInstalled.calledOnce);
- });
-
- test('installGwt returns a plugin', () => {
- const plugin = Gerrit.installGwt(
- 'http://test.com/plugins/testplugin/static/test.js');
- assert.isOk(plugin);
- assert.isOk(plugin._loadedGwt);
- });
-
test('attributeHelper', () => {
assert.isOk(plugin.attributeHelper());
});
@@ -441,28 +439,32 @@ limitations under the License.
test('installing preloaded plugin', () => {
let plugin;
- window.ASSETS_PATH = 'http://blips.com/chitz/';
+ 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/plugins/foo/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 => { plugin = p; }, '0.1',
- 'http://test.com/r/plugins/testplugin/static/test.js');
+ Gerrit.install(p => { baseUrlPlugin = p; }, '0.1',
+ 'http://test.com/r/plugins/baseurlplugin/static/test.js');
});
test('url', () => {
- assert.notEqual(plugin.url(), 'http://test.com/plugins/testplugin/');
- assert.equal(plugin.url(), 'http://test.com/r/plugins/testplugin/');
- assert.equal(plugin.url('/static/test.js'),
- 'http://test.com/r/plugins/testplugin/static/test.js');
+ assert.notEqual(baseUrlPlugin.url(),
+ 'http://test.com/plugins/baseurlplugin/');
+ assert.equal(baseUrlPlugin.url(),
+ 'http://test.com/r/plugins/baseurlplugin/');
+ assert.equal(baseUrlPlugin.url('/static/test.js'),
+ '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 bf6a046eb2..ca87956488 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
@@ -135,7 +135,7 @@ limitations under the License.
__key: 'key',
__url: '/changes/1/revisions/2/foo~bar',
};
- const sendStub = sandbox.stub().returns(Promise.reject('boom'));
+ const sendStub = sandbox.stub().returns(Promise.reject(new Error('boom')));
sandbox.stub(plugin, 'restApi').returns({
send: sendStub,
});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js
index b5c3dae0f8..f80e44a6be 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.js
@@ -20,6 +20,7 @@
function GrPluginEndpoints() {
this._endpoints = {};
this._callbacks = {};
+ this._dynamicPlugins = {};
}
GrPluginEndpoints.prototype.onNewEndpoint = function(endpoint, callback) {
@@ -59,8 +60,21 @@
}
};
+ /**
+ * Register a plugin to an endpoint.
+ *
+ * Dynamic plugins are registered to a specific prefix, such as
+ * 'change-list-header'. These plugins are then fetched by prefix to determine
+ * which endpoints to dynamically add to the page.
+ */
GrPluginEndpoints.prototype.registerModule = function(plugin, endpoint, type,
- moduleName, domHook) {
+ moduleName, domHook, dynamicEndpoint) {
+ if (dynamicEndpoint) {
+ if (!this._dynamicPlugins[dynamicEndpoint]) {
+ this._dynamicPlugins[dynamicEndpoint] = new Set();
+ }
+ this._dynamicPlugins[dynamicEndpoint].add(endpoint);
+ }
if (!this._endpoints[endpoint]) {
this._endpoints[endpoint] = [];
}
@@ -71,6 +85,12 @@
}
};
+ GrPluginEndpoints.prototype.getDynamicEndpoints = function(dynamicEndpoint) {
+ const plugins = this._dynamicPlugins[dynamicEndpoint];
+ if (!plugins) return [];
+ return Array.from(plugins);
+ };
+
/**
* Get detailed information about modules registered with an extension
* endpoint.
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 7e84a4b342..ce619a0190 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
@@ -57,9 +57,9 @@
* @return {!Promise}
*/
GrPluginRestApi.prototype.fetch = function(method, url, opt_payload,
- opt_errFn) {
+ opt_errFn, opt_contentType) {
return getRestApi().send(method, this.opt_prefix + url, opt_payload,
- opt_errFn);
+ opt_errFn, opt_contentType);
};
/**
@@ -73,20 +73,21 @@
* @return {!Promise} resolves on success, rejects on error.
*/
GrPluginRestApi.prototype.send = function(method, url, opt_payload,
- opt_errFn) {
- return this.fetch(method, url, opt_payload, opt_errFn).then(response => {
- if (response.status < 200 || response.status >= 300) {
- return response.text().then(text => {
- if (text) {
- return Promise.reject(text);
+ opt_errFn, opt_contentType) {
+ return this.fetch(method, url, opt_payload, opt_errFn, opt_contentType)
+ .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 Promise.reject(response.status);
+ return getRestApi().getResponseObject(response);
}
});
- } else {
- return getRestApi().getResponseObject(response);
- }
- });
};
/**
@@ -101,16 +102,18 @@
* @param {string} url URL without base path or plugin prefix
* @return {!Promise} resolves on success, rejects on error.
*/
- GrPluginRestApi.prototype.post = function(url, opt_payload) {
- return this.send('POST', url, opt_payload);
+ GrPluginRestApi.prototype.post = function(url, opt_payload, opt_errFn,
+ opt_contentType) {
+ return this.send('POST', url, opt_payload, opt_errFn, opt_contentType);
};
/**
* @param {string} url URL without base path or plugin prefix
* @return {!Promise} resolves on success, rejects on error.
*/
- GrPluginRestApi.prototype.put = function(url, opt_payload) {
- return this.send('PUT', url, opt_payload);
+ GrPluginRestApi.prototype.put = function(url, opt_payload, opt_errFn,
+ opt_contentType) {
+ return this.send('PUT', url, opt_payload, opt_errFn, opt_contentType);
};
/**
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 580bf77fe3..7376a1222f 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
@@ -99,10 +99,6 @@
STYLE: 'style',
};
- // GWT JSNI uses $wnd to refer to window.
- // http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
- window.$wnd = window;
-
function flushPreinstalls() {
if (window.Gerrit.flushPreinstalls) {
window.Gerrit.flushPreinstalls();
@@ -189,14 +185,36 @@
this, endpointName, EndpointType.STYLE, moduleName);
};
+ /**
+ * Registers an endpoint for the plugin.
+ */
Plugin.prototype.registerCustomComponent = function(
endpointName, opt_moduleName, opt_options) {
+ return this._registerCustomComponent(endpointName, opt_moduleName,
+ opt_options);
+ };
+
+ /**
+ * Registers a dynamic endpoint for the plugin.
+ *
+ * Dynamic plugins are registered by specific prefix, such as
+ * 'change-list-header'.
+ */
+ Plugin.prototype.registerDynamicCustomComponent = function(
+ endpointName, opt_moduleName, opt_options) {
+ const fullEndpointName = `${endpointName}-${this.getPluginName()}`;
+ return this._registerCustomComponent(fullEndpointName, opt_moduleName,
+ opt_options, endpointName);
+ };
+
+ Plugin.prototype._registerCustomComponent = function(
+ endpointName, opt_moduleName, opt_options, dynamicEndpoint) {
const type = opt_options && opt_options.replace ?
EndpointType.REPLACE : EndpointType.DECORATE;
const hook = this._domHooks.getDomHook(endpointName, opt_moduleName);
const moduleName = opt_moduleName || hook.getModuleName();
Gerrit._endpoints.registerModule(
- this, endpointName, type, moduleName, hook);
+ this, endpointName, type, moduleName, hook, dynamicEndpoint);
return hook.getPublicAPI();
};
@@ -217,9 +235,14 @@
};
Plugin.prototype.url = function(opt_path) {
- const base = Gerrit.BaseUrlBehavior.getBaseUrl();
- return this._url.origin + base + '/plugins/' +
- this._name + (opt_path || '/');
+ const relPath = '/plugins/' + this._name + (opt_path || '/');
+ if (window.location.origin === this._url.origin) {
+ // Plugin loaded from the same origin as gr-app, getBaseUrl in effect.
+ return this._url.origin + Gerrit.BaseUrlBehavior.getBaseUrl() + relPath;
+ } else {
+ // Plugin loaded from assets bundle, expect assets placed along with it.
+ return this._url.href.split('/plugins/' + this._name)[0] + relPath;
+ }
};
Plugin.prototype.screenUrl = function(opt_screenName) {
@@ -566,24 +589,6 @@
});
};
- /**
- * Install "stepping stones" API for GWT-compiled plugins by default.
- *
- * @deprecated best effort support, will be removed with GWT UI.
- */
- Gerrit.installGwt = function(url) {
- const name = getPluginNameFromUrl(url);
- let plugin;
- try {
- plugin = _plugins[name] || new Plugin(url);
- plugin.deprecated.install();
- Gerrit._pluginInstalled(url);
- } catch (e) {
- Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
- }
- return plugin;
- };
-
Gerrit.awaitPluginsLoaded = function() {
if (!_allPluginsPromise) {
if (Gerrit._arePluginsLoaded()) {
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 a8389dc756..387fe96441 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,6 +19,7 @@
Polymer({
is: 'gr-label-info',
+ _legacyUndefinedCheck: true,
properties: {
labelInfo: Object,
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 0de0881130..c437885465 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-label',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.TooltipBehavior,
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 54b38eb39f..a892522481 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,6 +19,7 @@
Polymer({
is: 'gr-labeled-autocomplete',
+ _legacyUndefinedCheck: true,
/**
* Fired when a value is chosen.
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 f70aff4bf6..4137485315 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
@@ -15,7 +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="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<dom-module id="gr-lib-loader">
+ <template>
+ <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
+ </template>
<script src="gr-lib-loader.js"></script>
</dom-module>
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 63214af01b..ba86b66cef 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,6 +22,7 @@
Polymer({
is: 'gr-lib-loader',
+ _legacyUndefinedCheck: true,
properties: {
_hljsState: {
@@ -84,6 +85,9 @@
_onHLJSLibLoaded() {
const lib = this._getHighlightLib();
this._hljsState.loading = false;
+ this.$.jsAPI.handleEvent(this.$.jsAPI.EventType.HIGHLIGHTJS_LOADED, {
+ hljs: lib,
+ });
for (const cb of this._hljsState.callbacks) {
cb(lib);
}
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 0dc3a7d55b..44a8791f10 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,6 +26,7 @@
Polymer({
is: 'gr-limited-text',
+ _legacyUndefinedCheck: true,
properties: {
/** The un-truncated text to display. */
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 f8f29b868a..8388a079b5 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,6 +19,7 @@
Polymer({
is: 'gr-linked-chip',
+ _legacyUndefinedCheck: true,
properties: {
href: String,
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 18f089bfa2..157ad5ed88 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,6 +19,7 @@
Polymer({
is: 'gr-linked-text',
+ _legacyUndefinedCheck: true,
properties: {
removeZeroWidthSpace: Boolean,
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 8b83eb345e..53d05e1044 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,6 +21,7 @@
Polymer({
is: 'gr-list-view',
+ _legacyUndefinedCheck: true,
properties: {
createNew: Boolean,
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 6df04a24ea..c167b3b54c 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
@@ -23,6 +23,7 @@
Polymer({
is: 'gr-overlay',
+ _legacyUndefinedCheck: true,
/**
* Fired when a fullscreen overlay is closed
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 2e056075c5..181c7bc751 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,6 +19,7 @@
Polymer({
is: 'gr-page-nav',
+ _legacyUndefinedCheck: true,
properties: {
_headerHeight: Number,
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 e2298c3d37..2fccc8defa 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,6 +22,7 @@
Polymer({
is: 'gr-repo-branch-picker',
+ _legacyUndefinedCheck: true,
properties: {
repo: {
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 ad1fac048d..546d0045fa 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
@@ -185,6 +185,7 @@
Polymer({
is: 'gr-rest-api-interface',
+ _legacyUndefinedCheck: true,
behaviors: [
Gerrit.PathListBehavior,
@@ -359,7 +360,7 @@
/**
* @param {string} url
- * @param {?Object=} opt_params URL params, key-value hash.
+ * @param {?Object|string=} opt_params URL params, key-value hash.
* @return {string}
*/
_urlWithParams(url, opt_params) {
@@ -468,10 +469,11 @@
saveRepoConfig(repo, config, opt_errFn) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
- const encodeName = encodeURIComponent(repo);
+ const url = `/projects/${encodeURIComponent(repo)}/config`;
+ this._cache.delete(url);
return this._send({
method: 'PUT',
- url: `/projects/${encodeName}/config`,
+ url,
body: config,
errFn: opt_errFn,
anonymizedUrl: '/projects/*/config',
@@ -1328,6 +1330,8 @@
* @param {function()=} opt_cancelCondition
*/
getChangeDetail(changeNum, opt_errFn, opt_cancelCondition) {
+ // This list MUST be kept in sync with
+ // ChangeIT#changeDetailsDoesNotRequireIndex
const options = [
this.ListChangesOption.ALL_COMMITS,
this.ListChangesOption.ALL_REVISIONS,
@@ -1357,30 +1361,32 @@
* @param {function()=} opt_cancelCondition
*/
getDiffChangeDetail(changeNum, opt_errFn, opt_cancelCondition) {
- const params = this.listChangesOptionsToHex(
+ const optionsHex = this.listChangesOptionsToHex(
this.ListChangesOption.ALL_COMMITS,
this.ListChangesOption.ALL_REVISIONS,
this.ListChangesOption.SKIP_MERGEABLE
);
- return this._getChangeDetail(changeNum, params, opt_errFn,
+ return this._getChangeDetail(changeNum, optionsHex, opt_errFn,
opt_cancelCondition);
},
/**
* @param {number|string} changeNum
+ * @param {string|undefined} optionsHex list changes options in hex
* @param {function(?Response, string=)=} opt_errFn
* @param {function()=} opt_cancelCondition
*/
- _getChangeDetail(changeNum, params, opt_errFn, opt_cancelCondition) {
+ _getChangeDetail(changeNum, optionsHex, opt_errFn, opt_cancelCondition) {
return this.getChangeActionURL(changeNum, null, '/detail').then(url => {
- const urlWithParams = this._urlWithParams(url, params);
+ const urlWithParams = this._urlWithParams(url, optionsHex);
+ const params = {O: optionsHex};
const req = {
url,
errFn: opt_errFn,
cancelCondition: opt_cancelCondition,
- params: {O: params},
+ params,
fetchOptions: this._etags.getOptions(urlWithParams),
- anonymizedUrl: '/changes/*~*/detail?O=' + params,
+ anonymizedUrl: '/changes/*~*/detail?O=' + optionsHex,
};
return this._fetchRawJSON(req).then(response => {
if (response && response.status === 304) {
@@ -1528,7 +1534,9 @@
* @param {function(?Response, string=)=} opt_errFn
*/
getChangeSuggestedReviewers(changeNum, inputVal, opt_errFn) {
- const params = {n: 10};
+ // More suggestions may obscure content underneath in the reply dialog,
+ // see issue 10793.
+ const params = {n: 6};
if (inputVal) { params.q = inputVal; }
return this._getChangeURLAndFetch({
changeNum,
@@ -2526,7 +2534,9 @@
_fetchB64File(url) {
return this._fetch({url: this.getBaseUrl() + url})
.then(response => {
- if (!response.ok) { return Promise.reject(response.statusText); }
+ if (!response.ok) {
+ return Promise.reject(new Error(response.statusText));
+ }
const type = response.headers.get('X-FYI-Content-Type');
return response.text()
.then(text => {
@@ -2686,12 +2696,12 @@
return this._send(req)
.then(response => {
if (response.status < 200 && response.status >= 300) {
- return Promise.reject();
+ return Promise.reject(new Error('error'));
}
return this.getResponseObject(response);
})
.then(obj => {
- if (!obj.valid) { return Promise.reject(); }
+ if (!obj.valid) { return Promise.reject(new Error('error')); }
return obj;
});
},
@@ -2721,12 +2731,12 @@
return this._send(req)
.then(response => {
if (response.status < 200 && response.status >= 300) {
- return Promise.reject();
+ return Promise.reject(new Error('error'));
}
return this.getResponseObject(response);
})
.then(obj => {
- if (!obj) { return Promise.reject(); }
+ if (!obj) { return Promise.reject(new Error('error')); }
return obj;
});
},
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 5d4a3b0f41..ef4e401e64 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
@@ -88,10 +88,10 @@ limitations under the License.
});
test('cached promise', done => {
- const promise = Promise.reject('foo');
+ const promise = Promise.reject(new Error('foo'));
element._cache.set('/foo', promise);
element._fetchSharedCacheURL({url: '/foo'}).catch(p => {
- assert.equal(p, 'foo');
+ assert.equal(p.message, 'foo');
done();
});
});
@@ -455,7 +455,7 @@ limitations under the License.
status: 403,
};
window.fetch.onFirstCall().returns(
- Promise.reject({message: 'Failed to fetch'}));
+ Promise.reject(new Error('Failed to fetch')));
window.fetch.onSecondCall().returns(Promise.resolve(fakeAuthResponse));
// Emulate logged in.
element._cache.set('/accounts/self/detail', {});
@@ -507,7 +507,7 @@ limitations under the License.
element._cache.set('/accounts/self/detail', true);
sandbox.spy(element, 'checkCredentials');
sandbox.stub(window, 'fetch', url => {
- return Promise.reject({message: 'Failed to fetch'});
+ return Promise.reject(new Error('Failed to fetch'));
});
return element.getConfig(true)
.catch(err => undefined)
@@ -1151,12 +1151,12 @@ limitations under the License.
test('_getChangeDetail passes params to ETags decorator', () => {
const changeNum = 4321;
element._projectLookup[changeNum] = 'test';
- const params = {foo: 'bar'};
const expectedUrl =
- window.CANONICAL_PATH + '/changes/test~4321/detail?foo=bar';
+ window.CANONICAL_PATH + '/changes/test~4321/detail?'+
+ '0=5&1=1&2=6&3=7&4=1&5=4';
sandbox.stub(element._etags, 'getOptions');
sandbox.stub(element._etags, 'collect');
- return element._getChangeDetail(changeNum, params).then(() => {
+ return element._getChangeDetail(changeNum, '516714').then(() => {
assert.isTrue(element._etags.getOptions.calledWithExactly(
expectedUrl));
assert.equal(element._etags.collect.lastCall.args[0], expectedUrl);
@@ -1169,7 +1169,7 @@ limitations under the License.
.returns(Promise.resolve(''));
sandbox.stub(element, '_fetchRawJSON')
.returns(Promise.resolve({ok: false, status: 500}));
- return element._getChangeDetail(123, {}, errFn).then(() => {
+ return element._getChangeDetail(123, '516714', errFn).then(() => {
assert.isTrue(errFn.called);
});
});
@@ -1185,7 +1185,7 @@ limitations under the License.
parsed: mockResponse,
raw: JSON.stringify(mockResponse),
}));
- return element._getChangeDetail(1).then(() => {
+ return element._getChangeDetail(1, '516714').then(() => {
assert.equal(Object.keys(element._projectLookup).length, 1);
assert.equal(element._projectLookup[1], 'test');
});
@@ -1216,7 +1216,7 @@ limitations under the License.
ok: true,
}));
- return element._getChangeDetail(123, {}).then(detail => {
+ return element._getChangeDetail(123, '516714').then(detail => {
assert.isFalse(getPayloadSpy.called);
assert.isTrue(collectSpy.calledOnce);
const cachedResponse = element._etags.getCachedPayload(requestUrl);
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 0a1b14ee49..05c2ceee5b 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
@@ -153,6 +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.js b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
index b732fa5f6c..85e1a616a1 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-select',
+ _legacyUndefinedCheck: true,
properties: {
bindValue: {
type: String,
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 2c546cc250..901b8ce2c0 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,6 +19,7 @@
Polymer({
is: 'gr-shell-command',
+ _legacyUndefinedCheck: true,
properties: {
command: String,
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 6d82dad9b4..83cd06cb81 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -30,6 +30,7 @@
Polymer({
is: 'gr-storage',
+ _legacyUndefinedCheck: true,
properties: {
_lastCleanup: Number,
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 a3da7d8116..7929fbec6d 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -54,6 +54,7 @@
Polymer({
is: 'gr-textarea',
+ _legacyUndefinedCheck: true,
/**
* @event bind-value-changed
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 c5de8f4844..b46cafb3b3 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,6 +19,7 @@
Polymer({
is: 'gr-tooltip-content',
+ _legacyUndefinedCheck: true,
properties: {
title: {
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 fb87b55831..3e16beb6f2 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
@@ -19,6 +19,7 @@
Polymer({
is: 'gr-tooltip',
+ _legacyUndefinedCheck: true,
properties: {
text: String,
diff --git a/polygerrit-ui/app/embed/embed.html b/polygerrit-ui/app/embed/embed.html
index 948916f9de..1b2f20fb23 100644
--- a/polygerrit-ui/app/embed/embed.html
+++ b/polygerrit-ui/app/embed/embed.html
@@ -25,7 +25,6 @@ limitations under the License.
<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">
<link rel="import" href="../elements/change-list/gr-change-list-view/gr-change-list-view.html">
-<link rel="import" href="../elements/change-list/gr-change-list/gr-change-list.html">
-<link rel="import" href="../elements/change-list/gr-create-change-help/gr-create-change-help.html">
<link rel="import" href="../elements/change-list/gr-dashboard-view/gr-dashboard-view.html">
+<link rel="import" href="../elements/change-list/gr-embed-dashboard/gr-embed-dashboard.html">
<link rel="import" href="../styles/themes/app-theme.html">
diff --git a/polygerrit-ui/app/embed/gr-diff.html b/polygerrit-ui/app/embed/gr-diff.html
new file mode 100644
index 0000000000..6aa9370114
--- /dev/null
+++ b/polygerrit-ui/app/embed/gr-diff.html
@@ -0,0 +1,25 @@
+<!--
+@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>
+ // Needed for JSCompiler to understand it's global.
+ // eslint-disable-next-line no-unused-vars, prefer-const
+ let Gerrit = window.Gerrit || {};
+ window.Gerrit = 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/samples/bind-parameters.html b/polygerrit-ui/app/samples/bind-parameters.html
index dc7a87a984..a7eb39a542 100644
--- a/polygerrit-ui/app/samples/bind-parameters.html
+++ b/polygerrit-ui/app/samples/bind-parameters.html
@@ -15,6 +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 b29823792d..0d38c63e71 100644
--- a/polygerrit-ui/app/samples/coverage-plugin.html
+++ b/polygerrit-ui/app/samples/coverage-plugin.html
@@ -50,6 +50,7 @@
const linesMissingCoverage = coverageData[path].linesMissingCoverage;
if (linesMissingCoverage.includes(line.afterNumber)) {
context.annotateRange(0, line.text.length, cssClass, 'right');
+ context.annotateLineNumber(cssClass, 'right');
}
}
}).enableToggleCheckbox('Display Coverage', checkbox => {
diff --git a/polygerrit-ui/app/samples/repo-command.html b/polygerrit-ui/app/samples/repo-command.html
index 67e528a185..37aca04fc3 100644
--- a/polygerrit-ui/app/samples/repo-command.html
+++ b/polygerrit-ui/app/samples/repo-command.html
@@ -29,6 +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 de293157de..527ebcebbd 100644
--- a/polygerrit-ui/app/samples/some-screen.html
+++ b/polygerrit-ui/app/samples/some-screen.html
@@ -38,6 +38,7 @@
<script>
Polymer({
is: 'some-screen-main',
+ _legacyUndefinedCheck: true,
properties: {
rootUrl: String,
},
diff --git a/polygerrit-ui/app/scripts/util.js b/polygerrit-ui/app/scripts/util.js
index b4ab21a616..624992b3e4 100644
--- a/polygerrit-ui/app/scripts/util.js
+++ b/polygerrit-ui/app/scripts/util.js
@@ -41,5 +41,39 @@
}
return '';
};
+
+ /**
+ * Make the promise cancelable.
+ *
+ * Returns a promise with a `cancel()` method wrapped around `promise`.
+ * Calling `cancel()` will reject the returned promise with
+ * {isCancelled: true} synchronously. If the inner promise for a cancelled
+ * promise resolves or rejects this is ignored.
+ */
+ util.makeCancelable = promise => {
+ // True if the promise is either resolved or reject (possibly cancelled)
+ let isDone = false;
+
+ let rejectPromise;
+
+ const wrappedPromise = new Promise((resolve, reject) => {
+ rejectPromise = reject;
+ promise.then(val => {
+ if (!isDone) resolve(val);
+ isDone = true;
+ }, error => {
+ if (!isDone) reject(error);
+ isDone = true;
+ });
+ });
+
+ wrappedPromise.cancel = () => {
+ if (isDone) return;
+ rejectPromise({isCanceled: true});
+ isDone = true;
+ };
+ return wrappedPromise;
+ };
+
window.util = util;
})(window);
diff --git a/polygerrit-ui/app/styles/gr-change-list-styles.html b/polygerrit-ui/app/styles/gr-change-list-styles.html
index aeca48a15c..8f72216ad6 100644
--- a/polygerrit-ui/app/styles/gr-change-list-styles.html
+++ b/polygerrit-ui/app/styles/gr-change-list-styles.html
@@ -55,8 +55,8 @@ limitations under the License.
.cell {
vertical-align: middle;
}
- th:not(.label),
- .cell:not(.label) {
+ th:not(.label):not(.endpoint),
+ .cell:not(.label):not(.endpoint) {
padding-right: 8px;
}
th.label {
@@ -128,8 +128,10 @@ limitations under the License.
.star {
width: 30px;
}
- .label {
+ .label, .endpoint {
border-left: 1px solid var(--border-color);
+ }
+ .label {
text-align: center;
width: 3rem;
}
diff --git a/polygerrit-ui/app/styles/gr-form-styles.html b/polygerrit-ui/app/styles/gr-form-styles.html
index 59b633fcd6..65c1ae3cf1 100644
--- a/polygerrit-ui/app/styles/gr-form-styles.html
+++ b/polygerrit-ui/app/styles/gr-form-styles.html
@@ -29,11 +29,15 @@ limitations under the License.
.gr-form-styles h2 {
margin-bottom: .3em;
}
+ .gr-form-styles h4 {
+ font-weight: var(--font-weight-bold);
+ }
.gr-form-styles fieldset {
border: none;
margin-bottom: 2em;
}
.gr-form-styles section {
+ display: flex;
margin: .25em 0;
min-height: 2em;
}
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index 374e81b473..ec47c53fdc 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.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.
-->
-<style is="custom-style">
-:root {
+<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. */
@@ -144,10 +144,11 @@ limitations under the License.
--syntax-template-tag-color: #FA8602;
--syntax-params-color: var(--primary-text-color);
--syntax-doctag-weight: bold;
+ --reply-overlay-z-index: 1000;
}
@media screen and (max-width: 50em) {
- :root {
+ html {
--default-horizontal-margin: .7rem;
}
}
-</style>
+</style></custom-style>
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index b985e8810c..718ac25658 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<dom-module id="dark-theme">
- <style is="custom-style">
+ <custom-style><style is="custom-style">
html {
--primary-text-color: #e2e2e2;
--view-background-color: #212121;
@@ -106,7 +106,9 @@ limitations under the License.
--syntax-doctag-weight: bold;
--syntax-params-color: var(--primary-text-color);
+ --reply-overlay-z-index: 1000;
+
background-color: var(--view-background-color);
}
- </style>
+ </style></custom-style>
</dom-module>
diff --git a/polygerrit-ui/app/test/common-test-setup.html b/polygerrit-ui/app/test/common-test-setup.html
index e0299da1b1..a549dd4c52 100644
--- a/polygerrit-ui/app/test/common-test-setup.html
+++ b/polygerrit-ui/app/test/common-test-setup.html
@@ -62,3 +62,4 @@ limitations under the License.
<link rel="import"
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>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 75ba7054d5..cd3aaeccf3 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -44,6 +44,7 @@ limitations under the License.
'admin/gr-group-members/gr-group-members_test.html',
'admin/gr-group/gr-group_test.html',
'admin/gr-permission/gr-permission_test.html',
+ 'admin/gr-plugin-config-array-editor/gr-plugin-config-array-editor_test.html',
'admin/gr-plugin-list/gr-plugin-list_test.html',
'admin/gr-repo-access/gr-repo-access_test.html',
'admin/gr-repo-command/gr-repo-command_test.html',
@@ -51,6 +52,7 @@ limitations under the License.
'admin/gr-repo-dashboards/gr-repo-dashboards_test.html',
'admin/gr-repo-detail-list/gr-repo-detail-list_test.html',
'admin/gr-repo-list/gr-repo-list_test.html',
+ 'admin/gr-repo-plugin-config/gr-repo-plugin-config_test.html',
'admin/gr-repo/gr-repo_test.html',
'admin/gr-rule-editor/gr-rule-editor_test.html',
'change-list/gr-change-list-item/gr-change-list-item_test.html',
@@ -104,9 +106,6 @@ limitations under the License.
'core/gr-smart-search/gr-smart-search_test.html',
'diff/gr-comment-api/gr-comment-api_test.html',
'diff/gr-diff-builder/gr-diff-builder_test.html',
- 'diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html',
- 'diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html',
- 'diff/gr-diff-comment/gr-diff-comment_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',
@@ -159,6 +158,8 @@ limitations under the License.
'shared/gr-button/gr-button_test.html',
'shared/gr-change-star/gr-change-star_test.html',
'shared/gr-change-status/gr-change-status_test.html',
+ '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-cursor-manager/gr-cursor-manager_test.html',
'shared/gr-date-formatter/gr-date-formatter_test.html',
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index ba685184e8..2f5df90075 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -17,6 +17,7 @@ package main
import (
"archive/zip"
"bufio"
+ "bytes"
"compress/gzip"
"encoding/json"
"errors"
@@ -32,20 +33,16 @@ import (
"regexp"
"strings"
- "github.com/robfig/soy"
- "github.com/robfig/soy/soyhtml"
"golang.org/x/tools/godoc/vfs/httpfs"
"golang.org/x/tools/godoc/vfs/zipfs"
)
var (
- plugins = flag.String("plugins", "", "comma seperated plugin paths to serve")
- port = flag.String("port", ":8081", "Port to serve HTTP requests on")
- prod = flag.Bool("prod", false, "Serve production assets")
- restHost = flag.String("host", "gerrit-review.googlesource.com", "Host to proxy requests to")
- scheme = flag.String("scheme", "https", "URL scheme")
-
- tofu *soyhtml.Tofu
+ 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.]*")
)
func main() {
@@ -61,55 +58,35 @@ func main() {
log.Fatal(err)
}
- tofu, err = resolveIndexTemplate()
- if err != nil {
- log.Fatal(err)
- }
-
workspace := os.Getenv("BUILD_WORKSPACE_DIRECTORY")
if err := os.Chdir(filepath.Join(workspace, "polygerrit-ui")); err != nil {
log.Fatal(err)
}
- http.HandleFunc("/index.html", handleIndex)
-
- if *prod {
- http.Handle("/", http.FileServer(http.Dir("dist")))
- } else {
- http.Handle("/", http.FileServer(http.Dir("app")))
- }
-
+ http.Handle("/", http.FileServer(http.Dir("app")))
http.Handle("/bower_components/",
http.FileServer(httpfs.New(zipfs.New(componentsArchive, "bower_components"))))
http.Handle("/fonts/",
http.FileServer(httpfs.New(zipfs.New(fontsArchive, "fonts"))))
- http.HandleFunc("/changes/", handleRESTProxy)
- http.HandleFunc("/accounts/", handleRESTProxy)
- http.HandleFunc("/config/", handleRESTProxy)
- http.HandleFunc("/projects/", handleRESTProxy)
+ http.HandleFunc("/index.html", handleIndex)
+ http.HandleFunc("/changes/", handleProxy)
+ http.HandleFunc("/accounts/", handleProxy)
+ http.HandleFunc("/config/", handleProxy)
+ http.HandleFunc("/projects/", handleProxy)
http.HandleFunc("/accounts/self/detail", handleAccountDetail)
+
if len(*plugins) > 0 {
http.Handle("/plugins/", http.StripPrefix("/plugins/",
http.FileServer(http.Dir("../plugins"))))
log.Println("Local plugins from", "../plugins")
} else {
- http.HandleFunc("/plugins/", handleRESTProxy)
+ http.HandleFunc("/plugins/", handleProxy)
}
log.Println("Serving on port", *port)
log.Fatal(http.ListenAndServe(*port, &server{}))
}
-func resolveIndexTemplate() (*soyhtml.Tofu, error) {
- basePath, err := resourceBasePath()
- if err != nil {
- return nil, err
- }
- return soy.NewBundle().
- AddTemplateFile(basePath + ".runfiles/gerrit/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy").
- CompileToTofu()
-}
-
func openDataArchive(path string) (*zip.ReadCloser, error) {
absBinPath, err := resourceBasePath()
if err != nil {
@@ -122,40 +99,40 @@ func resourceBasePath() (string, error) {
return filepath.Abs(os.Args[0])
}
-func handleIndex(w http.ResponseWriter, r *http.Request) {
- var obj = map[string]interface{}{
- "canonicalPath": "",
- "staticResourcePath": "",
+func handleIndex(writer http.ResponseWriter, originalRequest *http.Request) {
+ fakeRequest := &http.Request{
+ URL: &url.URL{
+ Path: "/",
+ },
}
- w.Header().Set("Content-Type", "text/html")
- tofu.Render(w, "com.google.gerrit.httpd.raw.Index", obj)
+ handleProxy(writer, fakeRequest)
}
-func handleRESTProxy(w http.ResponseWriter, r *http.Request) {
- req := &http.Request{
+func handleProxy(writer http.ResponseWriter, originalRequest *http.Request) {
+ patchedRequest := &http.Request{
Method: "GET",
URL: &url.URL{
Scheme: *scheme,
- Host: *restHost,
- Opaque: r.URL.EscapedPath(),
- RawQuery: r.URL.RawQuery,
+ Host: *host,
+ Opaque: originalRequest.URL.EscapedPath(),
+ RawQuery: originalRequest.URL.RawQuery,
},
}
- res, err := http.DefaultClient.Do(req)
+ response, err := http.DefaultClient.Do(patchedRequest)
if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
+ http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
- defer res.Body.Close()
- for name, values := range res.Header {
+ defer response.Body.Close()
+ for name, values := range response.Header {
for _, value := range values {
if name != "Content-Length" {
- w.Header().Add(name, value)
+ writer.Header().Add(name, value)
}
}
}
- w.WriteHeader(res.StatusCode)
- if _, err := io.Copy(w, patchResponse(r, res)); err != nil {
+ writer.WriteHeader(response.StatusCode)
+ if _, err := io.Copy(writer, patchResponse(originalRequest, response)); err != nil {
log.Println("Error copying response to ResponseWriter:", err)
return
}
@@ -188,8 +165,10 @@ func setJsonPropByPath(json map[string]interface{}, path []string, value interfa
}
}
-func patchResponse(r *http.Request, res *http.Response) io.Reader {
- switch r.URL.EscapedPath() {
+func patchResponse(req *http.Request, res *http.Response) io.Reader {
+ switch req.URL.EscapedPath() {
+ case "/":
+ return replaceCdn(res.Body)
case "/config/server/info":
return injectLocalPlugins(res.Body)
default:
@@ -197,13 +176,23 @@ func patchResponse(r *http.Request, res *http.Response) io.Reader {
}
}
-func injectLocalPlugins(r io.Reader) io.Reader {
+func replaceCdn(reader io.Reader) io.Reader {
+ buf := new(bytes.Buffer)
+ buf.ReadFrom(reader)
+ original := buf.String()
+
+ replaced := cdnPattern.ReplaceAllString(original, "")
+
+ return strings.NewReader(replaced)
+}
+
+func injectLocalPlugins(reader io.Reader) io.Reader {
if len(*plugins) == 0 {
- return r
+ return reader
}
// Skip escape prefix
- io.CopyN(ioutil.Discard, r, 5)
- dec := json.NewDecoder(r)
+ io.CopyN(ioutil.Discard, reader, 5)
+ dec := json.NewDecoder(reader)
var response map[string]interface{}
err := dec.Decode(&response)
diff --git a/proto/BUILD b/proto/BUILD
index 26efdbccd8..57be2658eb 100644
--- a/proto/BUILD
+++ b/proto/BUILD
@@ -12,23 +12,13 @@ java_proto_library(
deps = [":cache_proto"],
)
-genrule(
- name = "gen_reviewdb_proto",
- outs = ["reviewdb.proto"],
- cmd = "$(location //java/com/google/gerrit/proto:ProtoGen) -o $@",
- tools = ["//java/com/google/gerrit/proto:ProtoGen"],
-)
-
proto_library(
- name = "reviewdb_proto",
- srcs = [":reviewdb.proto"],
+ name = "entities_proto",
+ srcs = ["entities.proto"],
)
java_proto_library(
- name = "reviewdb_java_proto",
- visibility = [
- "//javatests/com/google/gerrit/proto:__pkg__",
- "//tools/eclipse:__pkg__",
- ],
- deps = [":reviewdb_proto"],
+ name = "entities_java_proto",
+ visibility = ["//visibility:public"],
+ deps = [":entities_proto"],
)
diff --git a/proto/cache.proto b/proto/cache.proto
index c2ac0d92ed..b34dbf3b86 100644
--- a/proto/cache.proto
+++ b/proto/cache.proto
@@ -61,10 +61,10 @@ message ChangeNotesKeyProto {
// were chosen ease of coding the initial implementation. In particular, where
// there already exists another serialization mechanism in Gerrit for
// serializing a particular field, we use that rather than defining a new proto
-// type. This includes ReviewDb types that can be serialized to proto using
-// ProtobufCodec as well as NoteDb and indexed types that are serialized using
-// JSON. We can always revisit this decision later, particularly when we
-// eliminate the ReviewDb types; it just requires bumping the cache version.
+// type. This includes types that can be serialized to proto using
+// ProtoConverters as well as NoteDb and indexed types that are serialized using
+// JSON. We can always revisit this decision later; it just requires bumping the
+// cache version.
//
// Note on nullability: there are a lot of nullable fields in ChangeNotesState
// and its dependencies. It's likely we could make some of them non-nullable,
@@ -134,10 +134,10 @@ message ChangeNotesStateProto {
repeated string hashtag = 5;
- // Raw PatchSet proto as produced by ProtobufCodec.
+ // Raw PatchSet proto as produced by PatchSetProtoConverter.
repeated bytes patch_set = 6;
- // Raw PatchSetApproval proto as produced by ProtobufCodec.
+ // Raw PatchSetApproval proto as produced by PatchSetApprovalProtoConverter.
repeated bytes approval = 7;
// Next ID: 4
@@ -175,14 +175,14 @@ message ChangeNotesStateProto {
// com.google.gerrit.server.index.change.ChangeField.StoredSubmitRecord.
repeated string submit_record = 14;
- // Raw ChangeMessage proto as produced by ProtobufCodec.
+ // Raw ChangeMessage proto as produced by ChangeMessageProtoConverter.
repeated bytes change_message = 15;
// JSON produced from com.google.gerrit.reviewdb.client.Comment.
repeated string published_comment = 16;
- int64 read_only_until = 17;
- bool has_read_only_until = 18;
+ reserved 17; // read_only_until
+ reserved 18; // has_read_only_until
}
@@ -234,3 +234,11 @@ message AllExternalIdsProto {
}
repeated ExternalIdProto external_id = 1;
}
+
+// Key for com.google.gerrit.server.git.PureRevertCache.
+// Next ID: 4
+message PureRevertKeyProto {
+ string project = 1;
+ bytes claimed_original = 2;
+ bytes claimed_revert = 3;
+}
diff --git a/proto/entities.proto b/proto/entities.proto
new file mode 100644
index 0000000000..d2851d382f
--- /dev/null
+++ b/proto/entities.proto
@@ -0,0 +1,158 @@
+// 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.
+
+syntax = "proto2";
+
+package devtools.gerritcodereview;
+
+option java_package = "com.google.gerrit.proto";
+
+// Serialized form of com.google.gerrit.reviewdb.client.Change.Id.
+// Next ID: 2
+message Change_Id {
+ required int32 id = 1;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.Change.Key.
+// Next ID: 2
+message Change_Key {
+ optional string id = 1;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.Change.
+// Next ID: 24
+message Change {
+ required Change_Id change_id = 1;
+ optional Change_Key change_key = 2;
+ optional int32 row_version = 3;
+ optional fixed64 created_on = 4;
+ optional fixed64 last_updated_on = 5;
+ optional Account_Id owner_account_id = 7;
+ optional Branch_NameKey dest = 8;
+ optional uint32 status = 10;
+ optional int32 current_patch_set_id = 12;
+ optional string subject = 13;
+ optional string topic = 14;
+ optional string original_subject = 17;
+ optional string submission_id = 18;
+ optional Account_Id assignee = 19;
+ optional bool is_private = 20;
+ optional bool work_in_progress = 21;
+ optional bool review_started = 22;
+ optional Change_Id revert_of = 23;
+
+ // Deleted fields, should not be reused:
+ reserved 6; // sortkey
+ reserved 9; // open
+ reserved 11; // nbrPatchSets
+ reserved 15; // lastSha1MergeTested
+ reserved 16; // mergeable
+ reserved 101; // note_db_state
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.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.
+// Next ID: 8
+message ChangeMessage {
+ required ChangeMessage_Key key = 1;
+ optional Account_Id author_id = 2;
+ optional fixed64 written_on = 3;
+ optional string message = 4;
+ optional PatchSet_Id patchset = 5;
+ optional string tag = 6;
+ optional Account_Id real_author = 7;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.PatchSet.Id.
+// Next ID: 3
+message PatchSet_Id {
+ required Change_Id change_id = 1;
+ required int32 patch_set_id = 2;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.PatchSet.
+// Next ID: 10
+message PatchSet {
+ required PatchSet_Id id = 1;
+ optional RevId revision = 2;
+ optional Account_Id uploader_account_id = 3;
+ optional fixed64 created_on = 4;
+ optional string groups = 6;
+ optional string push_certificate = 8;
+ optional string description = 9;
+
+ // Deleted fields, should not be reused:
+ reserved 5; // draft
+ reserved 7; // pushCertficate
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.Account.Id.
+// Next ID: 2
+message Account_Id {
+ required int32 id = 1;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.LabelId.
+// Next ID: 2
+message LabelId {
+ required string id = 1;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.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;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.PatchSetApproval.
+// Next ID: 9
+message PatchSetApproval {
+ required PatchSetApproval_Key key = 1;
+ optional int32 value = 2;
+ optional fixed64 granted = 3;
+ optional string tag = 6;
+ optional Account_Id real_account_id = 7;
+ optional bool post_submit = 8;
+
+ // Deleted fields, should not be reused:
+ reserved 4; // changeOpen
+ reserved 5; // changeSortKey
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.Project.NameKey.
+// Next ID: 2
+message Project_NameKey {
+ optional string name = 1;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.Branch.NameKey.
+// Next ID: 3
+message Branch_NameKey {
+ optional Project_NameKey project_name = 1;
+ optional string branch_name = 2;
+}
+
+// Serialized form of com.google.gerrit.reviewdb.client.RevId.
+// Next ID: 2
+message RevId {
+ optional string id = 1;
+}
diff --git a/proto/testing/BUILD b/proto/testing/BUILD
new file mode 100644
index 0000000000..f32d7457a6
--- /dev/null
+++ b/proto/testing/BUILD
@@ -0,0 +1,15 @@
+load("@rules_java//java:defs.bzl", "java_proto_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+proto_library(
+ name = "test_proto",
+ testonly = 1,
+ srcs = ["test.proto"],
+)
+
+java_proto_library(
+ name = "test_java_proto",
+ testonly = 1,
+ visibility = ["//visibility:public"],
+ deps = [":test_proto"],
+)
diff --git a/proto/testing/test.proto b/proto/testing/test.proto
new file mode 100644
index 0000000000..e28c9ff94a
--- /dev/null
+++ b/proto/testing/test.proto
@@ -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.
+
+syntax = "proto2";
+
+package devtools.gerritcodereview.testing;
+
+option java_package = "com.google.gerrit.proto.testing";
+
+// Test type for ProtobufSerializerTest
+// Next ID: 3
+message SerializableProto {
+ required int32 id = 1;
+ optional string text = 2;
+}
diff --git a/resources/com/google/gerrit/httpd/raw/HostPage.html b/resources/com/google/gerrit/httpd/raw/HostPage.html
deleted file mode 100644
index c0d84468d9..0000000000
--- a/resources/com/google/gerrit/httpd/raw/HostPage.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<html>
- <head>
- <title>Gerrit Code Review</title>
- <meta name="gwt:property" content="locale=en_US" />
- <script id="gerrit_hostpagedata"></script>
- <style id="gerrit_sitecss" type="text/css"></style>
- <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
- </head>
- <body>
- <div id="gerrit_topmenu"></div>
- <div id="gerrit_header"></div>
- <div id="gerrit_startinggerrit" style="margin-left: 10px;">
- <p>Loading <a href="https://www.gerritcodereview.com/" target="_blank">Gerrit Code Review</a> ...</p>
- <noscript>
- <p>Gerrit requires a JavaScript enabled browser.</p>
- </noscript>
- </div>
- <div id="gerrit_body"></div>
- <div style="clear: both">
- <div id="gerrit_footer"></div>
- <div id="gerrit_btmmenu"></div>
- </div>
- <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
- <script id="gerrit_module"></script>
- </body>
-</html>
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 9801b44caa..8f151a8c8c 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -16,16 +16,14 @@
{namespace com.google.gerrit.httpd.raw}
-/**
- * @param canonicalPath
- * @param staticResourcePath
- * @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? deprecateGwtUi
- */
{template .Index}
+ {@param canonicalPath: ?}
+ {@param staticResourcePath: ?}
+ {@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: ?}
<!DOCTYPE html>{\n}
<html lang="en">{\n}
<meta charset="utf-8">{\n}
@@ -40,10 +38,10 @@
<script>
window.CLOSURE_NO_DEPS = true;
{if $canonicalPath != ''}window.CANONICAL_PATH = '{$canonicalPath}';{/if}
- {if $deprecateGwtUi}window.DEPRECATE_GWT_UI = true;{/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}
</script>{\n}
{if $faviconPath}
@@ -73,7 +71,6 @@
<link rel="import" href="{$assetsPath}/{$assetsBundle}">{\n}
{/if}
- <link rel="preload" href="{$staticResourcePath}/elements/gr-app.js" as="script" crossorigin="anonymous">{\n}
<link rel="import" href="{$staticResourcePath}/elements/gr-app.html">{\n}
<body unresolved>{\n}
diff --git a/resources/com/google/gerrit/pgm/init/libraries.config b/resources/com/google/gerrit/pgm/init/libraries.config
deleted file mode 100644
index 6fe280c53c..0000000000
--- a/resources/com/google/gerrit/pgm/init/libraries.config
+++ /dev/null
@@ -1,50 +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.
-
-[library "mysqlDriver"]
- name = MySQL Connector/J 5.1.48
- url = https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.48/mysql-connector-java-5.1.48.jar
- sha1 = 9140be77aafa5050bf4bb936d560cbacb5a6b5c1
- remove = mysql-connector-java-.*[.]jar
-
-[library "mariadbDriver"]
- name = MariaDB Connector/J 2.3.0
- url = https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/2.3.0/mariadb-java-client-2.3.0.jar
- sha1 = c2b1a6002a169757d0649449288e9b3b776af76b
- remove = mariadb-java-client-.*[.]jar
-
-[library "oracleDriver"]
- name = Oracle JDBC driver 11g Release 2 (11.2.0)
- url = file:///u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
- sha1 = 2f89cd9176772c3a6c261ce6a8e3d0d4425f5679
- remove = ojdbc6.jar
-
-[library "db2Driver"]
- name = DB2 Type 4 JDBC driver (10.5)
- url = file:///opt/ibm/db2/V10.5/java/db2jcc4.jar
- sha1 = 9344d4fd41d6511f2d1d1deb7759056495b3a39b
- needs = db2DriverLicense
- remove = db2jcc4.jar
-
-# Omit SHA-1 for license JAR as it's not stable and depends on the product
-# the customer has purchased.
-[library "db2DriverLicense"]
- name = DB2 Type 4 JDBC driver license (10.5)
- url = file:///opt/ibm/db2/V10.5/java/db2jcc_license_cu.jar
- remove = db2jcc_license_cu.jar
-
-[library "hanaDriver"]
- name = HANA JDBC driver
- url = file:///usr/sap/hdbclient/ngdbc.jar
- remove = ngdbc.jar
diff --git a/resources/com/google/gerrit/reviewdb/BUILD b/resources/com/google/gerrit/reviewdb/BUILD
deleted file mode 100644
index 8a1b457510..0000000000
--- a/resources/com/google/gerrit/reviewdb/BUILD
+++ /dev/null
@@ -1,8 +0,0 @@
-filegroup(
- name = "reviewdb",
- srcs = glob(
- ["**/*"],
- exclude = ["BUILD"],
- ),
- visibility = ["//visibility:public"],
-)
diff --git a/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/resources/com/google/gerrit/reviewdb/server/index_generic.sql
deleted file mode 100644
index c58edb7926..0000000000
--- a/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ /dev/null
@@ -1,40 +0,0 @@
--- Gerrit 2 : Generic
---
-
--- Indexes to support @Query
---
-
--- *********************************************************************
--- ApprovalCategoryAccess
--- too small to bother indexing
-
-
--- *********************************************************************
--- ApprovalCategoryValueAccess
--- @PrimaryKey covers: byCategory
-
-
--- *********************************************************************
--- BranchAccess
--- @PrimaryKey covers: byProject
-
-
--- *********************************************************************
--- ChangeMessageAccess
--- @PrimaryKey covers: byChange
-
--- covers: byPatchSet
-CREATE INDEX change_messages_byPatchset
-ON change_messages (patchset_change_id, patchset_patch_set_id);
-
--- *********************************************************************
--- PatchLineCommentAccess
--- @PrimaryKey covers: published, draft
-CREATE INDEX patch_comment_drafts
-ON patch_comments (status, author_id);
-
-
--- *********************************************************************
--- PatchSetAccess
-CREATE INDEX patch_sets_byRevision
-ON patch_sets (revision);
diff --git a/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql b/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
deleted file mode 100644
index 7f0f1bd635..0000000000
--- a/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
+++ /dev/null
@@ -1,43 +0,0 @@
-delimiter #
--- Gerrit 2 : MaxDB
---
-
--- Indexes to support @Query
---
-
--- *********************************************************************
--- ApprovalCategoryAccess
--- too small to bother indexing
-
-
--- *********************************************************************
--- ApprovalCategoryValueAccess
--- @PrimaryKey covers: byCategory
-
-
--- *********************************************************************
--- BranchAccess
--- @PrimaryKey covers: byProject
-
-
--- *********************************************************************
--- ChangeMessageAccess
--- @PrimaryKey covers: byChange
-
--- covers: byPatchSet
-CREATE INDEX change_messages_byPatchset
-ON change_messages (patchset_change_id, patchset_patch_set_id)
-#
-
--- *********************************************************************
--- PatchLineCommentAccess
--- @PrimaryKey covers: published, draft
-CREATE INDEX patch_comment_drafts
-ON patch_comments (status, author_id)
-#
-
--- *********************************************************************
--- PatchSetAccess
-CREATE INDEX patch_sets_byRevision
-ON patch_sets (revision)
-#
diff --git a/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
deleted file mode 100644
index 439fed73c1..0000000000
--- a/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ /dev/null
@@ -1,87 +0,0 @@
--- Gerrit 2 : PostgreSQL
---
-
--- Cluster hot tables by their primary method of access
---
-ALTER TABLE patch_sets CLUSTER ON patch_sets_pkey;
-ALTER TABLE change_messages CLUSTER ON change_messages_pkey;
-ALTER TABLE patch_comments CLUSTER ON patch_comments_pkey;
-ALTER TABLE patch_set_approvals CLUSTER ON patch_set_approvals_pkey;
-
-CLUSTER;
-
-
--- Define function for conditional installation of PL/pgSQL.
--- This is required, because starting with PostgreSQL 9.0, PL/pgSQL
--- language is installed by default and database returns error when
--- we try to install it again.
---
--- Source: http://wiki.postgresql.org/wiki/CREATE_OR_REPLACE_LANGUAGE
--- Author: David Fetter
---
-
-delimiter //
-
-CREATE OR REPLACE FUNCTION make_plpgsql()
-RETURNS VOID
-LANGUAGE SQL
-AS $$
-CREATE LANGUAGE plpgsql;
-$$;
-
-//
-
-delimiter ;
-
-SELECT
- CASE
- WHEN EXISTS(
- SELECT 1
- FROM pg_catalog.pg_language
- WHERE lanname='plpgsql'
- )
- THEN NULL
- ELSE make_plpgsql() END;
-
-DROP FUNCTION make_plpgsql();
-
-delimiter ;
-
--- Indexes to support @Query
---
-
--- *********************************************************************
--- ApprovalCategoryAccess
--- too small to bother indexing
-
-
--- *********************************************************************
--- ApprovalCategoryValueAccess
--- @PrimaryKey covers: byCategory
-
-
--- *********************************************************************
--- BranchAccess
--- @PrimaryKey covers: byProject
-
-
--- *********************************************************************
--- ChangeMessageAccess
--- @PrimaryKey covers: byChange
-
--- covers: byPatchSet
-CREATE INDEX change_messages_byPatchset
-ON change_messages (patchset_change_id, patchset_patch_set_id);
-
--- *********************************************************************
--- PatchLineCommentAccess
--- @PrimaryKey covers: published, draft
-CREATE INDEX patch_comment_drafts
-ON patch_comments (author_id)
-WHERE status = 'd';
-
-
--- *********************************************************************
--- PatchSetAccess
-CREATE INDEX patch_sets_byRevision
-ON patch_sets (revision);
diff --git a/resources/com/google/gerrit/server/change/ChangeMessages.properties b/resources/com/google/gerrit/server/change/ChangeMessages.properties
index b2bcde3f01..ec20677445 100644
--- a/resources/com/google/gerrit/server/change/ChangeMessages.properties
+++ b/resources/com/google/gerrit/server/change/ChangeMessages.properties
@@ -1,11 +1,7 @@
-# Changes to this file should also be made in
-# gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}.
reviewerCantSeeChange = {0} does not have permission to see this change
-reviewerInactive = {0} identifies an inactive account
reviewerInvalid = {0} is not a valid user identifier
-reviewerNotFoundUser = {0} does not identify a registered user
reviewerNotFoundUserOrGroup = {0} does not identify a registered user or group
groupIsNotAllowed = The group {0} cannot be added as reviewer.
diff --git a/resources/com/google/gerrit/server/mail/Abandoned.soy b/resources/com/google/gerrit/server/mail/Abandoned.soy
index 623cfe2600..2785ffc8e7 100644
--- a/resources/com/google/gerrit/server/mail/Abandoned.soy
+++ b/resources/com/google/gerrit/server/mail/Abandoned.soy
@@ -19,12 +19,12 @@
/**
* .Abandoned template will determine the contents of the email related to a
* change being abandoned.
- * @param change
- * @param coverLetter
- * @param email
- * @param fromName
*/
{template .Abandoned kind="text"}
+ {@param change: ?}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
{$fromName} has abandoned this change.
{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/AbandonedHtml.soy b/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
index 75d940f1e2..9ad996ec11 100644
--- a/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
+++ b/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
@@ -16,12 +16,10 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param coverLetter
- * @param email
- * @param fromName
- */
{template .AbandonedHtml}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
<p>
{$fromName} <strong>abandoned</strong> this change.
</p>
diff --git a/resources/com/google/gerrit/server/mail/AddKey.soy b/resources/com/google/gerrit/server/mail/AddKey.soy
index be76aee9e1..8b609cf30d 100644
--- a/resources/com/google/gerrit/server/mail/AddKey.soy
+++ b/resources/com/google/gerrit/server/mail/AddKey.soy
@@ -19,9 +19,9 @@
/**
* The .AddKey template will determine the contents of the email related to
* adding a new SSH or GPG key to an account.
- * @param email
*/
{template .AddKey kind="text"}
+ {@param email: ?}
One or more new {$email.keyType} keys have been added to Gerrit Code Review at
{sp}{$email.gerritHost}:
diff --git a/resources/com/google/gerrit/server/mail/AddKeyHtml.soy b/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
index 04a0635ffc..ed4f435d66 100644
--- a/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
+++ b/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
@@ -16,10 +16,8 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param email
- */
{template .AddKeyHtml}
+ {@param email: ?}
<p>
One or more new {$email.keyType} keys have been added to Gerrit Code Review
at {$email.gerritHost}:
diff --git a/resources/com/google/gerrit/server/mail/ChangeFooter.soy b/resources/com/google/gerrit/server/mail/ChangeFooter.soy
index f1d201b1f8..a8170ca87d 100644
--- a/resources/com/google/gerrit/server/mail/ChangeFooter.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeFooter.soy
@@ -19,9 +19,9 @@
/**
* The .ChangeFooter template will determine the contents of the footer text
* that will be appended to ALL emails related to changes.
- * @param email
*/
{template .ChangeFooter kind="text"}
+ {@param email: ?}
--{sp}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
index f802366a94..b619c53dad 100644
--- a/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -16,11 +16,9 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param change
- * @param email
- */
{template .ChangeFooterHtml}
+ {@param change: ?}
+ {@param email: ?}
{if $email.changeUrl or $email.settingsUrl}
<p>
{if $email.changeUrl}
@@ -38,7 +36,7 @@
{if $email.changeUrl}
<div itemscope itemtype="http://schema.org/EmailMessage">
<div itemscope itemprop="action" itemtype="http://schema.org/ViewAction">
- <link itemprop="url" href="{$email.changeUrl |blessStringAsTrustedResourceUrlForLegacy}"/>
+ <link itemprop="url" href="{$email.changeUrl}"/>
<meta itemprop="name" content="View Change"/>
</div>
</div>
diff --git a/resources/com/google/gerrit/server/mail/ChangeSubject.soy b/resources/com/google/gerrit/server/mail/ChangeSubject.soy
index 48ec9a268a..7fcd213069 100644
--- a/resources/com/google/gerrit/server/mail/ChangeSubject.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeSubject.soy
@@ -19,13 +19,13 @@
/**
* The .ChangeSubject template will determine the contents of the email subject
* line for ALL emails related to changes.
- * @param branch
- * @param change
- * @param shortProjectName
- * @param instanceAndProjectName
- * @param addInstanceNameInSubject boolean
*/
{template .ChangeSubject kind="text"}
+ {@param branch: ?}
+ {@param change: ?}
+ {@param shortProjectName: ?}
+ {@param instanceAndProjectName: ?}
+ {@param addInstanceNameInSubject: ?} /** boolean */
{if not $addInstanceNameInSubject}
Change in {$shortProjectName}[{$branch.shortName}]: {$change.shortSubject}
{else}
diff --git a/resources/com/google/gerrit/server/mail/Comment.soy b/resources/com/google/gerrit/server/mail/Comment.soy
index f9a11cdc5b..1eb016b983 100644
--- a/resources/com/google/gerrit/server/mail/Comment.soy
+++ b/resources/com/google/gerrit/server/mail/Comment.soy
@@ -19,13 +19,13 @@
/**
* The .Comment template will determine the contents of the email related to a
* user submitting comments on changes.
- * @param change
- * @param coverLetter
- * @param email
- * @param fromName
- * @param commentFiles
*/
{template .Comment kind="text"}
+ {@param change: ?}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
+ {@param commentFiles: ?}
{$fromName} has posted comments on this change.
{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/CommentHtml.soy b/resources/com/google/gerrit/server/mail/CommentHtml.soy
index d554258c5c..534cbdb7e9 100644
--- a/resources/com/google/gerrit/server/mail/CommentHtml.soy
+++ b/resources/com/google/gerrit/server/mail/CommentHtml.soy
@@ -16,15 +16,13 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param commentFiles
- * @param commentCount
- * @param email
- * @param labels
- * @param patchSet
- * @param patchSetCommentBlocks
- */
{template .CommentHtml}
+ {@param commentFiles: ?}
+ {@param commentCount: ?}
+ {@param email: ?}
+ {@param labels: ?}
+ {@param patchSet: ?}
+ {@param patchSetCommentBlocks: ?}
{let $commentHeaderStyle kind="css"}
margin-bottom: 4px;
{/let}
diff --git a/resources/com/google/gerrit/server/mail/DeleteKey.soy b/resources/com/google/gerrit/server/mail/DeleteKey.soy
index 7a2cdf4dd2..30548c88c8 100644
--- a/resources/com/google/gerrit/server/mail/DeleteKey.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteKey.soy
@@ -19,9 +19,9 @@
/**
* The .DeleteKey template will determine the contents of the email related to
* deleting a SSH or GPG key.
- * @param email
*/
{template .DeleteKey kind="text"}
+ {@param email: ?}
One or more {$email.keyType} keys have been deleted on Gerrit Code Review at
{sp}{$email.gerritHost}:
diff --git a/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy b/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy
index 25c1bd814c..1ab3955dfd 100644
--- a/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy
@@ -16,10 +16,8 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param email
- */
{template .DeleteKeyHtml}
+ {@param email: ?}
<p>
One or more {$email.keyType} keys have been deleted on Gerrit Code Review
at {$email.gerritHost}:
diff --git a/resources/com/google/gerrit/server/mail/DeleteReviewer.soy b/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
index 065348ace9..3310249fc9 100644
--- a/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
@@ -19,12 +19,12 @@
/**
* The .DeleteReviewer template will determine the contents of the email related
* to removal of a reviewer (and the reviewer's votes) from reviews.
- * @param change
- * @param coverLetter
- * @param email
- * @param fromName
*/
{template .DeleteReviewer kind="text"}
+ {@param change: ?}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
{$fromName} has removed{sp}
{for $reviewerName in $email.reviewerNames}
{if not isFirst($reviewerName)},{sp}{/if}
diff --git a/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy b/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
index 0599b52480..54720fe7ef 100644
--- a/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
@@ -16,11 +16,9 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param email
- * @param fromName
- */
{template .DeleteReviewerHtml}
+ {@param email: ?}
+ {@param fromName: ?}
<p>
{$fromName}{sp}
<strong>
diff --git a/resources/com/google/gerrit/server/mail/DeleteVote.soy b/resources/com/google/gerrit/server/mail/DeleteVote.soy
index d493e09a09..0ee5454d34 100644
--- a/resources/com/google/gerrit/server/mail/DeleteVote.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteVote.soy
@@ -19,12 +19,12 @@
/**
* The .DeleteVote template will determine the contents of the email related
* to removing votes on changes.
- * @param change
- * @param coverLetter
- * @param email
- * @param fromName
*/
{template .DeleteVote kind="text"}
+ {@param change: ?}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
{$fromName} has removed a vote from this change.{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}{\n}
{\n}
Change subject: {$change.subject}{\n}
diff --git a/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy b/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
index cb8162d9c1..3a82927b5a 100644
--- a/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
@@ -16,12 +16,10 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param coverLetter
- * @param email
- * @param fromName
- */
{template .DeleteVoteHtml}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
<p>
{$fromName} <strong>removed a vote</strong> from this change.
</p>
diff --git a/resources/com/google/gerrit/server/mail/Footer.soy b/resources/com/google/gerrit/server/mail/Footer.soy
index e1890a8139..7483cd9d92 100644
--- a/resources/com/google/gerrit/server/mail/Footer.soy
+++ b/resources/com/google/gerrit/server/mail/Footer.soy
@@ -20,9 +20,9 @@
* The .Footer template will determine the contents of the footer text
* appended to the end of all outgoing emails after the ChangeFooter and
* CommentFooter.
- * @param footers
*/
{template .Footer kind="text"}
+ {@param footers: ?}
{for $footer in $footers}
{$footer}{\n}
{/for}
diff --git a/resources/com/google/gerrit/server/mail/FooterHtml.soy b/resources/com/google/gerrit/server/mail/FooterHtml.soy
index 938655c73d..ce934d3b6d 100644
--- a/resources/com/google/gerrit/server/mail/FooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/FooterHtml.soy
@@ -16,10 +16,8 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param footers
- */
{template .FooterHtml}
+ {@param footers: ?}
{\n}
{\n}
{for $footer in $footers}
diff --git a/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy b/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy
index b40e5e3903..38e679efc0 100644
--- a/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy
+++ b/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy
@@ -19,9 +19,9 @@
/**
* The .HttpPasswordUpdate template will determine the contents of the email related to
* adding, changing or deleting the HTTP password.
- * @param email
*/
{template .HttpPasswordUpdate kind="text"}
+ {@param email: ?}
The HTTP password was {$email.operation} on Gerrit Code Review at
{sp}{$email.gerritHost}.
diff --git a/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy b/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy
index b229f3a6a7..3c4594cf66 100644
--- a/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy
+++ b/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy
@@ -16,10 +16,8 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param email
- */
{template .HttpPasswordUpdateHtml}
+ {@param email: ?}
<p>
The HTTP password was {$email.operation} on Gerrit Code Review
at {$email.gerritHost}.
diff --git a/resources/com/google/gerrit/server/mail/Merged.soy b/resources/com/google/gerrit/server/mail/Merged.soy
index 47bfaf2ebc..899d1c0f4b 100644
--- a/resources/com/google/gerrit/server/mail/Merged.soy
+++ b/resources/com/google/gerrit/server/mail/Merged.soy
@@ -20,11 +20,11 @@
/**
* The .Merged template will determine the contents of the email related to
* a change successfully merged to the head.
- * @param change
- * @param email
- * @param fromName
*/
{template .Merged kind="text"}
+ {@param change: ?}
+ {@param email: ?}
+ {@param fromName: ?}
{$fromName} has submitted this change.
{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/MergedHtml.soy b/resources/com/google/gerrit/server/mail/MergedHtml.soy
index c51be96747..f0a47c71e8 100644
--- a/resources/com/google/gerrit/server/mail/MergedHtml.soy
+++ b/resources/com/google/gerrit/server/mail/MergedHtml.soy
@@ -16,12 +16,10 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param diffLines
- * @param email
- * @param fromName
- */
{template .MergedHtml}
+ {@param diffLines: ?}
+ {@param email: ?}
+ {@param fromName: ?}
<p>
{$fromName} <strong>submitted</strong> this change.
</p>
diff --git a/resources/com/google/gerrit/server/mail/NewChange.soy b/resources/com/google/gerrit/server/mail/NewChange.soy
index 8413894ee3..fa447e9ed5 100644
--- a/resources/com/google/gerrit/server/mail/NewChange.soy
+++ b/resources/com/google/gerrit/server/mail/NewChange.soy
@@ -19,13 +19,13 @@
/**
* The .NewChange template will determine the contents of the email related to a
* user submitting a new change for review.
- * @param change
- * @param email
- * @param ownerName
- * @param patchSet
- * @param projectName
*/
{template .NewChange kind="text"}
+ {@param change: ?}
+ {@param email: ?}
+ {@param ownerName: ?}
+ {@param patchSet: ?}
+ {@param projectName: ?}
{if $email.reviewerNames}
Hello{sp}
{for $reviewerName in $email.reviewerNames}
diff --git a/resources/com/google/gerrit/server/mail/NewChangeHtml.soy b/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
index 5bce8065b0..9de8707f5c 100644
--- a/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
+++ b/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
@@ -16,15 +16,13 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param diffLines
- * @param email
- * @param fromName
- * @param ownerName
- * @param patchSet
- * @param projectName
- */
{template .NewChangeHtml}
+ {@param diffLines: ?}
+ {@param email: ?}
+ {@param fromName: ?}
+ {@param ownerName: ?}
+ {@param patchSet: ?}
+ {@param projectName: ?}
<p>
{if $email.reviewerNames}
{$fromName} would like{sp}
diff --git a/resources/com/google/gerrit/server/mail/Private.soy b/resources/com/google/gerrit/server/mail/Private.soy
index bb32a7e920..510f15eff3 100644
--- a/resources/com/google/gerrit/server/mail/Private.soy
+++ b/resources/com/google/gerrit/server/mail/Private.soy
@@ -22,17 +22,17 @@
/**
* Private template to generate "View Change" buttons.
- * @param email
*/
{template .ViewChangeButton}
+ {@param email: ?}
<a href="{$email.changeUrl}">View Change</a>
{/template}
/**
* Private template to render PRE block with consistent font-sizing.
- * @param content
*/
{template .Pre}
+ {@param content: ?}
{let $preStyle kind="css"}
font-family: monospace,monospace; // Use this to avoid browsers scaling down
// monospace text.
@@ -53,10 +53,9 @@
*
* This mechanism encodes as little structure as possible in order to depend on
* the Soy autoescape mechanism for all of the content.
- *
- * @param content
*/
{template .WikiFormat}
+ {@param content: ?}
{let $blockquoteStyle kind="css"}
border-left: 1px solid #aaa;
margin: 10px 0;
@@ -87,10 +86,8 @@
{/for}
{/template}
-/**
- * @param diffLines
- */
{template .UnifiedDiff}
+ {@param diffLines: ?}
{let $addStyle kind="css"}
color: hsl(120, 100%, 40%);
{/let}
diff --git a/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy b/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
index 2886cc0e02..ee03de0fa0 100644
--- a/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
+++ b/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
@@ -19,9 +19,9 @@
/**
* The .RegisterNewEmail template will determine the contents of the email
* related to registering new email accounts.
- * @param email
*/
{template .RegisterNewEmail kind="text"}
+ {@param email: ?}
Welcome to Gerrit Code Review at {$email.gerritHost}.{\n}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy b/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
index 1cb0110390..bb84cf1b43 100644
--- a/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
+++ b/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
@@ -19,14 +19,14 @@
/**
* The .ReplacePatchSet template will determine the contents of the email
* related to a user submitting a new patchset for a change.
- * @param change
- * @param email
- * @param fromEmail
- * @param fromName
- * @param patchSet
- * @param projectName
*/
{template .ReplacePatchSet kind="text"}
+ {@param change: ?}
+ {@param email: ?}
+ {@param fromEmail: ?}
+ {@param fromName: ?}
+ {@param patchSet: ?}
+ {@param projectName: ?}
{if $email.reviewerNames and $fromEmail == $change.ownerEmail}
Hello{sp}
{for $reviewerName in $email.reviewerNames}
diff --git a/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy b/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
index e618bef78c..96cba5f218 100644
--- a/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
+++ b/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
@@ -16,15 +16,13 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param change
- * @param email
- * @param fromName
- * @param fromEmail
- * @param patchSet
- * @param projectName
- */
{template .ReplacePatchSetHtml}
+ {@param change: ?}
+ {@param email: ?}
+ {@param fromName: ?}
+ {@param fromEmail: ?}
+ {@param patchSet: ?}
+ {@param projectName: ?}
<p>
{$fromName} <strong>uploaded patch set #{$patchSet.patchSetId}</strong>{sp}
to{sp}
diff --git a/resources/com/google/gerrit/server/mail/Restored.soy b/resources/com/google/gerrit/server/mail/Restored.soy
index 4fc6d8c716..0ec65b3010 100644
--- a/resources/com/google/gerrit/server/mail/Restored.soy
+++ b/resources/com/google/gerrit/server/mail/Restored.soy
@@ -19,12 +19,12 @@
/**
* The .Restored template will determine the contents of the email related to a
* change being restored.
- * @param change
- * @param coverLetter
- * @param email
- * @param fromName
*/
{template .Restored kind="text"}
+ {@param change: ?}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
{$fromName} has restored this change.
{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/RestoredHtml.soy b/resources/com/google/gerrit/server/mail/RestoredHtml.soy
index bb856ac34b..bcd358fba4 100644
--- a/resources/com/google/gerrit/server/mail/RestoredHtml.soy
+++ b/resources/com/google/gerrit/server/mail/RestoredHtml.soy
@@ -16,11 +16,9 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param email
- * @param fromName
- */
{template .RestoredHtml}
+ {@param email: ?}
+ {@param fromName: ?}
<p>
{$fromName} <strong>restored</strong> this change.
</p>
diff --git a/resources/com/google/gerrit/server/mail/Reverted.soy b/resources/com/google/gerrit/server/mail/Reverted.soy
index fba8744298..32a65c641b 100644
--- a/resources/com/google/gerrit/server/mail/Reverted.soy
+++ b/resources/com/google/gerrit/server/mail/Reverted.soy
@@ -19,12 +19,12 @@
/**
* The .Reverted template will determine the contents of the email related
* to a change being reverted.
- * @param change
- * @param coverLetter
- * @param email
- * @param fromName
*/
{template .Reverted kind="text"}
+ {@param change: ?}
+ {@param coverLetter: ?}
+ {@param email: ?}
+ {@param fromName: ?}
{$fromName} has created a revert of this change.
{if $email.changeUrl} ( {$email.changeUrl} ){/if}{\n}
{\n}
diff --git a/resources/com/google/gerrit/server/mail/RevertedHtml.soy b/resources/com/google/gerrit/server/mail/RevertedHtml.soy
index b7b254eab8..69260ad65f 100644
--- a/resources/com/google/gerrit/server/mail/RevertedHtml.soy
+++ b/resources/com/google/gerrit/server/mail/RevertedHtml.soy
@@ -16,11 +16,9 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param email
- * @param fromName
- */
{template .RevertedHtml}
+ {@param email: ?}
+ {@param fromName: ?}
<p>
{$fromName} has <strong>created a revert</strong> of this change.
</p>
diff --git a/resources/com/google/gerrit/server/mail/SetAssignee.soy b/resources/com/google/gerrit/server/mail/SetAssignee.soy
index 98290e96a7..1fdf690969 100644
--- a/resources/com/google/gerrit/server/mail/SetAssignee.soy
+++ b/resources/com/google/gerrit/server/mail/SetAssignee.soy
@@ -19,13 +19,13 @@
/**
* The .SetAssignee template will determine the contents of the email related
* to a user being assigned to a change.
- * @param change
- * @param email
- * @param fromName
- * @param patchSet
- * @param projectName
*/
{template .SetAssignee kind="text"}
+ {@param change: ?}
+ {@param email: ?}
+ {@param fromName: ?}
+ {@param patchSet: ?}
+ {@param projectName: ?}
Hello{sp}
{$email.assigneeName},
diff --git a/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy b/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
index dbd3faeade..1826314521 100644
--- a/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
+++ b/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
@@ -16,14 +16,12 @@
{namespace com.google.gerrit.server.mail.template}
-/**
- * @param diffLines
- * @param email
- * @param fromName
- * @param patchSet
- * @param projectName
- */
{template .SetAssigneeHtml}
+ {@param diffLines: ?}
+ {@param email: ?}
+ {@param fromName: ?}
+ {@param patchSet: ?}
+ {@param projectName: ?}
<p>
{$fromName} has <strong>assigned</strong> a change to{sp}
{$email.assigneeName}.{sp}
diff --git a/tools/BUILD b/tools/BUILD
index 9a53c8b6c6..9ccc0651f4 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -39,12 +39,12 @@ java_package_configuration(
"-Xep:ElementsCountedInLoop:WARN",
"-Xep:EqualsHashCode:WARN",
"-Xep:EqualsIncompatibleType:WARN",
- "-Xep:ExpectedExceptionChecker:ERROR",
+ "-Xep:ExpectedExceptionChecker:OFF",
"-Xep:Finally:WARN",
"-Xep:FloatingPointLiteralPrecision:WARN",
"-Xep:FragmentInjection:WARN",
"-Xep:FragmentNotInstantiable:WARN",
- "-Xep:FunctionalInterfaceClash:WARN",
+ "-Xep:FunctionalInterfaceClash:ERROR",
"-Xep:FutureReturnValueIgnored:WARN",
"-Xep:GetClassOnEnum:WARN",
"-Xep:ImmutableAnnotationChecker:WARN",
diff --git a/tools/bzl/gwt.bzl b/tools/bzl/gwt.bzl
deleted file mode 100644
index cf1c47e627..0000000000
--- a/tools/bzl/gwt.bzl
+++ /dev/null
@@ -1,311 +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.
-
-load("//tools/bzl:genrule2.bzl", "genrule2")
-load("//tools/bzl:java.bzl", "java_library2")
-
-jar_filetype = [".jar"]
-
-BROWSERS = [
- "chrome",
- "firefox",
- "gecko1_8",
- "safari",
- "msie",
- "ie8",
- "ie9",
- "ie10",
- "edge",
-]
-
-ALIASES = {
- "chrome": "safari",
- "edge": "gecko1_8",
- "firefox": "gecko1_8",
- "msie": "ie10",
-}
-
-MODULE = "com.google.gerrit.GerritGwtUI"
-
-GWT_COMPILER = "com.google.gwt.dev.Compiler"
-
-GWT_JVM_ARGS = ["-Xmx512m"]
-
-GWT_COMPILER_ARGS = [
- "-XdisableClassMetadata",
-]
-
-GWT_COMPILER_ARGS_RELEASE_MODE = GWT_COMPILER_ARGS + [
- "-XdisableCastChecking",
-]
-
-GWT_PLUGIN_DEPS_NEVERLINK = [
- "//gerrit-plugin-gwtui:gwtui-api-lib-neverlink",
- "//lib/gwt:user-neverlink",
-]
-
-GWT_PLUGIN_DEPS = [
- "//gerrit-plugin-gwtui:gwtui-api-lib",
-]
-
-GWT_TRANSITIVE_DEPS = [
- "//lib:jsr305",
- "//lib/gwt:ant",
- "//lib/gwt:colt",
- "//lib/gwt:javax-validation",
- "//lib/gwt:javax-validation_src",
- "//lib/gwt:jsinterop-annotations",
- "//lib/gwt:jsinterop-annotations_src",
- "//lib/gwt:tapestry",
- "//lib/gwt:w3c-css-sac",
- "//lib/ow2:ow2-asm",
- "//lib/ow2:ow2-asm-analysis",
- "//lib/ow2:ow2-asm-commons",
- "//lib/ow2:ow2-asm-tree",
- "//lib/ow2:ow2-asm-util",
-]
-
-DEPS = GWT_TRANSITIVE_DEPS + [
- "//java/com/google/gwtexpui/css",
- "//lib:gwtjsonrpc",
- "//lib/gwt:dev",
- "//lib/jgit/org.eclipse.jgit:jgit-source",
-]
-
-USER_AGENT_XML = """<module rename-to='gerrit_ui'>
-<inherits name='%s'/>
-<set-property name='user.agent' value='%s'/>
-<set-property name='locale' value='default'/>
-</module>
-"""
-
-def gwt_module(gwt_xml = None, resources = [], srcs = [], **kwargs):
- if gwt_xml:
- resources = resources + [gwt_xml]
-
- java_library2(
- srcs = srcs,
- resources = resources,
- **kwargs
- )
-
-def _gwt_user_agent_module(ctx):
- """Generate user agent specific GWT module."""
- if not ctx.attr.user_agent:
- return None
-
- ua = ctx.attr.user_agent
- impl = ua
- if ua in ALIASES:
- impl = ALIASES[ua]
-
- # intermediate artifact: user agent speific GWT xml file
- gwt_user_agent_xml = ctx.actions.declare_file(ctx.label.name + "_gwt.xml")
- ctx.actions.write(
- output = gwt_user_agent_xml,
- content = USER_AGENT_XML % (MODULE, impl),
- )
-
- # intermediate artifact: user agent specific zip with GWT module
- gwt_user_agent_zip = ctx.actions.declare_file(ctx.label.name + "_gwt.zip")
- gwt = "%s_%s.gwt.xml" % (MODULE.replace(".", "/"), ua)
- dir = gwt_user_agent_zip.path + ".dir"
- cmd = " && ".join([
- "p=$PWD",
- "mkdir -p %s" % dir,
- "cd %s" % dir,
- "mkdir -p $(dirname %s)" % gwt,
- "cp $p/%s %s" % (gwt_user_agent_xml.path, gwt),
- "$p/%s cC $p/%s $(find . | sed 's|^./||')" % (ctx.executable._zip.path, gwt_user_agent_zip.path),
- ])
- ctx.actions.run_shell(
- inputs = [gwt_user_agent_xml],
- outputs = [gwt_user_agent_zip],
- tools = ctx.files._zip,
- command = cmd,
- mnemonic = "GenerateUserAgentGWTModule",
- )
-
- return struct(
- zip = gwt_user_agent_zip,
- module = MODULE + "_" + ua,
- )
-
-def _gwt_binary_impl(ctx):
- module = ctx.attr.module[0]
- output_zip = ctx.outputs.output
- output_dir = output_zip.path + ".gwt_output"
- deploy_dir = output_zip.path + ".gwt_deploy"
-
- deps = _get_transitive_closure(ctx)
-
- paths = [dep.path for dep in deps.to_list()]
-
- gwt_user_agent_modules = []
- ua = _gwt_user_agent_module(ctx)
- if ua:
- paths.append(ua.zip.path)
- gwt_user_agent_modules.append(ua.zip)
- module = ua.module
-
- cmd = "%s %s -Dgwt.normalizeTimestamps=true -cp %s %s -war %s -deploy %s " % (
- ctx.attr._jdk[java_common.JavaRuntimeInfo].java_executable_exec_path,
- " ".join(ctx.attr.jvm_args),
- ":".join(paths),
- GWT_COMPILER,
- output_dir,
- deploy_dir,
- )
-
- # TODO(davido): clean up command concatenation
- cmd += " ".join([
- "-style %s" % ctx.attr.style,
- "-optimize %s" % ctx.attr.optimize,
- "-strict",
- " ".join(ctx.attr.compiler_args),
- module + "\n",
- "rm -rf %s/gwt-unitCache\n" % output_dir,
- "root=`pwd`\n",
- "cd %s; $root/%s Cc ../%s $(find .)\n" % (
- output_dir,
- ctx.executable._zip.path,
- output_zip.basename,
- ),
- ])
-
- ctx.actions.run_shell(
- inputs = depset(direct = gwt_user_agent_modules, transitive = [deps]),
- outputs = [output_zip],
- tools = ctx.files._jdk + ctx.files._zip,
- mnemonic = "GwtBinary",
- progress_message = "GWT compiling " + output_zip.short_path,
- command = "set -e\n" + cmd,
- )
-
-def _get_transitive_closure(ctx):
- deps = []
- for dep in ctx.attr.module_deps:
- deps.append(dep[JavaInfo].transitive_runtime_deps)
- deps.append(dep[JavaInfo].transitive_source_jars)
- for dep in ctx.attr.deps:
- if JavaInfo in dep:
- deps.append(dep[JavaInfo].transitive_runtime_deps)
- elif hasattr(dep, "files"):
- deps.append(dep.files)
-
- return depset(transitive = deps)
-
-gwt_binary = rule(
- attrs = {
- "compiler_args": attr.string_list(),
- "jvm_args": attr.string_list(),
- "module": attr.string_list(default = [MODULE]),
- "module_deps": attr.label_list(allow_files = jar_filetype),
- "optimize": attr.string(default = "9"),
- "style": attr.string(default = "OBF"),
- "user_agent": attr.string(),
- "deps": attr.label_list(allow_files = jar_filetype),
- "_jdk": attr.label(
- default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
- cfg = "host",
- ),
- "_zip": attr.label(
- default = Label("@bazel_tools//tools/zip:zipper"),
- cfg = "host",
- executable = True,
- allow_single_file = True,
- ),
- },
- outputs = {
- "output": "%{name}.zip",
- },
- implementation = _gwt_binary_impl,
-)
-
-def gwt_genrule(suffix = ""):
- dbg = "ui_dbg" + suffix
- opt = "ui_opt" + suffix
- module_dep = ":ui_module" + suffix
- args = GWT_COMPILER_ARGS_RELEASE_MODE if suffix == "_r" else GWT_COMPILER_ARGS
-
- genrule2(
- name = "ui_optdbg" + suffix,
- srcs = [
- ":" + dbg,
- ":" + opt,
- ],
- cmd = "cd $$TMP;" +
- "unzip -q $$ROOT/$(location :%s);" % dbg +
- "mv" +
- " gerrit_ui/gerrit_ui.nocache.js" +
- " gerrit_ui/dbg_gerrit_ui.nocache.js;" +
- "unzip -qo $$ROOT/$(location :%s);" % opt +
- "mkdir -p $$(dirname $@);" +
- "zip -qrD $$ROOT/$@ .",
- outs = ["ui_optdbg" + suffix + ".zip"],
- visibility = ["//visibility:public"],
- )
-
- gwt_binary(
- name = opt,
- module = [MODULE],
- module_deps = [module_dep],
- deps = DEPS,
- compiler_args = args,
- jvm_args = GWT_JVM_ARGS,
- )
-
- gwt_binary(
- name = dbg,
- style = "PRETTY",
- optimize = "0",
- module_deps = [module_dep],
- deps = DEPS,
- compiler_args = GWT_COMPILER_ARGS,
- jvm_args = GWT_JVM_ARGS,
- )
-
-def gen_ui_module(name, suffix = ""):
- gwt_module(
- name = name + suffix,
- srcs = native.glob(["src/main/java/**/*.java"]),
- gwt_xml = "src/main/java/%s.gwt.xml" % MODULE.replace(".", "/"),
- resources = native.glob(
- ["src/main/java/**/*"],
- exclude = ["src/main/java/**/*.java"] +
- ["src/main/java/%s.gwt.xml" % MODULE.replace(".", "/")],
- ),
- deps = [
- "//gerrit-gwtui-common:diffy_logo",
- "//gerrit-gwtui-common:client",
- "//java/com/google/gwtexpui/css",
- "//lib/codemirror:codemirror" + suffix,
- "//lib/gwt:user",
- ],
- visibility = ["//visibility:public"],
- )
-
-def gwt_user_agent_permutations():
- for ua in BROWSERS:
- gwt_binary(
- name = "ui_%s" % ua,
- user_agent = ua,
- style = "PRETTY",
- optimize = "0",
- module = [MODULE],
- module_deps = [":ui_module"],
- deps = DEPS,
- compiler_args = GWT_COMPILER_ARGS,
- jvm_args = GWT_JVM_ARGS,
- )
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index d637ad4d2c..9160f1dee0 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -364,6 +364,7 @@ def _bundle_impl(ctx):
"python",
ctx.file._run_npm.path,
ctx.file._crisper_archive.path,
+ "--script-in-head=false",
"--always-write-script",
"--source",
bundled.path,
@@ -430,7 +431,7 @@ def bundle_assets(*args, **kwargs):
"""Combine html, js, css files and optionally split into js and html bundles."""
_bundle_rule(pkg = native.package_name(), *args, **kwargs)
-def polygerrit_plugin(name, app, srcs = [], deps = [], assets = None, plugin_name = None, **kwargs):
+def polygerrit_plugin(name, app, srcs = [], deps = [], externs = [], assets = None, plugin_name = None, **kwargs):
"""Bundles plugin dependencies for deployment.
This rule bundles all Polymer elements and JS dependencies into .html and .js files.
@@ -440,6 +441,7 @@ def polygerrit_plugin(name, app, srcs = [], deps = [], assets = None, plugin_nam
Args:
name: String, rule name.
app: String, the main or root source file.
+ externs: Fileset, external definitions that should not be bundled.
assets: Fileset, additional files to be used by plugin in runtime, exported to "plugins/${name}/static".
plugin_name: String, plugin name. ${name} is used if not provided.
"""
@@ -465,7 +467,7 @@ def polygerrit_plugin(name, app, srcs = [], deps = [], assets = None, plugin_nam
closure_js_library(
name = name + "_closure_lib",
- srcs = js_srcs,
+ srcs = js_srcs + externs,
convention = "GOOGLE",
no_closure_library = True,
deps = [
diff --git a/tools/bzl/junit.bzl b/tools/bzl/junit.bzl
index 21d2cd8e7e..1cf82ea975 100644
--- a/tools/bzl/junit.bzl
+++ b/tools/bzl/junit.bzl
@@ -82,8 +82,8 @@ def junit_tests(name, srcs, **kwargs):
)
jvm_flags = kwargs.get("jvm_flags", [])
jvm_flags = jvm_flags + select({
- "//:java_next": POST_JDK8_OPTS,
"//:java9": POST_JDK8_OPTS,
+ "//:java_next": POST_JDK8_OPTS,
"//conditions:default": [],
})
java_test(
diff --git a/tools/bzl/license-map.py b/tools/bzl/license-map.py
index ebe57f252a..2779130f47 100644
--- a/tools/bzl/license-map.py
+++ b/tools/bzl/license-map.py
@@ -54,6 +54,8 @@ if args.asciidoctor:
# We don't want any blank line before "= Gerrit Code Review - Licenses"
print("""= Gerrit Code Review - Licenses
+// DO NOT EDIT - GENERATED AUTOMATICALLY.
+
Gerrit open source software is licensed under the <<Apache2_0,Apache
License 2.0>>. Executable distributions also include other software
components that are provided under additional licenses.
diff --git a/tools/bzl/license.bzl b/tools/bzl/license.bzl
index d059216ec6..5a6bf7fa63 100644
--- a/tools/bzl/license.bzl
+++ b/tools/bzl/license.bzl
@@ -26,7 +26,7 @@ def license_map(name, targets = [], opts = [], **kwargs):
native.genrule(
name = "gen_license_txt_" + name,
cmd = "python $(location //tools/bzl:license-map.py) %s %s > $@" % (" ".join(opts), " ".join(xmls)),
- outs = [name + ".txt"],
+ outs = [name + ".gen.txt"],
tools = tools,
**kwargs
)
diff --git a/tools/bzl/maven_jar.bzl b/tools/bzl/maven_jar.bzl
index 7730804e88..177e80b0b3 100644
--- a/tools/bzl/maven_jar.bzl
+++ b/tools/bzl/maven_jar.bzl
@@ -140,8 +140,6 @@ def _maven_jar_impl(ctx):
args = [python, script, "-o", binjar_path, "-u", binurl]
if ctx.attr.sha1:
args.extend(["-v", sha1])
- if ctx.attr.unsign:
- args.append("--unsign")
for x in ctx.attr.exclude:
args.extend(["-x", x])
@@ -172,7 +170,6 @@ maven_jar = repository_rule(
"repository": attr.string(default = MAVEN_CENTRAL),
"sha1": attr.string(),
"src_sha1": attr.string(),
- "unsign": attr.bool(default = False),
"exports": attr.string_list(),
"deps": attr.string_list(),
"_download_script": attr.label(default = Label("//tools:download_file.py")),
diff --git a/tools/bzl/pkg_war.bzl b/tools/bzl/pkg_war.bzl
index 0943c51ebb..c9ac0fe5a0 100644
--- a/tools/bzl/pkg_war.bzl
+++ b/tools/bzl/pkg_war.bzl
@@ -19,7 +19,6 @@ jar_filetype = [".jar"]
LIBS = [
"//java/com/google/gerrit/common:version",
"//java/com/google/gerrit/httpd/init",
- "//lib:postgresql",
"//lib/bouncycastle:bcpkix",
"//lib/bouncycastle:bcprov",
"//lib/bouncycastle:bcpg",
@@ -138,14 +137,12 @@ _pkg_war = rule(
implementation = _war_impl,
)
-def pkg_war(name, ui = "ui_optdbg", context = [], doc = False, **kwargs):
+def pkg_war(name, ui = "polygerrit", context = [], doc = False, **kwargs):
doc_ctx = []
doc_lib = []
ui_deps = []
- if ui == "polygerrit" or ui == "ui_optdbg" or ui == "ui_optdbg_r":
+ if ui == "polygerrit":
ui_deps.append("//polygerrit-ui/app:polygerrit_ui")
- if ui and ui != "polygerrit":
- ui_deps.append("//gerrit-gwtui:%s" % ui)
if doc:
doc_ctx.append("//Documentation:html")
doc_lib.append("//Documentation:index")
diff --git a/tools/bzl/plugin.bzl b/tools/bzl/plugin.bzl
index 5ef8229c35..0387510b85 100644
--- a/tools/bzl/plugin.bzl
+++ b/tools/bzl/plugin.bzl
@@ -1,17 +1,6 @@
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
load("//tools/bzl:genrule2.bzl", "genrule2")
load("//:version.bzl", "GERRIT_VERSION")
-load(
- "//tools/bzl:gwt.bzl",
- "GWT_COMPILER_ARGS",
- "GWT_JVM_ARGS",
- "GWT_PLUGIN_DEPS_NEVERLINK",
- "GWT_TRANSITIVE_DEPS",
- "gwt_binary",
- _gwt_plugin_deps = "GWT_PLUGIN_DEPS",
-)
-
-GWT_PLUGIN_DEPS = _gwt_plugin_deps
PLUGIN_DEPS = ["//plugins:plugin-lib"]
@@ -29,7 +18,6 @@ def gerrit_plugin(
deps = [],
provided_deps = [],
srcs = [],
- gwt_module = [],
resources = [],
manifest_entries = [],
dir_name = None,
@@ -39,14 +27,12 @@ def gerrit_plugin(
name = name + "__plugin",
srcs = srcs,
resources = resources,
- deps = provided_deps + deps + GWT_PLUGIN_DEPS_NEVERLINK + PLUGIN_DEPS_NEVERLINK,
+ deps = provided_deps + deps + PLUGIN_DEPS_NEVERLINK,
visibility = ["//visibility:public"],
**kwargs
)
static_jars = []
- if gwt_module:
- static_jars = [":%s-static" % name]
if not dir_name:
dir_name = name
@@ -65,34 +51,6 @@ def gerrit_plugin(
**kwargs
)
- if gwt_module:
- java_library(
- name = name + "__gwt_module",
- resources = depset(srcs + resources).to_list(),
- runtime_deps = deps + GWT_PLUGIN_DEPS,
- visibility = ["//visibility:public"],
- **kwargs
- )
- genrule2(
- name = "%s-static" % name,
- cmd = " && ".join([
- "mkdir -p $$TMP/static",
- "unzip -qd $$TMP/static $(location %s__gwt_application)" % name,
- "cd $$TMP",
- "zip -qr $$ROOT/$@ .",
- ]),
- tools = [":%s__gwt_application" % name],
- outs = ["%s-static.jar" % name],
- )
- gwt_binary(
- name = name + "__gwt_application",
- module = [gwt_module],
- deps = GWT_PLUGIN_DEPS + GWT_TRANSITIVE_DEPS + ["//lib/gwt:dev"],
- module_deps = [":%s__gwt_module" % name],
- compiler_args = GWT_COMPILER_ARGS,
- jvm_args = GWT_JVM_ARGS,
- )
-
# TODO(davido): Remove manual merge of manifest file when this feature
# request is implemented: https://github.com/bazelbuild/bazel/issues/2009
# TODO(davido): Remove manual touch command when this issue is resolved:
diff --git a/tools/bzl/plugins.bzl b/tools/bzl/plugins.bzl
index 7fd7625774..adde59efb7 100644
--- a/tools/bzl/plugins.bzl
+++ b/tools/bzl/plugins.bzl
@@ -1,11 +1,15 @@
CORE_PLUGINS = [
"codemirror-editor",
"commit-message-length-validator",
+ "delete-project",
"download-commands",
+ "gitiles",
"hooks",
+ "plugin-manager",
"replication",
"reviewnotes",
"singleusergroup",
+ "webhooks",
]
CUSTOM_PLUGINS = [
diff --git a/tools/coverage.sh b/tools/coverage.sh
index 72e1d5bbdb..11e50e62f9 100755
--- a/tools/coverage.sh
+++ b/tools/coverage.sh
@@ -22,15 +22,27 @@ echo "Running 'bazel coverage'; this may take a while"
# coverage is expensive to run; use --jobs=2 to avoid overloading the
# machine.
-bazel coverage -k --jobs=${COVERAGE_CPUS:-2} -- ... -//javatests/com/google/gerrit/common:auto_value_tests
+bazel coverage -k --jobs=${COVERAGE_CPUS:-2} -- ...
# The coverage data contains filenames relative to the Java root, and
# genhtml has no logic to search these elsewhere. Workaround this
# limitation by running genhtml in a directory with the files in the
# right place. Also -inexplicably- genhtml wants to have the source
# files relative to the output directory.
-mkdir -p ${destdir}/
-cp -a */src/{main,test}/java/* ${destdir}/
+mkdir -p ${destdir}/java
+cp -r {java,javatests}/* ${destdir}/java
+
+mkdir -p ${destdir}/plugins
+for plugin in `find plugins/ -type d` -maxdepth 1
+do
+ mkdir -p ${destdir}/${plugin}/java
+ cp -r plugins/*/{java,javatests}/* ${destdir}/${plugin}/java
+
+ # for backwards compatibility support plugins with old file structure
+ mkdir -p ${destdir}/${plugin}/src/{main,test}/java
+ cp -r plugins/*/src/main/java/* ${destdir}/${plugin}/src/main/java
+ cp -r plugins/*/src/test/java/* ${destdir}/${plugin}/src/test/java
+done
base=$(bazel info bazel-testlogs)
for f in $(find ${base} -name 'coverage.dat') ; do
diff --git a/tools/download_file.py b/tools/download_file.py
index 7b3bf95c3d..f86fd3e034 100755
--- a/tools/download_file.py
+++ b/tools/download_file.py
@@ -81,7 +81,6 @@ parser.add_argument('-u', help='URL to download')
parser.add_argument('-v', help='expected content SHA-1')
parser.add_argument('-x', action='append', help='file to delete from ZIP')
parser.add_argument('--exclude_java_sources', action='store_true')
-parser.add_argument('--unsign', action='store_true')
args = parser.parse_args()
root_dir = args.o
@@ -140,18 +139,6 @@ if args.exclude_java_sources:
print('error opening %s: %s' % (cache_ent, err), file=stderr)
exit(1)
-if args.unsign:
- try:
- with ZipFile(cache_ent, 'r') as zf:
- for n in zf.namelist():
- if (n.endswith('.RSA')
- or n.endswith('.SF')
- or n.endswith('.LIST')):
- exclude.append(n)
- except (BadZipfile, LargeZipFile) as err:
- print('error opening %s: %s' % (cache_ent, err), file=stderr)
- exit(1)
-
safe_mkdirs(path.dirname(args.o))
if exclude:
try:
diff --git a/tools/eclipse/BUILD b/tools/eclipse/BUILD
index ddb686ad25..e091fc15ac 100644
--- a/tools/eclipse/BUILD
+++ b/tools/eclipse/BUILD
@@ -9,53 +9,39 @@ load(
)
TEST_DEPS = [
- "//gerrit-gwtui:ui_tests",
"//javatests/com/google/gerrit/elasticsearch:elasticsearch_test_utils",
"//javatests/com/google/gerrit/server:server_tests",
]
+TEST_DEPS_GENERATED = [
+ "//proto/testing:test_java_proto",
+]
+
DEPS = [
- "//gerrit-gwtdebug:gwtdebug",
- "//gerrit-gwtui:ui_module",
- "//gerrit-plugin-gwtui:gwtui-api-lib",
"//java/com/google/gerrit/acceptance:lib",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/asciidoctor:asciidoc_lib",
"//java/com/google/gerrit/asciidoctor:doc_indexer_lib",
"//lib/auto:auto-value",
- "//lib/gwt:ant",
- "//lib/gwt:colt",
- "//lib/gwt:javax-validation",
- "//lib/gwt:javax-validation_src",
- "//lib/gwt:jsinterop-annotations",
- "//lib/gwt:jsinterop-annotations_src",
- "//lib/gwt:tapestry",
- "//lib/gwt:w3c-css-sac",
- "//lib/jetty:servlets",
"//lib/prolog:compiler-lib",
- "//proto:reviewdb_java_proto",
+ "//proto:entities_java_proto",
]
java_library(
name = "classpath",
testonly = True,
- runtime_deps = LIBS + PGMLIBS + DEPS,
+ runtime_deps = LIBS + PGMLIBS + DEPS + TEST_DEPS_GENERATED,
)
classpath_collector(
name = "main_classpath_collect",
testonly = True,
- deps = LIBS + PGMLIBS + DEPS + TEST_DEPS +
+ deps = LIBS + PGMLIBS + DEPS + TEST_DEPS + TEST_DEPS_GENERATED +
["//plugins/%s:%s__plugin" % (n, n) for n in CORE_PLUGINS + CUSTOM_PLUGINS] +
["//plugins/%s:%s__plugin_test_deps" % (n, n) for n in CUSTOM_PLUGINS_TEST_DEPS],
)
classpath_collector(
- name = "gwt_classpath_collect",
- deps = ["//gerrit-gwtui:ui_module"],
-)
-
-classpath_collector(
name = "autovalue_classpath_collect",
deps = ["//lib/auto:auto-value"],
)
diff --git a/tools/eclipse/gerrit_gwt_debug.launch b/tools/eclipse/gerrit_gwt_debug.launch
deleted file mode 100644
index 593837a245..0000000000
--- a/tools/eclipse/gerrit_gwt_debug.launch
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/gerrit/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritGwtDebugLauncher.java"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="1"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
-<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7&quot; javaProject=&quot;gerrit&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;gerrit&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gerrit.gwtdebug.GerritGwtDebugLauncher"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-strict -noprecompile -src ${resource_loc:/gerrit}/java -workDir ${resource_loc:/gerrit}/.gwt_work_dir com.google.gerrit.GerritGwtUI -src ${resource_loc:/gerrit}/gerrit-plugin-gwtui/src/main/java -- --console-log --show-stack-trace -d ${resource_loc:/gerrit}/../gerrit_testsite"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024M&#10;-XX:MaxPermSize=256M&#10;-Dgerrit.disable-gwtui-recompile=true"/>
-</launchConfiguration>
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index fe20331f3f..649f7da781 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -22,7 +22,6 @@ import re
import sys
MAIN = '//tools/eclipse:classpath'
-GWT = '//gerrit-gwtui:ui_module'
AUTO = '//lib/auto:auto-value'
JRE = '/'.join([
'org.eclipse.jdt.launching.JRE_CONTAINER',
@@ -32,7 +31,6 @@ JRE = '/'.join([
# Map of targets to corresponding classpath collector rules
cp_targets = {
AUTO: '//tools/eclipse:autovalue_classpath_collect',
- GWT: '//tools/eclipse:gwt_classpath_collect',
MAIN: '//tools/eclipse:main_classpath_collect',
}
@@ -188,12 +186,25 @@ def gen_classpath(ext):
e.setAttribute('output', out)
if exported:
e.setAttribute('exported', 'true')
+ atts = None
if out and "test" in out:
atts = doc.createElement('attributes')
testAtt = doc.createElement('attribute')
testAtt.setAttribute('name', 'test')
testAtt.setAttribute('value', 'true')
atts.appendChild(testAtt)
+ if "apt_generated" in path:
+ if not atts:
+ atts = doc.createElement('attributes')
+ ignoreOptionalProblems = doc.createElement('attribute')
+ ignoreOptionalProblems.setAttribute('name', 'ignore_optional_problems')
+ ignoreOptionalProblems.setAttribute('value', 'true')
+ atts.appendChild(ignoreOptionalProblems)
+ optional = doc.createElement('attribute')
+ optional.setAttribute('name', 'optional')
+ optional.setAttribute('value', 'true')
+ atts.appendChild(optional)
+ if atts:
e.appendChild(atts)
doc.documentElement.appendChild(e)
@@ -201,8 +212,6 @@ def gen_classpath(ext):
src = set()
lib = set()
proto = set()
- gwt_src = set()
- gwt_lib = set()
plugins = set()
# Classpath entries are absolute for cross-cell support
@@ -210,10 +219,6 @@ def gen_classpath(ext):
srcs = re.compile('(.*/external/[^/]+)/jar/(.*)[.]jar')
for p in _query_classpath(MAIN):
if p.endswith('-src.jar'):
- # gwt_module() depends on -src.jar for Java to JavaScript compiles.
- if p.startswith("external"):
- p = os.path.join(ext, p)
- gwt_lib.add(p)
continue
m = java_library.match(p)
@@ -241,11 +246,6 @@ def gen_classpath(ext):
p = os.path.join(ext, p)
lib.add(p)
- for p in _query_classpath(GWT):
- m = java_library.match(p)
- if m:
- gwt_src.add(m.group(1))
-
classpathentry('src', 'java')
classpathentry('src', 'javatests', out='eclipse-out/test')
classpathentry('src', 'resources')
@@ -262,7 +262,10 @@ def gen_classpath(ext):
p = os.path.join(s, 'java')
if os.path.exists(p):
- classpathentry('src', p, out=out)
+ classpathentry('src', p, out=out + '/main')
+ p = os.path.join(s, 'javatests')
+ if os.path.exists(p):
+ classpathentry('src', p, out=out + '/test')
continue
for env in ['main', 'test']:
@@ -277,7 +280,7 @@ def gen_classpath(ext):
if os.path.exists(p):
classpathentry('src', p, out=o)
- for libs in [lib, gwt_lib]:
+ for libs in [lib]:
for j in sorted(libs):
s = None
m = srcs.match(j)
@@ -290,30 +293,18 @@ def gen_classpath(ext):
if args.plugins:
classpathentry('lib', j, s, exported=True)
else:
- # Filter out the source JARs that we pull through transitive
- # closure of GWT plugin API (we add source directories
- # themselves). Exception is libEdit-src.jar, that is needed
- # for GWT SDM to work.
- m = java_library.match(j)
- if m:
- if m.group(1).startswith("gerrit-") and \
- j.endswith("-src.jar") and \
- not j.endswith("libEdit-src.jar"):
- continue
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 = s.replace('.jar', '-src.jar')
classpathentry('lib', p, s)
- for s in sorted(gwt_src):
- p = os.path.join(ROOT, s, 'src', 'main', 'java')
- if os.path.exists(p):
- classpathentry('lib', p, out='eclipse-out/gwtsrc')
-
classpathentry('con', JRE)
classpathentry('output', 'eclipse-out/classes')
+ classpathentry('src', '.apt_generated')
+ classpathentry('src', '.apt_generated_tests', out="eclipse-out/test")
p = os.path.join(ROOT, '.classpath')
with open(p, 'w') as fd:
@@ -353,14 +344,8 @@ try:
gen_factorypath(ext_location)
gen_bazel_path(ext_location)
- # TODO(davido): Remove this when GWT gone
- gwt_working_dir = ".gwt_work_dir"
- if not os.path.isdir(gwt_working_dir):
- os.makedirs(os.path.join(ROOT, gwt_working_dir))
-
try:
- subprocess.check_call(_build_bazel_cmd('build', MAIN, GWT,
- '//java/org/eclipse/jgit:libEdit-src.jar'))
+ subprocess.check_call(_build_bazel_cmd('build', MAIN))
except subprocess.CalledProcessError:
exit(1)
except KeyboardInterrupt:
diff --git a/tools/js/bower2bazel.py b/tools/js/bower2bazel.py
index ec1ead5fc6..a896dba6c0 100755
--- a/tools/js/bower2bazel.py
+++ b/tools/js/bower2bazel.py
@@ -204,11 +204,12 @@ def dump_workspace(data, seeds, out):
for d in data:
if d["name"] in seeds:
continue
- out.write(""" bower_archive(
- name = "%(name)s",
- package = "%(normalized-name)s",
- version = "%(version)s",
- sha1 = "%(bazel-sha1)s")
+ out.write(""" bower_archive(
+ name = "%(name)s",
+ package = "%(normalized-name)s",
+ version = "%(version)s",
+ sha1 = "%(bazel-sha1)s",
+ )
""" % d)
@@ -216,21 +217,21 @@ def dump_build(data, seeds, out):
out.write('load("//tools/bzl:js.bzl", "bower_component")\n\n')
out.write('def define_bower_components():\n')
for d in data:
- out.write(" bower_component(\n")
- out.write(" name = \"%s\",\n" % d["name"])
- out.write(" license = \"//lib:LICENSE-%s\",\n" % d["bazel-license"])
+ out.write(" bower_component(\n")
+ out.write(" name = \"%s\",\n" % d["name"])
+ out.write(" license = \"//lib:LICENSE-%s\",\n" % d["bazel-license"])
deps = sorted(d.get("dependencies", {}).keys())
if deps:
if len(deps) == 1:
- out.write(" deps = [ \":%s\" ],\n" % deps[0])
+ out.write(" deps = [\":%s\"],\n" % deps[0])
else:
- out.write(" deps = [\n")
+ out.write(" deps = [\n")
for dep in deps:
- out.write(" \":%s\",\n" % dep)
- out.write(" ],\n")
+ out.write(" \":%s\",\n" % dep)
+ out.write(" ],\n")
if d["name"] in seeds:
- out.write(" seed = True,\n")
- out.write(" )\n")
+ out.write(" seed = True,\n")
+ out.write(" )\n")
# done
diff --git a/tools/maven/BUILD b/tools/maven/BUILD
index 10ed27ddf2..6cbd21990c 100644
--- a/tools/maven/BUILD
+++ b/tools/maven/BUILD
@@ -10,19 +10,16 @@ maven_package(
"gerrit-acceptance-framework": "//java/com/google/gerrit/acceptance:libframework-lib-src.jar",
"gerrit-extension-api": "//java/com/google/gerrit/extensions:libapi-src.jar",
"gerrit-plugin-api": "//plugins:plugin-api-sources_deploy.jar",
- "gerrit-plugin-gwtui": "//gerrit-plugin-gwtui:gwtui-api-source_deploy.jar",
},
doc = {
"gerrit-acceptance-framework": "//java/com/google/gerrit/acceptance:framework-javadoc",
"gerrit-extension-api": "//java/com/google/gerrit/extensions:extension-api-javadoc",
"gerrit-plugin-api": "//plugins:plugin-api-javadoc",
- "gerrit-plugin-gwtui": "//gerrit-plugin-gwtui:gwtui-api-javadoc",
},
jar = {
"gerrit-acceptance-framework": "//java/com/google/gerrit/acceptance:framework_deploy.jar",
"gerrit-extension-api": "//java/com/google/gerrit/extensions:extension-api_deploy.jar",
"gerrit-plugin-api": "//plugins:plugin-api_deploy.jar",
- "gerrit-plugin-gwtui": "//gerrit-plugin-gwtui:gwtui-api_deploy.jar",
},
repository = MAVEN_REPOSITORY,
url = URL,
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 07b3560a79..0ce6d4e5b0 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>2.16.23-SNAPSHOT</version>
+ <version>3.0.14-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Acceptance Test Framework</name>
<description>Framework for Gerrit's acceptance tests</description>
@@ -29,9 +29,6 @@
<name>Ben Rohlfs</name>
</developer>
<developer>
- <name>Dave Borowitz</name>
- </developer>
- <developer>
<name>David Ostrovsky</name>
</developer>
<developer>
@@ -44,9 +41,6 @@
<name>Han-Wen Nienhuys</name>
</developer>
<developer>
- <name>Hugo Arès</name>
- </developer>
- <developer>
<name>Luca Milanesio</name>
</developer>
<developer>
@@ -56,6 +50,9 @@
<name>Martin Fick</name>
</developer>
<developer>
+ <name>Matthias Sohn</name>
+ </developer>
+ <developer>
<name>Ole Rehmsen</name>
</developer>
<developer>
@@ -64,6 +61,9 @@
<developer>
<name>Saša Živkov</name>
</developer>
+ <developer>
+ <name>Sven Selberg</name>
+ </developer>
</developers>
<mailingLists>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index c71c5d20a6..32c43696ef 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>2.16.23-SNAPSHOT</version>
+ <version>3.0.14-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Extension API</name>
<description>API for Gerrit Extensions</description>
@@ -29,9 +29,6 @@
<name>Ben Rohlfs</name>
</developer>
<developer>
- <name>Dave Borowitz</name>
- </developer>
- <developer>
<name>David Ostrovsky</name>
</developer>
<developer>
@@ -44,9 +41,6 @@
<name>Han-Wen Nienhuys</name>
</developer>
<developer>
- <name>Hugo Arès</name>
- </developer>
- <developer>
<name>Luca Milanesio</name>
</developer>
<developer>
@@ -56,6 +50,9 @@
<name>Martin Fick</name>
</developer>
<developer>
+ <name>Matthias Sohn</name>
+ </developer>
+ <developer>
<name>Ole Rehmsen</name>
</developer>
<developer>
@@ -64,6 +61,9 @@
<developer>
<name>Saša Živkov</name>
</developer>
+ <developer>
+ <name>Sven Selberg</name>
+ </developer>
</developers>
<mailingLists>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index ccdbb22c9e..0f57b921da 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>2.16.23-SNAPSHOT</version>
+ <version>3.0.14-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Plugin API</name>
<description>API for Gerrit Plugins</description>
@@ -29,9 +29,6 @@
<name>Ben Rohlfs</name>
</developer>
<developer>
- <name>Dave Borowitz</name>
- </developer>
- <developer>
<name>David Ostrovsky</name>
</developer>
<developer>
@@ -44,9 +41,6 @@
<name>Han-Wen Nienhuys</name>
</developer>
<developer>
- <name>Hugo Arès</name>
- </developer>
- <developer>
<name>Luca Milanesio</name>
</developer>
<developer>
@@ -56,6 +50,9 @@
<name>Martin Fick</name>
</developer>
<developer>
+ <name>Matthias Sohn</name>
+ </developer>
+ <developer>
<name>Ole Rehmsen</name>
</developer>
<developer>
@@ -64,6 +61,9 @@
<developer>
<name>Saša Živkov</name>
</developer>
+ <developer>
+ <name>Sven Selberg</name>
+ </developer>
</developers>
<mailingLists>
diff --git a/tools/maven/gerrit-plugin-gwtui_pom.xml b/tools/maven/gerrit-plugin-gwtui_pom.xml
deleted file mode 100644
index c89b1f5310..0000000000
--- a/tools/maven/gerrit-plugin-gwtui_pom.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<project>
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.google.gerrit</groupId>
- <artifactId>gerrit-plugin-gwtui</artifactId>
- <version>2.16.23-SNAPSHOT</version>
- <packaging>jar</packaging>
- <name>Gerrit Code Review - Plugin GWT UI</name>
- <description>Common Classes for Gerrit GWT UI Plugins</description>
- <url>https://www.gerritcodereview.com/</url>
-
- <licenses>
- <license>
- <name>The Apache Software License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
-
- <scm>
- <url>https://gerrit.googlesource.com/gerrit</url>
- <connection>https://gerrit.googlesource.com/gerrit</connection>
- </scm>
-
- <developers>
- <developer>
- <name>Alice Kober-Sotzek</name>
- </developer>
- <developer>
- <name>Ben Rohlfs</name>
- </developer>
- <developer>
- <name>Dave Borowitz</name>
- </developer>
- <developer>
- <name>David Ostrovsky</name>
- </developer>
- <developer>
- <name>David Pursehouse</name>
- </developer>
- <developer>
- <name>Edwin Kempin</name>
- </developer>
- <developer>
- <name>Han-Wen Nienhuys</name>
- </developer>
- <developer>
- <name>Hugo Arès</name>
- </developer>
- <developer>
- <name>Luca Milanesio</name>
- </developer>
- <developer>
- <name>Marco Miller</name>
- </developer>
- <developer>
- <name>Martin Fick</name>
- </developer>
- <developer>
- <name>Ole Rehmsen</name>
- </developer>
- <developer>
- <name>Patrick Hiesel</name>
- </developer>
- <developer>
- <name>Saša Živkov</name>
- </developer>
- </developers>
-
- <mailingLists>
- <mailingList>
- <name>Repo and Gerrit Discussion</name>
- <post>repo-discuss@googlegroups.com</post>
- <subscribe>https://groups.google.com/forum/#!forum/repo-discuss</subscribe>
- <unsubscribe>https://groups.google.com/forum/#!forum/repo-discuss</unsubscribe>
- <archive>https://groups.google.com/forum/#!forum/repo-discuss</archive>
- </mailingList>
- </mailingLists>
-
- <issueManagement>
- <url>https://bugs.chromium.org/p/gerrit/issues/list</url>
- <system>Gerrit Issue Tracker</system>
- </issueManagement>
-</project>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index 0c72200cb7..50619250f0 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>2.16.23-SNAPSHOT</version>
+ <version>3.0.14-SNAPSHOT</version>
<packaging>war</packaging>
<name>Gerrit Code Review - WAR</name>
<description>Gerrit WAR</description>
@@ -29,9 +29,6 @@
<name>Ben Rohlfs</name>
</developer>
<developer>
- <name>Dave Borowitz</name>
- </developer>
- <developer>
<name>David Ostrovsky</name>
</developer>
<developer>
@@ -44,9 +41,6 @@
<name>Han-Wen Nienhuys</name>
</developer>
<developer>
- <name>Hugo Arès</name>
- </developer>
- <developer>
<name>Luca Milanesio</name>
</developer>
<developer>
@@ -56,6 +50,9 @@
<name>Martin Fick</name>
</developer>
<developer>
+ <name>Matthias Sohn</name>
+ </developer>
+ <developer>
<name>Ole Rehmsen</name>
</developer>
<developer>
@@ -64,6 +61,9 @@
<developer>
<name>Saša Živkov</name>
</developer>
+ <developer>
+ <name>Sven Selberg</name>
+ </developer>
</developers>
<mailingLists>
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 468eac2507..e4faf18c21 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -94,14 +94,37 @@ def declare_nongoogle_deps():
# and httpasyncclient as necessary.
maven_jar(
name = "elasticsearch-rest-client",
- artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.7.0",
- sha1 = "5fc25eec3940bc0e9b0ffddcf50554a609e9db8e",
+ artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.8.1",
+ sha1 = "59feefe006a96a39f83b0dfb6780847e06c1d0a8",
)
maven_jar(
name = "jackson-core",
- artifact = "com.fasterxml.jackson.core:jackson-core:2.11.0",
- sha1 = "f84302e14648f9f63c0c73951054aeb2ff0b810a",
+ artifact = "com.fasterxml.jackson.core:jackson-core:2.11.3",
+ sha1 = "c2351800432bdbdd8284c3f5a7f0782a352aa84a",
+ )
+
+ # Google internal dependencies: these are developed at Google, so there is
+ # no concern about version skew.
+
+ FLOGGER_VERS = "0.4"
+
+ maven_jar(
+ name = "flogger",
+ artifact = "com.google.flogger:flogger:" + FLOGGER_VERS,
+ sha1 = "9c8863dcc913b56291c0c88e6d4ca9715b43df98",
+ )
+
+ maven_jar(
+ name = "flogger-log4j-backend",
+ artifact = "com.google.flogger:flogger-log4j-backend:" + FLOGGER_VERS,
+ sha1 = "17aa5e31daa1354187e14b6978597d630391c028",
+ )
+
+ maven_jar(
+ name = "flogger-system-backend",
+ artifact = "com.google.flogger:flogger-system-backend:" + FLOGGER_VERS,
+ sha1 = "287b569d76abcd82f9de87fe41829fbc7ebd8ac9",
)
# Test-only dependencies below.
@@ -162,18 +185,18 @@ def declare_nongoogle_deps():
sha1 = "3e83394258ae2089be7219b971ec21a8288528ad",
)
- TESTCONTAINERS_VERSION = "1.14.2"
+ TESTCONTAINERS_VERSION = "1.14.3"
maven_jar(
name = "testcontainers",
artifact = "org.testcontainers:testcontainers:" + TESTCONTAINERS_VERSION,
- sha1 = "d74bc045fb5f30988b0adff20244412972a9f564",
+ sha1 = "071fc82ba663f469447a19434e7db90f3a872753",
)
maven_jar(
name = "testcontainers-elasticsearch",
artifact = "org.testcontainers:elasticsearch:" + TESTCONTAINERS_VERSION,
- sha1 = "66e1a6da0362beee83673b877c9c2e0536d6912c",
+ sha1 = "3709e2ebb0b6aa4e2ba2b6ca92ffdd3bf637a86c",
)
maven_jar(
diff --git a/tools/version.py b/tools/version.py
index 7ea54827b9..2326757a05 100755
--- a/tools/version.py
+++ b/tools/version.py
@@ -41,8 +41,7 @@ def replace_in_file(filename, src_pattern):
src_pattern = re.compile(r'^(\s*<version>)([-.\w]+)(</version>\s*)$',
re.MULTILINE)
for project in ['gerrit-acceptance-framework', 'gerrit-extension-api',
- 'gerrit-plugin-api', 'gerrit-plugin-gwtui',
- 'gerrit-war']:
+ 'gerrit-plugin-api', 'gerrit-war']:
pom = os.path.join('tools', 'maven', '%s_pom.xml' % project)
replace_in_file(pom, src_pattern)
diff --git a/version.bzl b/version.bzl
index 746238cba4..73fc14edc4 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 = "2.16.23-SNAPSHOT"
+GERRIT_VERSION = "3.0.14-SNAPSHOT"
diff --git a/webapp/WEB-INF/extra/jetty7/gerrit.xml b/webapp/WEB-INF/extra/jetty7/gerrit.xml
index cb0a2563e2..4102f56ac5 100644
--- a/webapp/WEB-INF/extra/jetty7/gerrit.xml
+++ b/webapp/WEB-INF/extra/jetty7/gerrit.xml
@@ -6,11 +6,7 @@
so it answers to simple URLs like "/$changeid" and "/mine".
* Copy this file to $JETTY_HOME/contexts/gerrit.xml
- * Edit url, username, password as necessary below for database.
- * Copy commons-dbcp-*.jar to $JETTY_HOME/lib/ext/
- * Copy commons-pool-*.jar to $JETTY_HOME/lib/ext/
- * Copy JDBC driver to $JETTY_HOME/lib/ext/
* Copy www/gerrit-*.war to $JETTY_HOME/webapps/gerrit.war
* Make sure you remove $JETTY_HOME/context/test.xml
@@ -33,36 +29,4 @@
<Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
</Array>
</Set>
-
- <New id="ReviewDb" class="org.eclipse.jetty.plus.jndi.Resource">
- <Arg></Arg>
- <Arg>jdbc/ReviewDb</Arg>
- <Arg>
- <New class="org.apache.commons.dbcp.BasicDataSource">
-<!-- PostgreSQL
- <Set name="driverClassName">org.postgresql.Driver</Set>
- <Set name="url">jdbc:postgresql:reviewdb</Set>
- <Set name="username">gerrit</Set>
- <Set name="password">secretkey</Set>
--->
-<!-- MySQL
- <Set name="driverClassName">com.mysql.jdbc.Driver</Set>
- <Set name="url">jdbc:mysql://localhost/reviewdb?user=gerrit&amp;password=secretkey</Set>
--->
-<!-- MariaDB
- <Set name="driverClassName">org.mariadb.jdbc.Driver</Set>
- <Set name="url">jdbc:mariadb://localhost/reviewdb?user=gerrit&amp;password=secretkey</Set>
--->
-<!-- H2
- <Set name="driverClassName">org.h2.Driver</Set>
- <Set name="url">jdbc:h2:file:ReviewDb</Set>
--->
- <Set name="initialSize">4</Set>
- <Set name="maxActive">8</Set>
- <Set name="minIdle">4</Set>
- <Set name="maxIdle">4</Set>
- <Set name="maxWait">30000</Set>
- </New>
- </Arg>
- </New>
</Configure>
diff --git a/webapp/WEB-INF/web.xml b/webapp/WEB-INF/web.xml
index e901357f62..540fe33824 100644
--- a/webapp/WEB-INF/web.xml
+++ b/webapp/WEB-INF/web.xml
@@ -1,11 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
- <resource-ref>
- <res-ref-name>jdbc/ReviewDb</res-ref-name>
- <res-type>javax.sql.DataSource</res-type>
- <res-auth>Container</res-auth>
- </resource-ref>
-
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.gerrit.httpd.init.WebAppInitializer</filter-class>